diff options
631 files changed, 24144 insertions, 14112 deletions
diff --git a/.clang-format b/.clang-format index 0b19cae838..ee8a8a8c92 100644 --- a/.clang-format +++ b/.clang-format @@ -1,17 +1,18 @@ --- +# clang-format version 10.0.0+ Language: Cpp # BasedOnStyle: Microsoft AccessModifierOffset: -2 AlignAfterOpenBracket: Align -AlignConsecutiveMacros: false -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlines: Right +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllConstructorInitializersOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Always AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All @@ -29,21 +30,21 @@ BreakBeforeBraces: Allman BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeColon +BreakConstructorInitializersBeforeComma: true +BreakConstructorInitializers: AfterColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true -ColumnLimit: 120 +ColumnLimit: 140 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false DisableFormat: false -ExperimentalAutoDetectBinPacking: false +ExperimentalAutoDetectBinPacking: true FixNamespaceComments: true ForEachMacros: - foreach @@ -62,18 +63,18 @@ IncludeCategories: SortPriority: 0 IncludeIsMainRegex: '(Test)?$' IncludeIsMainSourceRegex: '' -IndentCaseLabels: false +IndentCaseLabels: true IndentGotoLabels: true IndentPPDirectives: None IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true +KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None +NamespaceIndentation: Inner ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false @@ -94,16 +95,16 @@ SpaceAfterCStyleCast: true SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: false +SpaceBeforeCpp11BracedList: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyBlock: false SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 +SpacesBeforeTrailingComments: 2 SpacesInAngles: false -SpacesInContainerLiterals: true +SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false diff --git a/autobuild.xml b/autobuild.xml index 51515b3696..2470b2e5aa 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -76,9 +76,9 @@ <key>archive</key> <map> <key>hash</key> - <string>d670d00aa732b97d105d287b62582762</string> + <string>9b8bcc3be6dbe40a04c9c81c313f70dc</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55065/512118/apr_suite-1.4.5.539073-darwin64-539073.tar.bz2</string> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/68333/658209/apr_suite-1.4.5.548882-darwin64-548882.tar.bz2</string> </map> <key>name</key> <string>darwin64</string> @@ -112,9 +112,9 @@ <key>archive</key> <map> <key>hash</key> - <string>83b4a047db5f7ee462753d91e6277cba</string> + <string>6bdf460c18ee004b41a46afc80041a92</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55143/512317/apr_suite-1.4.5.539073-windows-539073.tar.bz2</string> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/68334/658225/apr_suite-1.4.5.548882-windows-548882.tar.bz2</string> </map> <key>name</key> <string>windows</string> @@ -124,16 +124,16 @@ <key>archive</key> <map> <key>hash</key> - <string>b3bbf168b39e25c08cc1febddeb33332</string> + <string>83104bfa4dabb77cd70d185e38a95b49</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55139/512304/apr_suite-1.4.5.539073-windows64-539073.tar.bz2</string> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/68332/658215/apr_suite-1.4.5.548882-windows64-548882.tar.bz2</string> </map> <key>name</key> <string>windows64</string> </map> </map> <key>version</key> - <string>1.4.5.539073</string> + <string>1.4.5.548882</string> </map> <key>boost</key> <map> @@ -244,9 +244,9 @@ <key>archive</key> <map> <key>hash</key> - <string>471b0b350955152fd87518575057dfc4</string> + <string>ae90d19cdcddf539f6d0b41cab12f918</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60326/566593/bugsplat-1.0.7.542667-darwin64-542667.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72773/702861/bugsplat-1.0.7.552580-darwin64-552580.tar.bz2</string> </map> <key>name</key> <string>darwin64</string> @@ -256,9 +256,9 @@ <key>archive</key> <map> <key>hash</key> - <string>70e8bf46145c4cbae6f93e8b70ba5499</string> + <string>f5936eceb6a33ff0f1cc31996a40f29c</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60320/566541/bugsplat-3.6.0.4.542667-windows-542667.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72774/702905/bugsplat-3.6.0.8.552580-windows-552580.tar.bz2</string> </map> <key>name</key> <string>windows</string> @@ -268,16 +268,16 @@ <key>archive</key> <map> <key>hash</key> - <string>a73696e859fad3f19f835740815a2bd3</string> + <string>9cd940754e53e0670030b3da5ba8f373</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60321/566542/bugsplat-3.6.0.4.542667-windows64-542667.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72775/702906/bugsplat-3.6.0.8.552580-windows64-552580.tar.bz2</string> </map> <key>name</key> <string>windows64</string> </map> </map> <key>version</key> - <string>1.0.7.542667</string> + <string>3.6.0.8.552580</string> </map> <key>colladadom</key> <map> @@ -580,9 +580,9 @@ <key>archive</key> <map> <key>hash</key> - <string>e145f8ea99a21712434e0e868d1885dc</string> + <string>856ba0e5b7be4bf683cf2849bce845e0</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/62333/588183/dullahan-1.7.0.202006240858_81.3.10_gb223419_chromium-81.0.4044.138-darwin64-544091.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72458/699860/dullahan-1.7.0.202011160759_81.3.10_gb223419_chromium-81.0.4044.138-darwin64-552313.tar.bz2</string> </map> <key>name</key> <string>darwin64</string> @@ -592,9 +592,9 @@ <key>archive</key> <map> <key>hash</key> - <string>fdbbbfc377e28cba664f2b1c54ea6086</string> + <string>515950c911a53ff910b18c7c417ea984</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/62331/588162/dullahan-1.7.0.202006241556_81.3.10_gb223419_chromium-81.0.4044.138-windows-544091.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72460/699870/dullahan-1.7.0.202011161603_81.3.10_gb223419_chromium-81.0.4044.138-windows-552313.tar.bz2</string> </map> <key>name</key> <string>windows</string> @@ -604,16 +604,16 @@ <key>archive</key> <map> <key>hash</key> - <string>d85a32d905b199534e8feafa34b28e39</string> + <string>f1dccbdfe0603f488eeee4c8f518c959</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/62332/588168/dullahan-1.7.0.202006241556_81.3.10_gb223419_chromium-81.0.4044.138-windows64-544091.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72459/699874/dullahan-1.7.0.202011161603_81.3.10_gb223419_chromium-81.0.4044.138-windows64-552313.tar.bz2</string> </map> <key>name</key> <string>windows64</string> </map> </map> <key>version</key> - <string>1.7.0.202006240858_81.3.10_gb223419_chromium-81.0.4044.138</string> + <string>1.7.0.202011161603_81.3.10_gb223419_chromium-81.0.4044.138</string> </map> <key>elfio</key> <map> @@ -748,9 +748,9 @@ <key>archive</key> <map> <key>hash</key> - <string>dc4c9122de8bf77f34cfc8227d10a272</string> + <string>89c37441a806ed80c0102d380eec6fd0</string> <key>url</key> - <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/59038/554626/fmodstudio-2.00.07.541681-darwin64-541681.tar.bz2</string> + <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/65400/612632/fmodstudio-2.00.11.546392-darwin64-546392.tar.bz2</string> </map> <key>name</key> <string>darwin64</string> @@ -760,9 +760,9 @@ <key>archive</key> <map> <key>hash</key> - <string>c491bdc1690f3d920c66be509ccc6ef2</string> + <string>5283050c22d31877cd9e0afbe6feb9fc</string> <key>url</key> - <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/59039/554632/fmodstudio-2.00.07.541681-linux-541681.tar.bz2</string> + <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/65398/612630/fmodstudio-2.00.11.546392-linux-546392.tar.bz2</string> </map> <key>name</key> <string>linux</string> @@ -772,9 +772,9 @@ <key>archive</key> <map> <key>hash</key> - <string>ae75cdb1cc9da824c9e270bf97bfdd6c</string> + <string>5a3c78f4a77ae6477986e33836725e8b</string> <key>url</key> - <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/54589/506866/fmodstudio-2.00.07.538806-linux64-538806.tar.bz2</string> + <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/65399/612631/fmodstudio-2.00.11.546392-linux64-546392.tar.bz2</string> </map> <key>name</key> <string>linux64</string> @@ -784,9 +784,9 @@ <key>archive</key> <map> <key>hash</key> - <string>7f0294b038eab2d89ecc73bbf08b6d94</string> + <string>0f44323b0d03b7d0d8a17eec83e103ce</string> <key>url</key> - <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/59042/554668/fmodstudio-2.00.07.541681-windows-541681.tar.bz2</string> + <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/65401/612647/fmodstudio-2.00.11.546392-windows-546392.tar.bz2</string> </map> <key>name</key> <string>windows</string> @@ -796,16 +796,16 @@ <key>archive</key> <map> <key>hash</key> - <string>da2e8e2b809d8fe635ee437baa2a7386</string> + <string>462d28eacf731a5d36ab031e7071c32a</string> <key>url</key> - <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/59041/554656/fmodstudio-2.00.07.541681-windows64-541681.tar.bz2</string> + <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/65402/612648/fmodstudio-2.00.11.546392-windows64-546392.tar.bz2</string> </map> <key>name</key> <string>windows64</string> </map> </map> <key>version</key> - <string>2.00.07.541681</string> + <string>2.00.11.546392</string> </map> <key>fontconfig</key> <map> @@ -2187,16 +2187,18 @@ <key>archive</key> <map> <key>hash</key> - <string>8501cbaa7e0f254614694da784a9c61c</string> + <string>40a87f5d505a141b2ec79513a6197c35</string> + <key>hash_algorithm</key> + <string>md5</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/64944/606925/llca-202008010216.546021-common-546021.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76516/728250/llca-202102021657.555615-common-555615.tar.bz2</string> </map> <key>name</key> <string>common</string> </map> </map> <key>version</key> - <string>202008010216.546021</string> + <string>202102021657.555615</string> </map> <key>llphysicsextensions_source</key> <map> @@ -3126,9 +3128,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>archive</key> <map> <key>hash</key> - <string>d463360491b6b5cb7a57cd67a90ececb</string> + <string>60f008c5fd31641ad4e61ac751ce15d1</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54838/510050/uriparser-0.8.0.1-darwin64-538968.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/75748/723495/uriparser-0.9.4-darwin64-555117.tar.bz2</string> </map> <key>name</key> <string>darwin64</string> @@ -3162,9 +3164,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>archive</key> <map> <key>hash</key> - <string>57a88be57694de6cf9f516125af2c4c9</string> + <string>00aff37a6f5e1fe08456702d28706cf6</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54963/511746/uriparser-0.8.0.1-windows-538968.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/75751/723507/uriparser-0.9.4-windows-555117.tar.bz2</string> </map> <key>name</key> <string>windows</string> @@ -3174,16 +3176,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>archive</key> <map> <key>hash</key> - <string>f39cc91f2a5dad13790ec18269844ae4</string> + <string>ff27a91f3941c7bef5e1613a064cb048</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54962/511739/uriparser-0.8.0.1-windows64-538968.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/75750/723506/uriparser-0.9.4-windows64-555117.tar.bz2</string> </map> <key>name</key> <string>windows64</string> </map> </map> <key>version</key> - <string>0.8.0.1</string> + <string>0.9.4</string> </map> <key>viewer-manager</key> <map> @@ -3204,9 +3206,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>archive</key> <map> <key>hash</key> - <string>c5ab9d9d7482e48cd76f4bf391900a8c</string> + <string>6989053898b8e81e904e75553e378820</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/43369/385585/viewer_manager-2.0.531000-darwin64-531000.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/77523/735051/viewer_manager-2.0.556340-darwin64-556340.tar.bz2</string> </map> <key>name</key> <string>darwin64</string> @@ -3228,9 +3230,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>archive</key> <map> <key>hash</key> - <string>6b10d7407686d9e12e63576256581e3e</string> + <string>3446c1e54bb32542677caad0ec0d42ac</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/43370/385592/viewer_manager-2.0.531000-windows-531000.tar.bz2</string> + <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/77525/735058/viewer_manager-2.0.556340-windows-556340.tar.bz2</string> </map> <key>name</key> <string>windows</string> @@ -3241,7 +3243,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>source_type</key> <string>hg</string> <key>version</key> - <string>2.0.531000</string> + <string>2.0.556340</string> </map> <key>vlc-bin</key> <map> @@ -3495,7 +3497,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <key>canonical_repo</key> <string>https://bitbucket.org/lindenlab/viewer</string> <key>copyright</key> - <string>Copyright (c) 2014, Linden Research, Inc.</string> + <string>Copyright (c) 2020, Linden Research, Inc.</string> <key>description</key> <string>Second Life Viewer</string> <key>license</key> @@ -3906,9 +3908,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <array> <string>-G</string> <string>${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN}</string> - <string>-DUNATTENDED:BOOL=ON</string> <string>-DINSTALL_PROPRIETARY=FALSE</string> <string>-DUSE_KDU=FALSE</string> + <string>-DOPENAL:BOOL=ON</string> </array> </map> <key>name</key> @@ -3979,6 +3981,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string> <string>-DUNATTENDED:BOOL=ON</string> <string>-DINSTALL_PROPRIETARY=FALSE</string> <string>-DUSE_KDU=FALSE</string> + <string>-DOPENAL:BOOL=ON</string> </array> </map> <key>name</key> @@ -490,10 +490,12 @@ then fi # Run upload extensions + # Ex: bugsplat if [ -d ${build_dir}/packages/upload-extensions ]; then for extension in ${build_dir}/packages/upload-extensions/*.sh; do begin_section "Upload Extension $extension" . $extension + [ $? -eq 0 ] || fatal "Upload of extension $extension failed" wait_for_codeticket end_section "Upload Extension $extension" done diff --git a/doc/contributions.txt b/doc/contributions.txt index 4d8165faf2..9340d69b97 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -223,6 +223,10 @@ Ansariel Hiller MAINT-8723 SL-10385 SL-10891 + SL-13364 + SL-13858 + SL-13697 + SL-3136 Aralara Rajal Arare Chantilly CHUIBUG-191 @@ -258,6 +262,11 @@ Beansy Twine Benja Kepler VWR-746 Benjamin Bigdipper +Beq Janus + BUG-227094 + SL-10288 + SL-13583 + SL-14766 Beth Walcher Bezilon Kasei Biancaluce Robbiani @@ -829,6 +838,9 @@ Khyota Wulluf Kimar Coba Kithrak Kirkorian Kitty Barnett + BUG-228664 + BUG-228665 + BUG-228719 VWR-19699 STORM-288 STORM-799 @@ -1301,6 +1313,7 @@ Shyotl Kuhr MAINT-2334 MAINT-6913 STORM-2143 + SL-11625 Siana Gearz STORM-960 STORM-1088 @@ -1340,6 +1353,7 @@ Sovereign Engineer MAINT-7343 SL-11079 OPEN-343 + SL-11625 SpacedOut Frye VWR-34 VWR-45 @@ -1445,6 +1459,8 @@ Thickbrick Sleaford STORM-956 STORM-1147 STORM-1325 +Thoys Pan + SL-12396 Thraxis Epsilon SVC-371 VWR-383 diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 865c057e33..8aea50e02b 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -63,7 +63,13 @@ if (WINDOWS) # Without PreferredToolArchitecture=x64, as of 2020-06-26 the 32-bit # compiler on our TeamCity build hosts has started running out of virtual # memory for the precompiled header file. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /p:PreferredToolArchitecture=x64") + # CP changed to only append the flag for 32bit builds - on 64bit builds, + # locally at least, the build output is spammed with 1000s of 'D9002' + # warnings about this switch being ignored. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + if( ADDRESS_SIZE EQUAL 32 ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /p:PreferredToolArchitecture=x64") + endif() set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Zo" diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index ec27267d9d..46ddb9d15b 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -61,6 +61,7 @@ if(WINDOWS) nghttp2.dll glod.dll libhunspell.dll + uriparser.dll ) # Filenames are different for 32/64 bit BugSplat file and we don't @@ -82,6 +83,10 @@ if(WINDOWS) set(release_files ${release_files} fmod.dll) endif (FMODSTUDIO) + if (OPENAL) + list(APPEND release_files openal32.dll alut.dll) + endif (OPENAL) + #******************************* # Copy MS C runtime dlls, required for packaging. if (MSVC80) @@ -161,6 +166,9 @@ elseif(DARWIN) libnghttp2.dylib libnghttp2.14.dylib libnghttp2.14.19.0.dylib + liburiparser.dylib + liburiparser.1.dylib + liburiparser.1.0.27.dylib ) if (FMODSTUDIO) diff --git a/indra/cmake/URIPARSER.cmake b/indra/cmake/URIPARSER.cmake index de146885a0..ecc5b74ef1 100644 --- a/indra/cmake/URIPARSER.cmake +++ b/indra/cmake/URIPARSER.cmake @@ -29,7 +29,7 @@ else (USESYSTEMLIBS) set(URIPARSER_PRELOAD_ARCHIVES -Wl,--whole-archive uriparser -Wl,--no-whole-archive) set(URIPARSER_LIBRARIES uriparser) elseif (DARWIN) - set(URIPARSER_LIBRARIES uriparser) + set(URIPARSER_LIBRARIES liburiparser.dylib) endif (WINDOWS) set(URIPARSER_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/uriparser) endif (USESYSTEMLIBS) diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt index e69de29bb2..48082f72f0 100644 --- a/indra/edit-me-to-trigger-new-build.txt +++ b/indra/edit-me-to-trigger-new-build.txt @@ -0,0 +1 @@ +12 diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp index f7fbb6fda1..90dfa04f28 100644 --- a/indra/llappearance/llavatarappearance.cpp +++ b/indra/llappearance/llavatarappearance.cpp @@ -171,10 +171,9 @@ LLAvatarAppearance::LLAvatarXmlInfo::~LLAvatarXmlInfo() //----------------------------------------------------------------------------- // Static Data //----------------------------------------------------------------------------- -LLXmlTree LLAvatarAppearance::sXMLTree; -LLXmlTree LLAvatarAppearance::sSkeletonXMLTree; LLAvatarSkeletonInfo* LLAvatarAppearance::sAvatarSkeletonInfo = NULL; LLAvatarAppearance::LLAvatarXmlInfo* LLAvatarAppearance::sAvatarXmlInfo = NULL; +LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary* LLAvatarAppearance::sAvatarDictionary = NULL; LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) : @@ -202,7 +201,7 @@ LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) : mBakedTextureDatas[i].mIsLoaded = false; mBakedTextureDatas[i].mIsUsed = false; mBakedTextureDatas[i].mMaskTexName = 0; - mBakedTextureDatas[i].mTextureIndex = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::bakedToLocalTextureIndex((LLAvatarAppearanceDefines::EBakedTextureIndex)i); + mBakedTextureDatas[i].mTextureIndex = sAvatarDictionary->bakedToLocalTextureIndex((LLAvatarAppearanceDefines::EBakedTextureIndex)i); } } @@ -215,8 +214,8 @@ void LLAvatarAppearance::initInstance() mRoot = createAvatarJoint(); mRoot->setName( "mRoot" ); - for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().end(); + for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator iter = sAvatarDictionary->getMeshEntries().begin(); + iter != sAvatarDictionary->getMeshEntries().end(); ++iter) { const EMeshIndex mesh_index = iter->first; @@ -261,8 +260,8 @@ void LLAvatarAppearance::initInstance() //------------------------------------------------------------------------- // associate baked textures with meshes //------------------------------------------------------------------------- - for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().end(); + for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator iter = sAvatarDictionary->getMeshEntries().begin(); + iter != sAvatarDictionary->getMeshEntries().end(); ++iter) { const EMeshIndex mesh_index = iter->first; @@ -336,6 +335,12 @@ void LLAvatarAppearance::initClass() //static void LLAvatarAppearance::initClass(const std::string& avatar_file_name_arg, const std::string& skeleton_file_name_arg) { + // init dictionary (don't repeat on second login attempt) + if (!sAvatarDictionary) + { + sAvatarDictionary = new LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary(); + } + std::string avatar_file_name; if (!avatar_file_name_arg.empty()) @@ -346,14 +351,15 @@ void LLAvatarAppearance::initClass(const std::string& avatar_file_name_arg, cons { avatar_file_name = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,AVATAR_DEFAULT_CHAR + "_lad.xml"); } - BOOL success = sXMLTree.parseFile( avatar_file_name, FALSE ); + LLXmlTree xml_tree; + BOOL success = xml_tree.parseFile( avatar_file_name, FALSE ); if (!success) { LL_ERRS() << "Problem reading avatar configuration file:" << avatar_file_name << LL_ENDL; } // now sanity check xml file - LLXmlTreeNode* root = sXMLTree.getRoot(); + LLXmlTreeNode* root = xml_tree.getRoot(); if (!root) { LL_ERRS() << "No root node found in avatar configuration file: " << avatar_file_name << LL_ENDL; @@ -400,8 +406,9 @@ void LLAvatarAppearance::initClass(const std::string& avatar_file_name_arg, cons } std::string skeleton_path; + LLXmlTree skeleton_xml_tree; skeleton_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,skeleton_file_name); - if (!parseSkeletonFile(skeleton_path)) + if (!parseSkeletonFile(skeleton_path, skeleton_xml_tree)) { LL_ERRS() << "Error parsing skeleton file: " << skeleton_path << LL_ENDL; } @@ -414,7 +421,7 @@ void LLAvatarAppearance::initClass(const std::string& avatar_file_name_arg, cons delete sAvatarSkeletonInfo; } sAvatarSkeletonInfo = new LLAvatarSkeletonInfo; - if (!sAvatarSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot())) + if (!sAvatarSkeletonInfo->parseXml(skeleton_xml_tree.getRoot())) { LL_ERRS() << "Error parsing skeleton XML file: " << skeleton_path << LL_ENDL; } @@ -453,9 +460,8 @@ void LLAvatarAppearance::initClass(const std::string& avatar_file_name_arg, cons void LLAvatarAppearance::cleanupClass() { delete_and_clear(sAvatarXmlInfo); - // *TODO: What about sAvatarSkeletonInfo ??? - sSkeletonXMLTree.cleanup(); - sXMLTree.cleanup(); + delete_and_clear(sAvatarDictionary); + delete_and_clear(sAvatarSkeletonInfo); } using namespace LLAvatarAppearanceDefines; @@ -577,12 +583,12 @@ void LLAvatarAppearance::computeBodySize() //----------------------------------------------------------------------------- // parseSkeletonFile() //----------------------------------------------------------------------------- -BOOL LLAvatarAppearance::parseSkeletonFile(const std::string& filename) +BOOL LLAvatarAppearance::parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree) { //------------------------------------------------------------------------- // parse the file //------------------------------------------------------------------------- - BOOL parsesuccess = sSkeletonXMLTree.parseFile( filename, FALSE ); + BOOL parsesuccess = skeleton_xml_tree.parseFile( filename, FALSE ); if (!parsesuccess) { @@ -591,7 +597,7 @@ BOOL LLAvatarAppearance::parseSkeletonFile(const std::string& filename) } // now sanity check xml file - LLXmlTreeNode* root = sSkeletonXMLTree.getRoot(); + LLXmlTreeNode* root = skeleton_xml_tree.getRoot(); if (!root) { LL_ERRS() << "No root node found in avatar skeleton file: " << filename << LL_ENDL; @@ -999,7 +1005,7 @@ BOOL LLAvatarAppearance::loadAvatar() { LLAvatarXmlInfo::LLAvatarMorphInfo *info = *iter; - EBakedTextureIndex baked = LLAvatarAppearanceDictionary::findBakedByRegionName(info->mRegion); + EBakedTextureIndex baked = sAvatarDictionary->findBakedByRegionName(info->mRegion); if (baked != BAKED_NUM_INDICES) { LLVisualParam* morph_param; @@ -1135,8 +1141,8 @@ BOOL LLAvatarAppearance::loadMeshNodes() switch(lod) case 0: mesh = &mHairMesh0; */ - for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator mesh_iter = LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().begin(); - mesh_iter != LLAvatarAppearanceDictionary::getInstance()->getMeshEntries().end(); + for (LLAvatarAppearanceDictionary::MeshEntries::const_iterator mesh_iter = sAvatarDictionary->getMeshEntries().begin(); + mesh_iter != sAvatarDictionary->getMeshEntries().end(); ++mesh_iter) { const EMeshIndex mesh_index = mesh_iter->first; @@ -1264,8 +1270,8 @@ BOOL LLAvatarAppearance::loadLayersets() // scan baked textures and associate the layerset with the appropriate one EBakedTextureIndex baked_index = BAKED_NUM_INDICES; - for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); + baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = baked_iter->second; diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h index 6a4dbf3726..b1c70f9064 100644 --- a/indra/llappearance/llavatarappearance.h +++ b/indra/llappearance/llavatarappearance.h @@ -156,7 +156,7 @@ public: protected: - static BOOL parseSkeletonFile(const std::string& filename); + static BOOL parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree); virtual void buildCharacter(); virtual BOOL loadAvatar(); @@ -211,9 +211,6 @@ public: // XML parse tree //-------------------------------------------------------------------- protected: - static LLXmlTree sXMLTree; // avatar config file - static LLXmlTree sSkeletonXMLTree; // avatar skeleton file - static LLAvatarSkeletonInfo* sAvatarSkeletonInfo; static LLAvatarXmlInfo* sAvatarXmlInfo; @@ -255,6 +252,7 @@ public: public: virtual void updateMeshTextures() = 0; virtual void dirtyMesh() = 0; // Dirty the avatar mesh + static const LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary *getDictionary() { return sAvatarDictionary; } protected: virtual void dirtyMesh(S32 priority) = 0; // Dirty the avatar mesh, with priority @@ -263,6 +261,9 @@ protected: polymesh_map_t mPolyMeshes; avatar_joint_list_t mMeshLOD; + // mesh entries and backed textures + static LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary* sAvatarDictionary; + /** Meshes ** ** *******************************************************************************/ diff --git a/indra/llappearance/llavatarappearancedefines.cpp b/indra/llappearance/llavatarappearancedefines.cpp index c72943bb82..2bcfd06c40 100644 --- a/indra/llappearance/llavatarappearancedefines.cpp +++ b/indra/llappearance/llavatarappearancedefines.cpp @@ -30,7 +30,6 @@ const S32 LLAvatarAppearanceDefines::SCRATCH_TEX_WIDTH = 1024; const S32 LLAvatarAppearanceDefines::SCRATCH_TEX_HEIGHT = 1024; -const S32 LLAvatarAppearanceDefines::IMPOSTOR_PERIOD = 2; using namespace LLAvatarAppearanceDefines; @@ -258,19 +257,17 @@ LLAvatarAppearanceDictionary::BakedEntry::BakedEntry(ETextureIndex tex_index, } } -// static -ETextureIndex LLAvatarAppearanceDictionary::bakedToLocalTextureIndex(EBakedTextureIndex index) +ETextureIndex LLAvatarAppearanceDictionary::bakedToLocalTextureIndex(EBakedTextureIndex index) const { - return LLAvatarAppearanceDictionary::getInstance()->getBakedTexture(index)->mTextureIndex; + return getBakedTexture(index)->mTextureIndex; } -// static EBakedTextureIndex LLAvatarAppearanceDictionary::findBakedByRegionName(std::string name) { U8 index = 0; while (index < BAKED_NUM_INDICES) { - const BakedEntry *be = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex) index); + const BakedEntry *be = getBakedTexture((EBakedTextureIndex) index); if (be && be->mName.compare(name) == 0) { // baked texture found @@ -282,16 +279,15 @@ EBakedTextureIndex LLAvatarAppearanceDictionary::findBakedByRegionName(std::stri return BAKED_NUM_INDICES; } -// static EBakedTextureIndex LLAvatarAppearanceDictionary::findBakedByImageName(std::string name) { U8 index = 0; while (index < BAKED_NUM_INDICES) { - const BakedEntry *be = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex) index); + const BakedEntry *be = getBakedTexture((EBakedTextureIndex) index); if (be) { - const TextureEntry *te = LLAvatarAppearanceDictionary::getInstance()->getTexture(be->mTextureIndex); + const TextureEntry *te = getTexture(be->mTextureIndex); if (te && te->mDefaultImageName.compare(name) == 0) { // baked texture found @@ -304,10 +300,9 @@ EBakedTextureIndex LLAvatarAppearanceDictionary::findBakedByImageName(std::strin return BAKED_NUM_INDICES; } -// static -LLWearableType::EType LLAvatarAppearanceDictionary::getTEWearableType(ETextureIndex index ) +LLWearableType::EType LLAvatarAppearanceDictionary::getTEWearableType(ETextureIndex index ) const { - return getInstance()->getTexture(index)->mWearableType; + return getTexture(index)->mWearableType; } // static diff --git a/indra/llappearance/llavatarappearancedefines.h b/indra/llappearance/llavatarappearancedefines.h index 5663d24293..49dfbebeea 100644 --- a/indra/llappearance/llavatarappearancedefines.h +++ b/indra/llappearance/llavatarappearancedefines.h @@ -39,7 +39,6 @@ namespace LLAvatarAppearanceDefines extern const S32 SCRATCH_TEX_WIDTH; extern const S32 SCRATCH_TEX_HEIGHT; -extern const S32 IMPOSTOR_PERIOD; static const U32 AVATAR_HOVER = 11001; @@ -143,13 +142,14 @@ typedef std::vector<LLWearableType::EType> wearables_vec_t; // // This holds const data - it is initialized once and the contents never change after that. //------------------------------------------------------------------------ -class LLAvatarAppearanceDictionary : public LLSingleton<LLAvatarAppearanceDictionary> +class LLAvatarAppearanceDictionary { //-------------------------------------------------------------------- // Constructors and Destructors //-------------------------------------------------------------------- - LLSINGLETON(LLAvatarAppearanceDictionary); - virtual ~LLAvatarAppearanceDictionary(); +public: + LLAvatarAppearanceDictionary(); + ~LLAvatarAppearanceDictionary(); private: void createAssociations(); @@ -235,14 +235,14 @@ public: //-------------------------------------------------------------------- public: // Convert from baked texture to associated texture; e.g. BAKED_HEAD -> TEX_HEAD_BAKED - static ETextureIndex bakedToLocalTextureIndex(EBakedTextureIndex t); + ETextureIndex bakedToLocalTextureIndex(EBakedTextureIndex t) const; // find a baked texture index based on its name - static EBakedTextureIndex findBakedByRegionName(std::string name); - static EBakedTextureIndex findBakedByImageName(std::string name); + EBakedTextureIndex findBakedByRegionName(std::string name); + EBakedTextureIndex findBakedByImageName(std::string name); // Given a texture entry, determine which wearable type owns it. - static LLWearableType::EType getTEWearableType(ETextureIndex index); + LLWearableType::EType getTEWearableType(ETextureIndex index) const; static BOOL isBakedImageId(const LLUUID& id); static EBakedTextureIndex assetIdToBakedTextureIndex(const LLUUID& id); diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index c90b11ae71..e5039141de 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -728,8 +728,8 @@ BOOL LLTexLayerInfo::parseXml(LLXmlTreeNode* node) /* if ("upper_shirt" == local_texture_name) mLocalTexture = TEX_UPPER_SHIRT; */ mLocalTexture = TEX_NUM_INDICES; - for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearance::getDictionary()->getTextures().begin(); + iter != LLAvatarAppearance::getDictionary()->getTextures().end(); iter++) { const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second; @@ -979,7 +979,7 @@ LLWearableType::EType LLTexLayerInterface::getWearableType() const return type; } - return LLAvatarAppearanceDictionary::getTEWearableType(te); + return LLAvatarAppearance::getDictionary()->getTEWearableType(te); } LLTexLayerInterface::ERenderPass LLTexLayerInterface::getRenderPass() const diff --git a/indra/llappearance/llwearable.cpp b/indra/llappearance/llwearable.cpp index 6079913a8e..28a36e6e41 100644 --- a/indra/llappearance/llwearable.cpp +++ b/indra/llappearance/llwearable.cpp @@ -183,7 +183,7 @@ void LLWearable::createVisualParams(LLAvatarAppearance *avatarp) void LLWearable::createLayers(S32 te, LLAvatarAppearance *avatarp) { LLTexLayerSet *layer_set = NULL; - const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearanceDictionary::getInstance()->getTexture((ETextureIndex)te); + const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)te); if (texture_dict && texture_dict->mIsUsedByBakedTexture) { const EBakedTextureIndex baked_index = texture_dict->mBakedTextureIndex; @@ -606,7 +606,7 @@ void LLWearable::syncImages(te_map_t &src, te_map_t &dst) // Deep copy of src (copies only those tes that are current, filling in defaults where needed) for( S32 te = 0; te < TEX_NUM_INDICES; te++ ) { - if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex) te) == mType) + if (LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex) te) == mType) { te_map_t::const_iterator iter = src.find(te); LLUUID image_id; diff --git a/indra/llappearance/llwearabledata.cpp b/indra/llappearance/llwearabledata.cpp index 2bf3b9085b..66cc4f3766 100644 --- a/indra/llappearance/llwearabledata.cpp +++ b/indra/llappearance/llwearabledata.cpp @@ -339,7 +339,7 @@ U32 LLWearableData::getWearableCount(const LLWearableType::EType type) const U32 LLWearableData::getWearableCount(const U32 tex_index) const { - const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType((LLAvatarAppearanceDefines::ETextureIndex)tex_index); + const LLWearableType::EType wearable_type = LLAvatarAppearance::getDictionary()->getTEWearableType((LLAvatarAppearanceDefines::ETextureIndex)tex_index); return getWearableCount(wearable_type); } @@ -349,7 +349,7 @@ LLUUID LLWearableData::computeBakedTextureHash(LLAvatarAppearanceDefines::EBaked LLUUID hash_id; bool hash_computed = false; LLMD5 hash; - const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture(baked_index); + const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearance::getDictionary()->getBakedTexture(baked_index); for (U8 i=0; i < baked_dict->mWearables.size(); i++) { diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 8b628a058e..558ede7bf6 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -60,6 +60,10 @@ if (FMODSTUDIO) endif (FMODSTUDIO) if (OPENAL) + include_directories( + ${OPENAL_LIBRARIES} + ) + list(APPEND llaudio_SOURCE_FILES llaudioengine_openal.cpp lllistener_openal.cpp diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index a38d8291fa..3bdd0302ee 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -55,7 +55,7 @@ LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL() bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata, const std::string &app_title) { mWindGen = NULL; - LLAudioEngine::init(num_channels, userdata); + LLAudioEngine::init(num_channels, userdata, app_title); if(!alutInit(NULL, NULL)) { diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp index a685df5925..dee642310e 100644 --- a/indra/llcharacter/lljoint.cpp +++ b/indra/llcharacter/lljoint.cpp @@ -408,7 +408,7 @@ void showJointScaleOverrides( const LLJoint& joint, const std::string& note, con bool LLJoint::aboveJointPosThreshold(const LLVector3& pos) const { LLVector3 diff = pos - getDefaultPosition(); - const F32 max_joint_pos_offset = 0.0001f; // 0.1 mm + const F32 max_joint_pos_offset = LL_JOINT_TRESHOLD_POS_OFFSET; // 0.1 mm return diff.lengthSquared() > max_joint_pos_offset * max_joint_pos_offset; } @@ -511,7 +511,7 @@ void LLJoint::clearAttachmentPosOverrides() // getAllAttachmentPosOverrides() //-------------------------------------------------------------------- void LLJoint::getAllAttachmentPosOverrides(S32& num_pos_overrides, - std::set<LLVector3>& distinct_pos_overrides) + std::set<LLVector3>& distinct_pos_overrides) const { num_pos_overrides = m_attachmentPosOverrides.count(); LLVector3OverrideMap::map_type::const_iterator it = m_attachmentPosOverrides.getMap().begin(); @@ -525,7 +525,7 @@ void LLJoint::getAllAttachmentPosOverrides(S32& num_pos_overrides, // getAllAttachmentScaleOverrides() //-------------------------------------------------------------------- void LLJoint::getAllAttachmentScaleOverrides(S32& num_scale_overrides, - std::set<LLVector3>& distinct_scale_overrides) + std::set<LLVector3>& distinct_scale_overrides) const { num_scale_overrides = m_attachmentScaleOverrides.count(); LLVector3OverrideMap::map_type::const_iterator it = m_attachmentScaleOverrides.getMap().begin(); diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h index aa997a4cf7..1b646b641f 100644 --- a/indra/llcharacter/lljoint.h +++ b/indra/llcharacter/lljoint.h @@ -53,6 +53,8 @@ const U32 LL_FACE_JOINT_NUM = (LL_CHARACTER_MAX_ANIMATED_JOINTS-2); const S32 LL_CHARACTER_MAX_PRIORITY = 7; const F32 LL_MAX_PELVIS_OFFSET = 5.f; +const F32 LL_JOINT_TRESHOLD_POS_OFFSET = 0.0001f; //0.1 mm + class LLVector3OverrideMap { public: @@ -287,9 +289,9 @@ public: void showAttachmentScaleOverrides(const std::string& av_info) const; void getAllAttachmentPosOverrides(S32& num_pos_overrides, - std::set<LLVector3>& distinct_pos_overrides); + std::set<LLVector3>& distinct_pos_overrides) const; void getAllAttachmentScaleOverrides(S32& num_scale_overrides, - std::set<LLVector3>& distinct_scale_overrides); + std::set<LLVector3>& distinct_scale_overrides) const; // These are used in checks of whether a pos/scale override is considered significant. bool aboveJointPosThreshold(const LLVector3& pos) const; diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 79aa18eaec..dd266630ea 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -72,6 +72,7 @@ set(llcommon_SOURCE_FILES llinitparam.cpp llinitdestroyclass.cpp llinstancetracker.cpp + llkeybind.cpp llleap.cpp llleaplistener.cpp llliveappconfig.cpp @@ -183,6 +184,7 @@ set(llcommon_HEADER_FILES llinitdestroyclass.h llinitparam.h llinstancetracker.h + llkeybind.h llkeythrottle.h llleap.h llleaplistener.h diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index e7b0e0ef8e..10b98f49aa 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -54,6 +54,17 @@ enum ETerrainBrushType E_LANDBRUSH_INVALID = 6 }; +enum EMouseClickType{ + CLICK_NONE = -1, + CLICK_LEFT = 0, + CLICK_MIDDLE, + CLICK_RIGHT, + CLICK_BUTTON4, + CLICK_BUTTON5, + CLICK_DOUBLELEFT, + CLICK_COUNT // 'size', CLICK_NONE does not counts +}; + // keys // Bit masks for various keyboard modifier keys. const MASK MASK_NONE = 0x0000; diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 252d08c830..6064a843ae 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -180,7 +180,7 @@ LLApp::~LLApp() if(mExceptionHandler != 0) delete mExceptionHandler; - SUBSYSTEM_CLEANUP(LLCommon); + SUBSYSTEM_CLEANUP_DBG(LLCommon); } // static diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index 984e90f376..db94765871 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -68,7 +68,7 @@ void ll_cleanup_apr() { gAPRInitialized = false; - LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL; + LL_DEBUGS("APR") << "Cleaning up APR" << LL_ENDL; LLThreadLocalPointerBase::destroyAllThreadLocalStorage(); diff --git a/indra/llcommon/llcleanup.cpp b/indra/llcommon/llcleanup.cpp index c5283507bf..1f34c2036a 100644 --- a/indra/llcommon/llcleanup.cpp +++ b/indra/llcommon/llcleanup.cpp @@ -20,10 +20,13 @@ #include "llerror.h" #include "llerrorcontrol.h" -void log_subsystem_cleanup(const char* file, int line, const char* function, +void log_subsystem_cleanup(LLError::ELevel level, + const char* file, + int line, + const char* function, const char* classname) { - LL_INFOS("Cleanup") << LLError::abbreviateFile(file) << "(" << line << "): " + LL_VLOGS(level, "Cleanup") << LLError::abbreviateFile(file) << "(" << line << "): " << "calling " << classname << "::cleanupClass() in " << function << LL_ENDL; } diff --git a/indra/llcommon/llcleanup.h b/indra/llcommon/llcleanup.h index a319171b5f..0f567ed5f6 100644 --- a/indra/llcommon/llcleanup.h +++ b/indra/llcommon/llcleanup.h @@ -21,13 +21,22 @@ // shutdown schemes. #define SUBSYSTEM_CLEANUP(CLASSNAME) \ do { \ - log_subsystem_cleanup(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, #CLASSNAME); \ + log_subsystem_cleanup(LLError::LEVEL_INFO, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, #CLASSNAME); \ + CLASSNAME::cleanupClass(); \ + } while (0) + +#define SUBSYSTEM_CLEANUP_DBG(CLASSNAME) \ + do { \ + log_subsystem_cleanup(LLError::LEVEL_DEBUG, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, #CLASSNAME); \ CLASSNAME::cleanupClass(); \ } while (0) // Use ancient do { ... } while (0) macro trick to permit a block of // statements with the same syntax as a single statement. -void log_subsystem_cleanup(const char* file, int line, const char* function, +void log_subsystem_cleanup(LLError::ELevel level, + const char* file, + int line, + const char* function, const char* classname); #endif /* ! defined(LL_LLCLEANUP_H) */ diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index 2d665c611b..96be913d17 100644 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -63,7 +63,7 @@ void LLCommon::cleanupClass() sMasterThreadRecorder = NULL; LLTrace::set_master_thread_recorder(NULL); LLThreadSafeRefCount::cleanupThreadSafeRefCount(); - SUBSYSTEM_CLEANUP(LLTimer); + SUBSYSTEM_CLEANUP_DBG(LLTimer); if (sAprInitialized) { ll_cleanup_apr(); diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 262929006d..23419a52a7 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -56,10 +56,6 @@ #include "stringize.h" #include "llexception.h" -#if LL_WINDOWS -#include <excpt.h> -#endif - // static LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) { @@ -253,29 +249,13 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl #if LL_WINDOWS -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific - -U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) -{ - if (code == STATUS_MSC_EXCEPTION) - { - // C++ exception, go on - return EXCEPTION_CONTINUE_SEARCH; - } - else - { - // handle it - return EXCEPTION_EXECUTE_HANDLER; - } -} - void LLCoros::winlevel(const callable_t& callable) { __try { callable(); } - __except (exception_filter(GetExceptionCode(), GetExceptionInformation())) + __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation())) { // convert to C++ styled exception // Note: it might be better to use _se_set_translator diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 922c003443..8355df9045 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -25,7 +25,6 @@ * $/LicenseInfo$ */ -#define _LLERROR_CPP_ #include "linden_common.h" #include "llerror.h" @@ -333,12 +332,9 @@ namespace LLError } // huh, that's odd, we should see one or the other prefix -- but don't // try to log unless logging is already initialized - if (is_available()) - { - // in Python, " or ".join(vector) -- but in C++, a PITB - LL_DEBUGS() << "Did not see 'class' or 'struct' prefix on '" - << name << "'" << LL_ENDL; - } + // in Python, " or ".join(vector) -- but in C++, a PITB + LL_DEBUGS() << "Did not see 'class' or 'struct' prefix on '" + << name << "'" << LL_ENDL; return name; #else // neither GCC nor Visual Studio @@ -439,12 +435,13 @@ namespace typedef std::vector<LLError::RecorderPtr> Recorders; typedef std::vector<LLError::CallSite*> CallSiteVector; - class Globals : public LLSingleton<Globals> + class Globals { - LLSINGLETON(Globals); + public: + static Globals* getInstance(); + protected: + Globals(); public: - std::ostringstream messageStream; - bool messageStreamInUse; std::string mFatalMessage; void addCallSite(LLError::CallSite&); @@ -454,12 +451,17 @@ namespace CallSiteVector callSites; }; - Globals::Globals() - : messageStream(), - messageStreamInUse(false), - callSites() - { - } + Globals::Globals() {} + + Globals* Globals::getInstance() + { + // According to C++11 Function-Local Initialization + // of static variables is supposed to be thread safe + // without risk of deadlocks. + static Globals inst; + + return &inst; + } void Globals::addCallSite(LLError::CallSite& site) { @@ -500,7 +502,7 @@ namespace LLError LevelMap mTagLevelMap; std::map<std::string, unsigned int> mUniqueLogMessages; - LLError::FatalHook mFatalHook; + LLError::FatalFunction mCrashFunction; LLError::TimeFunction mTimeFunction; Recorders mRecorders; @@ -513,14 +515,17 @@ namespace LLError typedef LLPointer<SettingsConfig> SettingsConfigPtr; - class Settings : public LLSingleton<Settings> + class Settings { - LLSINGLETON(Settings); + public: + static Settings* getInstance(); + protected: + Settings(); public: SettingsConfigPtr getSettingsConfig(); void reset(); - SettingsStoragePtr saveAndReset(); + SettingsStoragePtr saveAndReset(); void restore(SettingsStoragePtr pSettingsStorage); private: @@ -537,7 +542,7 @@ namespace LLError mFileLevelMap(), mTagLevelMap(), mUniqueLogMessages(), - mFatalHook(NULL), + mCrashFunction([](const std::string&){}), mTimeFunction(NULL), mRecorders(), mShouldLogCallCounter(0) @@ -554,6 +559,16 @@ namespace LLError { } + Settings* Settings::getInstance() + { + // According to C++11 Function-Local Initialization + // of static variables is supposed to be thread safe + // without risk of deadlocks. + static Settings inst; + + return &inst; + } + SettingsConfigPtr Settings::getSettingsConfig() { return mSettingsConfig; @@ -578,11 +593,6 @@ namespace LLError SettingsConfigPtr newSettingsConfig(dynamic_cast<SettingsConfig *>(pSettingsStorage.get())); mSettingsConfig = newSettingsConfig; } - - bool is_available() - { - return Settings::instanceExists() && Globals::instanceExists(); - } } namespace LLError @@ -709,9 +719,8 @@ namespace LLError::Settings::getInstance()->reset(); LLError::setDefaultLevel(LLError::LEVEL_INFO); - - LLError::setAlwaysFlush(true); - LLError::setEnabledLogTypesMask(0xFFFFFFFF); + LLError::setAlwaysFlush(true); + LLError::setEnabledLogTypesMask(0xFFFFFFFF); LLError::setTimeFunction(LLError::utcTime); // log_to_stderr is only false in the unit and integration tests to keep builds quieter @@ -746,20 +755,16 @@ namespace LLError commonInit(user_dir, app_dir, log_to_stderr); } - void setFatalHook(const FatalHook& fatal_hook) + void setFatalFunction(const FatalFunction& f) { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - LL_DEBUGS("FatalHook") << "set fatal hook to " << (fatal_hook ? "non-null" : "null") - << " was " << (s->mFatalHook ? "non-null" : "null") - << LL_ENDL; - s->mFatalHook = fatal_hook; - } - - FatalHook getFatalHook() + s->mCrashFunction = f; + } + + FatalFunction getFatalFunction() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - LL_DEBUGS("FatalHook") << "read fatal hook was " << (s->mFatalHook ? "non-null" : "null") << LL_ENDL; - return s->mFatalHook; + return s->mCrashFunction; } std::string getFatalMessage() @@ -1033,7 +1038,7 @@ namespace LLError std::pair<boost::shared_ptr<RECORDER>, Recorders::iterator> findRecorderPos() { - SettingsConfigPtr s = Settings::instance().getSettingsConfig(); + SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); // Since we promise to return an iterator, use a classic iterator // loop. auto end{s->mRecorders.end()}; @@ -1076,7 +1081,7 @@ namespace LLError auto found = findRecorderPos<RECORDER>(); if (found.first) { - SettingsConfigPtr s = Settings::instance().getSettingsConfig(); + SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); s->mRecorders.erase(found.second); } return bool(found.first); @@ -1312,14 +1317,6 @@ namespace LLError return false; } - // If we hit a logging request very late during shutdown processing, - // when either of the relevant LLSingletons has already been deleted, - // DO NOT resurrect them. - if (Settings::wasDeleted() || Globals::wasDeleted()) - { - return false; - } - SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); s->mShouldLogCallCounter++; @@ -1355,27 +1352,7 @@ namespace LLError } - std::ostringstream* Log::out() - { - LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5); - // If we hit a logging request very late during shutdown processing, - // when either of the relevant LLSingletons has already been deleted, - // DO NOT resurrect them. - if (lock.isLocked() && ! (Settings::wasDeleted() || Globals::wasDeleted())) - { - Globals* g = Globals::getInstance(); - - if (!g->messageStreamInUse) - { - g->messageStreamInUse = true; - return &g->messageStream; - } - } - - return new std::ostringstream; - } - - void Log::flush(std::ostringstream* out, char* message) + void Log::flush(const std::ostringstream& out, const CallSite& site) { LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5); if (!lock.isLocked()) @@ -1383,73 +1360,14 @@ namespace LLError return; } - // If we hit a logging request very late during shutdown processing, - // when either of the relevant LLSingletons has already been deleted, - // DO NOT resurrect them. - if (Settings::wasDeleted() || Globals::wasDeleted()) - { - return; - } - - if(strlen(out->str().c_str()) < 128) - { - strcpy(message, out->str().c_str()); - } - else - { - strncpy(message, out->str().c_str(), 127); - message[127] = '\0' ; - } - - Globals* g = Globals::getInstance(); - if (out == &g->messageStream) - { - g->messageStream.clear(); - g->messageStream.str(""); - g->messageStreamInUse = false; - } - else - { - delete out; - } - return ; - } - - ErrFatalHookResult Log::flush(std::ostringstream* out, const CallSite& site) - { - LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5); - if (!lock.isLocked()) - { - return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal - } - - // If we hit a logging request very late during shutdown processing, - // when either of the relevant LLSingletons has already been deleted, - // DO NOT resurrect them. - if (Settings::wasDeleted() || Globals::wasDeleted()) - { - return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal - } - Globals* g = Globals::getInstance(); SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - std::string message = out->str(); - if (out == &g->messageStream) - { - g->messageStream.clear(); - g->messageStream.str(""); - g->messageStreamInUse = false; - } - else - { - delete out; - } - + std::string message = out.str(); if (site.mPrintOnce) { - std::ostringstream message_stream; + std::ostringstream message_stream; std::map<std::string, unsigned int>::iterator messageIter = s->mUniqueLogMessages.find(message); if (messageIter != s->mUniqueLogMessages.end()) @@ -1462,7 +1380,7 @@ namespace LLError } else { - return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal + return; } } else @@ -1470,27 +1388,17 @@ namespace LLError message_stream << "ONCE: "; s->mUniqueLogMessages[message] = 1; } - message_stream << message; - message = message_stream.str(); + message_stream << message; + message = message_stream.str(); } writeToRecorders(site, message); if (site.mLevel == LEVEL_ERROR) { - if (s->mFatalHook) - { - return s->mFatalHook(message); - } - else - { - return ERR_CRASH; // calling macro should crash - } + g->mFatalMessage = message; + s->mCrashFunction(message); } - else - { - return ERR_DO_NOT_CRASH; // not ERROR, so do not crash - } } } @@ -1569,33 +1477,7 @@ namespace LLError namespace LLError { - char** LLCallStacks::sBuffer = NULL ; - S32 LLCallStacks::sIndex = 0 ; - - //static - void LLCallStacks::allocateStackBuffer() - { - if(sBuffer == NULL) - { - sBuffer = new char*[512] ; - sBuffer[0] = new char[512 * 128] ; - for(S32 i = 1 ; i < 512 ; i++) - { - sBuffer[i] = sBuffer[i-1] + 128 ; - } - sIndex = 0 ; - } - } - - void LLCallStacks::freeStackBuffer() - { - if(sBuffer != NULL) - { - delete [] sBuffer[0] ; - delete [] sBuffer ; - sBuffer = NULL ; - } - } + LLCallStacks::StringVector LLCallStacks::sBuffer ; //static void LLCallStacks::push(const char* function, const int line) @@ -1606,33 +1488,24 @@ namespace LLError return; } - if(sBuffer == NULL) - { - allocateStackBuffer(); - } - - if(sIndex > 511) + if(sBuffer.size() > 511) { clear() ; } - strcpy(sBuffer[sIndex], function) ; - sprintf(sBuffer[sIndex] + strlen(function), " line: %d ", line) ; - sIndex++ ; - - return ; + std::ostringstream out; + insert(out, function, line); + sBuffer.push_back(out.str()); } //static - std::ostringstream* LLCallStacks::insert(const char* function, const int line) + void LLCallStacks::insert(std::ostream& out, const char* function, const int line) { - std::ostringstream* _out = LLError::Log::out(); - *_out << function << " line " << line << " " ; - return _out ; + out << function << " line " << line << " " ; } //static - void LLCallStacks::end(std::ostringstream* _out) + void LLCallStacks::end(const std::ostringstream& out) { LLMutexTrylock lock(getMutex<STACKS_MUTEX>(), 5); if (!lock.isLocked()) @@ -1640,17 +1513,12 @@ namespace LLError return; } - if(sBuffer == NULL) - { - allocateStackBuffer(); - } - - if(sIndex > 511) + if(sBuffer.size() > 511) { clear() ; } - LLError::Log::flush(_out, sBuffer[sIndex++]) ; + sBuffer.push_back(out.str()); } //static @@ -1662,33 +1530,30 @@ namespace LLError return; } - if(sIndex > 0) + if(! sBuffer.empty()) { LL_INFOS() << " ************* PRINT OUT LL CALL STACKS ************* " << LL_ENDL; - while(sIndex > 0) + for (StringVector::const_reverse_iterator ri(sBuffer.rbegin()), re(sBuffer.rend()); + ri != re; ++ri) { - sIndex-- ; - LL_INFOS() << sBuffer[sIndex] << LL_ENDL; + LL_INFOS() << (*ri) << LL_ENDL; } LL_INFOS() << " *************** END OF LL CALL STACKS *************** " << LL_ENDL; } - if(sBuffer != NULL) - { - freeStackBuffer(); - } + cleanup(); } //static void LLCallStacks::clear() { - sIndex = 0 ; + sBuffer.clear(); } //static void LLCallStacks::cleanup() { - freeStackBuffer(); + clear(); } std::ostream& operator<<(std::ostream& out, const LLStacktrace&) diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 18d11cfa9d..d439136ca8 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -29,7 +29,9 @@ #define LL_LLERROR_H #include <sstream> +#include <string> #include <typeinfo> +#include <vector> #include "stdtypes.h" @@ -193,20 +195,12 @@ namespace LLError */ struct CallSite; - - enum ErrFatalHookResult { ERR_DO_NOT_CRASH, ERR_CRASH }; class LL_COMMON_API Log { public: static bool shouldLog(CallSite&); - static std::ostringstream* out(); - - static void flush(std::ostringstream* out, char* message); - - // returns false iff the calling macro should crash - static ErrFatalHookResult flush(std::ostringstream*, const CallSite&); - + static void flush(const std::ostringstream&, const CallSite&); static std::string demangle(const char* mangled); /// classname<TYPE>() template <typename T> @@ -277,7 +271,6 @@ namespace LLError class LL_COMMON_API NoClassInfo { }; // used to indicate no class info known for logging - //LLCallStacks keeps track of call stacks and output the call stacks to log file //when LLAppViewer::handleViewerCrash() is triggered. // @@ -288,18 +281,15 @@ namespace LLError class LL_COMMON_API LLCallStacks { private: - static char** sBuffer ; - static S32 sIndex ; - - static void allocateStackBuffer(); - static void freeStackBuffer(); + typedef std::vector<std::string> StringVector; + static StringVector sBuffer ; public: static void push(const char* function, const int line) ; - static std::ostringstream* insert(const char* function, const int line) ; + static void insert(std::ostream& out, const char* function, const int line) ; static void print() ; static void clear() ; - static void end(std::ostringstream* _out) ; + static void end(const std::ostringstream& out) ; static void cleanup(); }; @@ -313,10 +303,11 @@ namespace LLError //this is cheaper than llcallstacks if no need to output other variables to call stacks. #define LL_PUSH_CALLSTACKS() LLError::LLCallStacks::push(__FUNCTION__, __LINE__) -#define llcallstacks \ - { \ - std::ostringstream* _out = LLError::LLCallStacks::insert(__FUNCTION__, __LINE__) ; \ - (*_out) +#define llcallstacks \ + { \ + std::ostringstream _out; \ + LLError::LLCallStacks::insert(_out, __FUNCTION__, __LINE__) ; \ + _out #define llcallstacksendl \ LLError::End(); \ @@ -362,11 +353,11 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \ lllog_test_() -#define lllog_test_() \ - if (LL_UNLIKELY(_site.shouldLog())) \ - { \ - std::ostringstream* _out = LLError::Log::out(); \ - (*_out) +#define lllog_test_() \ + if (LL_UNLIKELY(_site.shouldLog())) \ + { \ + std::ostringstream _out; \ + _out #define lllog_site_args_(level, once, tags) \ level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \ @@ -385,29 +376,27 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; // LL_CONT << " for " << t << " seconds" << LL_ENDL; // //Such computation is done iff the message will be logged. -#define LL_CONT (*_out) +#define LL_CONT _out #define LL_NEWLINE '\n' -#ifdef _LLERROR_CPP_ -volatile int* gCauseCrash = NULL; -#else -volatile extern int* gCauseCrash; -#endif // _LLERROR_CPP_ - // Use this only in LL_ERRS or in a place that LL_ERRS may not be used -#define LLERROR_CRASH \ -{ \ - *gCauseCrash = 0; \ - exit(*gCauseCrash); \ +#define LLERROR_CRASH \ +{ \ + int* make_me_crash = NULL;\ + *make_me_crash = 0; \ + exit(*make_me_crash); \ } -#define LL_ENDL \ - LLError::End(); \ - if (LLError::ERR_CRASH == LLError::Log::flush(_out, _site)) \ - LLERROR_CRASH \ - } \ - } while(0) +#define LL_ENDL \ + LLError::End(); \ + LLError::Log::flush(_out, _site); \ + if (_site.mLevel == LLError::LEVEL_ERROR) \ + { \ + LLERROR_CRASH \ + } \ + } \ + } while(0) // NEW Macros for debugging, allow the passing of a string tag diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index d001a3bd03..e87bb7bf35 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -93,27 +93,42 @@ namespace LLError Control functions. */ - // A FatalHook is called if set using setFatalHook; its return controls - // whether or not the calling error logging code should crash. - // ERR_DO_NOT_CRASH should be used only in test code. - typedef boost::function<LLError::ErrFatalHookResult(const std::string&)> FatalHook; - - /// Supplement and control the default behavior of crashing on LL_ERRS - /// This may be used to suppress crashes only in test code; - /// otherwise, the FatalHook should always either return - /// the result from the previous hook (see getFatalHook below), - /// or return LLError::ERR_CRASH - LL_COMMON_API void setFatalHook(const FatalHook& fatal_hook); - // The fatal_hook function will be called when an message of LEVEL_ERROR + typedef boost::function<void(const std::string&)> FatalFunction; + + LL_COMMON_API void setFatalFunction(const FatalFunction&); + // The fatal function will be called after an message of LEVEL_ERROR // is logged. Note: supressing a LEVEL_ERROR message from being logged - // (by, for example, setting a class level to LEVEL_NONE), will also - // prevent the fatal_hook being called and the resulting deliberate crash + // (by, for example, setting a class level to LEVEL_NONE), will keep + // that message from causing the fatal function to be invoked. + // The passed FatalFunction will be the LAST log function called + // before LL_ERRS crashes its caller. A FatalFunction can throw an + // exception, or call exit(), to bypass the crash. It MUST disrupt the + // flow of control because no caller expects LL_ERRS to return. - /// Retrieve the previously-set FatalHook - LL_COMMON_API FatalHook getFatalHook(); + LL_COMMON_API FatalFunction getFatalFunction(); + // Retrieve the previously-set FatalFunction LL_COMMON_API std::string getFatalMessage(); - // Retrieve the message last passed to LL_ERRS, if any + // Retrieve the message last passed to FatalFunction, if any + + /// temporarily override the FatalFunction for the duration of a + /// particular scope, e.g. for unit tests + class LL_COMMON_API OverrideFatalFunction + { + public: + OverrideFatalFunction(const FatalFunction& func): + mPrev(getFatalFunction()) + { + setFatalFunction(func); + } + ~OverrideFatalFunction() + { + setFatalFunction(mPrev); + } + + private: + FatalFunction mPrev; + }; typedef std::string (*TimeFunction)(); LL_COMMON_API std::string utcTime(); @@ -134,14 +149,14 @@ namespace LLError virtual void recordMessage(LLError::ELevel, const std::string& message) = 0; // use the level for better display, not for filtering - virtual bool enabled() { return true; } + virtual bool enabled() { return true; } bool wantsTime(); bool wantsTags(); bool wantsLevel(); bool wantsLocation(); bool wantsFunctionName(); - bool wantsMultiline(); + bool wantsMultiline(); void showTime(bool show); void showTags(bool show); @@ -152,15 +167,35 @@ namespace LLError protected: bool mWantsTime; - bool mWantsTags; - bool mWantsLevel; - bool mWantsLocation; - bool mWantsFunctionName; - bool mWantsMultiline; + bool mWantsTags; + bool mWantsLevel; + bool mWantsLocation; + bool mWantsFunctionName; + bool mWantsMultiline; }; typedef boost::shared_ptr<Recorder> RecorderPtr; + /** + * Instantiate GenericRecorder with a callable(level, message) to get + * control on every log message without having to code an explicit + * Recorder subclass. + */ + template <typename CALLABLE> + class GenericRecorder: public Recorder + { + public: + GenericRecorder(const CALLABLE& callable): + mCallable(callable) + {} + void recordMessage(LLError::ELevel level, const std::string& message) override + { + mCallable(level, message); + } + private: + CALLABLE mCallable; + }; + /** * @NOTE: addRecorder() and removeRecorder() uses the boost::shared_ptr to allow for shared ownership * while still ensuring that the allocated memory is eventually freed @@ -168,6 +203,19 @@ namespace LLError LL_COMMON_API void addRecorder(RecorderPtr); LL_COMMON_API void removeRecorder(RecorderPtr); // each error message is passed to each recorder via recordMessage() + /** + * Call addGenericRecorder() with a callable(level, message) to get + * control on every log message without having to code an explicit + * Recorder subclass. Save the returned RecorderPtr if you later want to + * call removeRecorder(). + */ + template <typename CALLABLE> + RecorderPtr addGenericRecorder(const CALLABLE& callable) + { + RecorderPtr ptr{ new GenericRecorder<CALLABLE>(callable) }; + addRecorder(ptr); + return ptr; + } LL_COMMON_API void logToFile(const std::string& filename); LL_COMMON_API void logToStderr(); @@ -190,11 +238,6 @@ namespace LLError LL_COMMON_API std::string abbreviateFile(const std::string& filePath); LL_COMMON_API int shouldLogCallCount(); - - // Check whether Globals exists. This should only be used by LLSingleton - // infrastructure to avoid trying to log when our internal LLSingleton is - // unavailable -- circularity ensues. - LL_COMMON_API bool is_available(); }; #endif // LL_LLERRORCONTROL_H diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index 5ce8958687..b584b0ff8b 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -24,11 +24,14 @@ // `_GNU_SOURCE` macro or `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED` if // _Unwind_Backtrace is available without `_GNU_SOURCE`." #define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED + #if LL_WINDOWS // On Windows, header-only implementation causes macro collisions -- use // prebuilt library #define BOOST_STACKTRACE_LINK +#include <excpt.h> #endif // LL_WINDOWS + #include <boost/stacktrace.hpp> // other Linden headers #include "llerror.h" @@ -85,3 +88,25 @@ void annotate_exception_(boost::exception& exc) // Anyway, which of us is really going to examine more than 100 frames? exc << errinfo_stacktrace(boost::stacktrace::stacktrace(1, 100)); } + +#if LL_WINDOWS + +// For windows SEH exception handling we sometimes need a filter that will +// separate C++ exceptions from C SEH exceptions +static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific + +U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) +{ + if (code == STATUS_MSC_EXCEPTION) + { + // C++ exception, go on + return EXCEPTION_CONTINUE_SEARCH; + } + else + { + // handle it + return EXCEPTION_EXECUTE_HANDLER; + } +} + +#endif //LL_WINDOWS diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 422dd8810a..375bea4a57 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -102,4 +102,14 @@ void crash_on_unhandled_exception_(const char*, int, const char*, const std::str log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT) void log_unhandled_exception_(const char*, int, const char*, const std::string&); + +#if LL_WINDOWS + +// SEH exception filtering for use in __try __except +// Separates C++ exceptions from C SEH exceptions +// Todo: might be good idea to do some kind of seh_to_msc_wrapper(function, ARGS&&); +U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop); + +#endif //LL_WINDOWS + #endif /* ! defined(LL_LLEXCEPTION_H) */ diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp new file mode 100644 index 0000000000..38696c2258 --- /dev/null +++ b/indra/llcommon/llkeybind.cpp @@ -0,0 +1,395 @@ +/** + * @file llkeybind.cpp + * @brief Information about key combinations. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llkeybind.h" + +#include "llsd.h" +#include "llsdutil.h" + +LLKeyData::LLKeyData() + : + mMouse(CLICK_NONE), + mKey(KEY_NONE), + mMask(MASK_NONE), + mIgnoreMasks(false) +{ +} + +LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask) + : + mMouse(mouse), + mKey(key), + mMask(mask), + mIgnoreMasks(false) +{ +} + +LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, bool ignore_mask) + : + mMouse(mouse), + mKey(key), + mMask(MASK_NONE), + mIgnoreMasks(ignore_mask) +{ +} + +LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask) + : + mMouse(mouse), + mKey(key), + mMask(mask), + mIgnoreMasks(ignore_mask) +{ +} + +LLKeyData::LLKeyData(const LLSD &key_data) +{ + if (key_data.has("mouse")) + { + mMouse = (EMouseClickType)key_data["mouse"].asInteger(); + } + if (key_data.has("key")) + { + mKey = key_data["key"].asInteger(); + } + if (key_data.has("ignore_accelerators")) + { + mIgnoreMasks = key_data["ignore_accelerators"]; + } + if (key_data.has("mask")) + { + mMask = key_data["mask"].asInteger(); + } +} + +LLSD LLKeyData::asLLSD() const +{ + LLSD data; + data["mouse"] = (LLSD::Integer)mMouse; + data["key"] = (LLSD::Integer)mKey; + data["mask"] = (LLSD::Integer)mMask; + if (mIgnoreMasks) + { + data["ignore_accelerators"] = (LLSD::Boolean)mIgnoreMasks; + } + return data; +} + +bool LLKeyData::isEmpty() const +{ + return mMouse == CLICK_NONE && mKey == KEY_NONE; +} + +void LLKeyData::reset() +{ + mMouse = CLICK_NONE; + mKey = KEY_NONE; + mMask = MASK_NONE; + mIgnoreMasks = false; +} + +LLKeyData& LLKeyData::operator=(const LLKeyData& rhs) +{ + mMouse = rhs.mMouse; + mKey = rhs.mKey; + mMask = rhs.mMask; + mIgnoreMasks = rhs.mIgnoreMasks; + return *this; +} + +bool LLKeyData::operator==(const LLKeyData& rhs) +{ + 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; +} + +bool LLKeyData::operator!=(const LLKeyData& rhs) +{ + 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; +} + +bool LLKeyData::canHandle(const LLKeyData& data) const +{ + if (data.mKey == mKey + && data.mMouse == mMouse + && ((mIgnoreMasks && (data.mMask & mMask) == mMask) || data.mMask == mMask)) + { + return true; + } + return false; +} + +bool LLKeyData::canHandle(EMouseClickType mouse, KEY key, MASK mask) const +{ + if (mouse == mMouse + && key == mKey + && ((mIgnoreMasks && (mask & mMask) == mMask) || mask == mMask)) + { + return true; + } + return false; +} + +// LLKeyBind + +LLKeyBind::LLKeyBind(const LLSD &key_bind) +{ + if (key_bind.isArray()) + { + for (LLSD::array_const_iterator data = key_bind.beginArray(), endLists = key_bind.endArray(); + data != endLists; + data++ + ) + { + mData.push_back(LLKeyData(*data)); + } + } +} + +bool LLKeyBind::operator==(const LLKeyBind& rhs) +{ + U32 size = mData.size(); + if (size != rhs.mData.size()) return false; + + for (U32 i = 0; i < size; i++) + { + if (mData[i] != rhs.mData[i]) return false; + } + + return true; +} + +bool LLKeyBind::operator!=(const LLKeyBind& rhs) +{ + U32 size = mData.size(); + if (size != rhs.mData.size()) return true; + + for (U32 i = 0; i < size; i++) + { + if (mData[i] != rhs.mData[i]) return true; + } + + return false; +} + +bool LLKeyBind::isEmpty() const +{ + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (!iter->isEmpty()) return false; + } + return true; +} + +LLSD LLKeyBind::asLLSD() const +{ + S32 last = mData.size() - 1; + while (mData[last].empty()) + { + last--; + } + + LLSD data; + for (S32 i = 0; i <= last; ++i) + { + // append even if empty to not affect visual representation + data.append(mData[i].asLLSD()); + } + return data; +} + +bool LLKeyBind::canHandle(EMouseClickType mouse, KEY key, MASK mask) const +{ + if (mouse == CLICK_NONE && key == KEY_NONE) + { + // assume placeholder + return false; + } + + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (iter->canHandle(mouse, key, mask)) + { + return true; + } + } + return false; +} + +bool LLKeyBind::canHandleKey(KEY key, MASK mask) const +{ + return canHandle(CLICK_NONE, key, mask); +} + +bool LLKeyBind::canHandleMouse(EMouseClickType mouse, MASK mask) const +{ + return canHandle(mouse, KEY_NONE, mask); +} + +bool LLKeyBind::hasKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const +{ + if (mouse != CLICK_NONE || key != KEY_NONE) + { + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (iter->mKey == key + && iter->mMask == mask + && iter->mMouse == mouse + && iter->mIgnoreMasks == ignore) + { + return true; + } + } + } + return false; +} + +bool LLKeyBind::hasKeyData(const LLKeyData& data) const +{ + return hasKeyData(data.mMouse, data.mKey, data.mMask, data.mIgnoreMasks); +} + +bool LLKeyBind::hasKeyData(U32 index) const +{ + return mData.size() > index; +} + +S32 LLKeyBind::findKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const +{ + if (mouse != CLICK_NONE || key != KEY_NONE) + { + for (S32 i = 0; i < mData.size(); ++i) + { + if (mData[i].mKey == key + && mData[i].mMask == mask + && mData[i].mMouse == mouse + && mData[i].mIgnoreMasks == ignore) + { + return i; + } + } + } + return -1; +} + +S32 LLKeyBind::findKeyData(const LLKeyData& data) const +{ + return findKeyData(data.mMouse, data.mKey, data.mMask, data.mIgnoreMasks); +} + +LLKeyData LLKeyBind::getKeyData(U32 index) const +{ + if (mData.size() > index) + { + return mData[index]; + } + return LLKeyData(); +} + +bool LLKeyBind::addKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) +{ + if (!hasKeyData(mouse, key, mask, ignore)) + { + mData.push_back(LLKeyData(mouse, key, mask, ignore)); + return true; + } + return false; +} + +bool LLKeyBind::addKeyData(const LLKeyData& data) +{ + if (!hasKeyData(data)) + { + mData.push_back(data); + return true; + } + return false; +} + +void LLKeyBind::replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore, U32 index) +{ + replaceKeyData(LLKeyData(mouse, key, mask, ignore), index); +} + +void LLKeyBind::replaceKeyData(const LLKeyData& data, U32 index) +{ + if (!data.isEmpty()) + { + // if both click and key are none (isEmpty()), we are inserting a placeholder, we don't want to reset anything + // otherwise reset identical key + for (data_vector_t::iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (iter->mKey == data.mKey + && iter->mMouse == data.mMouse + && iter->mIgnoreMasks == data.mIgnoreMasks + && iter->mMask == data.mMask) + { + // Replacing only fully equal combinations even in case 'ignore' is set + // Reason: Simplicity and user might decide to do a 'move' command as W and Shift+Ctrl+W, and 'run' as Shift+W + iter->reset(); + break; + } + } + } + if (mData.size() <= index) + { + mData.resize(index + 1); + } + mData[index] = data; +} + +void LLKeyBind::resetKeyData(S32 index) +{ + if (mData.size() > index) + { + mData[index].reset(); + } +} + +void LLKeyBind::trimEmpty() +{ + S32 last = mData.size() - 1; + while (last >= 0 && mData[last].empty()) + { + mData.erase(mData.begin() + last); + last--; + } +} + +U32 LLKeyBind::getDataCount() +{ + return mData.size(); +} + diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h new file mode 100644 index 0000000000..c6b4bd970f --- /dev/null +++ b/indra/llcommon/llkeybind.h @@ -0,0 +1,106 @@ +/** + * @file llkeybind.h + * @brief Information about key combinations. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_KEYBIND_H +#define LL_KEYBIND_H + +#include "indra_constants.h" + +// KeyData - single key combination (mouse/mask/keyboard) +class LL_COMMON_API LLKeyData +{ +public: + LLKeyData(); + LLKeyData(EMouseClickType mouse, KEY key, MASK mask); + LLKeyData(EMouseClickType mouse, KEY key, bool ignore_mask); + LLKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask); + LLKeyData(const LLSD &key_data); + + LLSD asLLSD() const; + bool isEmpty() const; + bool empty() const { return isEmpty(); }; + void reset(); + LLKeyData& operator=(const LLKeyData& rhs); + bool operator==(const LLKeyData& rhs); + bool operator!=(const LLKeyData& rhs); + + bool canHandle(const LLKeyData& data) const; + bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const; + + EMouseClickType mMouse; + KEY mKey; + MASK mMask; + // Either to expect exact match or ignore not expected masks as long as expected mask-bit is present + bool mIgnoreMasks; +}; + +// One function can bind to multiple Key options +class LLKeyBind +{ +public: + LLKeyBind() {} + LLKeyBind(const LLSD &key_bind); + + bool operator==(const LLKeyBind& rhs); + bool operator!=(const LLKeyBind& rhs); + bool isEmpty() const; + bool empty() const { return isEmpty(); }; + + LLSD asLLSD() const; + + bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const; + bool canHandleKey(KEY key, MASK mask) const; + bool canHandleMouse(EMouseClickType mouse, MASK mask) const; + + // contains specified combination + bool hasKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const; + bool hasKeyData(const LLKeyData& data) const; + bool hasKeyData(U32 index) const; + + // index of contained LLKeyData + S32 findKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const; + S32 findKeyData(const LLKeyData& data) const; + + LLKeyData getKeyData(U32 index) const; + + // these methods enshure there will be no repeats + bool addKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore); + bool addKeyData(const LLKeyData& data); + void replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore, U32 index); + void replaceKeyData(const LLKeyData& data, U32 index); + void resetKeyData(S32 index); + void clear() { mData.clear(); } + // if there any empty LLKeyData in the end of the array, remove them + void trimEmpty(); + U32 getDataCount(); + +private: + typedef std::vector<LLKeyData> data_vector_t; + data_vector_t mData; +}; + + +#endif // LL_KEYBIND_H diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index 8293c35516..e8ea0ab398 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -144,6 +144,11 @@ public: mStderrConnection = childerr.getPump() .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1)); + // For our lifespan, intercept any LL_ERRS so we can notify plugin + mRecorder = LLError::addGenericRecorder( + [this](LLError::ELevel level, const std::string& message) + { onError(level, message); }); + // Send child a preliminary event reporting our own reply-pump name -- // which would otherwise be pretty tricky to guess! wstdin(mReplyPump.getName(), @@ -158,6 +163,7 @@ public: virtual ~LLLeapImpl() { LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL; + LLError::removeRecorder(mRecorder); } // Listener for failed launch attempt @@ -371,6 +377,30 @@ public: return false; } + void onError(LLError::ELevel level, const std::string& error) + { + if (level == LLError::LEVEL_ERROR) + { + // Notify plugin + LLSD event; + event["type"] = "error"; + event["error"] = error; + mReplyPump.post(event); + + // All the above really accomplished was to buffer the serialized + // event in our WritePipe. Have to pump mainloop a couple times to + // really write it out there... but time out in case we can't write. + LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN)); + LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); + LLSD nop; + F64 until = (LLTimer::getElapsedSeconds() + 2).value(); + while (childin.size() && LLTimer::getElapsedSeconds() < until) + { + mainloop.post(nop); + } + } + } + private: /// We always want to listen on mReplyPump with wstdin(); under some /// circumstances we'll also echo other LLEventPumps to the plugin. @@ -391,6 +421,7 @@ private: mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection; boost::scoped_ptr<LLEventPump::Blocker> mBlocker; LLProcess::ReadPipe::size_type mExpect; + LLError::RecorderPtr mRecorder; boost::scoped_ptr<LLLeapListener> mListener; }; diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index f410bd6a92..6b1986d0e9 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -28,7 +28,7 @@ #include "llsingleton.h" #include "llerror.h" -#include "llerrorcontrol.h" // LLError::is_available() +#include "llerrorcontrol.h" #include "lldependencies.h" #include "llexception.h" #include "llcoros.h" @@ -38,13 +38,6 @@ #include <sstream> #include <stdexcept> -namespace { -void log(LLError::ELevel level, - const char* p1, const char* p2, const char* p3, const char* p4); - -bool oktolog(); -} // anonymous namespace - // Our master list of all LLSingletons is itself an LLSingleton. We used to // store it in a function-local static, but that could get destroyed before // the last of the LLSingletons -- and ~LLSingletonBase() definitely wants to @@ -220,8 +213,8 @@ void LLSingletonBase::pop_initializing() if (list.empty()) { - logerrs("Underflow in stack of currently-initializing LLSingletons at ", - classname(this).c_str(), "::getInstance()"); + logerrs({"Underflow in stack of currently-initializing LLSingletons at ", + classname(this), "::getInstance()"}); } // Now we know list.back() exists: capture it @@ -242,9 +235,9 @@ void LLSingletonBase::pop_initializing() // Now validate the newly-popped LLSingleton. if (back != this) { - logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ", - classname(this).c_str(), "::getInstance() trying to pop ", - classname(back).c_str()); + logerrs({"Push/pop mismatch in stack of currently-initializing LLSingletons: ", + classname(this), "::getInstance() trying to pop ", + classname(back)}); } // log AFTER popping so logging singletons don't cry circularity @@ -279,8 +272,6 @@ void LLSingletonBase::reset_initializing(list_t::size_type size) void LLSingletonBase::MasterList::LockedInitializing::log(const char* verb, const char* name) { - if (oktolog()) - { LL_DEBUGS("LLSingleton") << verb << ' ' << demangle(name) << ';'; if (mList) { @@ -292,7 +283,6 @@ void LLSingletonBase::MasterList::LockedInitializing::log(const char* verb, cons } } LL_ENDL; - } } void LLSingletonBase::capture_dependency() @@ -336,15 +326,15 @@ void LLSingletonBase::capture_dependency() // // Example: LLNotifications singleton initializes default channels. // Channels register themselves with singleton once done. - logdebugs("LLSingleton circularity: ", out.str().c_str(), - classname(this).c_str(), ""); + logdebugs({"LLSingleton circularity: ", out.str(), + classname(this)}); } else { // Actual circularity with other singleton (or single singleton is used extensively). // Dependency can be unclear. - logwarns("LLSingleton circularity: ", out.str().c_str(), - classname(this).c_str(), ""); + logwarns({"LLSingleton circularity: ", out.str(), + classname(this)}); } } else @@ -357,8 +347,8 @@ void LLSingletonBase::capture_dependency() if (current->mDepends.insert(this).second) { // only log the FIRST time we hit this dependency! - logdebugs(classname(current).c_str(), - " depends on ", classname(this).c_str()); + logdebugs({classname(current), + " depends on ", classname(this)}); } } } @@ -393,7 +383,7 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort() // extracts just the first (key) element from each sorted_iterator, then // uses vec_t's range constructor... but frankly this is more // straightforward, as long as we remember the above reserve() call! - for (const SingletonDeps::sorted_iterator::value_type& pair : sdeps.sort()) + for (const SingletonDeps::sorted_iterator::value_type pair : sdeps.sort()) { ret.push_back(pair.first); } @@ -406,7 +396,7 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort() void LLSingletonBase::cleanup_() { - logdebugs("calling ", classname(this).c_str(), "::cleanupSingleton()"); + logdebugs({"calling ", classname(this), "::cleanupSingleton()"}); try { cleanupSingleton(); @@ -432,87 +422,64 @@ void LLSingletonBase::deleteAll() if (! sp->mDeleteSingleton) { // This Should Not Happen... but carry on. - logwarns(name.c_str(), "::mDeleteSingleton not initialized!"); + logwarns({name, "::mDeleteSingleton not initialized!"}); } else { // properly initialized: call it. - logdebugs("calling ", name.c_str(), "::deleteSingleton()"); + logdebugs({"calling ", name, "::deleteSingleton()"}); // From this point on, DO NOT DEREFERENCE sp! sp->mDeleteSingleton(); } } catch (const std::exception& e) { - logwarns("Exception in ", name.c_str(), "::deleteSingleton(): ", e.what()); + logwarns({"Exception in ", name, "::deleteSingleton(): ", e.what()}); } catch (...) { - logwarns("Unknown exception in ", name.c_str(), "::deleteSingleton()"); + logwarns({"Unknown exception in ", name, "::deleteSingleton()"}); } } } /*---------------------------- Logging helpers -----------------------------*/ namespace { -bool oktolog() -{ - // See comments in log() below. - return LLError::is_available(); -} -void log(LLError::ELevel level, - const char* p1, const char* p2, const char* p3, const char* p4) +std::ostream& operator<<(std::ostream& out, const LLSingletonBase::string_params& args) { - // The is_available() test below ensures that we'll stop logging once - // LLError has been cleaned up. If we had a similar portable test for - // std::cerr, this would be a good place to use it. - - // Check LLError::is_available() because some of LLError's infrastructure - // is itself an LLSingleton. If that LLSingleton has not yet been - // initialized, trying to log will engage LLSingleton machinery... and - // around and around we go. - if (LLError::is_available()) - { - LL_VLOGS(level, "LLSingleton") << p1 << p2 << p3 << p4 << LL_ENDL; - } - else + // However many args there are in args, stream each of them to 'out'. + for (auto arg : args) { - // Caller may be a test program, or something else whose stderr is - // visible to the user. - std::cerr << p1 << p2 << p3 << p4 << std::endl; + out << arg; } + return out; } } // anonymous namespace //static -void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logwarns(const string_params& args) { - log(LLError::LEVEL_WARN, p1, p2, p3, p4); + LL_WARNS("LLSingleton") << args << LL_ENDL; } //static -void LLSingletonBase::loginfos(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::loginfos(const string_params& args) { - log(LLError::LEVEL_INFO, p1, p2, p3, p4); + LL_INFOS("LLSingleton") << args << LL_ENDL; } //static -void LLSingletonBase::logdebugs(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logdebugs(const string_params& args) { - log(LLError::LEVEL_DEBUG, p1, p2, p3, p4); + LL_DEBUGS("LLSingleton") << args << LL_ENDL; } //static -void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logerrs(const string_params& args) { - log(LLError::LEVEL_ERROR, p1, p2, p3, p4); - // The other important side effect of LL_ERRS() is - // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG) - std::ostringstream out; - out << p1 << p2 << p3 << p4; - LLERROR_CRASH; + LL_ERRS("LLSingleton") << args << LL_ENDL; } std::string LLSingletonBase::demangle(const char* mangled) diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 30a5b21cf8..2eb39c6c8c 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -34,6 +34,7 @@ #include "lockstatic.h" #include "llthread.h" // on_main_thread() #include "llmainthreadtask.h" +#include <initializer_list> class LLSingletonBase: private boost::noncopyable { @@ -111,14 +112,13 @@ protected: void capture_dependency(); // delegate logging calls to llsingleton.cpp - static void logerrs(const char* p1, const char* p2="", - const char* p3="", const char* p4=""); - static void logwarns(const char* p1, const char* p2="", - const char* p3="", const char* p4=""); - static void loginfos(const char* p1, const char* p2="", - const char* p3="", const char* p4=""); - static void logdebugs(const char* p1, const char* p2="", - const char* p3="", const char* p4=""); +public: + typedef std::initializer_list<std::string_view> string_params; +protected: + static void logerrs (const string_params&); + static void logwarns (const string_params&); + static void loginfos (const string_params&); + static void logdebugs(const string_params&); static std::string demangle(const char* mangled); // these classname() declarations restate template functions declared in // llerror.h because we avoid #including that here @@ -327,8 +327,8 @@ private: // init stack to its previous size BEFORE logging so log-machinery // LLSingletons don't record a dependency on DERIVED_TYPE! LLSingleton_manage_master<DERIVED_TYPE>().reset_initializing(prev_size); - logwarns("Error constructing ", classname<DERIVED_TYPE>().c_str(), - ": ", err.what()); + logwarns({"Error constructing ", classname<DERIVED_TYPE>(), + ": ", err.what()}); // There isn't a separate EInitState value meaning "we attempted // to construct this LLSingleton subclass but could not," so use // DELETED. That seems slightly more appropriate than UNINITIALIZED. @@ -356,8 +356,8 @@ private: // BEFORE logging, so log-machinery LLSingletons don't record a // dependency on DERIVED_TYPE! pop_initializing(lk->mInstance); - logwarns("Error in ", classname<DERIVED_TYPE>().c_str(), - "::initSingleton(): ", err.what()); + logwarns({"Error in ", classname<DERIVED_TYPE>(), + "::initSingleton(): ", err.what()}); // Get rid of the instance entirely. This call depends on our // recursive_mutex. We could have a deleteSingleton(LockStatic&) // overload and pass lk, but we don't strictly need it. @@ -506,9 +506,9 @@ public: case CONSTRUCTING: // here if DERIVED_TYPE's constructor (directly or indirectly) // calls DERIVED_TYPE::getInstance() - logerrs("Tried to access singleton ", - classname<DERIVED_TYPE>().c_str(), - " from singleton constructor!"); + logerrs({"Tried to access singleton ", + classname<DERIVED_TYPE>(), + " from singleton constructor!"}); return nullptr; case INITIALIZING: @@ -523,9 +523,9 @@ public: case DELETED: // called after deleteSingleton() - logwarns("Trying to access deleted singleton ", - classname<DERIVED_TYPE>().c_str(), - " -- creating new instance"); + logwarns({"Trying to access deleted singleton ", + classname<DERIVED_TYPE>(), + " -- creating new instance"}); // fall through case UNINITIALIZED: case QUEUED: @@ -552,8 +552,8 @@ public: } // unlock 'lk' // Per the comment block above, dispatch to the main thread. - loginfos(classname<DERIVED_TYPE>().c_str(), - "::getInstance() dispatching to main thread"); + loginfos({classname<DERIVED_TYPE>(), + "::getInstance() dispatching to main thread"}); auto instance = LLMainThreadTask::dispatch( [](){ // VERY IMPORTANT to call getInstance() on the main thread, @@ -563,16 +563,16 @@ public: // the main thread processes them, only the FIRST such request // actually constructs the instance -- every subsequent one // simply returns the existing instance. - loginfos(classname<DERIVED_TYPE>().c_str(), - "::getInstance() on main thread"); + loginfos({classname<DERIVED_TYPE>(), + "::getInstance() on main thread"}); return getInstance(); }); // record the dependency chain tracked on THIS thread, not the main // thread (consider a getInstance() overload with a tag param that // suppresses dep tracking when dispatched to the main thread) capture_dependency(instance); - loginfos(classname<DERIVED_TYPE>().c_str(), - "::getInstance() returning on requesting thread"); + loginfos({classname<DERIVED_TYPE>(), + "::getInstance() returning on requesting thread"}); return instance; } @@ -641,16 +641,16 @@ private: // For organizational purposes this function shouldn't be called twice if (lk->mInitState != super::UNINITIALIZED) { - super::logerrs("Tried to initialize singleton ", - super::template classname<DERIVED_TYPE>().c_str(), - " twice!"); + super::logerrs({"Tried to initialize singleton ", + super::template classname<DERIVED_TYPE>(), + " twice!"}); return nullptr; } else if (on_main_thread()) { // on the main thread, simply construct instance while holding lock - super::logdebugs(super::template classname<DERIVED_TYPE>().c_str(), - "::initParamSingleton()"); + super::logdebugs({super::template classname<DERIVED_TYPE>(), + "::initParamSingleton()"}); super::constructSingleton(lk, std::forward<Args>(args)...); return lk->mInstance; } @@ -662,8 +662,8 @@ private: lk->mInitState = super::QUEUED; // very important to unlock here so main thread can actually process lk.unlock(); - super::loginfos(super::template classname<DERIVED_TYPE>().c_str(), - "::initParamSingleton() dispatching to main thread"); + super::loginfos({super::template classname<DERIVED_TYPE>(), + "::initParamSingleton() dispatching to main thread"}); // Normally it would be the height of folly to reference-bind // 'args' into a lambda to be executed on some other thread! By // the time that thread executed the lambda, the references would @@ -674,12 +674,12 @@ private: // references. auto instance = LLMainThreadTask::dispatch( [&](){ - super::loginfos(super::template classname<DERIVED_TYPE>().c_str(), - "::initParamSingleton() on main thread"); + super::loginfos({super::template classname<DERIVED_TYPE>(), + "::initParamSingleton() on main thread"}); return initParamSingleton_(std::forward<Args>(args)...); }); - super::loginfos(super::template classname<DERIVED_TYPE>().c_str(), - "::initParamSingleton() returning on requesting thread"); + super::loginfos({super::template classname<DERIVED_TYPE>(), + "::initParamSingleton() returning on requesting thread"}); return instance; } } @@ -707,14 +707,14 @@ public: { case super::UNINITIALIZED: case super::QUEUED: - super::logerrs("Uninitialized param singleton ", - super::template classname<DERIVED_TYPE>().c_str()); + super::logerrs({"Uninitialized param singleton ", + super::template classname<DERIVED_TYPE>()}); break; case super::CONSTRUCTING: - super::logerrs("Tried to access param singleton ", - super::template classname<DERIVED_TYPE>().c_str(), - " from singleton constructor!"); + super::logerrs({"Tried to access param singleton ", + super::template classname<DERIVED_TYPE>(), + " from singleton constructor!"}); break; case super::INITIALIZING: @@ -726,8 +726,8 @@ public: return lk->mInstance; case super::DELETED: - super::logerrs("Trying to access deleted param singleton ", - super::template classname<DERIVED_TYPE>().c_str()); + super::logerrs({"Trying to access deleted param singleton ", + super::template classname<DERIVED_TYPE>()}); break; } diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 1f8d558fbe..eff4dd91ea 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -55,6 +55,7 @@ #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_integral.hpp> #include <boost/type_traits/is_float.hpp> +#include "llfasttimer.h" using namespace llsd; @@ -925,8 +926,12 @@ LLMemoryInfo& LLMemoryInfo::refresh() return *this; } +static LLTrace::BlockTimerStatHandle FTM_MEMINFO_LOAD_STATS("MemInfo Load Stats"); + LLSD LLMemoryInfo::loadStatsMap() { + LL_RECORD_BLOCK_TIME(FTM_MEMINFO_LOAD_STATS); + // This implementation is derived from stream() code (as of 2011-06-29). Stats stats; @@ -948,24 +953,11 @@ LLSD LLMemoryInfo::loadStatsMap() stats.add("Total Virtual KB", state.ullTotalVirtual/div); stats.add("Avail Virtual KB", state.ullAvailVirtual/div); - PERFORMANCE_INFORMATION perf; - perf.cb = sizeof(perf); - GetPerformanceInfo(&perf, sizeof(perf)); - - SIZE_T pagekb(perf.PageSize/1024); - stats.add("CommitTotal KB", perf.CommitTotal * pagekb); - stats.add("CommitLimit KB", perf.CommitLimit * pagekb); - stats.add("CommitPeak KB", perf.CommitPeak * pagekb); - stats.add("PhysicalTotal KB", perf.PhysicalTotal * pagekb); - stats.add("PhysicalAvail KB", perf.PhysicalAvailable * pagekb); - stats.add("SystemCache KB", perf.SystemCache * pagekb); - stats.add("KernelTotal KB", perf.KernelTotal * pagekb); - stats.add("KernelPaged KB", perf.KernelPaged * pagekb); - stats.add("KernelNonpaged KB", perf.KernelNonpaged * pagekb); - stats.add("PageSize KB", pagekb); - stats.add("HandleCount", perf.HandleCount); - stats.add("ProcessCount", perf.ProcessCount); - stats.add("ThreadCount", perf.ThreadCount); + // SL-12122 - Call to GetPerformanceInfo() was removed here. Took + // on order of 10 ms, causing unacceptable frame time spike every + // second, and results were never used. If this is needed in the + // future, must find a way to avoid frame time impact (e.g. move + // to another thread, call much less often). PROCESS_MEMORY_COUNTERS_EX pmem; pmem.cb = sizeof(pmem); diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp index c275b90120..e4f229dd16 100644 --- a/indra/llcommon/lluriparser.cpp +++ b/indra/llcommon/lluriparser.cpp @@ -29,10 +29,13 @@ #include "linden_common.h" #include "lluriparser.h" +#if LL_DARWIN +#include <signal.h> +#include <setjmp.h> +#endif + LLUriParser::LLUriParser(const std::string& u) : mTmpScheme(false), mNormalizedTmp(false), mRes(0) { - mState.uri = &mUri; - if (u.find("://") == std::string::npos) { mNormalizedUri = "http://"; @@ -51,7 +54,7 @@ LLUriParser::~LLUriParser() S32 LLUriParser::parse() { - mRes = uriParseUriA(&mState, mNormalizedUri.c_str()); + mRes = uriParseSingleUriA(&mUri, mNormalizedUri.c_str(), NULL); return mRes; } @@ -158,31 +161,69 @@ void LLUriParser::extractParts() } } +#if LL_DARWIN +typedef void(*sighandler_t)(int); +jmp_buf return_to_normalize; +void uri_signal_handler(int signal) +{ + // Apparently signal handler throwing an exception doesn't work. + // This is ugly and unsafe due to not unwinding content of uriparser library, + // but unless we have a way to catch this as NSexception, jump appears to be the only option. + longjmp(return_to_normalize, 1 /*setjmp will return this value*/); +} +#endif + S32 LLUriParser::normalize() { mNormalizedTmp = mTmpScheme; if (!mRes) { - mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST); - - if (!mRes) - { - S32 chars_required; - mRes = uriToStringCharsRequiredA(&mUri, &chars_required); - - if (!mRes) - { - chars_required++; - std::vector<char> label_buf(chars_required); - mRes = uriToStringA(&label_buf[0], &mUri, chars_required, NULL); - - if (!mRes) - { - mNormalizedUri = &label_buf[mTmpScheme ? 7 : 0]; - mTmpScheme = false; - } - } - } +#if LL_DARWIN + sighandler_t last_handler; + last_handler = signal(SIGILL, &uri_signal_handler); // illegal instruction + if (setjmp(return_to_normalize)) + { + // Issue: external library crashed via signal + // If you encountered this, please try to figure out what's wrong: + // 1. Verify that library's input is 'sane' + // 2. Check if we have an NSexception to work with (unlikely) + // 3. See if passing same string causes exception to repeat + // + // Crash happens at uriNormalizeSyntaxExA + // Warning!!! This does not properly unwind stack, + // if this can be handled by NSexception, it needs to be remade + llassert(0); + + LL_WARNS() << "Uriparser crashed with SIGILL, while processing: " << mNormalizedUri << LL_ENDL; + signal(SIGILL, last_handler); + return 1; + } +#endif + + mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST); + +#if LL_DARWIN + signal(SIGILL, last_handler); +#endif + + if (!mRes) + { + S32 chars_required; + mRes = uriToStringCharsRequiredA(&mUri, &chars_required); + + if (!mRes) + { + chars_required++; + std::vector<char> label_buf(chars_required); + mRes = uriToStringA(&label_buf[0], &mUri, chars_required, NULL); + + if (!mRes) + { + mNormalizedUri = &label_buf[mTmpScheme ? 7 : 0]; + mTmpScheme = false; + } + } + } } if(mTmpScheme) diff --git a/indra/llcommon/lluriparser.h b/indra/llcommon/lluriparser.h index cfbf54f3c8..92626b9054 100644 --- a/indra/llcommon/lluriparser.h +++ b/indra/llcommon/lluriparser.h @@ -76,7 +76,6 @@ private: std::string mFragment; std::string mNormalizedUri; - UriParserStateA mState; UriUriA mUri; S32 mRes; diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index cdc2bf8c87..148c18aabe 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -26,6 +26,7 @@ */ #include <vector> +#include <stdexcept> #include "linden_common.h" @@ -69,25 +70,41 @@ namespace namespace { - static bool fatalWasCalled; - LLError::ErrFatalHookResult fatalHook(const std::string&) + static bool fatalWasCalled = false; + struct FatalWasCalled: public std::runtime_error { - fatalWasCalled = true; - return LLError::ERR_DO_NOT_CRASH; - } + FatalWasCalled(const std::string& what): std::runtime_error(what) {} + }; + void fatalCall(const std::string& msg) { throw FatalWasCalled(msg); } } +// Because we use LLError::setFatalFunction(fatalCall), any LL_ERRS call we +// issue will throw FatalWasCalled. But we want the test program to continue. +// So instead of writing: +// LL_ERRS("tag") << "some message" << LL_ENDL; +// write: +// CATCH(LL_ERRS("tag"), "some message"); +#define CATCH(logcall, expr) \ + try \ + { \ + logcall << expr << LL_ENDL; \ + } \ + catch (const FatalWasCalled&) \ + { \ + fatalWasCalled = true; \ + } + namespace tut { class TestRecorder : public LLError::Recorder { public: TestRecorder() - { - showTime(false); - } + { + showTime(false); + } virtual ~TestRecorder() - {} + {} virtual void recordMessage(LLError::ELevel level, const std::string& message) @@ -124,7 +141,7 @@ namespace tut mPriorErrorSettings = LLError::saveAndResetSettings(); LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - LLError::setFatalHook(fatalHook); + LLError::setFatalFunction(fatalCall); LLError::addRecorder(mRecorder); } @@ -256,7 +273,7 @@ namespace LL_DEBUGS("WriteTag","AnotherTag") << "one" << LL_ENDL; LL_INFOS("WriteTag") << "two" << LL_ENDL; LL_WARNS("WriteTag") << "three" << LL_ENDL; - LL_ERRS("WriteTag") << "four" << LL_ENDL; + CATCH(LL_ERRS("WriteTag"), "four"); } }; @@ -384,7 +401,7 @@ namespace std::string errorReturningLocation() { - LL_ERRS() << "die" << LL_ENDL; int this_line = __LINE__; + int this_line = __LINE__; CATCH(LL_ERRS(), "die"); return locationString(this_line); } } @@ -705,7 +722,7 @@ public: static void doDebug() { LL_DEBUGS() << "add dice" << LL_ENDL; } static void doInfo() { LL_INFOS() << "any idea" << LL_ENDL; } static void doWarn() { LL_WARNS() << "aim west" << LL_ENDL; } - static void doError() { LL_ERRS() << "ate eels" << LL_ENDL; } + static void doError() { CATCH(LL_ERRS(), "ate eels"); } static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } }; @@ -716,7 +733,7 @@ public: static void doDebug() { LL_DEBUGS() << "bed down" << LL_ENDL; } static void doInfo() { LL_INFOS() << "buy iron" << LL_ENDL; } static void doWarn() { LL_WARNS() << "bad word" << LL_ENDL; } - static void doError() { LL_ERRS() << "big easy" << LL_ENDL; } + static void doError() { CATCH(LL_ERRS(), "big easy"); } static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } }; @@ -781,33 +798,30 @@ namespace tut // proper cached, efficient lookup of filtering void ErrorTestObject::test<15>() { - LLError::setDefaultLevel(LLError::LEVEL_NONE); - - // Note that the setFatalHook in the ErrorTestData constructor - // increments the shouldLogCallCount + LLError::setDefaultLevel(LLError::LEVEL_NONE); TestAlpha::doInfo(); ensure_message_count(0); - ensure_equals("first check", LLError::shouldLogCallCount(), 2); + ensure_equals("first check", LLError::shouldLogCallCount(), 1); TestAlpha::doInfo(); ensure_message_count(0); - ensure_equals("second check", LLError::shouldLogCallCount(), 2); + ensure_equals("second check", LLError::shouldLogCallCount(), 1); LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG); TestAlpha::doInfo(); ensure_message_count(1); - ensure_equals("third check", LLError::shouldLogCallCount(), 3); + ensure_equals("third check", LLError::shouldLogCallCount(), 2); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("fourth check", LLError::shouldLogCallCount(), 3); + ensure_equals("fourth check", LLError::shouldLogCallCount(), 2); LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("fifth check", LLError::shouldLogCallCount(), 4); + ensure_equals("fifth check", LLError::shouldLogCallCount(), 3); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("sixth check", LLError::shouldLogCallCount(), 4); + ensure_equals("sixth check", LLError::shouldLogCallCount(), 3); } template<> template<> @@ -881,13 +895,10 @@ namespace tut namespace { std::string writeTagWithSpaceReturningLocation() - { - LL_DEBUGS("Write Tag") << "not allowed" << LL_ENDL; int this_line = __LINE__; - - std::ostringstream location; - location << LLError::abbreviateFile(__FILE__).c_str() << "(" << this_line << ")"; - return location.str(); - } + { + int this_line = __LINE__; CATCH(LL_DEBUGS("Write Tag"), "not allowed"); + return locationString(this_line); + } }; namespace tut @@ -901,9 +912,9 @@ namespace tut std::string location = writeTagWithSpaceReturningLocation(); std::string expected = "Space is not allowed in a log tag at " + location; - ensure_message_field_equals(0, LEVEL_FIELD, "ERROR"); - ensure_message_field_equals(0, MSG_FIELD, expected); - ensure("fatal callback called", fatalWasCalled); + ensure_message_field_equals(0, LEVEL_FIELD, "ERROR"); + ensure_message_field_equals(0, MSG_FIELD, expected); + ensure("fatal callback called", fatalWasCalled); } } diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index a6c44d5fdd..3779fb41bc 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -44,26 +44,24 @@ #include <list> #include <string> -// statically reference the function in test.cpp... it's short, we could -// replicate, but better to reuse -extern LLError::ErrFatalHookResult wouldHaveCrashed(const std::string& message); - struct WrapLLErrs { - WrapLLErrs() + WrapLLErrs(): // Resetting Settings discards the default Recorder that writes to // stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the // console output of successful tests, potentially confusing things. - :mPriorErrorSettings(LLError::saveAndResetSettings()) - ,mPriorFatalHook(LLError::getFatalHook()) + mPriorErrorSettings(LLError::saveAndResetSettings()), + // Save shutdown function called by LL_ERRS + mPriorFatal(LLError::getFatalFunction()) { // Make LL_ERRS call our own operator() method - LLError::setFatalHook(boost::bind(&WrapLLErrs::operator(), this, _1)); + LLError::setFatalFunction( + [this](const std::string& message){ (*this)(message); }); } ~WrapLLErrs() { - LLError::setFatalHook(mPriorFatalHook); + LLError::setFatalFunction(mPriorFatal); LLError::restoreSettings(mPriorErrorSettings); } @@ -72,7 +70,7 @@ struct WrapLLErrs FatalException(const std::string& what): LLException(what) {} }; - LLError::ErrFatalHookResult operator()(const std::string& message) + void operator()(const std::string& message) { // Save message for later in case consumer wants to sense the result directly error = message; @@ -108,7 +106,7 @@ struct WrapLLErrs std::string error; LLError::SettingsStoragePtr mPriorErrorSettings; - LLError::FatalHook mPriorFatalHook; + LLError::FatalFunction mPriorFatal; }; /** @@ -198,12 +196,13 @@ public: // with that output. If it turns out that saveAndResetSettings() has // some bad effect, give up and just let the DEBUG level log messages // display. - : boost::noncopyable() - , mOldSettings(LLError::saveAndResetSettings()) - , mPriorFatalHook(LLError::getFatalHook()) - , mRecorder(new CaptureLogRecorder()) + : boost::noncopyable(), + mFatalFunction(LLError::getFatalFunction()), + mOldSettings(LLError::saveAndResetSettings()), + mRecorder(new CaptureLogRecorder()) { - LLError::setFatalHook(wouldHaveCrashed); + // reinstate the FatalFunction we just reset + LLError::setFatalFunction(mFatalFunction); LLError::setDefaultLevel(level); LLError::addRecorder(mRecorder); } @@ -212,7 +211,6 @@ public: { LLError::removeRecorder(mRecorder); LLError::restoreSettings(mOldSettings); - LLError::setFatalHook(mPriorFatalHook); } /// Don't assume the message we want is necessarily the LAST log message @@ -220,18 +218,18 @@ public: /// for the sought string. std::string messageWith(const std::string& search, bool required=true) { - return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->messageWith(search, required); + return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->messageWith(search, required); } std::ostream& streamto(std::ostream& out) const { - return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out); + return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out); } private: + LLError::FatalFunction mFatalFunction; LLError::SettingsStoragePtr mOldSettings; - LLError::FatalHook mPriorFatalHook; - LLError::RecorderPtr mRecorder; + LLError::RecorderPtr mRecorder; }; #endif /* ! defined(LL_WRAPLLERRS_H) */ diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 8efaf75582..8bb6a657b1 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -179,6 +179,7 @@ if (DARWIN) libaprutil-1.0.dylib libexception_handler.dylib libnghttp2*.dylib + liburiparser*.dylib ${EXPAT_COPY} ) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 0f76ff23ea..ba31290c24 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -567,16 +567,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) // Use the viewer-based thread-safe API which has a // fast/safe check for proxy enable. Would like to // encapsulate this someway... - if (LLProxy::instanceExists()) - { - // Make sure proxy won't be initialized from here, - // it might conflict with LLStartUp::startLLProxy() - LLProxy::getInstance()->applyProxySettings(mCurlHandle); - } - else - { - LL_WARNS() << "Proxy is not initialized!" << LL_ENDL; - } + // Make sure proxy won't be getInstance() from here, + // it is not thread safe + LLProxy::applyProxySettings(mCurlHandle); } else if (gpolicy.mHttpProxy.size()) @@ -817,6 +810,7 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void const size_t do_size((std::min)(req_size, body_size - op->mCurlBodyPos)); const size_t read_size(op->mReqBody->read(op->mCurlBodyPos, static_cast<char *>(data), do_size)); + // FIXME: singleton's instance() is Thread unsafe! Even if stats accumulators inside are. HTTPStats::instance().recordDataUp(read_size); op->mCurlBodyPos += read_size; return read_size; @@ -1007,11 +1001,20 @@ CURLcode HttpOpRequest::curlSslCtxCallback(CURL *curl, void *sslctx, void *userd { HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata)); - if (op->mCallbackSSLVerify) - { - SSL_CTX * ctx = (SSL_CTX *)sslctx; - // disable any default verification for server certs - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + if (op->mCallbackSSLVerify) + { + SSL_CTX * ctx = (SSL_CTX *)sslctx; + if (op->mReqOptions && op->mReqOptions->getSSLVerifyPeer()) + { + // verification for ssl certs + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); + } + else + { + // disable any default verification for server certs + // Ex: setting urls (assume non-SL) for parcel media in LLFloaterURLEntry + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + } // set the verification callback. SSL_CTX_set_cert_verify_callback(ctx, sslCertVerifyCallback, userdata); // the calls are void diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 7a0c8cd8f5..aed8943439 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -583,29 +583,39 @@ static void bilinear_scale(const U8 *src, U32 srcW, U32 srcH, U32 srcCh, U32 src // LLImage //--------------------------------------------------------------------------- -LLImage::LLImage(bool use_new_byte_range, S32 minimal_reverse_byte_range_percent) +//static +std::string LLImage::sLastErrorMessage; +LLMutex* LLImage::sMutex = NULL; +bool LLImage::sUseNewByteRange = false; +S32 LLImage::sMinimalReverseByteRangePercent = 75; + +//static +void LLImage::initClass(bool use_new_byte_range, S32 minimal_reverse_byte_range_percent) { - mMutex = new LLMutex(); - mUseNewByteRange = use_new_byte_range; - mMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent; + sUseNewByteRange = use_new_byte_range; + sMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent; + sMutex = new LLMutex(); } -LLImage::~LLImage() +//static +void LLImage::cleanupClass() { - delete mMutex; - mMutex = NULL; + delete sMutex; + sMutex = NULL; } -const std::string& LLImage::getLastErrorMessage() +//static +const std::string& LLImage::getLastError() { static const std::string noerr("No Error"); - return mLastErrorMessage.empty() ? noerr : mLastErrorMessage; + return sLastErrorMessage.empty() ? noerr : sLastErrorMessage; } -void LLImage::setLastErrorMessage(const std::string& message) +//static +void LLImage::setLastError(const std::string& message) { - LLMutexLock m(mMutex); - mLastErrorMessage = message; + LLMutexLock m(sMutex); + sLastErrorMessage = message; } //--------------------------------------------------------------------------- diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 9f8d061293..f66b1666d7 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -30,7 +30,6 @@ #include "lluuid.h" #include "llstring.h" #include "llpointer.h" -#include "llsingleton.h" #include "lltrace.h" const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2 @@ -88,26 +87,25 @@ typedef enum e_image_codec //============================================================================ // library initialization class +// LLImage is frequently used in threads so do not convert it to LLSingleton -class LLImage : public LLParamSingleton<LLImage> +class LLImage { - LLSINGLETON(LLImage, bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75); - ~LLImage(); public: + static void initClass(bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75); + static void cleanupClass(); - const std::string& getLastErrorMessage(); - static const std::string& getLastError() { return getInstance()->getLastErrorMessage(); }; - void setLastErrorMessage(const std::string& message); - static void setLastError(const std::string& message) { getInstance()->setLastErrorMessage(message); } - - bool useNewByteRange() { return mUseNewByteRange; } - S32 getReverseByteRangePercent() { return mMinimalReverseByteRangePercent; } - -private: - LLMutex* mMutex; - std::string mLastErrorMessage; - bool mUseNewByteRange; - S32 mMinimalReverseByteRangePercent; + static const std::string& getLastError(); + static void setLastError(const std::string& message); + + static bool useNewByteRange() { return sUseNewByteRange; } + static S32 getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; } + +protected: + static LLMutex* sMutex; + static std::string sLastErrorMessage; + static bool sUseNewByteRange; + static S32 sMinimalReverseByteRangePercent; }; //============================================================================ diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index 71cab0554d..4bff21610f 100644 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -281,7 +281,7 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r S32 bytes; S32 new_bytes = (S32) (sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor); S32 old_bytes = (S32)((F32)(w*h*comp)*rate); - bytes = (LLImage::getInstance()->useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes); + bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes); bytes = llmax(bytes, calcHeaderSizeJ2C()); return bytes; } @@ -322,7 +322,7 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes) { S32 bytes_needed = calcDataSize(discard_level); // Use TextureReverseByteRange percent (see settings.xml) of the optimal size to qualify as correct rendering for the given discard level - if (bytes >= (bytes_needed*LLImage::getInstance()->getReverseByteRangePercent()/100)) + if (bytes >= (bytes_needed*LLImage::getReverseByteRangePercent()/100)) { break; } diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 11647c5518..18bc1b5a91 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -41,7 +41,7 @@ /// Exported functions ///---------------------------------------------------------------------------- static const std::string INV_ITEM_ID_LABEL("item_id"); -static const std::string INV_FOLDER_ID_LABEL("folder_id"); +static const std::string INV_FOLDER_ID_LABEL("cat_id"); static const std::string INV_PARENT_ID_LABEL("parent_id"); static const std::string INV_ASSET_TYPE_LABEL("type"); static const std::string INV_PREFERRED_TYPE_LABEL("preferred_type"); @@ -228,22 +228,6 @@ BOOL LLInventoryObject::importLegacyStream(std::istream& input_stream) return TRUE; } -// exportFile should be replaced with exportLegacyStream -// not sure whether exportLegacyStream(llofstream(fp)) would work, fp may need to get icramented... -BOOL LLInventoryObject::exportFile(LLFILE* fp, BOOL) const -{ - std::string uuid_str; - fprintf(fp, "\tinv_object\t0\n\t{\n"); - mUUID.toString(uuid_str); - fprintf(fp, "\t\tobj_id\t%s\n", uuid_str.c_str()); - mParentUUID.toString(uuid_str); - fprintf(fp, "\t\tparent_id\t%s\n", uuid_str.c_str()); - fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); - fprintf(fp, "\t\tname\t%s|\n", mName.c_str()); - fprintf(fp,"\t}\n"); - return TRUE; -} - BOOL LLInventoryObject::exportLegacyStream(std::ostream& output_stream, BOOL) const { std::string uuid_str; @@ -604,215 +588,6 @@ BOOL LLInventoryItem::unpackMessage(LLMessageSystem* msg, const char* block, S32 } // virtual -BOOL LLInventoryItem::importFile(LLFILE* fp) -{ - // *NOTE: Changing the buffer size will require changing the scanf - // calls below. - char buffer[MAX_STRING]; /* Flawfinder: ignore */ - char keyword[MAX_STRING]; /* Flawfinder: ignore */ - char valuestr[MAX_STRING]; /* Flawfinder: ignore */ - char junk[MAX_STRING]; /* Flawfinder: ignore */ - BOOL success = TRUE; - - keyword[0] = '\0'; - valuestr[0] = '\0'; - - mInventoryType = LLInventoryType::IT_NONE; - mAssetUUID.setNull(); - while(success && (!feof(fp))) - { - if (fgets(buffer, MAX_STRING, fp) == NULL) - { - buffer[0] = '\0'; - } - - sscanf(buffer, " %254s %254s", keyword, valuestr); /* Flawfinder: ignore */ - if(0 == strcmp("{",keyword)) - { - continue; - } - if(0 == strcmp("}", keyword)) - { - break; - } - else if(0 == strcmp("item_id", keyword)) - { - mUUID.set(valuestr); - } - else if(0 == strcmp("parent_id", keyword)) - { - mParentUUID.set(valuestr); - } - else if(0 == strcmp("permissions", keyword)) - { - success = mPermissions.importFile(fp); - } - else if(0 == strcmp("sale_info", keyword)) - { - // Sale info used to contain next owner perm. It is now in - // the permissions. Thus, we read that out, and fix legacy - // objects. It's possible this op would fail, but it - // should pick up the vast majority of the tasks. - BOOL has_perm_mask = FALSE; - U32 perm_mask = 0; - success = mSaleInfo.importFile(fp, has_perm_mask, perm_mask); - if(has_perm_mask) - { - if(perm_mask == PERM_NONE) - { - perm_mask = mPermissions.getMaskOwner(); - } - // fair use fix. - if(!(perm_mask & PERM_COPY)) - { - perm_mask |= PERM_TRANSFER; - } - mPermissions.setMaskNext(perm_mask); - } - } - else if(0 == strcmp("shadow_id", keyword)) - { - mAssetUUID.set(valuestr); - LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); - cipher.decrypt(mAssetUUID.mData, UUID_BYTES); - } - else if(0 == strcmp("asset_id", keyword)) - { - mAssetUUID.set(valuestr); - } - else if(0 == strcmp("type", keyword)) - { - mType = LLAssetType::lookup(valuestr); - } - else if(0 == strcmp("inv_type", keyword)) - { - mInventoryType = LLInventoryType::lookup(std::string(valuestr)); - } - else if(0 == strcmp("flags", keyword)) - { - sscanf(valuestr, "%x", &mFlags); - } - else if(0 == strcmp("name", keyword)) - { - //strcpy(valuestr, buffer + strlen(keyword) + 3); - // *NOTE: Not ANSI C, but widely supported. - sscanf( /* Flawfinder: ignore */ - buffer, - " %254s%254[\t]%254[^|]", - keyword, junk, valuestr); - - // IW: sscanf chokes and puts | in valuestr if there's no name - if (valuestr[0] == '|') - { - valuestr[0] = '\000'; - } - - mName.assign(valuestr); - LLStringUtil::replaceNonstandardASCII(mName, ' '); - LLStringUtil::replaceChar(mName, '|', ' '); - } - else if(0 == strcmp("desc", keyword)) - { - //strcpy(valuestr, buffer + strlen(keyword) + 3); - // *NOTE: Not ANSI C, but widely supported. - sscanf( /* Flawfinder: ignore */ - buffer, - " %254s%254[\t]%254[^|]", - keyword, junk, valuestr); - - if (valuestr[0] == '|') - { - valuestr[0] = '\000'; - } - - disclaimMem(mDescription); - mDescription.assign(valuestr); - claimMem(mDescription); - LLStringUtil::replaceNonstandardASCII(mDescription, ' '); - /* TODO -- ask Ian about this code - const char *donkey = mDescription.c_str(); - if (donkey[0] == '|') - { - LL_ERRS() << "Donkey" << LL_ENDL; - } - */ - } - else if(0 == strcmp("creation_date", keyword)) - { - S32 date; - sscanf(valuestr, "%d", &date); - mCreationDate = date; - } - else - { - LL_WARNS() << "unknown keyword '" << keyword - << "' in inventory import of item " << mUUID << LL_ENDL; - } - } - - // Need to convert 1.0 simstate files to a useful inventory type - // and potentially deal with bad inventory tyes eg, a landmark - // marked as a texture. - if((LLInventoryType::IT_NONE == mInventoryType) - || !inventory_and_asset_types_match(mInventoryType, mType)) - { - LL_DEBUGS() << "Resetting inventory type for " << mUUID << LL_ENDL; - mInventoryType = LLInventoryType::defaultForAssetType(mType); - } - - mPermissions.initMasks(mInventoryType); - - return success; -} - -BOOL LLInventoryItem::exportFile(LLFILE* fp, BOOL include_asset_key) const -{ - std::string uuid_str; - fprintf(fp, "\tinv_item\t0\n\t{\n"); - mUUID.toString(uuid_str); - fprintf(fp, "\t\titem_id\t%s\n", uuid_str.c_str()); - mParentUUID.toString(uuid_str); - fprintf(fp, "\t\tparent_id\t%s\n", uuid_str.c_str()); - mPermissions.exportFile(fp); - - // Check for permissions to see the asset id, and if so write it - // out as an asset id. Otherwise, apply our cheesy encryption. - if(include_asset_key) - { - U32 mask = mPermissions.getMaskBase(); - if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) - || (mAssetUUID.isNull())) - { - mAssetUUID.toString(uuid_str); - fprintf(fp, "\t\tasset_id\t%s\n", uuid_str.c_str()); - } - else - { - LLUUID shadow_id(mAssetUUID); - LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); - cipher.encrypt(shadow_id.mData, UUID_BYTES); - shadow_id.toString(uuid_str); - fprintf(fp, "\t\tshadow_id\t%s\n", uuid_str.c_str()); - } - } - else - { - LLUUID::null.toString(uuid_str); - fprintf(fp, "\t\tasset_id\t%s\n", uuid_str.c_str()); - } - fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); - const std::string inv_type_str = LLInventoryType::lookup(mInventoryType); - if(!inv_type_str.empty()) fprintf(fp, "\t\tinv_type\t%s\n", inv_type_str.c_str()); - fprintf(fp, "\t\tflags\t%08x\n", mFlags); - mSaleInfo.exportFile(fp); - fprintf(fp, "\t\tname\t%s|\n", mName.c_str()); - fprintf(fp, "\t\tdesc\t%s|\n", mDescription.c_str()); - fprintf(fp, "\t\tcreation_date\t%d\n", (S32) mCreationDate); - fprintf(fp,"\t}\n"); - return TRUE; -} - -// virtual BOOL LLInventoryItem::importLegacyStream(std::istream& input_stream) { // *NOTE: Changing the buffer size will require changing the scanf @@ -1463,90 +1238,7 @@ void LLInventoryCategory::unpackMessage(LLMessageSystem* msg, msg->getStringFast(block, _PREHASH_Name, mName, block_num); LLStringUtil::replaceNonstandardASCII(mName, ' '); } - -// virtual -BOOL LLInventoryCategory::importFile(LLFILE* fp) -{ - // *NOTE: Changing the buffer size will require changing the scanf - // calls below. - char buffer[MAX_STRING]; /* Flawfinder: ignore */ - char keyword[MAX_STRING]; /* Flawfinder: ignore */ - char valuestr[MAX_STRING]; /* Flawfinder: ignore */ - keyword[0] = '\0'; - valuestr[0] = '\0'; - while(!feof(fp)) - { - if (fgets(buffer, MAX_STRING, fp) == NULL) - { - buffer[0] = '\0'; - } - - sscanf( /* Flawfinder: ignore */ - buffer, - " %254s %254s", - keyword, valuestr); - if(0 == strcmp("{",keyword)) - { - continue; - } - if(0 == strcmp("}", keyword)) - { - break; - } - else if(0 == strcmp("cat_id", keyword)) - { - mUUID.set(valuestr); - } - else if(0 == strcmp("parent_id", keyword)) - { - mParentUUID.set(valuestr); - } - else if(0 == strcmp("type", keyword)) - { - mType = LLAssetType::lookup(valuestr); - } - else if(0 == strcmp("pref_type", keyword)) - { - mPreferredType = LLFolderType::lookup(valuestr); - } - else if(0 == strcmp("name", keyword)) - { - //strcpy(valuestr, buffer + strlen(keyword) + 3); - // *NOTE: Not ANSI C, but widely supported. - sscanf( /* Flawfinder: ignore */ - buffer, - " %254s %254[^|]", - keyword, valuestr); - mName.assign(valuestr); - LLStringUtil::replaceNonstandardASCII(mName, ' '); - LLStringUtil::replaceChar(mName, '|', ' '); - } - else - { - LL_WARNS() << "unknown keyword '" << keyword - << "' in inventory import category " << mUUID << LL_ENDL; - } - } - return TRUE; -} - -BOOL LLInventoryCategory::exportFile(LLFILE* fp, BOOL) const -{ - std::string uuid_str; - fprintf(fp, "\tinv_category\t0\n\t{\n"); - mUUID.toString(uuid_str); - fprintf(fp, "\t\tcat_id\t%s\n", uuid_str.c_str()); - mParentUUID.toString(uuid_str); - fprintf(fp, "\t\tparent_id\t%s\n", uuid_str.c_str()); - fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); - fprintf(fp, "\t\tpref_type\t%s\n", LLFolderType::lookup(mPreferredType).c_str()); - fprintf(fp, "\t\tname\t%s|\n", mName.c_str()); - fprintf(fp,"\t}\n"); - return TRUE; -} - - // virtual BOOL LLInventoryCategory::importLegacyStream(std::istream& input_stream) { @@ -1625,6 +1317,45 @@ BOOL LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, BOOL) return TRUE; } +LLSD LLInventoryCategory::exportLLSD() const +{ + LLSD cat_data; + cat_data[INV_FOLDER_ID_LABEL] = mUUID; + cat_data[INV_PARENT_ID_LABEL] = mParentUUID; + cat_data[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType); + cat_data[INV_PREFERRED_TYPE_LABEL] = LLFolderType::lookup(mPreferredType); + cat_data[INV_NAME_LABEL] = mName; + + return cat_data; +} + +bool LLInventoryCategory::importLLSD(const LLSD& cat_data) +{ + if (cat_data.has(INV_FOLDER_ID_LABEL)) + { + setUUID(cat_data[INV_FOLDER_ID_LABEL].asUUID()); + } + if (cat_data.has(INV_PARENT_ID_LABEL)) + { + setParent(cat_data[INV_PARENT_ID_LABEL].asUUID()); + } + if (cat_data.has(INV_ASSET_TYPE_LABEL)) + { + setType(LLAssetType::lookup(cat_data[INV_ASSET_TYPE_LABEL].asString())); + } + if (cat_data.has(INV_PREFERRED_TYPE_LABEL)) + { + setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString())); + } + if (cat_data.has(INV_NAME_LABEL)) + { + mName = cat_data[INV_NAME_LABEL].asString(); + LLStringUtil::replaceNonstandardASCII(mName, ' '); + LLStringUtil::replaceChar(mName, '|', ' '); + } + + return true; +} ///---------------------------------------------------------------------------- /// Local function definitions ///---------------------------------------------------------------------------- diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index 70b200e139..0f336a072f 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -95,8 +95,7 @@ public: // Implemented here so that a minimal information set can be transmitted // between simulator and viewer. //-------------------------------------------------------------------- - // virtual BOOL importFile(LLFILE* fp); - virtual BOOL exportFile(LLFILE* fp, BOOL include_asset_key = TRUE) const; + virtual BOOL importLegacyStream(std::istream& input_stream); virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const; @@ -197,8 +196,6 @@ public: // File Support //-------------------------------------------------------------------- public: - virtual BOOL importFile(LLFILE* fp); - virtual BOOL exportFile(LLFILE* fp, BOOL include_asset_key = TRUE) const; virtual BOOL importLegacyStream(std::istream& input_stream); virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const; @@ -247,7 +244,7 @@ public: LLInventoryCategory(const LLInventoryCategory* other); void copyCategory(const LLInventoryCategory* other); // LLRefCount requires custom copy protected: - ~LLInventoryCategory(); + virtual ~LLInventoryCategory(); //-------------------------------------------------------------------- // Accessors And Mutators @@ -269,11 +266,11 @@ public: // File Support //-------------------------------------------------------------------- public: - virtual BOOL importFile(LLFILE* fp); - virtual BOOL exportFile(LLFILE* fp, BOOL include_asset_key = TRUE) const; virtual BOOL importLegacyStream(std::istream& input_stream); virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const; + LLSD exportLLSD() const; + bool importLLSD(const LLSD& cat_data); //-------------------------------------------------------------------- // Member Variables //-------------------------------------------------------------------- diff --git a/indra/llinventory/llinventorysettings.cpp b/indra/llinventory/llinventorysettings.cpp index fdad50e8d4..81485b3a97 100644 --- a/indra/llinventory/llinventorysettings.cpp +++ b/indra/llinventory/llinventorysettings.cpp @@ -33,10 +33,6 @@ #include "llsingleton.h" #include "llinvtranslationbrdg.h" -//========================================================================= -namespace { - LLTranslationBridge::ptr_t sTranslator; -} //========================================================================= struct SettingsEntry : public LLDictionaryEntry @@ -49,7 +45,7 @@ struct SettingsEntry : public LLDictionaryEntry mLabel(name), mIconName(iconName) { - std::string transdname = sTranslator->getString(mLabel); + std::string transdname = LLSettingsType::getInstance()->mTranslator->getString(mLabel); if (!transdname.empty()) { mLabel = transdname; @@ -84,6 +80,16 @@ void LLSettingsDictionary::initSingleton() //========================================================================= +LLSettingsType::LLSettingsType(LLTranslationBridge::ptr_t &trans) +{ + mTranslator = trans; +} + +LLSettingsType::~LLSettingsType() +{ + mTranslator.reset(); +} + LLSettingsType::type_e LLSettingsType::fromInventoryFlags(U32 flags) { return (LLSettingsType::type_e)(flags & LLInventoryItemFlags::II_FLAGS_SUBTYPE_MASK); @@ -104,13 +110,3 @@ std::string LLSettingsType::getDefaultName(LLSettingsType::type_e type) return getDefaultName(ST_INVALID); return entry->mDefaultNewName; } - -void LLSettingsType::initClass(LLTranslationBridge::ptr_t &trans) -{ - sTranslator = trans; -} - -void LLSettingsType::cleanupClass() -{ - sTranslator.reset(); -} diff --git a/indra/llinventory/llinventorysettings.h b/indra/llinventory/llinventorysettings.h index 906540689c..6b6685d088 100644 --- a/indra/llinventory/llinventorysettings.h +++ b/indra/llinventory/llinventorysettings.h @@ -30,9 +30,15 @@ #include "llinventorytype.h" #include "llinvtranslationbrdg.h" +#include "llsingleton.h" -class LLSettingsType +class LLSettingsType : public LLParamSingleton<LLSettingsType> { + LLSINGLETON(LLSettingsType, LLTranslationBridge::ptr_t &trans); + ~LLSettingsType(); + + friend struct SettingsEntry; + public: enum type_e { @@ -48,8 +54,9 @@ public: static LLInventoryType::EIconName getIconName(type_e type); static std::string getDefaultName(type_e type); - static void initClass(LLTranslationBridge::ptr_t &trans); - static void cleanupClass(); +protected: + + LLTranslationBridge::ptr_t mTranslator; }; diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index e79b753514..0359d2f554 100644 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -565,148 +565,6 @@ void LLPermissions::unpackMessage(LLMessageSystem* msg, const char* block, S32 b } -// -// File support -// - -BOOL LLPermissions::importFile(LLFILE* fp) -{ - init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); - const S32 BUFSIZE = 16384; - - // *NOTE: Changing the buffer size will require changing the scanf - // calls below. - char buffer[BUFSIZE]; /* Flawfinder: ignore */ - char keyword[256]; /* Flawfinder: ignore */ - char valuestr[256]; /* Flawfinder: ignore */ - char uuid_str[256]; /* Flawfinder: ignore */ - U32 mask; - - keyword[0] = '\0'; - valuestr[0] = '\0'; - - while (!feof(fp)) - { - if (fgets(buffer, BUFSIZE, fp) == NULL) - { - buffer[0] = '\0'; - } - - sscanf( /* Flawfinder: ignore */ - buffer, - " %255s %255s", - keyword, valuestr); - if (!strcmp("{", keyword)) - { - continue; - } - if (!strcmp("}",keyword)) - { - break; - } - else if (!strcmp("creator_mask", keyword)) - { - // legacy support for "creator" masks - sscanf(valuestr, "%x", &mask); - mMaskBase = mask; - fixFairUse(); - } - else if (!strcmp("base_mask", keyword)) - { - sscanf(valuestr, "%x", &mask); - mMaskBase = mask; - //fixFairUse(); - } - else if (!strcmp("owner_mask", keyword)) - { - sscanf(valuestr, "%x", &mask); - mMaskOwner = mask; - } - else if (!strcmp("group_mask", keyword)) - { - sscanf(valuestr, "%x", &mask); - mMaskGroup = mask; - } - else if (!strcmp("everyone_mask", keyword)) - { - sscanf(valuestr, "%x", &mask); - mMaskEveryone = mask; - } - else if (!strcmp("next_owner_mask", keyword)) - { - sscanf(valuestr, "%x", &mask); - mMaskNextOwner = mask; - } - else if (!strcmp("creator_id", keyword)) - { - sscanf(valuestr, "%255s", uuid_str); /* Flawfinder: ignore */ - mCreator.set(uuid_str); - } - else if (!strcmp("owner_id", keyword)) - { - sscanf(valuestr, "%255s", uuid_str); /* Flawfinder: ignore */ - mOwner.set(uuid_str); - } - else if (!strcmp("last_owner_id", keyword)) - { - sscanf(valuestr, "%255s", uuid_str); /* Flawfinder: ignore */ - mLastOwner.set(uuid_str); - } - else if (!strcmp("group_id", keyword)) - { - sscanf(valuestr, "%255s", uuid_str); /* Flawfinder: ignore */ - mGroup.set(uuid_str); - } - else if (!strcmp("group_owned", keyword)) - { - sscanf(valuestr, "%d", &mask); - if(mask) mIsGroupOwned = true; - else mIsGroupOwned = false; - } - else - { - LL_INFOS() << "unknown keyword " << keyword << " in permissions import" << LL_ENDL; - } - } - fix(); - return TRUE; -} - - -BOOL LLPermissions::exportFile(LLFILE* fp) const -{ - std::string uuid_str; - - fprintf(fp, "\tpermissions 0\n"); - fprintf(fp, "\t{\n"); - - fprintf(fp, "\t\tbase_mask\t%08x\n", mMaskBase); - fprintf(fp, "\t\towner_mask\t%08x\n", mMaskOwner); - fprintf(fp, "\t\tgroup_mask\t%08x\n", mMaskGroup); - fprintf(fp, "\t\teveryone_mask\t%08x\n", mMaskEveryone); - fprintf(fp, "\t\tnext_owner_mask\t%08x\n", mMaskNextOwner); - - mCreator.toString(uuid_str); - fprintf(fp, "\t\tcreator_id\t%s\n", uuid_str.c_str()); - - mOwner.toString(uuid_str); - fprintf(fp, "\t\towner_id\t%s\n", uuid_str.c_str()); - - mLastOwner.toString(uuid_str); - fprintf(fp, "\t\tlast_owner_id\t%s\n", uuid_str.c_str()); - - mGroup.toString(uuid_str); - fprintf(fp, "\t\tgroup_id\t%s\n", uuid_str.c_str()); - - if(mIsGroupOwned) - { - fprintf(fp, "\t\tgroup_owned\t1\n"); - } - fprintf(fp,"\t}\n"); - return TRUE; -} - - BOOL LLPermissions::importLegacyStream(std::istream& input_stream) { init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index 89c66f6ebd..27252f7b97 100644 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -311,10 +311,6 @@ public: void packMessage(LLMessageSystem* msg) const; void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); - // Load/save support - BOOL importFile(LLFILE* fp); - BOOL exportFile(LLFILE* fp) const; - BOOL importLegacyStream(std::istream& input_stream); BOOL exportLegacyStream(std::ostream& output_stream) const; diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index 63e34d188e..b7231ee239 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -78,16 +78,6 @@ U32 LLSaleInfo::getCRC32() const return rv; } - -BOOL LLSaleInfo::exportFile(LLFILE* fp) const -{ - fprintf(fp, "\tsale_info\t0\n\t{\n"); - fprintf(fp, "\t\tsale_type\t%s\n", lookup(mSaleType)); - fprintf(fp, "\t\tsale_price\t%d\n", mSalePrice); - fprintf(fp,"\t}\n"); - return TRUE; -} - BOOL LLSaleInfo::exportLegacyStream(std::ostream& output_stream) const { output_stream << "\tsale_info\t0\n\t{\n"; @@ -129,69 +119,6 @@ bool LLSaleInfo::fromLLSD(const LLSD& sd, BOOL& has_perm_mask, U32& perm_mask) return true; } -// Deleted LLSaleInfo::exportFileXML() and LLSaleInfo::importXML() -// because I can't find any non-test code references to it. 2009-05-04 JC - -BOOL LLSaleInfo::importFile(LLFILE* fp, BOOL& has_perm_mask, U32& perm_mask) -{ - has_perm_mask = FALSE; - - // *NOTE: Changing the buffer size will require changing the scanf - // calls below. - char buffer[MAX_STRING]; /* Flawfinder: ignore */ - char keyword[MAX_STRING]; /* Flawfinder: ignore */ - char valuestr[MAX_STRING]; /* Flawfinder: ignore */ - BOOL success = TRUE; - - keyword[0] = '\0'; - valuestr[0] = '\0'; - while(success && (!feof(fp))) - { - if (fgets(buffer, MAX_STRING, fp) == NULL) - { - buffer[0] = '\0'; - } - - sscanf( /* Flawfinder: ignore */ - buffer, - " %254s %254s", - keyword, valuestr); - if(!keyword[0]) - { - continue; - } - if(0 == strcmp("{",keyword)) - { - continue; - } - if(0 == strcmp("}", keyword)) - { - break; - } - else if(0 == strcmp("sale_type", keyword)) - { - mSaleType = lookup(valuestr); - } - else if(0 == strcmp("sale_price", keyword)) - { - sscanf(valuestr, "%d", &mSalePrice); - mSalePrice = llclamp(mSalePrice, 0, S32_MAX); - } - else if (!strcmp("perm_mask", keyword)) - { - //LL_INFOS() << "found deprecated keyword perm_mask" << LL_ENDL; - has_perm_mask = TRUE; - sscanf(valuestr, "%x", &perm_mask); - } - else - { - LL_WARNS() << "unknown keyword '" << keyword - << "' in sale info import" << LL_ENDL; - } - } - return success; -} - BOOL LLSaleInfo::importLegacyStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask) { has_perm_mask = FALSE; diff --git a/indra/llinventory/llsaleinfo.h b/indra/llinventory/llsaleinfo.h index 4e98ccf6ff..3c8952838b 100644 --- a/indra/llinventory/llsaleinfo.h +++ b/indra/llinventory/llsaleinfo.h @@ -84,11 +84,6 @@ public: void setSalePrice(S32 price); //void setNextOwnerPermMask(U32 mask) { mNextOwnerPermMask = mask; } - - // file serialization - BOOL exportFile(LLFILE* fp) const; - BOOL importFile(LLFILE* fp, BOOL& has_perm_mask, U32& perm_mask); - BOOL exportLegacyStream(std::ostream& output_stream) const; LLSD asLLSD() const; operator LLSD() const { return asLLSD(); } diff --git a/indra/llinventory/llsettingsbase.h b/indra/llinventory/llsettingsbase.h index 1d118f0789..f7a9d5b7cd 100644 --- a/indra/llinventory/llsettingsbase.h +++ b/indra/llinventory/llsettingsbase.h @@ -359,7 +359,6 @@ protected: virtual parammapping_t getParameterMap() const { return parammapping_t(); } LLSD mSettings; - bool mIsValid; LLSD cloneSettings() const; diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index 306c732920..81937dbda5 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -1295,6 +1295,9 @@ void LLSettingsSky::clampColor(LLColor3& color, F32 gamma, F32 scale) const color = linear; } +// Similar/Shared Algorithms: +// indra\llinventory\llsettingssky.cpp -- LLSettingsSky::calculateLightSettings() +// indra\newview\app_settings\shaders\class1\windlight\atmosphericsFuncs.glsl -- calcAtmosphericVars() void LLSettingsSky::calculateLightSettings() const { // Initialize temp variables diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index 7b15552f24..e8b063bffe 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -28,9 +28,9 @@ #include "linden_common.h" #include "llsd.h" +#include "llsdserialize.h" #include "../llinventory.h" - #include "../test/lltut.h" @@ -320,27 +320,39 @@ namespace tut template<> template<> void inventory_object::test<7>() { - LLFILE* fp = LLFile::fopen("linden_file.dat","w+"); - if(!fp) + std::string filename("linden_file.dat"); + llofstream fileXML(filename.c_str()); + if (!fileXML.is_open()) { LL_ERRS() << "file could not be opened\n" << LL_ENDL; return; } LLPointer<LLInventoryItem> src1 = create_random_inventory_item(); - src1->exportFile(fp, TRUE); - fclose(fp); + fileXML << LLSDOStreamer<LLSDNotationFormatter>(src1->asLLSD()) << std::endl; + fileXML.close(); - LLPointer<LLInventoryItem> src2 = new LLInventoryItem(); - fp = LLFile::fopen("linden_file.dat","r+"); - if(!fp) + + LLPointer<LLInventoryItem> src2 = new LLInventoryItem(); + llifstream file(filename.c_str()); + if (!file.is_open()) { LL_ERRS() << "file could not be opened\n" << LL_ENDL; return; } - - src2->importFile(fp); - fclose(fp); + std::string line; + LLPointer<LLSDParser> parser = new LLSDNotationParser(); + std::getline(file, line); + LLSD s_item; + std::istringstream iss(line); + if (parser->parse(iss, s_item, line.length()) == LLSDParser::PARSE_FAILURE) + { + LL_ERRS()<< "Parsing cache failed" << LL_ENDL; + return; + } + src2->fromLLSD(s_item); + + file.close(); ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); @@ -457,27 +469,39 @@ namespace tut template<> template<> void inventory_object::test<13>() { - LLFILE* fp = LLFile::fopen("linden_file.dat","w"); - if(!fp) + std::string filename("linden_file.dat"); + llofstream fileXML(filename.c_str()); + if (!fileXML.is_open()) { - LL_ERRS() << "file coudnt be opened\n" << LL_ENDL; + LL_ERRS() << "file could not be opened\n" << LL_ENDL; return; } - + LLPointer<LLInventoryCategory> src1 = create_random_inventory_cat(); - src1->exportFile(fp, TRUE); - fclose(fp); + fileXML << LLSDOStreamer<LLSDNotationFormatter>(src1->exportLLSD()) << std::endl; + fileXML.close(); - LLPointer<LLInventoryCategory> src2 = new LLInventoryCategory(); - fp = LLFile::fopen("linden_file.dat","r"); - if(!fp) + llifstream file(filename.c_str()); + if (!file.is_open()) { - LL_ERRS() << "file coudnt be opened\n" << LL_ENDL; + LL_ERRS() << "file could not be opened\n" << LL_ENDL; return; } - - src2->importFile(fp); - fclose(fp); + std::string line; + LLPointer<LLSDParser> parser = new LLSDNotationParser(); + std::getline(file, line); + LLSD s_item; + std::istringstream iss(line); + if (parser->parse(iss, s_item, line.length()) == LLSDParser::PARSE_FAILURE) + { + LL_ERRS()<< "Parsing cache failed" << LL_ENDL; + return; + } + + file.close(); + + LLPointer<LLInventoryCategory> src2 = new LLInventoryCategory(); + src2->importLLSD(s_item); ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h index 222f3cf235..27abf39537 100644 --- a/indra/llmath/llvector4a.h +++ b/indra/llmath/llvector4a.h @@ -46,10 +46,10 @@ class LLRotation; // of this writing, July 08, 2010) about getting it implemented before you resort to // LLVector3/LLVector4. ///////////////////////////////// -class LLVector4a; +struct LLVector4a; LL_ALIGN_PREFIX(16) -class LLVector4a +struct LLVector4a { public: @@ -92,6 +92,7 @@ public: // CONSTRUCTORS //////////////////////////////////// + //LLVector4a is plain data which should never have a default constructor or destructor(malloc&free won't trigger it) LLVector4a() { //DO NOT INITIALIZE -- The overhead is completely unnecessary ll_assert_aligned(this,16); diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 7da53bf8c8..e085fa6ada 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -5241,9 +5241,9 @@ public: LLVCacheTriangleData* tri = *iter; if (tri->mActive) { - tri->mScore = tri->mVertex[0]->mScore; - tri->mScore += tri->mVertex[1]->mScore; - tri->mScore += tri->mVertex[2]->mScore; + tri->mScore = tri->mVertex[0] ? tri->mVertex[0]->mScore : 0; + tri->mScore += tri->mVertex[1] ? tri->mVertex[1]->mScore : 0; + tri->mScore += tri->mVertex[2] ? tri->mVertex[2]->mScore : 0; if (!mBestTriangle || mBestTriangle->mScore < tri->mScore) { diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index c799d8eefc..7e4572f50f 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -262,7 +262,7 @@ public: virtual void logAssetStorageInfo() = 0; - void checkForTimeouts(); + virtual void checkForTimeouts(); void getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id, const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype, diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index fbd65cc67b..c67f59bc0c 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -145,10 +145,10 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU } LLSD httpResults; + bool success = true; try { - bool success = true; LLCoreHttpUtil::HttpCoroutineAdapter httpAdapter("NameCache", sHttpPolicy); LLSD results = httpAdapter.getAndSuspend(sHttpRequest, url); @@ -163,35 +163,47 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU else { httpResults = results["http_result"]; - success = httpResults["success"].asBoolean(); - if (!success) + if (!httpResults.isMap()) { - LL_WARNS("AvNameCache") << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code " - << httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL; + success = false; + LL_WARNS("AvNameCache") << " Invalid http_result returned from LLCoreHttpUtil::HttpCoroHandler." << LL_ENDL; } - } - - if (!success) - { // on any sort of failure add dummy records for any agent IDs - // in this request that we do not have cached already - std::vector<LLUUID>::const_iterator it = agentIds.begin(); - for ( ; it != agentIds.end(); ++it) + else { - const LLUUID& agent_id = *it; - LLAvatarNameCache::getInstance()->handleAgentError(agent_id); + success = httpResults["success"].asBoolean(); + if (!success) + { + LL_WARNS("AvNameCache") << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code " + << httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL; + } } - return; } - LLAvatarNameCache::getInstance()->handleAvNameCacheSuccess(results, httpResults); + if (LLAvatarNameCache::instanceExists()) + { + if (!success) + { // on any sort of failure add dummy records for any agent IDs + // in this request that we do not have cached already + std::vector<LLUUID>::const_iterator it = agentIds.begin(); + for (; it != agentIds.end(); ++it) + { + const LLUUID& agent_id = *it; + LLAvatarNameCache::getInstance()->handleAgentError(agent_id); + } + return; + } + LLAvatarNameCache::getInstance()->handleAvNameCacheSuccess(results, httpResults); + } } catch (...) { LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName() - << "('" << url << "', " << agentIds.size() - << " http result: " << httpResults.asString() - << " Agent Ids)")); + << "('" << url << "', " + << agentIds.size() << "Agent Ids," + << " http result: " << S32(success) + << " has response: " << S32(httpResults.size()) + << ")")); throw; } } @@ -285,12 +297,27 @@ void LLAvatarNameCache::processName(const LLUUID& agent_id, const LLAvatarName& return; } + bool updated_account = true; // assume obsolete value for new arrivals by default + + std::map<LLUUID, LLAvatarName>::iterator it = mCache.find(agent_id); + if (it != mCache.end() + && (*it).second.getAccountName() == av_name.getAccountName()) + { + updated_account = false; + } + // Add to the cache mCache[agent_id] = av_name; // Suppress request from the queue mPendingQueue.erase(agent_id); + // notify mute list about changes + if (updated_account && mAccountNameChangedCallback) + { + mAccountNameChangedCallback(agent_id, av_name); + } + // Signal everyone waiting on this name signal_map_t::iterator sig_it = mSignalMap.find(agent_id); if (sig_it != mSignalMap.end()) @@ -303,6 +330,8 @@ void LLAvatarNameCache::processName(const LLUUID& agent_id, const LLAvatarName& delete signal; signal = NULL; } + + } void LLAvatarNameCache::requestNamesViaCapability() diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index ba89d569f3..549d1703fa 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -42,6 +42,7 @@ class LLAvatarNameCache : public LLSingleton<LLAvatarNameCache> ~LLAvatarNameCache(); public: typedef boost::signals2::signal<void (void)> use_display_name_signal_t; + typedef boost::function<void (const LLUUID id, const LLAvatarName& av_name)> account_name_changed_callback_t; // Import/export the name cache to file. bool importFile(std::istream& istr); @@ -103,6 +104,8 @@ public: void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb); + void setAccountNameChangedCallback(const account_name_changed_callback_t& cb) { mAccountNameChangedCallback = cb; } + private: // Handle name response off network. void processName(const LLUUID& agent_id, @@ -141,6 +144,7 @@ private: private: use_display_name_signal_t mUseDisplayNamesSignal; + account_name_changed_callback_t mAccountNameChangedCallback; // Cache starts in a paused state until we can determine if the // current region supports display names. diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index 42c19e3b1c..4d76dacdf7 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -47,7 +47,7 @@ static const std::map<std::string, U32> DefaultPoolSizes{ }; static const U32 DEFAULT_POOL_SIZE = 5; -static const U32 DEFAULT_QUEUE_SIZE = 4096; +const U32 LLCoprocedureManager::DEFAULT_QUEUE_SIZE = 4096; //========================================================================= class LLCoprocedurePool: private boost::noncopyable @@ -77,12 +77,12 @@ public: /// inline size_t countActive() const { - return mActiveCoprocs.size(); + return mActiveCoprocsCount; } /// Returns the total number of coprocedures either queued or in active processing. /// - inline size_t count() const + inline S32 count() const { return countPending() + countActive(); } @@ -113,12 +113,10 @@ private: // because the consuming coroutine might outlive this LLCoprocedurePool // instance. typedef boost::shared_ptr<CoprocQueue_t> CoprocQueuePtr; - typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t; std::string mPoolName; - size_t mPoolSize, mPending{0}; + size_t mPoolSize, mActiveCoprocsCount, mPending; CoprocQueuePtr mPendingCoprocs; - ActiveCoproc_t mActiveCoprocs; LLTempBoundListener mStatusListener; typedef std::map<std::string, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> CoroAdapterMap_t; @@ -191,8 +189,13 @@ LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &pool, const s void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn) { + // functions to discover and store the pool sizes mPropertyQueryFn = queryfn; mPropertyDefineFn = updatefn; + + // workaround until we get mutex into initializePool + initializePool("AssetStorage"); + initializePool("Upload"); } //------------------------------------------------------------------------- @@ -276,7 +279,9 @@ void LLCoprocedureManager::close(const std::string &pool) LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): mPoolName(poolName), mPoolSize(size), - mPendingCoprocs(boost::make_shared<CoprocQueue_t>(DEFAULT_QUEUE_SIZE)), + mActiveCoprocsCount(0), + mPending(0), + mPendingCoprocs(boost::make_shared<CoprocQueue_t>(LLCoprocedureManager::DEFAULT_QUEUE_SIZE)), mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), mCoroMapping() { @@ -327,7 +332,7 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): mCoroMapping.insert(CoroAdapterMap_t::value_type(pooledCoro, httpAdapter)); } - LL_INFOS("CoProcMgr") << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items, queue max " << DEFAULT_QUEUE_SIZE << LL_ENDL; + LL_INFOS("CoProcMgr") << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items, queue max " << LLCoprocedureManager::DEFAULT_QUEUE_SIZE << LL_ENDL; } LLCoprocedurePool::~LLCoprocedurePool() @@ -355,7 +360,7 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced } // The queue should never fill up. - LL_ERRS("CoProcMgr") << "Enqueue failed (" << unsigned(pushed) << ")" << LL_ENDL; + LL_ERRS("CoProcMgr") << "Enqueue into '" << name << "' failed (" << unsigned(pushed) << ")" << LL_ENDL; return {}; // never executed, pacify the compiler } @@ -401,8 +406,7 @@ void LLCoprocedurePool::coprocedureInvokerCoro( } // we actually popped an item --mPending; - - ActiveCoproc_t::iterator itActive = mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)).first; + mActiveCoprocsCount++; LL_DEBUGS("CoProcMgr") << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\" (" << mPending << " left)" << LL_ENDL; @@ -410,19 +414,25 @@ void LLCoprocedurePool::coprocedureInvokerCoro( { coproc->mProc(httpAdapter, coproc->mId); } + catch (const LLCoros::Stop &e) + { + LL_INFOS("LLCoros") << "coprocedureInvokerCoro terminating because " + << e.what() << LL_ENDL; + throw; // let toplevel handle this as LLContinueError + } catch (...) { LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName << "', id=" << coproc->mId.asString() << ") in pool '" << mPoolName << "'")); // must NOT omit this or we deplete the pool - mActiveCoprocs.erase(itActive); + mActiveCoprocsCount--; continue; } LL_DEBUGS("CoProcMgr") << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; - mActiveCoprocs.erase(itActive); + mActiveCoprocsCount--; } } diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h index 70204ba02b..d5557c129f 100644 --- a/indra/llmessage/llcoproceduremanager.h +++ b/indra/llmessage/llcoproceduremanager.h @@ -91,6 +91,9 @@ private: SettingQuery_t mPropertyQueryFn; SettingUpdate_t mPropertyDefineFn; + +public: + static const U32 DEFAULT_QUEUE_SIZE; }; #endif diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 7d96ac4b02..64c01bd9eb 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -85,15 +85,15 @@ const F64 LLExperienceCache::DEFAULT_EXPIRATION = 600.0; const S32 LLExperienceCache::DEFAULT_QUOTA = 128; // this is megabytes const int LLExperienceCache::SEARCH_PAGE_SIZE = 30; +bool LLExperienceCache::sShutdown = false; + //========================================================================= -LLExperienceCache::LLExperienceCache(): - mShutdown(false) +LLExperienceCache::LLExperienceCache() { } LLExperienceCache::~LLExperienceCache() { - } void LLExperienceCache::initSingleton() @@ -122,7 +122,7 @@ void LLExperienceCache::cleanup() { cache_stream << (*this); } - mShutdown = true; + sShutdown = true; } //------------------------------------------------------------------------- @@ -344,7 +344,7 @@ void LLExperienceCache::requestExperiences() ostr << urlBase << "?page_size=" << PAGE_SIZE1; RequestQueue_t requests; - while (!mRequestQueue.empty()) + while (!mRequestQueue.empty() && !sShutdown) { RequestQueue_t::iterator it = mRequestQueue.begin(); LLUUID key = (*it); @@ -398,8 +398,6 @@ void LLExperienceCache::idleCoro() LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL; do { - llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS); - if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) { eraseExpired(); @@ -410,7 +408,9 @@ void LLExperienceCache::idleCoro() requestExperiences(); } - } while (!mShutdown); + llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS); + + } while (!sShutdown); // 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.) diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index f9ff69c2b6..1c97133723 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -142,7 +142,7 @@ private: LLFrameTimer mEraseExpiredTimer; // Periodically clean out expired entries from the cache CapabilityQuery_t mCapability; std::string mCacheFileName; - bool mShutdown; + static bool sShutdown; // control for coroutines, they exist out of LLExperienceCache's scope, so they need a static control void idleCoro(); void eraseExpired(); diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 86bcfe6881..749e599c66 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -40,6 +40,7 @@ // incoming packet just to do a simple bool test. The getter for this // member is also static bool LLProxy::sUDPProxyEnabled = false; +LLProxy* LLProxy::sProxyInstance = NULL; // Some helpful TCP static functions. static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataout, apr_size_t outlen, char * datain, apr_size_t maxinlen); // Do a TCP data handshake @@ -60,11 +61,21 @@ LLProxy::LLProxy(): LLProxy::~LLProxy() { - if (ll_apr_is_initialized()) - { - stopSOCKSProxy(); - disableHTTPProxy(); - } + if (ll_apr_is_initialized()) + { + // locks mutex + stopSOCKSProxy(); + disableHTTPProxy(); + } + // The primary safety of sProxyInstance is the fact that by the + // point SUBSYSTEM_CLEANUP(LLProxy) gets called, nothing should + // be capable of using proxy + sProxyInstance = NULL; +} + +void LLProxy::initSingleton() +{ + sProxyInstance = this; } /** @@ -424,28 +435,28 @@ void LLProxy::cleanupClass() void LLProxy::applyProxySettings(CURL* handle) { // Do a faster unlocked check to see if we are supposed to proxy. - if (mHTTPProxyEnabled) + if (sProxyInstance && sProxyInstance->mHTTPProxyEnabled) { - // We think we should proxy, lock the proxy mutex. - LLMutexLock lock(&mProxyMutex); + // We think we should proxy, lock the proxy mutex. sProxyInstance is not protected by mutex + LLMutexLock lock(&sProxyInstance->mProxyMutex); // Now test again to verify that the proxy wasn't disabled between the first check and the lock. - if (mHTTPProxyEnabled) + if (sProxyInstance->mHTTPProxyEnabled) { - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()), CURLOPT_PROXY); - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort()), CURLOPT_PROXYPORT); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXY, sProxyInstance->mHTTPProxy.getIPString().c_str()), CURLOPT_PROXY); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, sProxyInstance->mHTTPProxy.getPort()), CURLOPT_PROXYPORT); - if (mProxyType == LLPROXY_SOCKS) + if (sProxyInstance->mProxyType == LLPROXY_SOCKS) { - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5), CURLOPT_PROXYTYPE); - if (mAuthMethodSelected == METHOD_PASSWORD) + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5), CURLOPT_PROXYTYPE); + if (sProxyInstance->mAuthMethodSelected == METHOD_PASSWORD) { - std::string auth_string = mSocksUsername + ":" + mSocksPassword; - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()), CURLOPT_PROXYUSERPWD); + std::string auth_string = sProxyInstance->mSocksUsername + ":" + sProxyInstance->mSocksPassword; + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()), CURLOPT_PROXYUSERPWD); } } else { - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP), CURLOPT_PROXYTYPE); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP), CURLOPT_PROXYTYPE); } } } diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index a1ffa9e5d5..25f6977e14 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -226,6 +226,8 @@ class LLProxy: public LLSingleton<LLProxy> LLSINGLETON(LLProxy); LOG_CLASS(LLProxy); + /*virtual*/ void initSingleton(); + public: // Static check for enabled status for UDP packets. Call from main thread only. static bool isSOCKSProxyEnabled() { return sUDPProxyEnabled; } @@ -251,7 +253,7 @@ public: // Apply the current proxy settings to a curl request. Doesn't do anything if mHTTPProxyEnabled is false. // Safe to call from any thread. - void applyProxySettings(CURL* handle); + static void applyProxySettings(CURL* handle); // Start a connection to the SOCKS 5 proxy. Call from main thread only. S32 startSOCKSProxy(LLHost host); @@ -344,6 +346,10 @@ private: /*########################################################################################### END OF SHARED MEMBERS ###########################################################################################*/ + + // A hack to get arround getInstance() and capture_dependency() which are unsafe to use inside threads + // set/reset on init/cleanup, strictly for use in applyProxySettings + static LLProxy* sProxyInstance; }; #endif diff --git a/indra/llmessage/llteleportflags.h b/indra/llmessage/llteleportflags.h index b3fcad036e..fd1e702832 100644 --- a/indra/llmessage/llteleportflags.h +++ b/indra/llmessage/llteleportflags.h @@ -44,6 +44,8 @@ const U32 TELEPORT_FLAGS_VIA_REGION_ID = 1 << 12; const U32 TELEPORT_FLAGS_IS_FLYING = 1 << 13; const U32 TELEPORT_FLAGS_SHOW_RESET_HOME = 1 << 14; const U32 TELEPORT_FLAGS_FORCE_REDIRECT = 1 << 15; // used to force a redirect to some random location - used when kicking someone from land. +const U32 TELEPORT_FLAGS_VIA_GLOBAL_COORDS = 1 << 16; +const U32 TELEPORT_FLAGS_WITHIN_REGION = 1 << 17; const U32 TELEPORT_FLAGS_MASK_VIA = TELEPORT_FLAGS_VIA_LURE | TELEPORT_FLAGS_VIA_LANDMARK diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 58069afdf9..6d51adc685 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -857,12 +857,12 @@ void LLPluginClassMedia::paste() } void LLPluginClassMedia::setUserDataPath(const std::string &user_data_path_cache, - const std::string &user_data_path_cookies, + const std::string &username, const std::string &user_data_path_cef_log) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_user_data_path"); - message.setValue("cache_path", user_data_path_cache); - message.setValue("cookies_path", user_data_path_cookies); + message.setValue("cache_path", user_data_path_cache); + message.setValue("username", username); // cef shares cache between users but creates user-based contexts message.setValue("cef_log_file", user_data_path_cef_log); bool cef_verbose_log = gSavedSettings.getBOOL("CefVerboseLog"); diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 9d11ee0421..382f891e0c 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -195,7 +195,7 @@ public: bool canPaste() const { return mCanPaste; }; // These can be called before init(), and they will be queued and sent before the media init message. - void setUserDataPath(const std::string &user_data_path_cache, const std::string &user_data_path_cookies, const std::string &user_data_path_cef_log); + void setUserDataPath(const std::string &user_data_path_cache, const std::string &username, const std::string &user_data_path_cef_log); void setLanguageCode(const std::string &language_code); void setPluginsEnabled(const bool enabled); void setJavascriptEnabled(const bool enabled); diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index c5304d2ccf..d93ec8cf4b 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -225,6 +225,18 @@ void LLPluginProcessChild::idle(void) } setState(STATE_UNLOADED); } + + if (mInstance) + { + // Provide some time to the plugin + // example: CEF on "cleanup" sets shutdown request, but it still needs idle loop to actually shutdown + LLPluginMessage message("base", "idle"); + message.setValueReal("time", PLUGIN_IDLE_SECONDS); + sendMessageToPlugin(message); + + mInstance->idle(); + } + break; case STATE_UNLOADED: diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index eb6cb1b503..7d18bae947 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -152,8 +152,18 @@ void LLPluginProcessParent::shutdown() mapInstances_t::iterator it; for (it = sInstances.begin(); it != sInstances.end(); ++it) { - (*it).second->setState(STATE_GOODBYE); - (*it).second->idle(); + EState state = (*it).second->mState; + if (state != STATE_CLEANUP + || state != STATE_EXITING + || state != STATE_DONE + || state != STATE_ERROR) + { + (*it).second->setState(STATE_GOODBYE); + } + if (state != STATE_DONE) + { + (*it).second->idle(); + } } sInstances.clear(); } diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 139f48fef8..dfa29fb539 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -343,7 +343,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa return LLModel::NO_ERRORS ; } -LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly) +LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly, LLSD& log_msg) { domPRef p = poly->getP(); domListOfUInts& idx = p->getValue(); @@ -403,6 +403,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac LLVolumeFace::VertexMapData::PointMap point_map; U32 cur_idx = 0; + bool log_tc_msg = true; for (U32 i = 0; i < vcount.getCount(); ++i) { //for each polygon U32 first_index = 0; @@ -426,8 +427,21 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac if (tc_source) { - cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0], - tc[idx[cur_idx+tc_offset]*2+1]); + U64 idx_x = idx[cur_idx + tc_offset] * 2 + 0; + U64 idx_y = idx[cur_idx + tc_offset] * 2 + 1; + + if (idx_y < tc.getCount()) + { + cv.mTexCoord.setVec(tc[idx_x], tc[idx_y]); + } + else if (log_tc_msg) + { + log_tc_msg = false; + LL_WARNS() << "Texture coordinates data is not complete." << LL_ENDL; + LLSD args; + args["Message"] = "IncompleteTC"; + log_msg.append(args); + } } if (norm_source) @@ -1215,7 +1229,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do for (S32 i = 0; i < childCount; ++i) { domNode* pNode = daeSafeCast<domNode>(children[i]); - if ( isNodeAJoint( pNode ) ) + if (pNode) { processJointNode( pNode, mJointList ); } @@ -1458,7 +1472,8 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do std::string lookingForJoint = (*jointIt).c_str(); //Look for the joint xform that we extracted from the skeleton, using the jointIt as the key //and store it in the alternate bind matrix - if ( mJointMap.find( lookingForJoint ) != mJointMap.end() ) + if (mJointMap.find(lookingForJoint) != mJointMap.end() + && model->mSkinInfo.mInvBindMatrix.size() > i) { LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i]; newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() ); @@ -1470,6 +1485,12 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do } } + U32 bind_count = model->mSkinInfo.mAlternateBindMatrix.size(); + if (bind_count > 0 && bind_count != jointCnt) + { + LL_WARNS("Mesh") << "Model " << model->mLabel << " has invalid joint bind matrix list." << LL_ENDL; + } + //grab raw position array domVertices* verts = mesh->getVertices(); @@ -1834,59 +1855,61 @@ void LLDAELoader::processJointNode( domNode* pNode, JointTransformMap& jointTran //LL_WARNS()<<"ProcessJointNode# Node:" <<pNode->getName()<<LL_ENDL; //1. handle the incoming node - extract out translation via SID or element + if (isNodeAJoint(pNode)) + { + LLMatrix4 workingTransform; - LLMatrix4 workingTransform; - - //Pull out the translate id and store it in the jointTranslations map - daeSIDResolver jointResolverA( pNode, "./translate" ); - domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() ); - daeSIDResolver jointResolverB( pNode, "./location" ); - domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() ); + //Pull out the translate id and store it in the jointTranslations map + daeSIDResolver jointResolverA(pNode, "./translate"); + domTranslate* pTranslateA = daeSafeCast<domTranslate>(jointResolverA.getElement()); + daeSIDResolver jointResolverB(pNode, "./location"); + domTranslate* pTranslateB = daeSafeCast<domTranslate>(jointResolverB.getElement()); - //Translation via SID was successful - if ( pTranslateA ) - { - extractTranslation( pTranslateA, workingTransform ); - } - else - if ( pTranslateB ) - { - extractTranslation( pTranslateB, workingTransform ); - } - else - { - //Translation via child from element - daeElement* pTranslateElement = getChildFromElement( pNode, "translate" ); - if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() ) - { - //LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL; - daeSIDResolver jointResolver( pNode, "./matrix" ); - domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() ); - if ( pMatrix ) - { - //LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL; - domFloat4x4 domArray = pMatrix->getValue(); - for ( int i = 0; i < 4; i++ ) - { - for( int j = 0; j < 4; j++ ) - { - workingTransform.mMatrix[i][j] = domArray[i + j*4]; - } - } - } - else - { - LL_WARNS()<< "The found element is not translate or matrix node - most likely a corrupt export!" <<LL_ENDL; - } - } - else - { - extractTranslationViaElement( pTranslateElement, workingTransform ); - } - } + //Translation via SID was successful + if (pTranslateA) + { + extractTranslation(pTranslateA, workingTransform); + } + else + if (pTranslateB) + { + extractTranslation(pTranslateB, workingTransform); + } + else + { + //Translation via child from element + daeElement* pTranslateElement = getChildFromElement(pNode, "translate"); + if (!pTranslateElement || pTranslateElement->typeID() != domTranslate::ID()) + { + //LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL; + daeSIDResolver jointResolver(pNode, "./matrix"); + domMatrix* pMatrix = daeSafeCast<domMatrix>(jointResolver.getElement()); + if (pMatrix) + { + //LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL; + domFloat4x4 domArray = pMatrix->getValue(); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + workingTransform.mMatrix[i][j] = domArray[i + j * 4]; + } + } + } + else + { + LL_WARNS() << "The found element is not translate or matrix node - most likely a corrupt export!" << LL_ENDL; + } + } + else + { + extractTranslationViaElement(pTranslateElement, workingTransform); + } + } - //Store the working transform relative to the nodes name. - jointTransforms[ pNode->getName() ] = workingTransform; + //Store the working transform relative to the nodes name. + jointTransforms[pNode->getName()] = workingTransform; + } //2. handle the nodes children @@ -2356,7 +2379,7 @@ LLColor4 LLDAELoader::getDaeColor(daeElement* element) return value; } -bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh) +bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh, LLSD& log_msg) { LLModel::EModelStatus status = LLModel::NO_ERRORS; domTriangles_Array& tris = mesh->getTriangles_array(); @@ -2378,7 +2401,7 @@ bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh) for (U32 i = 0; i < polys.getCount(); ++i) { domPolylistRef& poly = polys.get(i); - status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly); + status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly, log_msg); if(status != LLModel::NO_ERRORS) { @@ -2442,7 +2465,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo // Get the whole set of volume faces // - addVolumeFacesFromDomMesh(ret, mesh); + addVolumeFacesFromDomMesh(ret, mesh, mWarningsArray); U32 volume_faces = ret->getNumVolumeFaces(); @@ -2515,7 +2538,8 @@ bool LLDAELoader::createVolumeFacesFromDomMesh(LLModel* pModel, domMesh* mesh) { pModel->ClearFacesAndMaterials(); - addVolumeFacesFromDomMesh(pModel, mesh); + LLSD placeholder; + addVolumeFacesFromDomMesh(pModel, mesh, placeholder); if (pModel->getNumVolumeFaces() > 0) { diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h index 4e990dbe5e..2b211343e1 100644 --- a/indra/llprimitive/lldaeloader.h +++ b/indra/llprimitive/lldaeloader.h @@ -89,7 +89,7 @@ protected: //Verify that a controller matches vertex counts bool verifyController( domController* pController ); - static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh); + static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh, LLSD& log_msg); static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh); static LLModel* loadModelFromDomMesh(domMesh* mesh); diff --git a/indra/llprimitive/llmaterial.cpp b/indra/llprimitive/llmaterial.cpp index a219ac1450..a1bfc4edd9 100644 --- a/indra/llprimitive/llmaterial.cpp +++ b/indra/llprimitive/llmaterial.cpp @@ -28,6 +28,8 @@ #include "llmaterial.h" +#include "../llrender/llglheaders.h" + /** * Materials cap parameters */ @@ -105,6 +107,8 @@ LLMaterial::LLMaterial() , mSpecularLightExponent(LLMaterial::DEFAULT_SPECULAR_LIGHT_EXPONENT) , mEnvironmentIntensity(LLMaterial::DEFAULT_ENV_INTENSITY) , mDiffuseAlphaMode(LLMaterial::DIFFUSE_ALPHA_MODE_BLEND) + , mDiffuseFormatPrimary(GL_RGBA) + , mDiffuseBaked(false) , mAlphaMaskCutoff(0) { } @@ -311,6 +315,20 @@ void LLMaterial::setEnvironmentIntensity(U8 intensity) mEnvironmentIntensity = intensity; } +U8 LLMaterial::getDiffuseAlphaModeRender() const +{ + if (mDiffuseBaked + || mDiffuseFormatPrimary == GL_RGBA + || mDiffuseFormatPrimary == GL_ALPHA) + { + return mDiffuseAlphaMode; + } + else + { + return DIFFUSE_ALPHA_MODE_NONE; + } +} + U8 LLMaterial::getDiffuseAlphaMode() const { return mDiffuseAlphaMode; @@ -321,6 +339,26 @@ void LLMaterial::setDiffuseAlphaMode(U8 alpha_mode) mDiffuseAlphaMode = alpha_mode; } +U32 LLMaterial::getDiffuseFormatPrimary() const +{ + return mDiffuseFormatPrimary; +} + +void LLMaterial::setDiffuseFormatPrimary(U32 format_primary) +{ + mDiffuseFormatPrimary = format_primary; +} + +bool LLMaterial::getIsDiffuseBaked() const +{ + return mDiffuseBaked; +} + +void LLMaterial::setDiffuseBaked(bool baked) +{ + mDiffuseBaked = baked; +} + U8 LLMaterial::getAlphaMaskCutoff() const { return mAlphaMaskCutoff; @@ -437,7 +475,7 @@ U32 LLMaterial::getShaderMask(U32 alpha_mode) } else { - ret = getDiffuseAlphaMode(); + ret = getDiffuseAlphaModeRender(); } llassert(ret < SHADER_COUNT); diff --git a/indra/llprimitive/llmaterial.h b/indra/llprimitive/llmaterial.h index d58b7ee812..1207917568 100644 --- a/indra/llprimitive/llmaterial.h +++ b/indra/llprimitive/llmaterial.h @@ -115,8 +115,17 @@ public: void setSpecularLightExponent(U8 exponent); U8 getEnvironmentIntensity() const; void setEnvironmentIntensity(U8 intensity); + + // getDiffuseAlphaModeRender takes into account if image supports alpha + // and returns value apropriate for render + // getDiffuseAlphaMode() returns value as is + U8 getDiffuseAlphaModeRender() const; U8 getDiffuseAlphaMode() const; void setDiffuseAlphaMode(U8 alpha_mode); + U32 getDiffuseFormatPrimary() const; + void setDiffuseFormatPrimary(U32 format_primary); + bool getIsDiffuseBaked() const; + void setDiffuseBaked(bool baked); U8 getAlphaMaskCutoff() const; void setAlphaMaskCutoff(U8 cutoff); @@ -147,6 +156,8 @@ protected: U8 mSpecularLightExponent; U8 mEnvironmentIntensity; U8 mDiffuseAlphaMode; + U32 mDiffuseFormatPrimary; // value from texture, LLGLenum, is not included in fromLLSD/asLLSD + bool mDiffuseBaked; // is not included in fromLLSD/asLLSD U8 mAlphaMaskCutoff; }; diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index a2d9b4cd9b..702a1b5238 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -434,7 +434,7 @@ void LLModel::generateNormals(F32 angle_cutoff) if (vol_face.mNumIndices > 65535) { - LL_WARNS() << "Too many vertices for normal generation to work." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Too many vertices for normal generation to work." << LL_ENDL; continue; } @@ -1100,7 +1100,7 @@ bool LLModel::loadModel(std::istream& is) { if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024)) { - LL_WARNS() << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; return false; } } @@ -1132,7 +1132,7 @@ bool LLModel::loadModel(std::istream& is) if (header[lod_name[lod]]["offset"].asInteger() == -1 || header[lod_name[lod]]["size"].asInteger() == 0 ) { //cannot load requested LOD - LL_WARNS() << "LoD data is invalid!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "LoD data is invalid!" << LL_ENDL; return false; } @@ -1195,7 +1195,7 @@ bool LLModel::loadModel(std::istream& is) } else { - LL_WARNS() << "unpackVolumeFaces failed!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "unpackVolumeFaces failed!" << LL_ENDL; } return false; @@ -1223,7 +1223,7 @@ bool LLModel::isMaterialListSubset( LLModel* ref ) if (!foundRef) { - LL_INFOS() << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; + LL_INFOS("MESHSKININFO") << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; return false; } } @@ -1259,7 +1259,7 @@ bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCn bool isASubset = isMaterialListSubset( ref ); if ( !isASubset ) { - LL_INFOS()<<"Material of model is not a subset of reference."<<LL_ENDL; + LL_INFOS("MESHSKININFO")<<"Material of model is not a subset of reference."<<LL_ENDL; return false; } @@ -1398,6 +1398,14 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin) mInvBindMatrix.push_back(mat); } + + if (mJointNames.size() != mInvBindMatrix.size()) + { + LL_WARNS("MESHSKININFO") << "Joints vs bind matrix count mismatch. Dropping joint bindings." << LL_ENDL; + mJointNames.clear(); + mJointNums.clear(); + mInvBindMatrix.clear(); + } } if (skin.has("bind_shape_matrix")) @@ -1842,14 +1850,14 @@ bool validate_face(const LLVolumeFace& face) { if (face.mIndices[i] >= face.mNumVertices) { - LL_WARNS() << "Face has invalid index." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has invalid index." << LL_ENDL; return false; } } if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0) { - LL_WARNS() << "Face has invalid number of indices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has invalid number of indices." << LL_ENDL; return false; } @@ -1879,7 +1887,7 @@ bool validate_model(const LLModel* mdl) { if (mdl->getNumVolumeFaces() == 0) { - LL_WARNS() << "Model has no faces!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Model has no faces!" << LL_ENDL; return false; } @@ -1887,13 +1895,13 @@ bool validate_model(const LLModel* mdl) { if (mdl->getVolumeFace(i).mNumVertices == 0) { - LL_WARNS() << "Face has no vertices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has no vertices." << LL_ENDL; return false; } if (mdl->getVolumeFace(i).mNumIndices == 0) { - LL_WARNS() << "Face has no indices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has no indices." << LL_ENDL; return false; } diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp index 4e468ff45f..5171621007 100644 --- a/indra/llprimitive/llmodelloader.cpp +++ b/indra/llprimitive/llmodelloader.cpp @@ -127,7 +127,7 @@ LLModelLoader::LLModelLoader( , mStateCallback(state_cb) , mOpaqueData(opaque_userdata) , mRigValidJointUpload(true) -, mLegacyRigValid(true) +, mLegacyRigFlags(0) , mNoNormalize(false) , mNoOptimize(false) , mCacheOnlyHitIfRigged(false) @@ -136,6 +136,7 @@ LLModelLoader::LLModelLoader( { assert_main_thread(); sActiveLoaderList.push_back(this) ; + mWarningsArray = LLSD::emptyArray(); } LLModelLoader::~LLModelLoader() @@ -146,6 +147,7 @@ LLModelLoader::~LLModelLoader() void LLModelLoader::run() { + mWarningsArray.clear(); doLoadModel(); doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this)); } @@ -387,7 +389,7 @@ void LLModelLoader::critiqueRigForUploadApplicability( const std::vector<std::st //2. It is suitable for upload as standard av with just skin weights bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset ); - bool isRigLegacyOK = isRigLegacy( jointListFromAsset ); + U32 legacy_rig_flags = determineRigLegacyFlags( jointListFromAsset ); // It's OK that both could end up being true. @@ -401,19 +403,16 @@ void LLModelLoader::critiqueRigForUploadApplicability( const std::vector<std::st setRigValidForJointPositionUpload( false ); } - if ( !isRigLegacyOK) - { - // This starts out true, becomes false if false for any loaded - // mesh. - setLegacyRigValid( false ); - } + legacy_rig_flags |= getLegacyRigFlags(); + // This starts as 0, changes if any loaded mesh has issues + setLegacyRigFlags(legacy_rig_flags); } //----------------------------------------------------------------------------- -// isRigLegacy() +// determineRigLegacyFlags() //----------------------------------------------------------------------------- -bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAsset ) +U32 LLModelLoader::determineRigLegacyFlags( const std::vector<std::string> &jointListFromAsset ) { //No joints in asset if ( jointListFromAsset.size() == 0 ) @@ -426,7 +425,12 @@ bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAs { LL_WARNS() << "Rigged to " << jointListFromAsset.size() << " joints, max is " << mMaxJointsPerMesh << LL_ENDL; LL_WARNS() << "Skinning disabled due to too many joints" << LL_ENDL; - return false; + LLSD args; + args["Message"] = "TooManyJoint"; + args["[JOINTS]"] = LLSD::Integer(jointListFromAsset.size()); + args["[MAX]"] = LLSD::Integer(mMaxJointsPerMesh); + mWarningsArray.append(args); + return LEGACY_RIG_FLAG_TOO_MANY_JOINTS; } // Unknown joints in asset @@ -437,16 +441,24 @@ bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAs if (mJointMap.find(*it)==mJointMap.end()) { LL_WARNS() << "Rigged to unrecognized joint name " << *it << LL_ENDL; + LLSD args; + args["Message"] = "UnrecognizedJoint"; + args["[NAME]"] = *it; + mWarningsArray.append(args); unknown_joint_count++; } } if (unknown_joint_count>0) { LL_WARNS() << "Skinning disabled due to unknown joints" << LL_ENDL; - return false; + LLSD args; + args["Message"] = "UnknownJoints"; + args["[COUNT]"] = LLSD::Integer(unknown_joint_count); + mWarningsArray.append(args); + return LEGACY_RIG_FLAG_UNKNOWN_JOINT; } - return true; + return LEGACY_RIG_OK; } //----------------------------------------------------------------------------- // isRigSuitableForJointPositionUpload() diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h index 643c45a6d8..fbc74554a0 100644 --- a/indra/llprimitive/llmodelloader.h +++ b/indra/llprimitive/llmodelloader.h @@ -42,6 +42,10 @@ typedef std::deque<std::string> JointNameSet; const S32 SLM_SUPPORTED_VERSION = 3; const S32 NUM_LOD = 4; +const U32 LEGACY_RIG_OK = 0; +const U32 LEGACY_RIG_FLAG_TOO_MANY_JOINTS = 1; +const U32 LEGACY_RIG_FLAG_UNKNOWN_JOINT = 2; + class LLModelLoader : public LLThread { public: @@ -166,7 +170,7 @@ public: void critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset ); //Determines if a rig is a legacy from the joint list - bool isRigLegacy( const std::vector<std::string> &jointListFromAsset ); + U32 determineRigLegacyFlags( const std::vector<std::string> &jointListFromAsset ); //Determines if a rig is suitable for upload bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset ); @@ -174,8 +178,9 @@ public: const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; } void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; } - const bool isLegacyRigValid( void ) const { return mLegacyRigValid; } - void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; } + const bool isLegacyRigValid(void) const { return mLegacyRigFlags == 0; } + U32 getLegacyRigFlags() const { return mLegacyRigFlags; } + void setLegacyRigFlags( U32 rigFlags ) { mLegacyRigFlags = rigFlags; } //----------------------------------------------------------------------------- // isNodeAJoint() @@ -185,6 +190,9 @@ public: return name != NULL && mJointMap.find(name) != mJointMap.end(); } + const LLSD logOut() const { return mWarningsArray; } + void clearLog() { mWarningsArray.clear(); } + protected: LLModelLoader::load_callback_t mLoadCallback; @@ -194,13 +202,15 @@ protected: void* mOpaqueData; bool mRigValidJointUpload; - bool mLegacyRigValid; + U32 mLegacyRigFlags; bool mNoNormalize; bool mNoOptimize; JointTransformMap mJointTransformMap; + LLSD mWarningsArray; // preview floater will pull logs from here + static std::list<LLModelLoader*> sActiveLoaderList; static bool isAlive(LLModelLoader* loader) ; }; diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index dbe71e2882..33a33af160 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -45,6 +45,7 @@ bool font_desc_init_from_xml(LLXMLNodePtr node, LLFontDescriptor& desc); bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node); const std::string MACOSX_FONT_PATH_LIBRARY = "/Library/Fonts/"; +const std::string MACOSX_FONT_SUPPLEMENTAL = "Supplemental/"; LLFontDescriptor::LLFontDescriptor(): mStyle(0) @@ -473,6 +474,8 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) font_paths.push_back(sys_path + *file_name_it); #if LL_DARWIN font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + *file_name_it); + font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL + *file_name_it); + font_paths.push_back(sys_path + MACOSX_FONT_SUPPLEMENTAL + *file_name_it); #endif bool is_ft_collection = (std::find(ft_collection_list.begin(), ft_collection_list.end(), *file_name_it) != ft_collection_list.end()); diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 498dfca1a3..ee02a90b54 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -965,6 +965,80 @@ std::string LLGLManager::getRawGLString() return gl_string; } +void LLGLManager::asLLSD(LLSD& info) +{ + // Currently these are duplicates of fields in "system". + info["gpu_vendor"] = mGLVendorShort; + info["gpu_version"] = mDriverVersionVendorString; + info["opengl_version"] = mGLVersionString; + + info["vram"] = mVRAM; + + // Extensions used by everyone + info["has_multitexture"] = mHasMultitexture; + info["has_ati_mem_info"] = mHasATIMemInfo; + info["has_nvx_mem_info"] = mHasNVXMemInfo; + info["num_texture_units"] = mNumTextureUnits; + info["has_mip_map_generation"] = mHasMipMapGeneration; + info["has_compressed_textures"] = mHasCompressedTextures; + info["has_framebuffer_object"] = mHasFramebufferObject; + info["max_samples"] = mMaxSamples; + info["has_blend_func_separate"] = mHasBlendFuncSeparate; + + // ARB Extensions + info["has_vertex_buffer_object"] = mHasVertexBufferObject; + info["has_vertex_array_object"] = mHasVertexArrayObject; + info["has_sync"] = mHasSync; + info["has_map_buffer_range"] = mHasMapBufferRange; + info["has_flush_buffer_range"] = mHasFlushBufferRange; + info["has_pbuffer"] = mHasPBuffer; + info["has_shader_objects"] = mHasShaderObjects; + info["has_vertex_shader"] = mHasVertexShader; + info["has_fragment_shader"] = mHasFragmentShader; + info["num_texture_image_units"] = mNumTextureImageUnits; + info["has_occlusion_query"] = mHasOcclusionQuery; + info["has_timer_query"] = mHasTimerQuery; + info["has_occlusion_query2"] = mHasOcclusionQuery2; + info["has_point_parameters"] = mHasPointParameters; + info["has_draw_buffers"] = mHasDrawBuffers; + info["has_depth_clamp"] = mHasDepthClamp; + info["has_texture_rectangle"] = mHasTextureRectangle; + info["has_texture_multisample"] = mHasTextureMultisample; + info["has_transform_feedback"] = mHasTransformFeedback; + info["max_sample_mask_words"] = mMaxSampleMaskWords; + info["max_color_texture_samples"] = mMaxColorTextureSamples; + info["max_depth_texture_samples"] = mMaxDepthTextureSamples; + info["max_integer_samples"] = mMaxIntegerSamples; + + // Other extensions. + info["has_anisotropic"] = mHasAnisotropic; + info["has_arb_env_combine"] = mHasARBEnvCombine; + info["has_cube_map"] = mHasCubeMap; + info["has_debug_output"] = mHasDebugOutput; + info["has_srgb_texture"] = mHassRGBTexture; + info["has_srgb_framebuffer"] = mHassRGBFramebuffer; + info["has_texture_srgb_decode"] = mHasTexturesRGBDecode; + + // Vendor-specific extensions + info["is_ati"] = mIsATI; + info["is_nvidia"] = mIsNVIDIA; + info["is_intel"] = mIsIntel; + info["is_gf2or4mx"] = mIsGF2or4MX; + info["is_gf3"] = mIsGF3; + info["is_gf_gfx"] = mIsGFFX; + info["ati_offset_vertical_lines"] = mATIOffsetVerticalLines; + info["ati_old_driver"] = mATIOldDriver; + + // Other fields + info["has_requirements"] = mHasRequirements; + info["has_separate_specular_color"] = mHasSeparateSpecularColor; + info["debug_gpu"] = mDebugGPU; + info["max_vertex_range"] = mGLMaxVertexRange; + info["max_index_range"] = mGLMaxIndexRange; + info["max_texture_size"] = mGLMaxTextureSize; + info["gl_renderer"] = mGLRenderer; +} + void LLGLManager::shutdownGL() { if (mInited) @@ -2700,10 +2774,11 @@ LLGLSPipelineBlendSkyBox::LLGLSPipelineBlendSkyBox(bool depth_test, bool depth_w } #if LL_WINDOWS -// Expose desired use of high-performance graphics processor to Optimus driver +// Expose desired use of high-performance graphics processor to Optimus driver and to AMD driver extern "C" -{ - _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +{ + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; + __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 91ef4e9102..966c4b3c77 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -165,6 +165,8 @@ public: void printGLInfoString(); void getGLInfo(LLSD& info); + void asLLSD(LLSD& info); + // In ALL CAPS std::string mGLVendor; std::string mGLVendorShort; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index ff74380217..0151d20128 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -985,38 +985,56 @@ BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) return FALSE; } - if( !mHasExplicitFormat ) - { - switch (mComponents) - { - case 1: - // Use luminance alpha (for fonts) - mFormatInternal = GL_LUMINANCE8; - mFormatPrimary = GL_LUMINANCE; - mFormatType = GL_UNSIGNED_BYTE; - break; - case 2: - // Use luminance alpha (for fonts) - mFormatInternal = GL_LUMINANCE8_ALPHA8; - mFormatPrimary = GL_LUMINANCE_ALPHA; - mFormatType = GL_UNSIGNED_BYTE; - break; - case 3: - mFormatInternal = GL_RGB8; - mFormatPrimary = GL_RGB; - mFormatType = GL_UNSIGNED_BYTE; - break; - case 4: - mFormatInternal = GL_RGBA8; - mFormatPrimary = GL_RGBA; - mFormatType = GL_UNSIGNED_BYTE; - break; - default: - LL_ERRS() << "Bad number of components for texture: " << (U32)getComponents() << LL_ENDL; - } - } + if (!mHasExplicitFormat) + { + switch (mComponents) + { + case 1: + // Use luminance alpha (for fonts) + mFormatInternal = GL_LUMINANCE8; + mFormatPrimary = GL_LUMINANCE; + mFormatType = GL_UNSIGNED_BYTE; + break; + case 2: + // Use luminance alpha (for fonts) + mFormatInternal = GL_LUMINANCE8_ALPHA8; + mFormatPrimary = GL_LUMINANCE_ALPHA; + mFormatType = GL_UNSIGNED_BYTE; + break; + case 3: +#if USE_SRGB_DECODE + if (gGLManager.mHasTexturesRGBDecode) + { + mFormatInternal = GL_SRGB8; + } + else +#endif + { + mFormatInternal = GL_RGB8; + } + mFormatPrimary = GL_RGB; + mFormatType = GL_UNSIGNED_BYTE; + break; + case 4: +#if USE_SRGB_DECODE + if (gGLManager.mHasTexturesRGBDecode) + { + mFormatInternal = GL_SRGB8_ALPHA8; + } + else +#endif + { + mFormatInternal = GL_RGBA8; + } + mFormatPrimary = GL_RGBA; + mFormatType = GL_UNSIGNED_BYTE; + break; + default: + LL_ERRS() << "Bad number of components for texture: " << (U32) getComponents() << LL_ENDL; + } + } - mCurrentDiscardLevel = discard_level; + mCurrentDiscardLevel = discard_level; mDiscardLevelInAtlas = discard_level; mTexelsInAtlas = raw_image->getWidth() * raw_image->getHeight() ; mLastBindTime = sLastFrameTime; @@ -1368,6 +1386,15 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S return FALSE; } + if (mHasExplicitFormat && + ((mFormatPrimary == GL_RGBA && mComponents < 4) || + (mFormatPrimary == GL_RGB && mComponents < 3))) + + { + LL_WARNS() << "Incorrect format: " << std::hex << mFormatPrimary << " components: " << (U32)mComponents << LL_ENDL; + mHasExplicitFormat = FALSE; + } + if( !mHasExplicitFormat ) { switch (mComponents) diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 4f3d7eed0a..61ddc8d59b 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -133,6 +133,7 @@ public: S32 getMipBytes(S32 discard_level = -1) const; BOOL getBoundRecently() const; BOOL isJustBound() const; + BOOL getHasExplicitFormat() const { return mHasExplicitFormat; } LLGLenum getPrimaryFormat() const { return mFormatPrimary; } LLGLenum getFormatType() const { return mFormatType; } @@ -197,7 +198,7 @@ private: U16 mPickMaskWidth; U16 mPickMaskHeight; S8 mUseMipMaps; - S8 mHasExplicitFormat; // If false (default), GL format is f(mComponents) + BOOL mHasExplicitFormat; // If false (default), GL format is f(mComponents) S8 mAutoGenMips; BOOL mIsMask; diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index ebc4659bcf..11d9ef3f57 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -849,26 +849,32 @@ void LLTexUnit::debugTextureUnit(void) } } -void LLTexUnit::setTextureColorSpace(eTextureColorSpace space) { +void LLTexUnit::setTextureColorSpace(eTextureColorSpace space) +{ mTexColorSpace = space; #if USE_SRGB_DECODE - if (gGLManager.mHasTexturesRGBDecode) { - - if (space == TCS_SRGB) { + if (gGLManager.mHasTexturesRGBDecode) + { + if (space == TCS_SRGB) + { glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT); } - else { + else + { glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); } - if (gDebugGL) { + if (gDebugGL) + { assert_glerror(); } } + else #endif - glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); - + { + glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); + } } LLLightState::LLLightState(S32 index) @@ -1192,46 +1198,46 @@ void LLRender::refreshState(void) void LLRender::syncLightState() { - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + LLGLSLShader *shader = LLGLSLShader::sCurBoundShaderPtr; - if (!shader) - { - return; - } + if (!shader) + { + return; + } - if (shader->mLightHash != mLightHash) - { - shader->mLightHash = mLightHash; + if (shader->mLightHash != mLightHash) + { + shader->mLightHash = mLightHash; - LLVector4 position[8]; - LLVector3 direction[8]; - LLVector4 attenuation[8]; - LLVector3 diffuse[8]; - LLVector3 diffuse_b[8]; - bool sun_primary[8]; + LLVector4 position[LL_NUM_LIGHT_UNITS]; + LLVector3 direction[LL_NUM_LIGHT_UNITS]; + LLVector4 attenuation[LL_NUM_LIGHT_UNITS]; + LLVector3 diffuse[LL_NUM_LIGHT_UNITS]; + LLVector3 diffuse_b[LL_NUM_LIGHT_UNITS]; + bool sun_primary[LL_NUM_LIGHT_UNITS]; - for (U32 i = 0; i < 8; i++) - { - LLLightState* light = mLightState[i]; + for (U32 i = 0; i < LL_NUM_LIGHT_UNITS; i++) + { + LLLightState *light = mLightState[i]; - position[i] = light->mPosition; - direction[i] = light->mSpotDirection; + position[i] = light->mPosition; + direction[i] = light->mSpotDirection; attenuation[i].set(light->mLinearAtten, light->mQuadraticAtten, light->mSpecular.mV[2], light->mSpecular.mV[3]); - diffuse[i].set(light->mDiffuse.mV); + diffuse[i].set(light->mDiffuse.mV); diffuse_b[i].set(light->mDiffuseB.mV); sun_primary[i] = light->mSunIsPrimary; - } + } - shader->uniform4fv(LLShaderMgr::LIGHT_POSITION, 8, position[0].mV); - shader->uniform3fv(LLShaderMgr::LIGHT_DIRECTION, 8, direction[0].mV); - shader->uniform4fv(LLShaderMgr::LIGHT_ATTENUATION, 8, attenuation[0].mV); - shader->uniform3fv(LLShaderMgr::LIGHT_DIFFUSE, 8, diffuse[0].mV); - shader->uniform4fv(LLShaderMgr::LIGHT_AMBIENT, 1, mAmbientLightColor.mV); + shader->uniform4fv(LLShaderMgr::LIGHT_POSITION, LL_NUM_LIGHT_UNITS, position[0].mV); + shader->uniform3fv(LLShaderMgr::LIGHT_DIRECTION, LL_NUM_LIGHT_UNITS, direction[0].mV); + shader->uniform4fv(LLShaderMgr::LIGHT_ATTENUATION, LL_NUM_LIGHT_UNITS, attenuation[0].mV); + shader->uniform3fv(LLShaderMgr::LIGHT_DIFFUSE, LL_NUM_LIGHT_UNITS, diffuse[0].mV); + shader->uniform4fv(LLShaderMgr::LIGHT_AMBIENT, 1, mAmbientLightColor.mV); shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_primary[0] ? 1 : 0); shader->uniform4fv(LLShaderMgr::AMBIENT, 1, mAmbientLightColor.mV); shader->uniform4fv(LLShaderMgr::SUNLIGHT_COLOR, 1, diffuse[0].mV); shader->uniform4fv(LLShaderMgr::MOONLIGHT_COLOR, 1, diffuse_b[0].mV); - } + } } void LLRender::syncMatrices() @@ -1539,11 +1545,17 @@ void LLRender::matrixMode(eMatrixMode mode) { U32 tex_index = gGL.getCurrentTexUnitIndex(); // the shaders don't actually reference anything beyond texture_matrix0/1 outside of terrain rendering - llassert_always(tex_index <= 3); - mode = eMatrixMode(MM_TEXTURE0 + gGL.getCurrentTexUnitIndex()); + llassert(tex_index <= 3); + mode = eMatrixMode(MM_TEXTURE0 + tex_index); + if (mode > MM_TEXTURE3) + { + // getCurrentTexUnitIndex() can go as high as 32 (LL_NUM_TEXTURE_LAYERS) + // Large value will result in a crash at mMatrix + LL_WARNS_ONCE() << "Attempted to assign matrix mode out of bounds: " << mode << LL_ENDL; + mode = MM_TEXTURE0; + } } - llassert(mode < NUM_MATRIX_MODES); mMatrixMode = mode; } diff --git a/indra/llrender/llrender2dutils.h b/indra/llrender/llrender2dutils.h index 70ab006fd6..8c01784071 100644 --- a/indra/llrender/llrender2dutils.h +++ b/indra/llrender/llrender2dutils.h @@ -32,6 +32,7 @@ #include "llpointer.h" // LLPointer<> #include "llrect.h" +#include "llsingleton.h" #include "llglslshader.h" class LLColor4; diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 9fb4f7f2b0..e3c0255290 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -501,23 +501,23 @@ U32 LLRenderTarget::getNumTextures() const return mTex.size(); } - void LLRenderTarget::bindTexture(U32 index, S32 channel, LLTexUnit::eTextureFilterOptions filter_options) { - gGL.getTexUnit(channel)->bindManual(mUsage, getTexture(index)); + gGL.getTexUnit(channel)->bindManual(mUsage, getTexture(index)); bool isSRGB = false; llassert(mInternalFormat.size() > index); switch (mInternalFormat[index]) { - case GL_SRGB_ALPHA: case GL_SRGB: + case GL_SRGB8: + case GL_SRGB_ALPHA: case GL_SRGB8_ALPHA8: isSRGB = true; - break; + break; default: - break; + break; } gGL.getTexUnit(channel)->setTextureFilteringOption(filter_options); diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 1383020873..236ebbd78f 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1180,7 +1180,7 @@ void LLShaderMgr::initAttribsAndUniforms() llassert(mReservedUniforms.size() == LLShaderMgr::MULTI_LIGHT_FAR_Z+1); - + //NOTE: MUST match order in eGLSLReservedUniforms mReservedUniforms.push_back("proj_mat"); mReservedUniforms.push_back("proj_near"); mReservedUniforms.push_back("proj_p"); diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 6a02cd9c19..7d2b09ca4a 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -1193,7 +1193,7 @@ bool LLVertexBuffer::createGLBuffer(U32 size) return true; } - bool sucsess = true; + bool success = true; mEmpty = true; @@ -1215,9 +1215,9 @@ bool LLVertexBuffer::createGLBuffer(U32 size) if (!mMappedData) { - sucsess = false; + success = false; } - return sucsess; + return success; } bool LLVertexBuffer::createGLIndices(U32 size) @@ -1232,7 +1232,7 @@ bool LLVertexBuffer::createGLIndices(U32 size) return true; } - bool sucsess = true; + bool success = true; mEmpty = true; @@ -1257,9 +1257,9 @@ bool LLVertexBuffer::createGLIndices(U32 size) if (!mMappedIndexData) { - sucsess = false; + success = false; } - return sucsess; + return success; } void LLVertexBuffer::destroyGLBuffer() @@ -1306,7 +1306,7 @@ bool LLVertexBuffer::updateNumVerts(S32 nverts) { llassert(nverts >= 0); - bool sucsess = true; + bool success = true; if (nverts > 65536) { @@ -1318,34 +1318,34 @@ bool LLVertexBuffer::updateNumVerts(S32 nverts) if (needed_size > mSize || needed_size <= mSize/2) { - sucsess &= createGLBuffer(needed_size); + success &= createGLBuffer(needed_size); } sVertexCount -= mNumVerts; mNumVerts = nverts; sVertexCount += mNumVerts; - return sucsess; + return success; } bool LLVertexBuffer::updateNumIndices(S32 nindices) { llassert(nindices >= 0); - bool sucsess = true; + bool success = true; U32 needed_size = sizeof(U16) * nindices; if (needed_size > mIndicesSize || needed_size <= mIndicesSize/2) { - sucsess &= createGLIndices(needed_size); + success &= createGLIndices(needed_size); } sIndexCount -= mNumIndices; mNumIndices = nindices; sIndexCount += mNumIndices; - return sucsess; + return success; } bool LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create) @@ -1358,10 +1358,10 @@ bool LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create) LL_ERRS() << "Bad vertex buffer allocation: " << nverts << " : " << nindices << LL_ENDL; } - bool sucsess = true; + bool success = true; - sucsess &= updateNumVerts(nverts); - sucsess &= updateNumIndices(nindices); + success &= updateNumVerts(nverts); + success &= updateNumIndices(nindices); if (create && (nverts || nindices)) { @@ -1377,7 +1377,7 @@ bool LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create) } } - return sucsess; + return success; } static LLTrace::BlockTimerStatHandle FTM_SETUP_VERTEX_ARRAY("Setup VAO"); diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index edcbc3fbb7..809d72208f 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -55,6 +55,7 @@ LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params) , mTabComparator( NULL ) , mNoVisibleTabsHelpText(NULL) , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString()) + , mSkipScrollToChild(false) { initNoTabsWidget(params.no_matched_tabs_text); @@ -655,6 +656,37 @@ void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*) { updateLayout(getRect().getWidth(),getRect().getHeight()); } + +// virtual +void LLAccordionCtrl::onUpdateScrollToChild(const LLUICtrl *cntrl) +{ + if (mScrollbar && mScrollbar->getVisible() && !mSkipScrollToChild) + { + // same as scrollToShowRect + LLRect rect; + cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this); + + // Translate to parent coordinatess to check if we are in visible rectangle + rect.translate(getRect().mLeft, getRect().mBottom); + + if (!getRect().contains(rect)) + { + // for accordition's scroll, height is in pixels + // Back to local coords and calculate position for scroller + S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom; + S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop; + + S32 scroll_pos = llclamp(mScrollbar->getDocPos(), + bottom, // min vertical scroll + top); // max vertical scroll + + mScrollbar->setDocPos(scroll_pos); + } + } + + LLUICtrl::onUpdateScrollToChild(cntrl); +} + void LLAccordionCtrl::onOpen (const LLSD& key) { for(size_t i=0;i<mAccordionTabs.size();++i) diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h index 1fe64c472e..2828254472 100644 --- a/indra/llui/llaccordionctrl.h +++ b/indra/llui/llaccordionctrl.h @@ -111,6 +111,7 @@ public: void draw(); void onScrollPosChangeCallback(S32, LLScrollbar*); + virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); void onOpen (const LLSD& key); S32 notifyParent(const LLSD& info); @@ -137,6 +138,8 @@ public: bool getFitParent() const {return mFitParent;} + void setSkipScrollToChild(bool skip) { mSkipScrollToChild = skip; } + private: void initNoTabsWidget(const LLTextBox::Params& tb_params); void updateNoTabsHelpTextVisibility(); @@ -182,6 +185,8 @@ private: F32 mAutoScrollRate; LLTextBox* mNoVisibleTabsHelpText; + bool mSkipScrollToChild; + std::string mNoMatchedTabsOrigString; std::string mNoVisibleTabsOrigString; diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 1034a21905..098621b543 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -452,6 +452,35 @@ void LLAccordionCtrlTab::onVisibilityChange(BOOL new_visibility) notifyParent(LLSD().with("child_visibility_change", new_visibility)); } +// virtual +void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl) +{ + if (mScrollbar && mScrollbar->getVisible()) + { + LLRect rect; + cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this); + + // Translate to parent coordinatess to check if we are in visible rectangle + rect.translate(getRect().mLeft, getRect().mBottom); + + if (!getRect().contains(rect)) + { + // for accordition's scroll, height is in pixels + // Back to local coords and calculate position for scroller + S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom; + S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop; + + S32 scroll_pos = llclamp(mScrollbar->getDocPos(), + bottom, // min vertical scroll + top); // max vertical scroll + + mScrollbar->setDocPos(scroll_pos); + } + } + + LLUICtrl::onUpdateScrollToChild(cntrl); +} + BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask) { if(mCollapsible && mHeaderVisible && mCanOpenClose) diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h index 0263bce4be..2c72e8c036 100644 --- a/indra/llui/llaccordionctrltab.h +++ b/indra/llui/llaccordionctrltab.h @@ -159,6 +159,7 @@ public: * Raises notifyParent event with "child_visibility_change" = new_visibility */ void onVisibilityChange(BOOL new_visibility); + virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); // Changes expand/collapse state and triggers expand/collapse callbacks virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/llui/llbadgeowner.cpp b/indra/llui/llbadgeowner.cpp index 0557cd4375..5f11c383ef 100644 --- a/indra/llui/llbadgeowner.cpp +++ b/indra/llui/llbadgeowner.cpp @@ -56,6 +56,14 @@ void LLBadgeOwner::initBadgeParams(const LLBadge::Params& p) } } +void LLBadgeOwner::reshapeBadge(const LLRect& new_rect) +{ + if (mBadge) + { + mBadge->setShape(new_rect); + } +} + void LLBadgeOwner::setBadgeVisibility(bool visible) { if (mBadge) diff --git a/indra/llui/llbadgeowner.h b/indra/llui/llbadgeowner.h index 01ed95f3a3..4ce208fa0d 100644 --- a/indra/llui/llbadgeowner.h +++ b/indra/llui/llbadgeowner.h @@ -46,6 +46,7 @@ public: bool hasBadgeHolderParent() const { return mHasBadgeHolderParent; }; void setBadgeVisibility(bool visible); void setDrawBadgeAtTop(bool draw_at_top); + void reshapeBadge(const LLRect& new_rect); private: diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 27444b7f5b..9682c3bc10 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -643,7 +643,8 @@ void LLButton::draw() LLColor4 highlighting_color = LLColor4::white; LLColor4 glow_color = LLColor4::white; LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; - LLUIImage* imagep = NULL; + LLUIImage* imagep = NULL; + LLUIImage* image_glow = NULL; // Cancel sticking of color, if the button is pressed, // or when a flashing of the previously selected button is ended @@ -710,17 +711,18 @@ void LLButton::draw() imagep = mImageDisabled; } + image_glow = imagep; + if (mFlashing) { - // if button should flash and we have icon for flashing, use it as image for button - if(flash && mImageFlash) + if (flash && mImageFlash) { - // setting flash to false to avoid its further influence on glow - flash = false; - imagep = mImageFlash; + // if button should flash and we have icon for flashing, use it as image for button + image_glow = mImageFlash; } - // else use usual flashing via flash_color - else if (mFlashingTimer) + + // provide fade-in and fade-out via flash_color + if (mFlashingTimer) { LLColor4 flash_color = mFlashBgColor.get(); use_glow_effect = TRUE; @@ -734,6 +736,11 @@ void LLButton::draw() { glow_color = highlighting_color; } + else + { + // will fade from highlight color + glow_color = flash_color; + } } } @@ -806,7 +813,7 @@ void LLButton::draw() if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); - imagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha)); + image_glow->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha)); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } @@ -817,7 +824,7 @@ void LLButton::draw() if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); - imagep->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha)); + image_glow->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha)); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 7629ed1fea..572d36996c 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -205,6 +205,7 @@ public: void setFlashing( bool b, bool force_flashing = false ); BOOL getFlashing() const { return mFlashing; } LLFlashTimer* getFlashTimer() {return mFlashingTimer;} + void setFlashColor(const LLUIColor &color) { mFlashBgColor = color; }; void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } LLFontGL::HAlign getHAlign() const { return mHAlign; } diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 6a51c4240b..08da599ef2 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -187,10 +187,32 @@ void LLCheckBoxCtrl::clear() void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) { - S32 label_top = mLabel->getRect().mTop; - mLabel->reshapeToFitText(); + LLRect rect = getRect(); + S32 delta_width = width - rect.getWidth(); + S32 delta_height = height - rect.getHeight(); - LLRect label_rect = mLabel->getRect(); + if (delta_width || delta_height) + { + // adjust our rectangle + rect.mRight = getRect().mLeft + width; + rect.mTop = getRect().mBottom + height; + setRect(rect); + } + + // reshapeToFitText reshapes label to minimal size according to last bounding box + // it will work fine in case of decrease of space, but if we get more space or text + // becomes longer, label will fail to grow so reinit label's dimentions. + + static LLUICachedControl<S32> llcheckboxctrl_hpad("UICheckboxctrlHPad", 0); + LLRect label_rect = mLabel->getRect(); + S32 new_width = getRect().getWidth() - label_rect.mLeft - llcheckboxctrl_hpad; + label_rect.mRight = label_rect.mLeft + new_width; + mLabel->setRect(label_rect); + + S32 label_top = label_rect.mTop; + mLabel->reshapeToFitText(TRUE); + + label_rect = mLabel->getRect(); if (label_top != label_rect.mTop && mWordWrap == WRAP_DOWN) { // reshapeToFitText uses LLView::reshape() which always reshapes @@ -210,6 +232,8 @@ void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft), llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight())); mButton->setShape(btn_rect); + + updateBoundingRect(); } //virtual diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index c7f0326ed4..52dc908655 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -514,6 +514,14 @@ S32 LLComboBox::getCurrentIndex() const return -1; } +void LLComboBox::setEnabledByValue(const LLSD& value, BOOL enabled) +{ + LLScrollListItem *found = mList->getItem(value); + if (found) + { + found->setEnabled(enabled); + } +} void LLComboBox::createLineEditor(const LLComboBox::Params& p) { diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index 7d38c051a5..4af3313162 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -158,6 +158,8 @@ public: BOOL setCurrentByIndex( S32 index ); S32 getCurrentIndex() const; + void setEnabledByValue(const LLSD& value, BOOL enabled); + void createLineEditor(const Params&); //======================================================================== diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index abb043f428..e9c980ad9a 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -303,12 +303,10 @@ void LLFloater::initFloater(const Params& p) mButtonsEnabled[BUTTON_CLOSE] = TRUE; } - // Help button: '?' - if ( !mHelpTopic.empty() ) - { - mButtonsEnabled[BUTTON_HELP] = TRUE; - } - + // Help button: '?' + //SL-14050 Disable all Help question marks + mButtonsEnabled[BUTTON_HELP] = FALSE; + // Minimize button only for top draggers if ( !mDragOnLeft && mCanMinimize ) { diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index e718fcec46..0c1dcc301b 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -342,7 +342,9 @@ static LLTrace::BlockTimerStatHandle FTM_FILTER("Filter Folder View"); void LLFolderView::filter( LLFolderViewFilter& filter ) { LL_RECORD_BLOCK_TIME(FTM_FILTER); - filter.resetTime(llclamp(LLUI::getInstance()->mSettingGroups["config"]->getS32(mParentPanel.get()->getVisible() ? "FilterItemsMaxTimePerFrameVisible" : "FilterItemsMaxTimePerFrameUnvisible"), 1, 100)); + static LLCachedControl<S32> filter_visible(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameVisible", 10); + static LLCachedControl<S32> filter_hidden(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameUnvisible", 1); + filter.resetTime(llclamp(mParentPanel.get()->getVisible() ? filter_visible() : filter_hidden(), 1, 100)); // Note: we filter the model, not the view getViewModelItem()->filter(filter); diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 9a1f7de73b..1c6c7b1b35 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -122,6 +122,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) : LLView(p), mLabelWidth(0), mLabelWidthDirty(false), + mSuffixNeedsRefresh(false), mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT), mParentFolder( NULL ), mIsSelected( FALSE ), @@ -181,11 +182,25 @@ LLFolderViewItem::~LLFolderViewItem() BOOL LLFolderViewItem::postBuild() { - refresh(); + LLFolderViewModelItem& vmi = *getViewModelItem(); + // getDisplayName() is expensive (due to internal getLabelSuffix() and name building) + // it also sets search strings so it requires a filter reset + mLabel = vmi.getDisplayName(); + setToolTip(vmi.getName()); + + // Dirty the filter flag of the model from the view (CHUI-849) + vmi.dirtyFilter(); + + // Don't do full refresh on constructor if it is possible to avoid + // it significantly slows down bulk view creation. + // Todo: Ideally we need to move getDisplayName() out of constructor as well. + // Like: make a logic that will let filter update search string, + // while LLFolderViewItem::arrange() updates visual part + mSuffixNeedsRefresh = true; + mLabelWidthDirty = true; return TRUE; } - LLFolderView* LLFolderViewItem::getRoot() { return mRoot; @@ -280,24 +295,51 @@ BOOL LLFolderViewItem::isPotentiallyVisible(S32 filter_generation) void LLFolderViewItem::refresh() { - LLFolderViewModelItem& vmi = *getViewModelItem(); + LLFolderViewModelItem& vmi = *getViewModelItem(); + + mLabel = vmi.getDisplayName(); + setToolTip(vmi.getName()); + // icons are slightly expensive to get, can be optimized + // see LLInventoryIcon::getIcon() + mIcon = vmi.getIcon(); + mIconOpen = vmi.getIconOpen(); + mIconOverlay = vmi.getIconOverlay(); - mLabel = vmi.getDisplayName(); + if (mRoot->useLabelSuffix()) + { + // Very Expensive! + // Can do a number of expensive checks, like checking active motions, wearables or friend list + mLabelStyle = vmi.getLabelStyle(); + mLabelSuffix = vmi.getLabelSuffix(); + } - setToolTip(vmi.getName()); - mIcon = vmi.getIcon(); - mIconOpen = vmi.getIconOpen(); - mIconOverlay = vmi.getIconOverlay(); + // Dirty the filter flag of the model from the view (CHUI-849) + vmi.dirtyFilter(); + + mLabelWidthDirty = true; + mSuffixNeedsRefresh = false; +} + +void LLFolderViewItem::refreshSuffix() +{ + LLFolderViewModelItem const* vmi = getViewModelItem(); + + // icons are slightly expensive to get, can be optimized + // see LLInventoryIcon::getIcon() + mIcon = vmi->getIcon(); + mIconOpen = vmi->getIconOpen(); + mIconOverlay = vmi->getIconOverlay(); if (mRoot->useLabelSuffix()) { - mLabelStyle = vmi.getLabelStyle(); - mLabelSuffix = vmi.getLabelSuffix(); + // Very Expensive! + // Can do a number of expensive checks, like checking active motions, wearables or friend list + mLabelStyle = vmi->getLabelStyle(); + mLabelSuffix = vmi->getLabelSuffix(); } - mLabelWidthDirty = true; - // Dirty the filter flag of the model from the view (CHUI-849) - vmi.dirtyFilter(); + mLabelWidthDirty = true; + mSuffixNeedsRefresh = false; } // Utility function for LLFolderView @@ -348,6 +390,12 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height ) : 0; if (mLabelWidthDirty) { + if (mSuffixNeedsRefresh) + { + // Expensive. But despite refreshing label, + // it is purely visual, so it is fine to do at our laisure + refreshSuffix(); + } mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + mLabelPaddingRight; mLabelWidthDirty = false; } diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 61c39e0175..da09d139e9 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -95,6 +95,7 @@ protected: LLPointer<LLFolderViewModelItem> mViewModelItem; LLFontGL::StyleFlags mLabelStyle; std::string mLabelSuffix; + bool mSuffixNeedsRefresh; //suffix and icons LLUIImagePtr mIcon, mIconOpen, mIconOverlay; @@ -266,8 +267,13 @@ public: virtual BOOL passedFilter(S32 filter_generation = -1); virtual BOOL isPotentiallyVisible(S32 filter_generation = -1); - // refresh information from the object being viewed. - virtual void refresh(); + // refresh information from the object being viewed. + // refreshes label, suffixes and sets icons. Expensive! + // Causes filter update + virtual void refresh(); + // refreshes suffixes and sets icons. Expensive! + // Does not need filter update + virtual void refreshSuffix(); // LLView functionality virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); diff --git a/indra/llui/llfolderviewmodel.cpp b/indra/llui/llfolderviewmodel.cpp index 3b45fb53a2..ea106b5fae 100644 --- a/indra/llui/llfolderviewmodel.cpp +++ b/indra/llui/llfolderviewmodel.cpp @@ -48,7 +48,8 @@ std::string LLFolderViewModelCommon::getStatusText() void LLFolderViewModelCommon::filter() { - getFilter().resetTime(llclamp(LLUI::getInstance()->mSettingGroups["config"]->getS32("FilterItemsMaxTimePerFrameVisible"), 1, 100)); + static LLCachedControl<S32> filter_visible(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameVisible", 10); + getFilter().resetTime(llclamp(filter_visible(), 1, 100)); mFolderView->getViewModelItem()->filter(getFilter()); } diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index f71a88c56e..e62b2779dd 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -204,6 +204,7 @@ public: virtual bool hasChildren() const = 0; virtual void addChild(LLFolderViewModelItem* child) = 0; virtual void removeChild(LLFolderViewModelItem* child) = 0; + virtual void clearChildren() = 0; // This method will be called to determine if a drop can be // performed, and will set drop to TRUE if a drop is @@ -286,16 +287,6 @@ public: virtual void addChild(LLFolderViewModelItem* child) { - // Avoid duplicates: bail out if that child is already present in the list - // Note: this happens when models are created before views - child_list_t::const_iterator iter; - for (iter = mChildren.begin(); iter != mChildren.end(); iter++) - { - if (child == *iter) - { - return; - } - } mChildren.push_back(child); child->setParent(this); dirtyFilter(); @@ -311,9 +302,8 @@ public: virtual void clearChildren() { - // As this is cleaning the whole list of children wholesale, we do need to delete the pointed objects - // This is different and not equivalent to calling removeChild() on each child - std::for_each(mChildren.begin(), mChildren.end(), DeletePointer()); + // We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest + std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); }); mChildren.clear(); dirtyDescendantsFilter(); dirtyFilter(); diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 4aae1e374b..29a156e933 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -166,7 +166,7 @@ void LLLayoutPanel::setVisible( BOOL visible ) void LLLayoutPanel::reshape( S32 width, S32 height, BOOL called_from_parent /*= TRUE*/ ) { - if (width == getRect().getWidth() && height == getRect().getHeight()) return; + if (width == getRect().getWidth() && height == getRect().getHeight() && !LLView::sForceReshape) return; if (!mIgnoreReshape && mAutoResize == false) { diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 70304cdfd2..1badd54fca 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -2151,6 +2151,7 @@ void LLLineEditor::clear() void LLLineEditor::onTabInto() { selectAll(); + LLUICtrl::onTabInto(); } //virtual diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 5568a84494..37dbe9b40e 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -213,6 +213,12 @@ LLSD LLMenuItemGL::getValue() const } //virtual +bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + return (mAcceleratorKey == key) && (mAcceleratorMask == mask); +} + +//virtual BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) { if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) @@ -263,13 +269,13 @@ BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list -BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp) +BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLMenuKeyboardBinding*> *listp) { - LLKeyBinding *accelerator = NULL; + LLMenuKeyboardBinding *accelerator = NULL; if (mAcceleratorKey != KEY_NONE) { - std::list<LLKeyBinding*>::iterator list_it; + std::list<LLMenuKeyboardBinding*>::iterator list_it; for (list_it = listp->begin(); list_it != listp->end(); ++list_it) { accelerator = *list_it; @@ -293,7 +299,7 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp) } if (!accelerator) { - accelerator = new LLKeyBinding; + accelerator = new LLMenuKeyboardBinding; if (accelerator) { accelerator->mKey = mAcceleratorKey; @@ -1017,6 +1023,11 @@ BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) return TRUE; } +bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + return getBranch() && getBranch()->hasAccelerator(key, mask); +} + BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) { return getBranch() && getBranch()->handleAcceleratorKey(key, mask); @@ -1024,7 +1035,7 @@ BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list -BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list<LLKeyBinding*> *listp) +BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list<LLMenuKeyboardBinding*> *listp) { LLMenuGL* branch = getBranch(); if (!branch) @@ -3023,6 +3034,27 @@ void LLMenuGL::updateParent(LLView* parentp) } } +bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + if (key == KEY_NONE) + { + return false; + } + // Note: checking this way because mAccelerators seems to be broken + // mAccelerators probably needs to be cleaned up or fixed + // It was used for dupplicate accelerator avoidance. + item_list_t::const_iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + LLMenuItemGL* itemp = *item_iter; + if (itemp->hasAccelerator(key, mask)) + { + return true; + } + } + return false; +} + BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) { // don't handle if not enabled @@ -3244,7 +3276,7 @@ void hide_top_view( LLView* view ) // x and y are the desired location for the popup, in the spawning_view's // coordinate frame, NOT necessarily the mouse location // static -void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) +void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x, S32 mouse_y) { const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size const S32 CURSOR_WIDTH = 12; @@ -3275,12 +3307,6 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) } } - // Save click point for detecting cursor moves before mouse-up. - // Must be in local coords to compare with mouseUp events. - // If the mouse doesn't move, the menu will stay open ala the Mac. - // See also LLContextMenu::show() - S32 mouse_x, mouse_y; - // Resetting scrolling position if (menu->isScrollable() && menu->isScrollPositionOnShowReset()) { @@ -3291,7 +3317,18 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) menu->needsArrange(); menu->arrangeAndClear(); - LLUI::getInstance()->getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y); + if ((mouse_x == 0) || (mouse_y == 0)) + + { + // Save click point for detecting cursor moves before mouse-up. + // Must be in local coords to compare with mouseUp events. + // If the mouse doesn't move, the menu will stay open ala the Mac. + // See also LLContextMenu::show() + + LLUI::getInstance()->getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y); + } + + LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y); const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect(); diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 1f11f26192..273bd789c4 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -42,6 +42,13 @@ extern S32 MENU_BAR_HEIGHT; extern S32 MENU_BAR_WIDTH; +class LLMenuKeyboardBinding +{ +public: + KEY mKey; + MASK mMask; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLMenuItemGL // @@ -91,6 +98,7 @@ public: /*virtual*/ void setValue(const LLSD& value); /*virtual*/ LLSD getValue() const; + virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; virtual BOOL handleAcceleratorKey(KEY key, MASK mask); LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); } @@ -109,7 +117,7 @@ public: virtual void setBriefItem(BOOL brief); virtual BOOL isBriefItem() const; - virtual BOOL addToAcceleratorList(std::list<LLKeyBinding*> *listp); + virtual BOOL addToAcceleratorList(std::list<LLMenuKeyboardBinding*> *listp); void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; } BOOL getAllowKeyRepeat() const { return mAllowKeyRepeat; } @@ -436,7 +444,8 @@ public: /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); /*virtual*/ void removeChild( LLView* ctrl); /*virtual*/ BOOL postBuild(); - + + virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; virtual BOOL handleAcceleratorKey(KEY key, MASK mask); LLMenuGL* findChildMenuByName(const std::string& name, BOOL recurse) const; @@ -510,7 +519,7 @@ public: void createJumpKeys(); // Show popup at a specific location, in the spawn_view's coordinate frame - static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y); + static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x = 0, S32 mouse_y = 0); // Whether to drop shadow menu bar void setDropShadowed( const BOOL shadowed ); @@ -628,10 +637,11 @@ public: virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; virtual BOOL handleAcceleratorKey(KEY key, MASK mask); // check if we've used these accelerators already - virtual BOOL addToAcceleratorList(std::list <LLKeyBinding*> *listp); + virtual BOOL addToAcceleratorList(std::list <LLMenuKeyboardBinding*> *listp); // called to rebuild the draw label virtual void buildDrawLabel( void ); @@ -797,7 +807,7 @@ private: void checkMenuTrigger(); - std::list <LLKeyBinding*> mAccelerators; + std::list <LLMenuKeyboardBinding*> mAccelerators; BOOL mAltKeyTrigger; }; diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index 20e2b569f1..b3df7c154b 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -509,6 +509,7 @@ void LLMultiSliderCtrl::onTabInto() { mEditor->onTabInto(); } + LLF32UICtrl::onTabInto(); } void LLMultiSliderCtrl::reportInvalidData() diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 6a7075301b..06ec648178 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -255,7 +255,7 @@ LLNotificationForm::LLNotificationForm(const LLSD& sd) } else { - LL_WARNS() << "Invalid form data " << sd << LL_ENDL; + LL_WARNS("Notifications") << "Invalid form data " << sd << LL_ENDL; mFormData = LLSD::emptyArray(); } } @@ -448,11 +448,11 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par mUniqueContext.push_back(context.value); } - LL_DEBUGS() << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL; + LL_DEBUGS("Notifications") << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL; BOOST_FOREACH(const LLNotificationTemplate::Tag& tag, p.tags) { - LL_DEBUGS() << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL; + LL_DEBUGS("Notifications") << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL; mTags.push_back(tag.value); } @@ -1398,8 +1398,14 @@ void LLNotifications::initSingleton() createDefaultChannels(); } +void LLNotifications::cleanupSingleton() +{ + clear(); +} + void LLNotifications::createDefaultChannels() { + LL_INFOS("Notifications") << "Generating default notification channels" << LL_ENDL; // now construct the various channels AFTER loading the notifications, // because the history channel is going to rewrite the stored notifications file mDefaultChannels.push_back(new LLNotificationChannel("Enabled", "", @@ -1455,7 +1461,7 @@ void LLNotifications::forceResponse(const LLNotification::Params& params, S32 op if (selected_item.isUndefined()) { - LL_WARNS() << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL; + LL_WARNS("Notifications") << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL; return; } response[selected_item["name"].asString()] = true; @@ -1489,12 +1495,12 @@ void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements) if (found != replacements.end()) { replacement = found->second; - LL_DEBUGS() << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL; + LL_DEBUGS("Notifications") << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL; it->second->setValue(replacement); } else { - LL_WARNS() << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL; + LL_WARNS("Notifications") << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL; } } } @@ -1533,7 +1539,7 @@ void addPathIfExists(const std::string& new_path, std::vector<std::string>& path bool LLNotifications::loadTemplates() { - LL_INFOS() << "Reading notifications template" << LL_ENDL; + LL_INFOS("Notifications") << "Reading notifications template" << LL_ENDL; // Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it // output all relevant pathnames instead of just the ones from the most // specific skin. @@ -1604,7 +1610,7 @@ bool LLNotifications::loadTemplates() mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification)); } - LL_INFOS() << "...done" << LL_ENDL; + LL_INFOS("Notifications") << "...done" << LL_ENDL; return true; } @@ -1832,7 +1838,7 @@ bool LLNotifications::isVisibleByRules(LLNotificationPtr n) for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++) { // An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule. - LL_DEBUGS() + LL_DEBUGS("Notifications") << "notification \"" << n->getName() << "\" " << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, " << "name = \"" << (*it)->mName << "\" " @@ -1877,7 +1883,7 @@ bool LLNotifications::isVisibleByRules(LLNotificationPtr n) if((*it)->mResponse.empty()) { // Response property is empty. Cancel this notification. - LL_DEBUGS() << "cancelling notification " << n->getName() << LL_ENDL; + LL_DEBUGS("Notifications") << "cancelling notification " << n->getName() << LL_ENDL; cancel(n); } @@ -1888,7 +1894,7 @@ bool LLNotifications::isVisibleByRules(LLNotificationPtr n) // TODO: verify that the response template has an item with the correct name response[(*it)->mResponse] = true; - LL_DEBUGS() << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL; + LL_DEBUGS("Notifications") << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL; n->respond(response); } @@ -1900,7 +1906,7 @@ bool LLNotifications::isVisibleByRules(LLNotificationPtr n) break; } - LL_DEBUGS() << "allowing notification " << n->getName() << LL_ENDL; + LL_DEBUGS("Notifications") << "allowing notification " << n->getName() << LL_ENDL; return true; } @@ -1961,7 +1967,7 @@ void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id, // from PE merge - we should figure out if this is the right thing to do if (name.empty()) { - LL_WARNS() << "Empty name received for Id: " << agent_id << LL_ENDL; + LL_WARNS("Notifications") << "Empty name received for Id: " << agent_id << LL_ENDL; name = SYSTEM_FROM; } diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index cac687f53d..2f4578da17 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -957,6 +957,7 @@ public: private: /*virtual*/ void initSingleton(); + /*virtual*/ void cleanupSingleton(); void loadPersistentNotifications(); @@ -1069,6 +1070,7 @@ public: LLPersistentNotificationChannel() : LLNotificationChannel("Persistent", "Visible", ¬ificationFilter) {} + virtual ~LLPersistentNotificationChannel() {} typedef std::vector<LLNotificationPtr> history_list_t; history_list_t::iterator beginHistory() { sortHistory(); return mHistory.begin(); } diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index 8000efad0e..13839da400 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -50,6 +50,10 @@ LLScrollListCell* LLScrollListCell::create(const LLScrollListCell::Params& cell_ { cell = new LLScrollListDate(cell_p); } + else if (cell_p.type() == "icontext") + { + cell = new LLScrollListIconText(cell_p); + } else // default is "text" { cell = new LLScrollListText(cell_p); @@ -168,7 +172,7 @@ U32 LLScrollListText::sCount = 0; LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) : LLScrollListCell(p), - mText(p.value().asString()), + mText(p.label.isProvided() ? p.label() : p.value().asString()), mFont(p.font), mColor(p.color), mUseColor(p.color.isProvided()), @@ -192,7 +196,7 @@ LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) void LLScrollListText::highlightText(S32 offset, S32 num_chars) { mHighlightOffset = offset; - mHighlightCount = num_chars; + mHighlightCount = llmax(0, num_chars); } //virtual @@ -292,11 +296,12 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col if (mHighlightCount > 0) { + // Highlight text S32 left = 0; switch(mFontAlignment) { case LLFontGL::LEFT: - left = mFont->getWidth(mText.getString(), 0, mHighlightOffset); + left = mFont->getWidth(mText.getString(), 1, mHighlightOffset); break; case LLFontGL::RIGHT: left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX); @@ -319,7 +324,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col switch(mFontAlignment) { case LLFontGL::LEFT: - start_x = 0.f; + start_x = 1.f; break; case LLFontGL::RIGHT: start_x = (F32)getWidth(); @@ -435,3 +440,139 @@ const LLSD LLScrollListDate::getValue() const { return mDate; } + +// +// LLScrollListIconText +// +LLScrollListIconText::LLScrollListIconText(const LLScrollListCell::Params& p) + : LLScrollListText(p), + mIcon(p.value().isUUID() ? LLUI::getUIImageByID(p.value().asUUID()) : LLUI::getUIImage(p.value().asString())), + mPad(4) +{ + mTextWidth = getWidth() - mPad /*padding*/ - mFont->getLineHeight(); +} + +LLScrollListIconText::~LLScrollListIconText() +{ +} + +const LLSD LLScrollListIconText::getValue() const +{ + if (mIcon.isNull()) + { + return LLStringUtil::null; + } + return mIcon->getName(); +} + +void LLScrollListIconText::setValue(const LLSD& value) +{ + if (value.isUUID()) + { + // don't use default image specified by LLUUID::null, use no image in that case + LLUUID image_id = value.asUUID(); + mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL); + } + else + { + std::string value_string = value.asString(); + if (LLUUID::validate(value_string)) + { + setValue(LLUUID(value_string)); + } + else if (!value_string.empty()) + { + mIcon = LLUI::getUIImage(value.asString()); + } + else + { + mIcon = NULL; + } + } +} + +void LLScrollListIconText::setWidth(S32 width) +{ + LLScrollListCell::setWidth(width); + // Assume that iamge height and width is identical to font height and width + mTextWidth = width - mPad /*padding*/ - mFont->getLineHeight(); +} + + +void LLScrollListIconText::draw(const LLColor4& color, const LLColor4& highlight_color) const +{ + LLColor4 display_color; + if (mUseColor) + { + display_color = mColor; + } + else + { + display_color = color; + } + + S32 icon_height = mFont->getLineHeight(); + S32 icon_space = mIcon ? (icon_height + mPad) : 0; + + if (mHighlightCount > 0) + { + S32 left = 0; + switch (mFontAlignment) + { + case LLFontGL::LEFT: + left = mFont->getWidth(mText.getString(), icon_space + 1, mHighlightOffset); + break; + case LLFontGL::RIGHT: + left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX) - icon_space; + break; + case LLFontGL::HCENTER: + left = (getWidth() - mFont->getWidth(mText.getString()) - icon_space) / 2; + break; + } + LLRect highlight_rect(left - 2, + mFont->getLineHeight() + 1, + left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, + 1); + mRoundedRectImage->draw(highlight_rect, highlight_color); + } + + // Try to draw the entire string + F32 right_x; + U32 string_chars = mText.length(); + F32 start_text_x = 0.f; + S32 start_icon_x = 0; + switch (mFontAlignment) + { + case LLFontGL::LEFT: + start_text_x = icon_space + 1; + start_icon_x = 1; + break; + case LLFontGL::RIGHT: + start_text_x = (F32)getWidth(); + start_icon_x = getWidth() - mFont->getWidth(mText.getString()) - icon_space; + break; + case LLFontGL::HCENTER: + F32 center = (F32)getWidth()* 0.5f; + start_text_x = center + ((F32)icon_space * 0.5f); + start_icon_x = center - (((F32)icon_space + mFont->getWidth(mText.getString())) * 0.5f); + break; + } + mFont->render(mText.getWString(), 0, + start_text_x, 0.f, + display_color, + mFontAlignment, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + string_chars, + getTextWidth(), + &right_x, + TRUE); + + if (mIcon) + { + mIcon->draw(start_icon_x, 0, icon_height, icon_height, mColor); + } +} + + diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h index d625ebddcc..19576fb247 100644 --- a/indra/llui/llscrolllistcell.h +++ b/indra/llui/llscrolllistcell.h @@ -59,7 +59,8 @@ public: visible; Optional<void*> userdata; - Optional<LLSD> value; + Optional<LLSD> value; // state of checkbox, icon id/name, date + Optional<std::string> label; // description or text Optional<std::string> tool_tip; Optional<const LLFontGL*> font; @@ -75,6 +76,7 @@ public: enabled("enabled", true), visible("visible", true), value("value"), + label("label"), tool_tip("tool_tip", ""), font("font", LLFontGL::getFontSansSerifSmall()), font_color("font_color", LLColor4::black), @@ -152,11 +154,12 @@ public: void setText(const LLStringExplicit& text); void setFontStyle(const U8 font_style); -private: +protected: LLUIString mText; S32 mTextWidth; const LLFontGL* mFont; LLColor4 mColor; + LLColor4 mHighlightColor; U8 mUseColor; LLFontGL::HAlign mFontAlignment; BOOL mVisible; @@ -169,7 +172,7 @@ private: }; /* - * Cell displaying an image. + * Cell displaying an image. AT the moment, this is specifically UI image */ class LLScrollListIcon : public LLScrollListCell { @@ -223,4 +226,26 @@ private: LLDate mDate; }; +/* +* Cell displaying icon and text. +*/ + +class LLScrollListIconText : public LLScrollListText +{ +public: + LLScrollListIconText(const LLScrollListCell::Params& p); + /*virtual*/ ~LLScrollListIconText(); + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; + /*virtual*/ const LLSD getValue() const; + /*virtual*/ void setValue(const LLSD& value); + + + S32 getIconWidth() const; + /*virtual*/ void setWidth(S32 width);/* { LLScrollListCell::setWidth(width); mTextWidth = width - ; }*/ + +private: + LLPointer<LLUIImage> mIcon; + S32 mPad; +}; + #endif diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 763c3aeb81..be85f1cb6a 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -115,6 +115,13 @@ struct SortScrollListItem // LLScrollListCtrl //--------------------------------------------------------------------------- +void LLScrollListCtrl::SelectionTypeNames::declareValues() +{ + declare("row", LLScrollListCtrl::ROW); + declare("cell", LLScrollListCtrl::CELL); + declare("header", LLScrollListCtrl::HEADER); +} + LLScrollListCtrl::Contents::Contents() : columns("column"), rows("row") @@ -128,10 +135,13 @@ LLScrollListCtrl::Params::Params() has_border("draw_border"), draw_heading("draw_heading"), search_column("search_column", 0), + selection_type("selection_type", ROW), sort_column("sort_column", -1), sort_ascending("sort_ascending", true), + can_sort("can_sort", true), mouse_wheel_opaque("mouse_wheel_opaque", false), commit_on_keyboard_movement("commit_on_keyboard_movement", true), + commit_on_selection_change("commit_on_selection_change", false), heading_height("heading_height"), page_lines("page_lines", 0), background_visible("background_visible"), @@ -162,10 +172,12 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) mMaxSelectable(0), mAllowKeyboardMovement(true), mCommitOnKeyboardMovement(p.commit_on_keyboard_movement), - mCommitOnSelectionChange(false), + mCommitOnSelectionChange(p.commit_on_selection_change), mSelectionChanged(false), + mSelectionType(p.selection_type), mNeedsScroll(false), mCanSelect(true), + mCanSort(p.can_sort), mColumnsDirty(false), mMaxItemCount(INT_MAX), mBorderThickness( 2 ), @@ -762,14 +774,20 @@ void LLScrollListCtrl::updateColumns(bool force_update) } } + bool header_changed_width = false; // expand last column header we encountered to full list width if (last_header) { + S32 old_width = last_header->getColumn()->getWidth(); S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft); last_header->reshape(new_width, last_header->getRect().getHeight()); last_header->setVisible(mDisplayColumnHeaders && new_width > 0); - last_header->getColumn()->setWidth(new_width); - } + if (old_width != new_width) + { + last_header->getColumn()->setWidth(new_width); + header_changed_width = true; + } + } // propagate column widths to individual cells if (columns_changed_width || force_update) @@ -788,6 +806,20 @@ void LLScrollListCtrl::updateColumns(bool force_update) } } } + else if (header_changed_width) + { + item_list::iterator iter; + S32 index = last_header->getColumn()->mIndex; // Not always identical to last column! + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem *itemp = *iter; + LLScrollListCell* cell = itemp->getColumn(index); + if (cell) + { + cell->setWidth(last_header->getColumn()->getWidth()); + } + } + } } void LLScrollListCtrl::setHeadingHeight(S32 heading_height) @@ -819,7 +851,15 @@ BOOL LLScrollListCtrl::selectFirstItem() { if (!itemp->getSelected()) { - selectItem(itemp); + switch (mSelectionType) + { + case CELL: + selectItem(itemp, 0); + break; + case HEADER: + case ROW: + selectItem(itemp, -1); + } } success = TRUE; mOriginalSelection = 0; @@ -878,7 +918,8 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) { if( itemp->getEnabled() ) { - selectItem(itemp, FALSE); + // TODO: support range selection for cells + selectItem(itemp, -1, FALSE); success = TRUE; } } @@ -1004,10 +1045,14 @@ void LLScrollListCtrl::clearHighlightedItems() void LLScrollListCtrl::mouseOverHighlightNthItem(S32 target_index) { - if (mHighlightedItem != target_index) - { - mHighlightedItem = target_index; - } + if (mHighlightedItem != target_index) + { + if (mHighlightedItem >= 0 && mHighlightedItem < mItemList.size()) + { + mItemList[mHighlightedItem]->setHoverCell(-1); + } + mHighlightedItem = target_index; + } } S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids ) @@ -1022,7 +1067,8 @@ S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids ) { if (item->getEnabled() && (item->getUUID() == (*iditr))) { - selectItem(item,FALSE); + // TODO: support multiple selection for cells + selectItem(item, -1, FALSE); ++count; break; } @@ -1095,7 +1141,7 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) { if (prev_item) { - selectItem(prev_item, !extend_selection); + selectItem(prev_item, cur_item->getSelectedCell(), !extend_selection); } else { @@ -1139,7 +1185,7 @@ void LLScrollListCtrl::selectNextItem( BOOL extend_selection) { if (next_item) { - selectItem(next_item, !extend_selection); + selectItem(next_item, cur_item->getSelectedCell(), !extend_selection); } else { @@ -1210,7 +1256,7 @@ BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sen bool found = NULL != item; if(found) { - selectItem(item); + selectItem(item, -1); } if (mCommitOnSelectionChange) @@ -1278,7 +1324,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE; if (select) { - selectItem(item); + selectItem(item, -1); found = TRUE; break; } @@ -1318,7 +1364,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen // find offset of matching text (might have leading whitespace) S32 offset = item_label.find(target_trimmed); cellp->highlightText(offset, target_trimmed.size()); - selectItem(item); + selectItem(item, -1); found = TRUE; break; } @@ -1380,18 +1426,34 @@ BOOL LLScrollListCtrl::setSelectedByValue(const LLSD& value, BOOL selected) for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { LLScrollListItem* item = *iter; - if (item->getEnabled() && (item->getValue().asString() == value.asString())) - { - if (selected) - { - selectItem(item); - } - else - { - deselectItem(item); - } - found = TRUE; - break; + if (item->getEnabled()) + { + if (value.isBinary()) + { + if (item->getValue().isBinary()) + { + LLSD::Binary data1 = value.asBinary(); + LLSD::Binary data2 = item->getValue().asBinary(); + found = std::equal(data1.begin(), data1.end(), data2.begin()) ? TRUE : FALSE; + } + } + else + { + found = item->getValue().asString() == value.asString() ? TRUE : FALSE; + } + + if (found) + { + if (selected) + { + selectItem(item, -1); + } + else + { + deselectItem(item); + } + break; + } } } @@ -1464,7 +1526,7 @@ void LLScrollListCtrl::drawItems() S32 max_columns = 0; - LLColor4 highlight_color = LLColor4::white; + LLColor4 highlight_color = LLColor4::white; // ex: text inside cells static LLUICachedControl<F32> type_ahead_timeout ("TypeAheadTimeout", 0); highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout(), 0.4f, 0.f); @@ -1490,7 +1552,8 @@ void LLScrollListCtrl::drawItems() max_columns = llmax(max_columns, item->getNumColumns()); LLColor4 fg_color; - LLColor4 bg_color(LLColor4::transparent); + LLColor4 hover_color(LLColor4::transparent); + LLColor4 select_color(LLColor4::transparent); if( mScrollLines <= line && line < mScrollLines + num_page_lines ) { @@ -1499,44 +1562,44 @@ void LLScrollListCtrl::drawItems() { if(item->getHighlighted()) // if it's highlighted, average the colors { - bg_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f); + select_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f); } else // otherwise just select-highlight it { - bg_color = mBgSelectedColor.get(); + select_color = mBgSelectedColor.get(); } fg_color = (item->getEnabled() ? mFgSelectedColor.get() : mFgDisabledColor.get()); } - else if (mHighlightedItem == line && mCanSelect) + if (mHighlightedItem == line && mCanSelect) { if(item->getHighlighted()) // if it's highlighted, average the colors { - bg_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f); + hover_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f); } else // otherwise just hover-highlight it { - bg_color = mHoveredColor.get(); + hover_color = mHoveredColor.get(); } } else if (item->getHighlighted()) { - bg_color = mHighlightedColor.get(); + hover_color = mHighlightedColor.get(); } else { if (mDrawStripes && (line % 2 == 0) && (max_columns > 1)) { - bg_color = mBgStripeColor.get(); + hover_color = mBgStripeColor.get(); } } if (!item->getEnabled()) { - bg_color = mBgReadOnlyColor.get(); + hover_color = mBgReadOnlyColor.get(); } - item->draw(item_rect, fg_color % alpha, bg_color% alpha, highlight_color % alpha, mColumnPadding); + item->draw(item_rect, fg_color % alpha, hover_color% alpha, select_color% alpha, highlight_color % alpha, mColumnPadding); cur_y -= mLineHeight; } @@ -1688,7 +1751,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if (mLastSelected == NULL) { - selectItem(hit_item); + selectItem(hit_item, getColumnIndexFromOffset(x)); } else { @@ -1712,7 +1775,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) LLScrollListItem *item = *itor; if (item == hit_item || item == lastSelected) { - selectItem(item, FALSE); + selectItem(item, getColumnIndexFromOffset(x), FALSE); selecting = !selecting; if (hit_item == lastSelected) { @@ -1722,7 +1785,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) } if (selecting) { - selectItem(item, FALSE); + selectItem(item, getColumnIndexFromOffset(x), FALSE); } } } @@ -1737,7 +1800,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) { - selectItem(hit_item, FALSE); + selectItem(hit_item, getColumnIndexFromOffset(x), FALSE); } else { @@ -1751,12 +1814,12 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) else { deselectAllItems(TRUE); - selectItem(hit_item); + selectItem(hit_item, getColumnIndexFromOffset(x)); } } else { - selectItem(hit_item); + selectItem(hit_item, getColumnIndexFromOffset(x)); } selection_changed = mSelectionChanged; @@ -2124,8 +2187,29 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) { LLScrollListItem* item = hitItem(x, y); if (item) - { - mouseOverHighlightNthItem(getItemIndex(item)); + { + mouseOverHighlightNthItem(getItemIndex(item)); + switch (mSelectionType) + { + case CELL: + item->setHoverCell(getColumnIndexFromOffset(x)); + break; + case HEADER: + { + S32 cell = getColumnIndexFromOffset(x); + if (cell > 0) + { + item->setHoverCell(cell); + } + else + { + item->setHoverCell(-1); + } + break; + } + case ROW: + break; + } } else { @@ -2173,6 +2257,58 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) handled = TRUE; } break; + case KEY_LEFT: + if (mAllowKeyboardMovement || hasFocus()) + { + // TODO: support multi-select + LLScrollListItem *item = getFirstSelected(); + if (item) + { + S32 cell = item->getSelectedCell(); + switch (mSelectionType) + { + case CELL: + if (cell < mColumns.size()) cell++; + break; + case HEADER: + if (cell == -1) cell = 1; + else if (cell > 1 && cell < mColumns.size()) cell++; // skip header + break; + case ROW: + cell = -1; + break; + } + item->setSelectedCell(cell); + handled = TRUE; + } + } + break; + case KEY_RIGHT: + if (mAllowKeyboardMovement || hasFocus()) + { + // TODO: support multi-select + LLScrollListItem *item = getFirstSelected(); + if (item) + { + S32 cell = item->getSelectedCell(); + switch (mSelectionType) + { + case CELL: + if (cell >= 0) cell--; + break; + case HEADER: + if (cell > 1) cell--; + else if (cell == 1) cell = -1; // skip header + break; + case ROW: + cell = -1; + break; + } + item->setSelectedCell(cell); + handled = TRUE; + } + } + break; case KEY_PAGE_UP: if (mAllowKeyboardMovement || hasFocus()) { @@ -2341,7 +2477,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) { - selectItem(item); + selectItem(item, -1); mNeedsScroll = true; cellp->highlightText(0, 1); mSearchTimer.reset(); @@ -2393,7 +2529,7 @@ BOOL LLScrollListCtrl::isRepeatedChars(const LLWString& string) const return TRUE; } -void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_item) +void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, BOOL select_single_item) { if (!itemp) return; @@ -2412,6 +2548,18 @@ void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_it deselectAllItems(TRUE); } itemp->setSelected(TRUE); + switch (mSelectionType) + { + case CELL: + itemp->setSelectedCell(cell); + break; + case HEADER: + itemp->setSelectedCell(cell <= 0 ? -1 : cell); + break; + case ROW: + itemp->setSelectedCell(-1); + break; + } mLastSelected = itemp; mSelectionChanged = true; } @@ -2672,7 +2820,7 @@ void LLScrollListCtrl::selectAll() LLScrollListItem *itemp = *iter; if( itemp->getEnabled() ) { - selectItem(itemp, FALSE); + selectItem(itemp, -1, FALSE); } } @@ -2801,6 +2949,8 @@ void LLScrollListCtrl::onClickColumn(void *userdata) LLScrollListCtrl *parent = info->mParentCtrl; if (!parent) return; + if (!parent->mCanSort) return; + S32 column_index = info->mIndex; LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 43e1c0d707..0cc481b113 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -54,6 +54,18 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler, public LLCtrlListInterface, public LLCtrlScrollInterface { public: + typedef enum e_selection_type + { + ROW, // default + CELL, // does not support multi-selection + HEADER, // when pointing to cells in column 0 will highlight whole row, otherwise cell, no multi-select + } ESelectionType; + + struct SelectionTypeNames : public LLInitParam::TypeValuesHelper<LLScrollListCtrl::ESelectionType, SelectionTypeNames> + { + static void declareValues(); + }; + struct Contents : public LLInitParam::Block<Contents> { Multiple<LLScrollListColumn::Params> columns; @@ -97,8 +109,11 @@ public: // behavioral flags Optional<bool> multi_select, commit_on_keyboard_movement, + commit_on_selection_change, mouse_wheel_opaque; + Optional<ESelectionType, SelectionTypeNames> selection_type; + // display flags Optional<bool> has_border, draw_heading, @@ -115,7 +130,8 @@ public: // sort and search behavior Optional<S32> search_column, sort_column; - Optional<bool> sort_ascending; + Optional<bool> sort_ascending, + can_sort; // whether user is allowed to sort // colors Optional<LLUIColor> fg_unselected_color, @@ -432,7 +448,7 @@ private: void updateLineHeightInsert(LLScrollListItem* item); void reportInvalidInput(); BOOL isRepeatedChars(const LLWString& string) const; - void selectItem(LLScrollListItem* itemp, BOOL single_select = TRUE); + void selectItem(LLScrollListItem* itemp, S32 cell, BOOL single_select = TRUE); void deselectItem(LLScrollListItem* itemp); void commitIfChanged(); BOOL setSort(S32 column, BOOL ascending); @@ -457,9 +473,11 @@ private: bool mCommitOnKeyboardMovement; bool mCommitOnSelectionChange; bool mSelectionChanged; + ESelectionType mSelectionType; bool mNeedsScroll; bool mMouseWheelOpaque; bool mCanSelect; + bool mCanSort; // Whether user is allowed to sort bool mDisplayColumnHeaders; bool mColumnsDirty; bool mColumnWidthsDirty; diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp index df22c88afb..51c615dd00 100644 --- a/indra/llui/llscrolllistitem.cpp +++ b/indra/llui/llscrolllistitem.cpp @@ -40,6 +40,8 @@ LLScrollListItem::LLScrollListItem( const Params& p ) : mSelected(FALSE), mHighlighted(FALSE), + mHoverIndex(-1), + mSelectedIndex(-1), mEnabled(p.enabled), mUserdata(p.userdata), mItemValue(p.value) @@ -53,6 +55,28 @@ LLScrollListItem::~LLScrollListItem() mColumns.clear(); } +void LLScrollListItem::setSelected(BOOL b) +{ + mSelected = b; + mSelectedIndex = -1; +} + +void LLScrollListItem::setHighlighted(BOOL b) +{ + mHighlighted = b; + mHoverIndex = -1; +} + +void LLScrollListItem::setHoverCell(S32 cell) +{ + mHoverIndex = cell; +} + +void LLScrollListItem::setSelectedCell(S32 cell) +{ + mSelectedIndex = cell; +} + void LLScrollListItem::addColumn(const LLScrollListCell::Params& p) { mColumns.push_back(LLScrollListCell::create(p)); @@ -120,12 +144,21 @@ std::string LLScrollListItem::getContentsCSV() const } -void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) +void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& hover_color, const LLColor4& select_color, const LLColor4& highlight_color, S32 column_padding) { // draw background rect gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLRect bg_rect = rect; - gl_rect_2d( bg_rect, bg_color ); + if (mSelectedIndex < 0 && getSelected()) + { + // Whole item is highlighted/selected + gl_rect_2d(bg_rect, select_color); + } + else if (mHoverIndex < 0) + { + // Whole item is highlighted/selected + gl_rect_2d(bg_rect, hover_color); + } S32 cur_x = rect.mLeft; S32 num_cols = getNumColumns(); @@ -141,6 +174,25 @@ void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const { LLUI::translate((F32) cur_x, (F32) rect.mBottom); + if (mSelectedIndex == cur_col) + { + // select specific cell + LLRect highlight_rect(0, + cell->getHeight(), + cell->getWidth(), + 0); + gl_rect_2d(highlight_rect, select_color); + } + else if (mHoverIndex == cur_col) + { + // highlight specific cell + LLRect highlight_rect(0, + cell->getHeight(), + cell->getWidth() , + 0); + gl_rect_2d(highlight_rect, hover_color); + } + cell->draw( fg_color, highlight_color ); } LLUI::popMatrix(); diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h index 13655b5873..d2c3dd7721 100644 --- a/indra/llui/llscrolllistitem.h +++ b/indra/llui/llscrolllistitem.h @@ -77,15 +77,21 @@ public: virtual ~LLScrollListItem(); - void setSelected( BOOL b ) { mSelected = b; } + void setSelected( BOOL b ); BOOL getSelected() const { return mSelected; } void setEnabled( BOOL b ) { mEnabled = b; } BOOL getEnabled() const { return mEnabled; } - void setHighlighted( BOOL b ) { mHighlighted = b; } + void setHighlighted( BOOL b ); BOOL getHighlighted() const { return mHighlighted; } + void setSelectedCell( S32 cell ); + S32 getSelectedCell() const { return mSelectedIndex; } + + void setHoverCell( S32 cell ); + S32 getHoverCell() const { return mHoverIndex; } + void setUserdata( void* userdata ) { mUserdata = userdata; } void* getUserdata() const { return mUserdata; } @@ -107,14 +113,21 @@ public: std::string getContentsCSV() const; - virtual void draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding); + virtual void draw(const LLRect& rect, + const LLColor4& fg_color, + const LLColor4& hover_color, // highlight/hover selection of whole item or cell + const LLColor4& select_color, // highlight/hover selection of whole item or cell + const LLColor4& highlight_color, // highlights contents of cells (ex: text) + S32 column_padding); protected: LLScrollListItem( const Params& ); private: BOOL mSelected; - BOOL mHighlighted; + BOOL mHighlighted; + S32 mHoverIndex; + S32 mSelectedIndex; BOOL mEnabled; void* mUserdata; LLSD mItemValue; diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 3b89a8ca63..d80a434f22 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -479,6 +479,7 @@ void LLSliderCtrl::onTabInto() { mEditor->onTabInto(); } + LLF32UICtrl::onTabInto(); } void LLSliderCtrl::reportInvalidData() diff --git a/indra/llui/llspellcheck.cpp b/indra/llui/llspellcheck.cpp index 296ea09079..ebd8ca0923 100644 --- a/indra/llui/llspellcheck.cpp +++ b/indra/llui/llspellcheck.cpp @@ -49,8 +49,6 @@ LLSpellChecker::settings_change_signal_t LLSpellChecker::sSettingsChangeSignal; LLSpellChecker::LLSpellChecker() : mHunspell(NULL) { - // Load initial dictionary information - refreshDictionaryMap(); } LLSpellChecker::~LLSpellChecker() @@ -58,6 +56,12 @@ LLSpellChecker::~LLSpellChecker() delete mHunspell; } +void LLSpellChecker::initSingleton() +{ + // Load initial dictionary information + refreshDictionaryMap(); +} + bool LLSpellChecker::checkSpelling(const std::string& word) const { if ( (!mHunspell) || (word.length() < 3) || (0 != mHunspell->spell(word.c_str())) ) diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h index f1964cc091..3da5e30955 100644 --- a/indra/llui/llspellcheck.h +++ b/indra/llui/llspellcheck.h @@ -47,6 +47,7 @@ public: protected: void addToDictFile(const std::string& dict_path, const std::string& word); void initHunspell(const std::string& dict_language); + void initSingleton(); public: typedef std::list<std::string> dict_list_t; diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index ce3fc29d32..ee78b82429 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -442,7 +442,8 @@ void LLSpinCtrl::setAllowEdit(BOOL allow_edit) void LLSpinCtrl::onTabInto() { - mEditor->onTabInto(); + mEditor->onTabInto(); + LLF32UICtrl::onTabInto(); } diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 6521b883f8..e6b43da8e5 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -220,6 +220,8 @@ LLTabContainer::Params::Params() last_tab("last_tab"), use_custom_icon_ctrl("use_custom_icon_ctrl", false), open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false), + enable_tabs_flashing("enable_tabs_flashing", false), + tabs_flashing_color("tabs_flashing_color"), tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0), use_ellipses("use_ellipses"), font_halign("halign") @@ -259,6 +261,8 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p) mCustomIconCtrlUsed(p.use_custom_icon_ctrl), mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop), mTabIconCtrlPad(p.tab_icon_ctrl_pad), + mEnableTabsFlashing(p.enable_tabs_flashing), + mTabsFlashingColor(p.tabs_flashing_color), mUseTabEllipses(p.use_ellipses) { static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0); @@ -280,6 +284,11 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p) mMinTabWidth = tabcntr_vert_tab_min_width; } + if (p.tabs_flashing_color.isProvided()) + { + mEnableTabsFlashing = true; + } + initButtons( ); } @@ -1102,6 +1111,10 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) p.pad_left( mLabelPadLeft ); p.pad_right(2); } + + // inits flash timer + p.button_flash_enable = mEnableTabsFlashing; + p.flash_color = mTabsFlashingColor; // *TODO : It seems wrong not to use p in both cases considering the way p is initialized if (mCustomIconCtrlUsed) diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 6bf963313c..8f8cedb1b9 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -109,6 +109,12 @@ public: * Open tabs on hover in drag and drop situations */ Optional<bool> open_tabs_on_drag_and_drop; + + /** + * Enable tab flashing + */ + Optional<bool> enable_tabs_flashing; + Optional<LLUIColor> tabs_flashing_color; /** * Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true) @@ -310,6 +316,8 @@ private: bool mCustomIconCtrlUsed; bool mOpenTabsOnDragAndDrop; + bool mEnableTabsFlashing; + LLUIColor mTabsFlashingColor; S32 mTabIconCtrlPad; bool mUseTabEllipses; }; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 83b851eed2..20bea7fe24 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1017,7 +1017,38 @@ BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask) // handle triple click if (!mTripleClickTimer.hasExpired()) { - selectAll(); + S32 real_line = getLineNumFromDocIndex(mCursorPos, false); + S32 line_start = -1; + S32 line_end = -1; + for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end(); + it != end_it; + ++it) + { + if (it->mLineNum < real_line) + { + continue; + } + if (it->mLineNum > real_line) + { + break; + } + if (line_start == -1) + { + line_start = it->mDocIndexStart; + } + line_end = it->mDocIndexEnd; + line_end = llclamp(line_end, 0, getLength()); + } + + if (line_start == -1) + { + return TRUE; + } + + mSelectionEnd = line_start; + mSelectionStart = line_end; + setCursorPos(line_start); + return TRUE; } @@ -1148,7 +1179,7 @@ BOOL LLTextBase::handleToolTip(S32 x, S32 y, MASK mask) void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent) { - if (width != getRect().getWidth() || height != getRect().getHeight()) + if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape) { bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false; @@ -2228,6 +2259,18 @@ void LLTextBase::needsReflow(S32 index) mReflowIndex = llmin(mReflowIndex, index); } +S32 LLTextBase::removeFirstLine() +{ + if (!mLineInfoList.empty()) + { + S32 length = getLineEnd(0); + deselect(); + removeStringNoUndo(0, length); + return length; + } + return 0; +} + void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params) { segment_vec_t segments; diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 8687e7aa2a..4e966b7cef 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -404,6 +404,7 @@ public: virtual void setText(const LLStringExplicit &utf8str , const LLStyle::Params& input_params = LLStyle::Params()); // uses default style virtual std::string getText() const; void setMaxTextLength(S32 length) { mMaxTextByteLength = length; } + S32 getMaxTextLength() { return mMaxTextByteLength; } // wide-char versions void setWText(const LLWString& text); @@ -432,6 +433,7 @@ public: S32 getLength() const { return getWText().length(); } S32 getLineCount() const { return mLineInfoList.size(); } + S32 removeFirstLine(); // returns removed length void addDocumentChild(LLView* view); void removeDocumentChild(LLView* view); diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 0afd32f332..134afc005b 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -163,13 +163,13 @@ BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text } -void LLTextBox::reshapeToFitText() +void LLTextBox::reshapeToFitText(BOOL called_from_parent) { reflow(); S32 width = getTextPixelWidth(); S32 height = getTextPixelHeight(); - reshape( width + 2 * mHPad, height + 2 * mVPad, FALSE ); + reshape( width + 2 * mHPad, height + 2 * mVPad, called_from_parent ); } diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index 061d2dd23d..c3e3b61912 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -60,7 +60,7 @@ public: void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } void setClickedCallback( boost::function<void (void*)> cb, void* userdata = NULL ); - void reshapeToFitText(); + void reshapeToFitText(BOOL called_from_parent = FALSE); S32 getTextPixelWidth(); S32 getTextPixelHeight(); diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index c98da0d410..544a76e8d5 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -721,8 +721,9 @@ void LLUICtrl::resetDirty() } // virtual -void LLUICtrl::onTabInto() +void LLUICtrl::onTabInto() { + onUpdateScrollToChild(this); } // virtual diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 333d03f208..20dda54771 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -454,13 +454,17 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const } // -// LLUrlEntrySeconlifeURL Describes *secondlife.com/ *lindenlab.com/ and *tilia-inc.com/ urls to substitute icon 'hand.png' before link +// LLUrlEntrySeconlifeURL Describes *secondlife.com/ *lindenlab.com/ *secondlifegrid.net/ and *tilia-inc.com/ urls to substitute icon 'hand.png' before link // LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL() { mPattern = boost::regex("((http://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com)" "|" - "(https://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(:\\d{1,5})?))" + "(http://([-\\w\\.]*\\.)?secondlifegrid\\.net)" + "|" + "(https://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(:\\d{1,5})?)" + "|" + "(https://([-\\w\\.]*\\.)?secondlifegrid\\.net(:\\d{1,5})?))" "\\/\\S*", boost::regex::perl|boost::regex::icase); @@ -495,12 +499,14 @@ std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const } // -// LLUrlEntrySimpleSecondlifeURL Describes *secondlife.com *lindenlab.com and *tilia-inc.com urls to substitute icon 'hand.png' before link +// LLUrlEntrySimpleSecondlifeURL Describes *secondlife.com *lindenlab.com *secondlifegrid.net and *tilia-inc.com urls to substitute icon 'hand.png' before link // LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL() { - mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(?!\\S)", - boost::regex::perl|boost::regex::icase); + mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(?!\\S)" + "|" + "https?://([-\\w\\.]*\\.)?secondlifegrid\\.net(?!\\S)", + boost::regex::perl|boost::regex::icase); mIcon = "Hand"; mMenuName = "menu_url_http.xml"; @@ -511,8 +517,7 @@ LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL() // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about // -LLUrlEntryAgent::LLUrlEntryAgent() : - mAvatarNameCacheConnection() +LLUrlEntryAgent::LLUrlEntryAgent() { mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); @@ -543,7 +548,15 @@ void LLUrlEntryAgent::callObservers(const std::string &id, void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { - mAvatarNameCacheConnection.disconnect(); + avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); + if (it != mAvatarNameCacheConnections.end()) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + mAvatarNameCacheConnections.erase(it); + } std::string label = av_name.getCompleteName(); @@ -630,11 +643,17 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } else { - if (mAvatarNameCacheConnection.connected()) + avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id); + if (it != mAvatarNameCacheConnections.end()) { - mAvatarNameCacheConnection.disconnect(); + if (it->second.connected()) + { + it->second.disconnect(); + } + mAvatarNameCacheConnections.erase(it); } - mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); + mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); + addObserver(agent_id_string, url, cb); return LLTrans::getString("LoadingData"); } @@ -695,14 +714,21 @@ std::string LLUrlEntryAgent::getIcon(const std::string &url) // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) // -LLUrlEntryAgentName::LLUrlEntryAgentName() : - mAvatarNameCacheConnection() +LLUrlEntryAgentName::LLUrlEntryAgentName() {} void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { - mAvatarNameCacheConnection.disconnect(); + avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id); + if (it != mAvatarNameCacheConnections.end()) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + mAvatarNameCacheConnections.erase(it); + } std::string label = getName(av_name); // received the agent name from the server - tell our observers @@ -737,11 +763,17 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab } else { - if (mAvatarNameCacheConnection.connected()) + avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id); + if (it != mAvatarNameCacheConnections.end()) { - mAvatarNameCacheConnection.disconnect(); + if (it->second.connected()) + { + it->second.disconnect(); + } + mAvatarNameCacheConnections.erase(it); } - mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); + mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); + addObserver(agent_id_string, url, cb); return LLTrans::getString("LoadingData"); } @@ -1475,4 +1507,43 @@ void LLUrlEntryExperienceProfile::onExperienceDetails( const LLSD& experience_de callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), name, LLStringUtil::null); } +// +// LLUrlEntryEmail Describes an IPv6 address +// +LLUrlEntryIPv6::LLUrlEntryIPv6() + : LLUrlEntryBase() +{ + mHostPath = "https?://\\[([a-f0-9:]+:+)+[a-f0-9]+]"; + mPattern = boost::regex(mHostPath + "(:\\d{1,5})?(/\\S*)?", + boost::regex::perl | boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryIPv6::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase); + boost::match_results<std::string::const_iterator> matches; + + if (boost::regex_search(url, matches, regex)) + { + return url.substr(0, matches[0].length()); + } + else + { + return url; + } +} + +std::string LLUrlEntryIPv6::getQuery(const std::string &url) const +{ + boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase); + boost::match_results<std::string::const_iterator> matches; + + return boost::regex_replace(url, regex, ""); +} +std::string LLUrlEntryIPv6::getUrl(const std::string &string) const +{ + return string; +} diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 78c149d9fd..4af1ab5096 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -212,10 +212,14 @@ public: LLUrlEntryAgent(); ~LLUrlEntryAgent() { - if (mAvatarNameCacheConnection.connected()) + for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it) { - mAvatarNameCacheConnection.disconnect(); + if (it->second.connected()) + { + it->second.disconnect(); + } } + mAvatarNameCacheConnections.clear(); } /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); /*virtual*/ std::string getIcon(const std::string &url); @@ -227,7 +231,9 @@ protected: /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - boost::signals2::connection mAvatarNameCacheConnection; + + typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t; + avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; /// @@ -241,10 +247,14 @@ public: LLUrlEntryAgentName(); ~LLUrlEntryAgentName() { - if (mAvatarNameCacheConnection.connected()) + for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it) { - mAvatarNameCacheConnection.disconnect(); + if (it->second.connected()) + { + it->second.disconnect(); + } } + mAvatarNameCacheConnections.clear(); } /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); /*virtual*/ LLStyle::Params getStyle() const; @@ -253,7 +263,9 @@ protected: virtual std::string getName(const LLAvatarName& avatar_name) = 0; private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); - boost::signals2::connection mAvatarNameCacheConnection; + + typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t; + avatar_name_cache_connection_map_t mAvatarNameCacheConnections; }; @@ -513,5 +525,18 @@ public: /*virtual*/ std::string getUrl(const std::string &string) const; }; +/// +/// LLUrlEntryEmail Describes an IPv6 address +/// +class LLUrlEntryIPv6 : public LLUrlEntryBase +{ +public: + LLUrlEntryIPv6(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getQuery(const std::string &url) const; + + std::string mHostPath; +}; #endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index ba6fa1e2e9..321a0ec5b9 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -79,6 +79,7 @@ LLUrlRegistry::LLUrlRegistry() mUrlEntrySLLabel = new LLUrlEntrySLLabel(); registerUrl(mUrlEntrySLLabel); registerUrl(new LLUrlEntryEmail()); + registerUrl(new LLUrlEntryIPv6()); } LLUrlRegistry::~LLUrlRegistry() diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index bd213d594a..cd47e2ecea 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -646,6 +646,16 @@ void LLView::onVisibilityChange ( BOOL new_visibility ) } // virtual +void LLView::onUpdateScrollToChild(const LLUICtrl * cntrl) +{ + LLView* parent_view = getParent(); + if (parent_view) + { + parent_view->onUpdateScrollToChild(cntrl); + } +} + +// virtual void LLView::translate(S32 x, S32 y) { mRect.translate(x, y); @@ -1392,7 +1402,9 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) S32 delta_x = child_rect.mLeft - viewp->getRect().mLeft; S32 delta_y = child_rect.mBottom - viewp->getRect().mBottom; viewp->translate( delta_x, delta_y ); - if (child_rect.getWidth() != viewp->getRect().getWidth() || child_rect.getHeight() != viewp->getRect().getHeight()) + if (child_rect.getWidth() != viewp->getRect().getWidth() + || child_rect.getHeight() != viewp->getRect().getHeight() + || sForceReshape) { viewp->reshape(child_rect.getWidth(), child_rect.getHeight()); } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index db81900aaf..5c91c37d3c 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -301,6 +301,7 @@ public: virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); virtual void onVisibilityChange ( BOOL new_visibility ); + virtual void onUpdateScrollToChild(const LLUICtrl * cntrl); void pushVisible(BOOL visible) { mLastVisible = mVisible; setVisible(visible); } void popVisible() { setVisible(mLastVisible); } diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 3c34fd269e..4a4fdb72e3 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -903,4 +903,38 @@ namespace tut "and even no www something lindenlab.com", ""); } + + template<> template<> + void object::test<16>() + { + // + // test LLUrlEntryIPv6 + // + LLUrlEntryIPv6 url; + + // Regex tests. + testRegex("match urls with a protocol", url, + "this url should match http://[::1]", + "http://[::1]"); + + testRegex("match urls with a protocol and query", url, + "this url should match http://[::1]/file.mp3", + "http://[::1]/file.mp3"); + + testRegex("match urls with a protocol", url, + "this url should match http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]", + "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]"); + + testRegex("match urls with port", url, + "let's specify some port http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080", + "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080"); + + testRegex("don't match urls w/o protocol", url, + "looks like an url something [2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d] but no https prefix", + ""); + + testRegex("don't match incorrect urls", url, + "http://[ 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d ]", + ""); + } } diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index f6f6c3931c..5404ac50e5 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -327,7 +327,7 @@ BOOL LLKeyboard::keyFromString(const std::string& str, KEY *key) // static -std::string LLKeyboard::stringFromKey(KEY key) +std::string LLKeyboard::stringFromKey(KEY key, bool translate) { std::string res = get_if_there(sKeysToNames, key, std::string()); if (res.empty()) @@ -338,16 +338,60 @@ std::string LLKeyboard::stringFromKey(KEY key) res = std::string(buffer); } - LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; - if (trans != NULL) + if (translate) { - res = trans(res.c_str()); + LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; + if (trans != NULL) + { + res = trans(res.c_str()); + } } return res; } +//static +std::string LLKeyboard::stringFromAccelerator(MASK accel_mask) +{ + std::string res; + + LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; + + if (trans == NULL) + { + LL_ERRS() << "No mKeyStringTranslator" << LL_ENDL; + return res; + } + // Append any masks +#ifdef LL_DARWIN + // Standard Mac names for modifier keys in menu equivalents + // We could use the symbol characters, but they only exist in certain fonts. + if (accel_mask & MASK_CONTROL) + { + if (accel_mask & MASK_MAC_CONTROL) + { + res.append(trans("accel-mac-control")); + } + else + { + res.append(trans("accel-mac-command")); // Symbol would be "\xE2\x8C\x98" + } + } + if (accel_mask & MASK_ALT) + res.append(trans("accel-mac-option")); // Symbol would be "\xE2\x8C\xA5" + if (accel_mask & MASK_SHIFT) + res.append(trans("accel-mac-shift")); // Symbol would be "\xE2\x8C\xA7" +#else + if (accel_mask & MASK_CONTROL) + res.append(trans("accel-win-control")); + if (accel_mask & MASK_ALT) + res.append(trans("accel-win-alt")); + if (accel_mask & MASK_SHIFT) + res.append(trans("accel-win-shift")); +#endif + return res; +} //static std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key ) { @@ -359,41 +403,7 @@ std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key ) return res; } - LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; - - if( trans == NULL ) - { - LL_ERRS() << "No mKeyStringTranslator" << LL_ENDL; - return res; - } - - // Append any masks -#ifdef LL_DARWIN - // Standard Mac names for modifier keys in menu equivalents - // We could use the symbol characters, but they only exist in certain fonts. - if( accel_mask & MASK_CONTROL ) - { - if ( accel_mask & MASK_MAC_CONTROL ) - { - res.append( trans("accel-mac-control") ); - } - else - { - res.append( trans("accel-mac-command") ); // Symbol would be "\xE2\x8C\x98" - } - } - if( accel_mask & MASK_ALT ) - res.append( trans("accel-mac-option") ); // Symbol would be "\xE2\x8C\xA5" - if( accel_mask & MASK_SHIFT ) - res.append( trans("accel-mac-shift") ); // Symbol would be "\xE2\x8C\xA7" -#else - if( accel_mask & MASK_CONTROL ) - res.append( trans("accel-win-control") ); - if( accel_mask & MASK_ALT ) - res.append( trans("accel-win-alt") ); - if( accel_mask & MASK_SHIFT ) - res.append( trans("accel-win-shift") ); -#endif + res.append(stringFromAccelerator(accel_mask)); std::string key_string = LLKeyboard::stringFromKey(key); if ((accel_mask & MASK_NORMALKEYS) && (key_string[0] == '-' || key_string[0] == '=' || key_string[0] == '+')) diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index 6f2dc87317..36bd8bcbed 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -38,10 +38,10 @@ enum EKeystate { KEYSTATE_DOWN, KEYSTATE_LEVEL, - KEYSTATE_UP + KEYSTATE_UP }; -typedef boost::function<void(EKeystate keystate)> LLKeyFunc; +typedef boost::function<bool(EKeystate keystate)> LLKeyFunc; typedef std::string (LLKeyStringTranslatorFunc)(const char *label); enum EKeyboardInsertMode @@ -50,15 +50,6 @@ enum EKeyboardInsertMode LL_KIM_OVERWRITE }; -class LLKeyBinding -{ -public: - KEY mKey; - MASK mMask; -// const char *mName; // unused - LLKeyFunc mFunction; -}; - class LLWindowCallbacks; class LLKeyboard @@ -103,7 +94,8 @@ public: static BOOL maskFromString(const std::string& str, MASK *mask); // False on failure static BOOL keyFromString(const std::string& str, KEY *key); // False on failure - static std::string stringFromKey(KEY key); + static std::string stringFromKey(KEY key, bool translate = true); + static std::string stringFromAccelerator( MASK accel_mask ); // separated for convinience, returns with "+": "Shift+" or "Shift+Alt+"... static std::string stringFromAccelerator( MASK accel_mask, KEY key ); void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; } diff --git a/indra/llwindow/llmousehandler.cpp b/indra/llwindow/llmousehandler.cpp index d5fa65fe4b..e41ebd42f3 100644 --- a/indra/llwindow/llmousehandler.cpp +++ b/indra/llwindow/llmousehandler.cpp @@ -27,7 +27,7 @@ #include "llmousehandler.h" //virtual -BOOL LLMouseHandler::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) +BOOL LLMouseHandler::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL handled = FALSE; if (down) diff --git a/indra/llwindow/llmousehandler.h b/indra/llwindow/llmousehandler.h index 1dcd0348d8..d221dd117c 100644 --- a/indra/llwindow/llmousehandler.h +++ b/indra/llwindow/llmousehandler.h @@ -29,6 +29,7 @@ #include "linden_common.h" #include "llrect.h" +#include "indra_constants.h" // Mostly-abstract interface. // Intended for use via multiple inheritance. @@ -46,16 +47,7 @@ public: SHOW_ALWAYS, } EShowToolTip; - typedef enum { - CLICK_LEFT, - CLICK_MIDDLE, - CLICK_RIGHT, - CLICK_BUTTON4, - CLICK_BUTTON5, - CLICK_DOUBLELEFT - } EClickType; - - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0; virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) = 0; virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask) = 0; diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm index 5b9dce02c4..d2c5b11c3d 100644 --- a/indra/llwindow/llopenglview-objc.mm +++ b/indra/llwindow/llopenglview-objc.mm @@ -359,10 +359,10 @@ attributedStringInfo getSegments(NSAttributedString *str) callRightMouseDown(mMousePos, [theEvent modifierFlags]); mSimulatedRightClick = true; } else { - if ([theEvent clickCount] >= 2) + if ([theEvent clickCount] == 2) { callDoubleClick(mMousePos, [theEvent modifierFlags]); - } else if ([theEvent clickCount] == 1) { + } else if ([theEvent clickCount] >= 1) { callLeftMouseDown(mMousePos, [theEvent modifierFlags]); } } diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index d77997a928..30bc743e72 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -263,6 +263,18 @@ std::vector<std::string> LLWindow::getDynamicFallbackFontList() #endif } +// static +std::vector<std::string> LLWindow::getDisplaysResolutionList() +{ +#if LL_WINDOWS + return LLWindowWin32::getDisplaysResolutionList(); +#elif LL_DARWIN + return LLWindowMacOSX::getDisplaysResolutionList(); +#else + return std::vector<std::string>(); +#endif +} + #define UTF16_IS_HIGH_SURROGATE(U) ((U16)((U) - 0xD800) < 0x0400) #define UTF16_IS_LOW_SURROGATE(U) ((U16)((U) - 0xDC00) < 0x0400) #define UTF16_SURROGATE_PAIR_TO_UTF32(H,L) (((H) << 10) + (L) - (0xD800 << 10) - 0xDC00 + 0x00010000) diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index a05ba8cbba..d4d5b76937 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -168,6 +168,12 @@ public: // Get system UI size based on DPI (for 96 DPI UI size should be 1.0) virtual F32 getSystemUISize() { return 1.0; } + + static std::vector<std::string> getDisplaysResolutionList(); + + // windows only DirectInput8 for joysticks + virtual void* getDirectInput8() { return NULL; }; + virtual bool getInputDevices(U32 device_type_filter, void * devices_callback, void* userdata) { return false; }; protected: LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags); virtual ~LLWindow(); diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 2604a23c85..0d0607a0bb 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -41,6 +41,7 @@ #include <OpenGL/OpenGL.h> #include <Carbon/Carbon.h> #include <CoreServices/CoreServices.h> +#include <CoreGraphics/CGDisplayConfiguration.h> extern BOOL gDebugWindowProc; BOOL gHiDPISupport = TRUE; @@ -1911,6 +1912,35 @@ void LLWindowMacOSX::interruptLanguageTextInput() commitCurrentPreedit(mGLView); } +std::vector<std::string> LLWindowMacOSX::getDisplaysResolutionList() +{ + std::vector<std::string> resolution_list; + + CGDirectDisplayID display_ids[10]; + uint32_t found_displays = 0; + CGError err = CGGetActiveDisplayList(10, display_ids, &found_displays); + + if (kCGErrorSuccess != err) + { + LL_WARNS() << "Couldn't get a list of active displays" << LL_ENDL; + return std::vector<std::string>(); + } + + for (uint32_t i = 0; i < found_displays; i++) + { + S32 monitor_width = CGDisplayPixelsWide(display_ids[i]); + S32 monitor_height = CGDisplayPixelsHigh(display_ids[i]); + + std::ostringstream sstream; + sstream << monitor_width << "x" << monitor_height;; + std::string res = sstream.str(); + + resolution_list.push_back(res); + } + + return resolution_list; +} + //static std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList() { diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 24651027e8..bf45238c8d 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -114,6 +114,8 @@ public: /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); /*virtual*/ F32 getSystemUISize(); + static std::vector<std::string> getDisplaysResolutionList(); + static std::vector<std::string> getDynamicFallbackFontList(); // Provide native key event data diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 7783505c27..f774cd0d31 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -38,6 +38,7 @@ // Linden library includes #include "llerror.h" +#include "llexception.h" #include "llfasttimer.h" #include "llgl.h" #include "llstring.h" @@ -59,6 +60,9 @@ #include <dinput.h> #include <Dbt.h.> +#include <InitGuid.h> // needed for llurlentry test to build on some systems +#pragma comment(lib, "dxguid.lib") // needed for llurlentry test to build on some systems +#pragma comment(lib, "dinput8") const S32 MAX_MESSAGE_PER_UPDATE = 20; const S32 BITS_PER_PIXEL = 32; @@ -76,6 +80,7 @@ const F32 ICON_FLASH_TIME = 0.5f; extern BOOL gDebugWindowProc; LPWSTR gIconResource = IDI_APPLICATION; +LPDIRECTINPUT8 gDirectInput8; LLW32MsgCallback gAsyncMsgCallback = NULL; @@ -117,7 +122,7 @@ void show_window_creation_error(const std::string& title) LL_WARNS("Window") << title << LL_ENDL; } -HGLRC SafeCreateContext(HDC hdc) +HGLRC SafeCreateContext(HDC &hdc) { __try { @@ -129,6 +134,22 @@ HGLRC SafeCreateContext(HDC hdc) } } +GLuint SafeChoosePixelFormat(HDC &hdc, const PIXELFORMATDESCRIPTOR *ppfd) +{ + __try + { + return ChoosePixelFormat(hdc, ppfd); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // convert to C++ styled exception + // C exception don't allow classes, so it's a regular char array + char integer_string[32]; + sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); + throw std::exception(integer_string); + } +} + //static BOOL LLWindowWin32::sIsClassRegistered = FALSE; @@ -400,6 +421,39 @@ LLWinImm::~LLWinImm() } +class LLMonitorInfo +{ +public: + + std::vector<std::string> getResolutionsList() { return mResList; } + + LLMonitorInfo() + { + EnumDisplayMonitors(0, 0, MonitorEnum, (LPARAM)this); + } + +private: + + static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData) + { + int monitor_width = lprcMonitor->right - lprcMonitor->left; + int monitor_height = lprcMonitor->bottom - lprcMonitor->top; + + std::ostringstream sstream; + sstream << monitor_width << "x" << monitor_height;; + std::string res = sstream.str(); + + LLMonitorInfo* pThis = reinterpret_cast<LLMonitorInfo*>(pData); + pThis->mResList.push_back(res); + + return TRUE; + } + + std::vector<std::string> mResList; +}; + +static LLMonitorInfo sMonitorInfo; + LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, @@ -428,7 +482,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, memset(mCurrentGammaRamp, 0, sizeof(mCurrentGammaRamp)); memset(mPrevGammaRamp, 0, sizeof(mPrevGammaRamp)); mCustomGammaSet = FALSE; - + mWindowHandle = NULL; + if (!SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &mMouseVanish, 0)) { mMouseVanish = TRUE; @@ -482,6 +537,21 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, mhInstance = GetModuleHandle(NULL); mWndProc = NULL; + // Init Direct Input - needed for joystick / Spacemouse + + LPDIRECTINPUT8 di8_interface; + HRESULT status = DirectInput8Create( + mhInstance, // HINSTANCE hinst, + DIRECTINPUT_VERSION, // DWORD dwVersion, + IID_IDirectInput8, // REFIID riidltf, + (LPVOID*)&di8_interface, // LPVOID * ppvOut, + NULL // LPUNKNOWN punkOuter + ); + if (status == DI_OK) + { + gDirectInput8 = di8_interface; + } + mSwapMethod = SWAP_METHOD_UNDEFINED; // No WPARAM yet. @@ -784,9 +854,6 @@ void LLWindowWin32::close() resetDisplayResolution(); } - // Don't process events in our mainWindowProc any longer. - SetWindowLongPtr(mWindowHandle, GWLP_USERDATA, NULL); - // Make sure cursor is visible and we haven't mangled the clipping state. showCursor(); setMouseClipping(FALSE); @@ -832,16 +899,24 @@ void LLWindowWin32::close() LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL; - // Make sure we don't leave a blank toolbar button. - ShowWindow(mWindowHandle, SW_HIDE); + if (IsWindow(mWindowHandle)) + { + // Make sure we don't leave a blank toolbar button. + ShowWindow(mWindowHandle, SW_HIDE); - // This causes WM_DESTROY to be sent *immediately* - if (!destroy_window_handler(mWindowHandle)) - { - OSMessageBox(mCallbacks->translateString("MBDestroyWinFailed"), - mCallbacks->translateString("MBShutdownErr"), - OSMB_OK); - } + // This causes WM_DESTROY to be sent *immediately* + if (!destroy_window_handler(mWindowHandle)) + { + OSMessageBox(mCallbacks->translateString("MBDestroyWinFailed"), + mCallbacks->translateString("MBShutdownErr"), + OSMB_OK); + } + } + else + { + // Something killed the window while we were busy destroying gl or handle somehow got broken + LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; + } mWindowHandle = NULL; } @@ -1133,7 +1208,10 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO << " Height: " << (window_rect.bottom - window_rect.top) << " Fullscreen: " << mFullscreen << LL_ENDL; - DestroyWindow(mWindowHandle); + if (mWindowHandle && !destroy_window_handler(mWindowHandle)) + { + LL_WARNS("Window") << "Failed to properly close window before recreating it!" << LL_ENDL; + } mWindowHandle = CreateWindowEx(dw_ex_style, mWindowClassName, mWindowTitle, @@ -1189,13 +1267,26 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO LL_INFOS("Window") << "Device context retrieved." << LL_ENDL ; - if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd))) - { - close(); - OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"), - mCallbacks->translateString("MBError"), OSMB_OK); - return FALSE; - } + try + { + // Looks like ChoosePixelFormat can crash in case of faulty driver + if (!(pixel_format = SafeChoosePixelFormat(mhDC, &pfd))) + { + LL_WARNS("Window") << "ChoosePixelFormat failed, code: " << GetLastError() << LL_ENDL; + OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return FALSE; + } + } + catch (...) + { + LOG_UNHANDLED_EXCEPTION("ChoosePixelFormat"); + OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return FALSE; + } LL_INFOS("Window") << "Pixel format chosen." << LL_ENDL ; @@ -1453,8 +1544,12 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO ReleaseDC (mWindowHandle, mhDC); // Release The Device Context mhDC = 0; // Zero The Device Context } - DestroyWindow (mWindowHandle); // Destroy The Window - + + // Destroy The Window + if (mWindowHandle && !destroy_window_handler(mWindowHandle)) + { + LL_WARNS("Window") << "Failed to properly close window!" << LL_ENDL; + } mWindowHandle = CreateWindowEx(dw_ex_style, mWindowClassName, @@ -3397,7 +3492,10 @@ void LLSplashScreenWin32::hideImpl() { if (mWindow) { - DestroyWindow(mWindow); + if (!destroy_window_handler(mWindow)) + { + LL_WARNS("Window") << "Failed to properly close splash screen window!" << LL_ENDL; + } mWindow = NULL; } } @@ -4170,6 +4268,28 @@ void LLWindowWin32::setDPIAwareness() } } +void* LLWindowWin32::getDirectInput8() +{ + return &gDirectInput8; +} + +bool LLWindowWin32::getInputDevices(U32 device_type_filter, void * di8_devices_callback, void* userdata) +{ + if (gDirectInput8 != NULL) + { + // Enumerate devices + HRESULT status = gDirectInput8->EnumDevices( + (DWORD) device_type_filter, // DWORD dwDevType, + (LPDIENUMDEVICESCALLBACK)di8_devices_callback, // LPDIENUMDEVICESCALLBACK lpCallback, // BOOL DIEnumDevicesCallback( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef ) // BOOL CALLBACK DinputDevice::DevicesCallback + (LPVOID*)userdata, // LPVOID pvRef + DIEDFL_ATTACHEDONLY // DWORD dwFlags + ); + + return status == DI_OK; + } + return false; +} + F32 LLWindowWin32::getSystemUISize() { F32 scale_value = 1.f; @@ -4232,6 +4352,12 @@ F32 LLWindowWin32::getSystemUISize() } //static +std::vector<std::string> LLWindowWin32::getDisplaysResolutionList() +{ + return sMonitorInfo.getResolutionsList(); +} + +//static std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList() { // Fonts previously in getFontListSans() have moved to fonts.xml. diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 9cd16eb993..0b3d14fb16 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -114,8 +114,12 @@ public: LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url ); + static std::vector<std::string> getDisplaysResolutionList(); static std::vector<std::string> getDynamicFallbackFontList(); static void setDPIAwareness(); + + /*virtual*/ void* getDirectInput8(); + /*virtual*/ bool getInputDevices(U32 device_type_filter, void * di8_devices_callback, void* userdata); protected: LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp index 80a414d00f..ead8634df4 100644 --- a/indra/llxml/llcontrol.cpp +++ b/indra/llxml/llcontrol.cpp @@ -641,6 +641,24 @@ LLSD LLControlGroup::getLLSD(const std::string& name) return get<LLSD>(name); } +LLSD LLControlGroup::asLLSD(bool diffs_only) +{ + // Dump all stored values as LLSD + LLSD result = LLSD::emptyArray(); + for (ctrl_name_table_t::iterator iter = mNameTable.begin(); + iter != mNameTable.end(); iter++) + { + LLControlVariable *control = iter->second; + if (!control || control->isType(TYPE_STRING) || (diffs_only && control->isDefault())) + { + continue; + } + const std::string& name = iter->first; + result[name] = getLLSD(name); + } + return result; +} + BOOL LLControlGroup::controlExists(const std::string& name) { ctrl_name_table_t::iterator iter = mNameTable.find(name); diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index 99946607f4..19508becc3 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -242,6 +242,8 @@ public: LLColor4 getColor4(const std::string& name); LLColor3 getColor3(const std::string& name); + LLSD asLLSD(bool diffs_only); + // generic getter template<typename T> T get(const std::string& name) { diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index bc967c9bca..0bb62d79ff 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -98,7 +98,9 @@ private: bool mCanCut; bool mCanCopy; bool mCanPaste; + std::string mRootCachePath; std::string mCachePath; + std::string mContextCachePath; std::string mCefLogFile; bool mCefLogVerbose; std::vector<std::string> mPickedFiles; @@ -458,7 +460,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string) } else if (message_name == "cleanup") { - mVolumeCatcher.setVolume(0); // Hack: masks CEF exit issues mCEFLib->requestExit(); } else if (message_name == "force_exit") @@ -527,7 +528,9 @@ void MediaPluginCEF::receiveMessage(const char* message_string) settings.accept_language_list = mHostLanguage; settings.background_color = 0xffffffff; settings.cache_enabled = true; + settings.root_cache_path = mRootCachePath; settings.cache_path = mCachePath; + settings.context_cache_path = mContextCachePath; settings.cookies_enabled = mCookiesEnabled; settings.disable_gpu = mDisableGPU; #if LL_DARWIN @@ -583,9 +586,25 @@ void MediaPluginCEF::receiveMessage(const char* message_string) else if (message_name == "set_user_data_path") { std::string user_data_path_cache = message_in.getValue("cache_path"); - std::string user_data_path_cookies = message_in.getValue("cookies_path"); + std::string subfolder = message_in.getValue("username"); - mCachePath = user_data_path_cache + "cef_cache"; + mRootCachePath = user_data_path_cache + "cef_cache"; + if (!subfolder.empty()) + { + std::string delim; +#if LL_WINDOWS + // media plugin doesn't have access to gDirUtilp + delim = "\\"; +#else + delim = "/"; +#endif + mCachePath = mRootCachePath + delim + subfolder; + } + else + { + mCachePath = mRootCachePath; + } + mContextCachePath = ""; // disabled by "" mCefLogFile = message_in.getValue("cef_log_file"); mCefLogVerbose = message_in.getValueBoolean("cef_verbose_log"); } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a4cbe73e8a..e417b7ac7b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -232,6 +232,7 @@ set(viewer_SOURCE_FILES llfloaterconversationpreview.cpp llfloaterdeleteprefpreset.cpp llfloaterdestinations.cpp + llfloatereditenvironmentbase.cpp llfloatereditextdaycycle.cpp llfloaterenvironmentadjust.cpp llfloaterevent.cpp @@ -373,6 +374,7 @@ set(viewer_SOURCE_FILES llinventoryobserver.cpp llinventorypanel.cpp lljoystickbutton.cpp + llkeyconflict.cpp lllandmarkactions.cpp lllandmarklist.cpp lllegacyatmospherics.cpp @@ -399,6 +401,7 @@ set(viewer_SOURCE_FILES llmenuoptionpathfindingrebakenavmesh.cpp llmeshrepository.cpp llmimetypes.cpp + llmodelpreview.cpp llmorphview.cpp llmoveview.cpp llmutelist.cpp @@ -553,6 +556,7 @@ set(viewer_SOURCE_FILES llsecapi.cpp llsechandler_basic.cpp llselectmgr.cpp + llsetkeybinddialog.cpp llsettingspicker.cpp llsettingsvo.cpp llshareavatarhandler.cpp @@ -658,7 +662,7 @@ set(viewer_SOURCE_FILES llviewerjointattachment.cpp llviewerjointmesh.cpp llviewerjoystick.cpp - llviewerkeyboard.cpp + llviewerinput.cpp llviewerlayer.cpp llviewermedia.cpp llviewermedia_streamingaudio.cpp @@ -862,6 +866,7 @@ set(viewer_HEADER_FILES llfloaterconversationpreview.h llfloaterdeleteprefpreset.h llfloaterdestinations.h + llfloatereditenvironmentbase.h llfloatereditextdaycycle.h llfloaterenvironmentadjust.h llfloaterevent.h @@ -1004,6 +1009,7 @@ set(viewer_HEADER_FILES llinventoryobserver.h llinventorypanel.h lljoystickbutton.h + llkeyconflict.h lllandmarkactions.h lllandmarklist.h lllightconstants.h @@ -1030,6 +1036,7 @@ set(viewer_HEADER_FILES llmenuoptionpathfindingrebakenavmesh.h llmeshrepository.h llmimetypes.h + llmodelpreview.h llmorphview.h llmoveview.h llmutelist.h @@ -1174,6 +1181,7 @@ set(viewer_HEADER_FILES llsecapi.h llsechandler_basic.h llselectmgr.h + llsetkeybinddialog.h llsettingspicker.h llsettingsvo.h llsidepanelappearance.h @@ -1281,7 +1289,7 @@ set(viewer_HEADER_FILES llviewerjointattachment.h llviewerjointmesh.h llviewerjoystick.h - llviewerkeyboard.h + llviewerinput.h llviewerlayer.h llviewermedia.h llviewermediafocus.h @@ -1616,6 +1624,10 @@ if (WINDOWS) # causes those systems to run in a Windows 8 compatibility mode, which works. LIST(APPEND viewer_SOURCE_FILES windows.manifest) endif (ADDRESS_SIZE EQUAL 64) + + if (OPENAL) + LIST(APPEND viewer_LIBRARIES ${OPENAL_LIBRARIES}) + endif (OPENAL) endif (WINDOWS) # Add the xui files. This is handy for searching for xui elements @@ -1654,7 +1666,7 @@ set(viewer_APPSETTINGS_FILES app_settings/grass.xml app_settings/high_graphics.xml app_settings/ignorable_dialogs.xml - app_settings/keys.xml + app_settings/key_bindings.xml app_settings/keywords_lsl_default.xml app_settings/logcontrol.xml app_settings/low_graphics.xml @@ -1717,6 +1729,12 @@ if (FMODSTUDIO) endif (FMODSTUDIO) set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") + +if (HAVOK OR HAVOK_TPV) + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_HAVOK") +endif (HAVOK OR HAVOK_TPV) + +# progress view disables/enables icons based on available packages set_source_files_properties(llprogressview.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") list(APPEND viewer_SOURCE_FILES ${viewer_HEADER_FILES}) @@ -1798,6 +1816,8 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/Release/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/Debug/libhunspell.dll + ${SHARED_LIB_STAGING_DIR}/Release/uriparser.dll + ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/uriparser.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/SLVoice.exe ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libsndfile-1.dll ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxoal.dll @@ -1842,6 +1862,13 @@ if (WINDOWS) ) endif (FMODSTUDIO) + if (OPENAL) + list(APPEND COPY_INPUT_DEPENDENCIES + ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/OpenAL32.dll + ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/alut.dll + ) + endif (OPENAL) + add_custom_command( OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat COMMAND ${PYTHON_EXECUTABLE} @@ -1852,6 +1879,7 @@ if (WINDOWS) --artwork=${ARTWORK_DIR} "--bugsplat=${BUGSPLAT_DB}" "--fmodstudio=${FMODSTUDIO}" + "--openal=${OPENAL}" --build=${CMAKE_CURRENT_BINARY_DIR} --buildtype=${CMAKE_BUILD_TYPE} "--channel=${VIEWER_CHANNEL}" @@ -1915,6 +1943,7 @@ if (WINDOWS) --artwork=${ARTWORK_DIR} "--bugsplat=${BUGSPLAT_DB}" "--fmodstudio=${FMODSTUDIO}" + "--openal=${OPENAL}" --build=${CMAKE_CURRENT_BINARY_DIR} --buildtype=${CMAKE_BUILD_TYPE} "--channel=${VIEWER_CHANNEL}" @@ -2065,6 +2094,7 @@ if (LINUX) --artwork=${ARTWORK_DIR} "--bugsplat=${BUGSPLAT_DB}" "--fmodstudio=${FMODSTUDIO}" + "--openal=${OPENAL}" --build=${CMAKE_CURRENT_BINARY_DIR} --buildtype=${CMAKE_BUILD_TYPE} "--channel=${VIEWER_CHANNEL}" @@ -2092,6 +2122,7 @@ if (LINUX) --artwork=${ARTWORK_DIR} "--bugsplat=${BUGSPLAT_DB}" "--fmodstudio=${FMODSTUDIO}" + "--openal=${OPENAL}" --build=${CMAKE_CURRENT_BINARY_DIR} --buildtype=${CMAKE_BUILD_TYPE} "--channel=${VIEWER_CHANNEL}" @@ -2129,7 +2160,7 @@ if (DARWIN) set(MACOSX_BUNDLE_BUNDLE_NAME "SecondLife") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") set(MACOSX_BUNDLE_BUNDLE_VERSION "${VIEWER_SHORT_VERSION}${VIEWER_MACOSX_PHASE}${VIEWER_REVISION}") - set(MACOSX_BUNDLE_COPYRIGHT "Copyright © Linden Research, Inc. 2019") + set(MACOSX_BUNDLE_COPYRIGHT "Copyright © Linden Research, Inc. 2020") set(MACOSX_BUNDLE_NSMAIN_NIB_FILE "SecondLife.nib") set(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "LLApplication") @@ -2169,6 +2200,7 @@ if (DARWIN) --artwork=${ARTWORK_DIR} "--bugsplat=${BUGSPLAT_DB}" "--fmodstudio=${FMODSTUDIO}" + "--openal=${OPENAL}" --build=${CMAKE_CURRENT_BINARY_DIR} --buildtype=${CMAKE_BUILD_TYPE} --bundleid=${MACOSX_BUNDLE_GUI_IDENTIFIER} @@ -2207,6 +2239,7 @@ if (DARWIN) --artwork=${ARTWORK_DIR} "--bugsplat=${BUGSPLAT_DB}" "--fmodstudio=${FMODSTUDIO}" + "--openal=${OPENAL}" --build=${CMAKE_CURRENT_BINARY_DIR} --buildtype=${CMAKE_BUILD_TYPE} "--channel=${VIEWER_CHANNEL}" diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 4b20d9700d..427dc381ad 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.4.8 +6.4.19 diff --git a/indra/newview/app_settings/keys.xml b/indra/newview/app_settings/key_bindings.xml index a8037fec05..4f6deb1f98 100644 --- a/indra/newview/app_settings/keys.xml +++ b/indra/newview/app_settings/key_bindings.xml @@ -28,34 +28,11 @@ <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> - <binding key="A" mask="SHIFT" command="slide_left"/> - <binding key="D" mask="SHIFT" command="slide_right"/> - <binding key="W" mask="SHIFT" command="push_forward"/> - <binding key="S" mask="SHIFT" command="push_backward"/> - <binding key="E" mask="SHIFT" command="jump"/> - <binding key="C" mask="SHIFT" command="push_down"/> - <binding key="F" mask="SHIFT" command="toggle_fly"/> - <binding key="SPACE" mask="NONE" command="stop_moving"/> <binding key="ENTER" mask="NONE" command="start_chat"/> <binding key="DIVIDE" mask="NONE" command="start_gesture"/> - <binding key="LEFT" mask="SHIFT" command="slide_left"/> - <binding key="RIGHT" mask="SHIFT" command="slide_right"/> - <binding key="UP" mask="SHIFT" command="push_forward"/> - <binding key="DOWN" mask="SHIFT" command="push_backward"/> - <binding key="PGUP" mask="SHIFT" command="jump"/> - <binding key="PGDN" mask="SHIFT" command="push_down"/> - - <binding key="PAD_LEFT" mask="SHIFT" command="slide_left"/> - <binding key="PAD_RIGHT" mask="SHIFT" command="slide_right"/> - <binding key="PAD_UP" mask="SHIFT" command="push_forward"/> - <binding key="PAD_DOWN" mask="SHIFT" command="push_backward"/> - <binding key="PAD_PGUP" mask="SHIFT" command="jump"/> - <binding key="PAD_PGDN" mask="SHIFT" command="push_down"/> - <binding key="PAD_HOME" mask="SHIFT" command="toggle_fly"/> - <binding key="PAD_ENTER" mask="SHIFT" command="start_chat"/> - <binding key="PAD_DIVIDE" mask="SHIFT" command="start_gesture"/> + <binding key="" mask="NONE" mouse="MMB" command="toggle_voice"/> </first_person> <third_person> <binding key="A" mask="NONE" command="turn_left"/> @@ -64,15 +41,10 @@ <binding key="D" mask="SHIFT" command="slide_right"/> <binding key="W" mask="NONE" command="push_forward"/> <binding key="S" mask="NONE" command="push_backward"/> - <binding key="W" mask="SHIFT" command="push_forward"/> - <binding key="S" mask="SHIFT" command="push_backward"/> <binding key="E" mask="NONE" command="jump"/> <binding key="C" mask="NONE" command="push_down"/> - <binding key="E" mask="SHIFT" command="jump"/> - <binding key="C" mask="SHIFT" command="push_down"/> <binding key="F" mask="NONE" command="toggle_fly"/> - <binding key="F" mask="SHIFT" command="toggle_fly"/> <binding key="SPACE" mask="NONE" command="stop_moving"/> <binding key="ENTER" mask="NONE" command="start_chat"/> @@ -84,13 +56,8 @@ <binding key="RIGHT" mask="SHIFT" command="slide_right"/> <binding key="UP" mask="NONE" command="push_forward"/> <binding key="DOWN" mask="NONE" command="push_backward"/> - <binding key="UP" mask="SHIFT" command="push_forward"/> - <binding key="DOWN" mask="SHIFT" command="push_backward"/> <binding key="PGUP" mask="NONE" command="jump"/> <binding key="PGDN" mask="NONE" command="push_down"/> - <binding key="PGUP" mask="SHIFT" command="jump"/> - <binding key="PGDN" mask="SHIFT" command="push_down"/> - <binding key="HOME" mask="SHIFT" command="toggle_fly"/> <binding key="HOME" mask="NONE" command="toggle_fly"/> <binding key="PAD_LEFT" mask="NONE" command="turn_left"/> @@ -99,20 +66,12 @@ <binding key="PAD_RIGHT" mask="SHIFT" command="slide_right"/> <binding key="PAD_UP" mask="NONE" command="push_forward"/> <binding key="PAD_DOWN" mask="NONE" command="push_backward"/> - <binding key="PAD_UP" mask="SHIFT" command="push_forward"/> - <binding key="PAD_DOWN" mask="SHIFT" command="push_backward"/> <binding key="PAD_PGUP" mask="NONE" command="jump"/> <binding key="PAD_PGDN" mask="NONE" command="push_down"/> - <binding key="PAD_PGUP" mask="SHIFT" command="jump"/> - <binding key="PAD_PGDN" mask="SHIFT" command="push_down"/> <binding key="PAD_HOME" mask="NONE" command="toggle_fly"/> - <binding key="PAD_HOME" mask="SHIFT" command="toggle_fly"/> <binding key="PAD_CENTER" mask="NONE" command="stop_moving"/> - <binding key="PAD_CENTER" mask="SHIFT" command="stop_moving"/> <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> - <binding key="PAD_ENTER" mask="SHIFT" command="start_chat"/> <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> - <binding key="PAD_DIVIDE" mask="SHIFT" command="start_gesture"/> <!--Camera controls in third person on Alt--> <binding key="LEFT" mask="ALT" command="spin_around_cw"/> @@ -139,28 +98,14 @@ <binding key="PAD_DIVIDE" mask="ALT" command="start_gesture"/> <!--mimic alt zoom behavior with keyboard only--> - <binding key="A" mask="CTL_ALT" command="spin_around_cw"/> - <binding key="D" mask="CTL_ALT" command="spin_around_ccw"/> <binding key="W" mask="CTL_ALT" command="spin_over"/> <binding key="S" mask="CTL_ALT" command="spin_under"/> - <binding key="E" mask="CTL_ALT" command="spin_over"/> - <binding key="C" mask="CTL_ALT" command="spin_under"/> - <binding key="LEFT" mask="CTL_ALT" command="spin_around_cw"/> - <binding key="RIGHT" mask="CTL_ALT" command="spin_around_ccw"/> <binding key="UP" mask="CTL_ALT" command="spin_over"/> <binding key="DOWN" mask="CTL_ALT" command="spin_under"/> - <binding key="PGUP" mask="CTL_ALT" command="spin_over"/> - <binding key="PGDN" mask="CTL_ALT" command="spin_under"/> - <binding key="PAD_LEFT" mask="CTL_ALT" command="spin_around_cw"/> - <binding key="PAD_RIGHT" mask="CTL_ALT" command="spin_around_ccw"/> <binding key="PAD_UP" mask="CTL_ALT" command="spin_over"/> <binding key="PAD_DOWN" mask="CTL_ALT" command="spin_under"/> - <binding key="PAD_PGUP" mask="CTL_ALT" command="spin_over"/> - <binding key="PAD_PGDN" mask="CTL_ALT" command="spin_under"/> - <binding key="PAD_ENTER" mask="CTL_ALT" command="start_chat"/> - <binding key="PAD_DIVIDE" mask="CTL_ALT" command="start_gesture"/> <!--Therefore pan on Alt-Shift--> <binding key="A" mask="CTL_ALT_SHIFT" command="pan_left"/> @@ -179,63 +124,10 @@ <binding key="PAD_DOWN" mask="CTL_ALT_SHIFT" command="pan_down"/> <binding key="PAD_ENTER" mask="CTL_ALT_SHIFT" command="start_chat"/> <binding key="PAD_DIVIDE" mask="CTL_ALT_SHIFT" command="start_gesture"/> - </third_person> - <!-- Basic editing camera control --> - <edit> - <binding key="A" mask="NONE" command="spin_around_cw"/> - <binding key="D" mask="NONE" command="spin_around_ccw"/> - <binding key="W" mask="NONE" command="move_forward"/> - <binding key="S" mask="NONE" command="move_backward"/> - <binding key="E" mask="NONE" command="spin_over"/> - <binding key="C" mask="NONE" command="spin_under"/> - <binding key="ENTER" mask="NONE" command="start_chat"/> - <binding key="DIVIDE" mask="NONE" command="start_gesture"/> - <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> - <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> - - <binding key="LEFT" mask="NONE" command="spin_around_cw"/> - <binding key="RIGHT" mask="NONE" command="spin_around_ccw"/> - <binding key="UP" mask="NONE" command="move_forward"/> - <binding key="DOWN" mask="NONE" command="move_backward"/> - <binding key="PGUP" mask="NONE" command="spin_over"/> - <binding key="PGDN" mask="NONE" command="spin_under"/> - - <binding key="A" mask="SHIFT" command="pan_left"/> - <binding key="D" mask="SHIFT" command="pan_right"/> - <binding key="W" mask="SHIFT" command="pan_up"/> - <binding key="S" mask="SHIFT" command="pan_down"/> - - <binding key="LEFT" mask="SHIFT" command="pan_left"/> - <binding key="RIGHT" mask="SHIFT" command="pan_right"/> - <binding key="UP" mask="SHIFT" command="pan_up"/> - <binding key="DOWN" mask="SHIFT" command="pan_down"/> - - <!--Walking works with ALT held down.--> - <binding key="A" mask="ALT" command="slide_left"/> - <binding key="D" mask="ALT" command="slide_right"/> - <binding key="W" mask="ALT" command="push_forward"/> - <binding key="S" mask="ALT" command="push_backward"/> - <binding key="E" mask="ALT" command="jump"/> - <binding key="C" mask="ALT" command="push_down"/> - - <binding key="LEFT" mask="ALT" command="slide_left"/> - <binding key="RIGHT" mask="ALT" command="slide_right"/> - <binding key="UP" mask="ALT" command="push_forward"/> - <binding key="DOWN" mask="ALT" command="push_backward"/> - <binding key="PGUP" mask="ALT" command="jump"/> - <binding key="PGDN" mask="ALT" command="push_down"/> - <binding key="HOME" mask="ALT" command="toggle_fly"/> - - <binding key="PAD_LEFT" mask="ALT" command="slide_left"/> - <binding key="PAD_RIGHT" mask="ALT" command="slide_right"/> - <binding key="PAD_UP" mask="ALT" command="push_forward"/> - <binding key="PAD_DOWN" mask="ALT" command="push_backward"/> - <binding key="PAD_PGUP" mask="ALT" command="jump"/> - <binding key="PAD_PGDN" mask="ALT" command="push_down"/> - <binding key="PAD_ENTER" mask="ALT" command="start_chat"/> - <binding key="PAD_DIVIDE" mask="ALT" command="start_gesture"/> - </edit> + <binding key="" mask="NONE" mouse="MMB" command="toggle_voice"/> + <binding key="" mask="NONE" mouse="LMB" command="walk_to"/> + </third_person> <sitting> <binding key="A" mask="ALT" command="spin_around_cw"/> <binding key="D" mask="ALT" command="spin_around_ccw"/> @@ -251,15 +143,11 @@ <binding key="PGUP" mask="ALT" command="spin_over"/> <binding key="PGDN" mask="ALT" command="spin_under"/> - <binding key="A" mask="CTL_ALT" command="spin_around_cw"/> - <binding key="D" mask="CTL_ALT" command="spin_around_ccw"/> <binding key="W" mask="CTL_ALT" command="spin_over"/> <binding key="S" mask="CTL_ALT" command="spin_under"/> <binding key="E" mask="CTL_ALT" command="spin_over"/> <binding key="C" mask="CTL_ALT" command="spin_under"/> - <binding key="LEFT" mask="CTL_ALT" command="spin_around_cw"/> - <binding key="RIGHT" mask="CTL_ALT" command="spin_around_ccw"/> <binding key="UP" mask="CTL_ALT" command="spin_over"/> <binding key="DOWN" mask="CTL_ALT" command="spin_under"/> <binding key="PGUP" mask="CTL_ALT" command="spin_over"/> @@ -294,23 +182,23 @@ <binding key="A" mask="SHIFT" command="slide_left"/> <binding key="D" mask="SHIFT" command="slide_right"/> <binding key="W" mask="SHIFT" command="move_forward_sitting"/> - <binding key="S" mask="SHIFT" command="move_backward_sitting"/> - <binding key="E" mask="SHIFT" command="spin_over_sitting"/> - <binding key="C" mask="SHIFT" command="spin_under_sitting"/> + <binding key="S" mask="SHIFT" command="move_backward_sitting"/> + <binding key="E" mask="SHIFT" command="spin_over_sitting"/> + <binding key="C" mask="SHIFT" command="spin_under_sitting"/> <binding key="LEFT" mask="SHIFT" command="slide_left"/> <binding key="RIGHT" mask="SHIFT" command="slide_right"/> <binding key="UP" mask="SHIFT" command="move_forward_sitting"/> - <binding key="DOWN" mask="SHIFT" command="move_backward_sitting"/> - <binding key="PGUP" mask="SHIFT" command="spin_over_sitting"/> - <binding key="PGDN" mask="SHIFT" command="spin_under_sitting"/> + <binding key="DOWN" mask="SHIFT" command="move_backward_sitting"/> + <binding key="PGUP" mask="SHIFT" command="spin_over_sitting"/> + <binding key="PGDN" mask="SHIFT" command="spin_under_sitting"/> <binding key="PAD_LEFT" mask="SHIFT" command="slide_left"/> <binding key="PAD_RIGHT" mask="SHIFT" command="slide_right"/> <binding key="PAD_UP" mask="SHIFT" command="move_forward_sitting"/> - <binding key="PAD_DOWN" mask="SHIFT" command="move_backward_sitting"/> - <binding key="PAD_PGUP" mask="SHIFT" command="spin_over_sitting"/> - <binding key="PAD_PGDN" mask="SHIFT" command="spin_under_sitting"/> + <binding key="PAD_DOWN" mask="SHIFT" command="move_backward_sitting"/> + <binding key="PAD_PGUP" mask="SHIFT" command="spin_over_sitting"/> + <binding key="PAD_PGDN" mask="SHIFT" command="spin_under_sitting"/> <binding key="PAD_ENTER" mask="SHIFT" command="start_chat"/> <binding key="PAD_DIVIDE" mask="SHIFT" command="start_gesture"/> @@ -334,6 +222,8 @@ <binding key="ENTER" mask="NONE" command="start_chat"/> <binding key="DIVIDE" mask="NONE" command="start_gesture"/> + + <binding key="" mask="NONE" mouse="MMB" command="toggle_voice"/> </sitting> <edit_avatar> <!--Avatar editing camera controls--> @@ -359,5 +249,7 @@ <binding key="PAD_PGDN" mask="NONE" command="edit_avatar_spin_under"/> <binding key="PAD_ENTER" mask="NONE" command="start_chat"/> <binding key="PAD_DIVIDE" mask="NONE" command="start_gesture"/> + + <binding key="" mask="NONE" mouse="MMB" command="toggle_voice"/> </edit_avatar> -</keys>
\ No newline at end of file +</keys> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a00aa86d78..77f0fd99bc 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3542,7 +3542,7 @@ <key>DoubleClickAutoPilot</key> <map> <key>Comment</key> - <string>Enable double-click auto pilot</string> + <string>(Obsolete)Enable double-click auto pilot</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -3550,10 +3550,10 @@ <key>Value</key> <integer>0</integer> </map> - <key>DoubleClickTeleport</key> + <key>DoubleClickTeleport</key> <map> <key>Comment</key> - <string>Enable double-click to teleport where allowed</string> + <string>Enable double-click to teleport where allowed (afects minimap and people panel)</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -5242,6 +5242,17 @@ <key>Value</key> <string /> </map> + <key>JoystickDeviceUUID</key> + <map> + <key>Comment</key> + <string>Preffered device ID.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string /> + </map> <key>JoystickMouselookYaw</key> <map> <key>Comment</key> @@ -5924,7 +5935,7 @@ <key>Type</key> <string>String</string> <key>Value</key> - <string>http://map.secondlife.com.s3.amazonaws.com/</string> + <string>https://map.secondlife.com/</string> </map> <key>CurrentMapServerURL</key> <map> @@ -6719,7 +6730,7 @@ <integer>600</integer> </map> <key>MigrateCacheDirectory</key> - <map> + <map> <key>Comment</key> <string>Check for old version of disk cache to migrate to current location</string> <key>Persist</key> @@ -7969,7 +7980,6 @@ <key>Value</key> <integer>13</integer> </map> - <key>PreviewAmbientColor</key> <map> <key>Comment</key> @@ -7986,8 +7996,6 @@ <real>1.0</real> </array> </map> - - <key>PreviewDiffuse0</key> <map> <key>Comment</key> @@ -8274,7 +8282,7 @@ <key>PushToTalkButton</key> <map> <key>Comment</key> - <string>Which button or keyboard key is used for push-to-talk</string> + <string>(Obsolete)Which button or keyboard key is used for push-to-talk</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -14420,7 +14428,7 @@ <key>VoiceCallsFriendsOnly</key> <map> <key>Comment</key> - <string>Only accept voice calls from residents on your friends list</string> + <string>(Deprecated) Only accept voice calls from residents on your friends list</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -15161,7 +15169,7 @@ <key>Value</key> <real>1</real> </map> - <key>PoolSizeVAssetStorage</key> + <key>PoolSizeAssetStorage</key> <map> <key>Comment</key> <string>Coroutine Pool size for AssetStorage requests</string> @@ -15559,7 +15567,7 @@ <key>ClickToWalk</key> <map> <key>Comment</key> - <string>Click in world to walk to location</string> + <string>(obsolete)Click in world to walk to location</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -16618,3 +16626,4 @@ </map> </llsd> + diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 8f4ca6c633..537744b44c 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -220,6 +220,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>VoiceCallsFriendsOnly</key> + <map> + <key>Comment</key> + <string>Only accept voice calls and receive IMs from residents on your friends list</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>VoiceEffectDefault</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl b/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl index caa4fe1f65..b7036e02cf 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl @@ -1,24 +1,24 @@ -/** +/** * @file WLCloudsV.glsl * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2005, 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$ */ @@ -33,26 +33,26 @@ ATTRIBUTE vec2 texcoord0; /////////////////////////////////////////////////////////////////////////////// // Output parameters -VARYING vec4 vary_CloudColorSun; -VARYING vec4 vary_CloudColorAmbient; +VARYING vec4 vary_CloudColorSun; +VARYING vec4 vary_CloudColorAmbient; VARYING float vary_CloudDensity; -VARYING vec2 vary_texcoord0; -VARYING vec2 vary_texcoord1; -VARYING vec2 vary_texcoord2; -VARYING vec2 vary_texcoord3; +VARYING vec2 vary_texcoord0; +VARYING vec2 vary_texcoord1; +VARYING vec2 vary_texcoord2; +VARYING vec2 vary_texcoord3; VARYING float altitude_blend_factor; // Inputs uniform vec3 camPosLocal; -uniform vec4 lightnorm; -uniform vec4 sunlight_color; -uniform vec4 moonlight_color; -uniform int sun_up_factor; -uniform vec4 ambient_color; -uniform vec4 blue_horizon; -uniform vec4 blue_density; +uniform vec4 lightnorm; +uniform vec4 sunlight_color; +uniform vec4 moonlight_color; +uniform int sun_up_factor; +uniform vec4 ambient_color; +uniform vec4 blue_horizon; +uniform vec4 blue_density; uniform float haze_horizon; uniform float haze_density; @@ -60,7 +60,7 @@ uniform float cloud_shadow; uniform float density_multiplier; uniform float max_y; -uniform vec4 glow; +uniform vec4 glow; uniform float sun_moon_glow_factor; uniform vec4 cloud_color; @@ -70,133 +70,123 @@ uniform float cloud_scale; // NOTE: Keep these in sync! // indra\newview\app_settings\shaders\class1\deferred\skyV.glsl // indra\newview\app_settings\shaders\class1\deferred\cloudsV.glsl +// indra\newview\app-settings\shaders\class2\windlight\cloudsV.glsl // indra\newview\lllegacyatmospherics.cpp +// indra\newview\llsettingsvo.cpp void main() { - - // World / view / projection - gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); - - // Texture coords - vary_texcoord0 = texcoord0; - vary_texcoord0.xy -= 0.5; - vary_texcoord0.xy /= cloud_scale; - vary_texcoord0.xy += 0.5; - - vary_texcoord1 = vary_texcoord0; - vary_texcoord1.x += lightnorm.x * 0.0125; - vary_texcoord1.y += lightnorm.z * 0.0125; - - vary_texcoord2 = vary_texcoord0 * 16.; - vary_texcoord3 = vary_texcoord1 * 16.; - - // Get relative position - vec3 P = position.xyz - camPosLocal.xyz + vec3(0,50,0); - - altitude_blend_factor = clamp((P.y + 512.0) / max_y, 0.0, 1.0); - - // Set altitude - if (P.y > 0.) - { - P *= (max_y / P.y); - } - else - { - altitude_blend_factor = 0; // SL-11589 Fix clouds drooping below horizon - P *= (-32000. / P.y); - } - - // Can normalize then - vec3 Pn = normalize(P); - float Plen = length(P); - - // Initialize temp variables - vec4 temp1 = vec4(0.); - vec4 temp2 = vec4(0.); - vec4 blue_weight; - vec4 haze_weight; - //vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - vec4 sunlight = sunlight_color; - vec4 light_atten; - - float dens_mul = density_multiplier; - - // Sunlight attenuation effect (hue and brightness) due to atmosphere - // this is used later for sunlight modulation at various altitudes - light_atten = (blue_density + vec4(haze_density * 0.25)) * (dens_mul * max_y); - - // Calculate relative weights - temp1 = abs(blue_density) + vec4(abs(haze_density)); - blue_weight = blue_density / temp1; - haze_weight = haze_density / temp1; - - // Compute sunlight from P & lightnorm (for long rays like sky) - temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y ); - temp2.y = 1. / temp2.y; - sunlight *= exp( - light_atten * temp2.y); - - // Distance - temp2.z = Plen * dens_mul; - - // Transparency (-> temp1) - // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati - // compiler gets confused. - temp1 = exp(-temp1 * temp2.z); - - - // Compute haze glow - temp2.x = dot(Pn, lightnorm.xyz); - temp2.x = 1. - temp2.x; - // temp2.x is 0 at the sun and increases away from sun - temp2.x = max(temp2.x, .001); - // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) - temp2.x *= glow.x; - // Higher glow.x gives dimmer glow (because next step is 1 / "angle") - temp2.x = pow(temp2.x, glow.z); - // glow.z should be negative, so we're doing a sort of (1 / "angle") function - - temp2.x *= sun_moon_glow_factor; - - // Add "minimum anti-solar illumination" - temp2.x += .25; - - // Increase ambient when there are more clouds - vec4 tmpAmbient = ambient_color; - tmpAmbient += (1. - tmpAmbient) * cloud_shadow * 0.5; - - // Dim sunlight by cloud shadow percentage - sunlight *= (1. - cloud_shadow); - - // Haze color below cloud - vec4 additiveColorBelowCloud = ( blue_horizon * blue_weight * (sunlight + tmpAmbient) - + (haze_horizon * haze_weight) * (sunlight * temp2.x + tmpAmbient) - ); - - // CLOUDS - temp2.y = max(0., lightnorm.y * 2.); - temp2.y = 1. / temp2.y; - sunlight *= exp( - light_atten * temp2.y); - - // Cloud color out - vary_CloudColorSun = (sunlight * temp2.x) * cloud_color; - vary_CloudColorAmbient = tmpAmbient * cloud_color; - - // Attenuate cloud color by atmosphere - temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds - vary_CloudColorSun *= temp1; - vary_CloudColorAmbient *= temp1; - vec4 oHazeColorBelowCloud = additiveColorBelowCloud * (1. - temp1); - - // Make a nice cloud density based on the cloud_shadow value that was passed in. - vary_CloudDensity = 2. * (cloud_shadow - 0.25); - - - // Combine these to minimize register use - vary_CloudColorAmbient += oHazeColorBelowCloud; - - // needs this to compile on mac - //vary_AtmosAttenuation = vec3(0.0,0.0,0.0); - - // END CLOUDS + // World / view / projection + gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); + + // Texture coords + // SL-13084 EEP added support for custom cloud textures -- flip them horizontally to match the preview of Clouds > Cloud Scroll + vary_texcoord0 = vec2(-texcoord0.x, texcoord0.y); // See: LLSettingsVOSky::applySpecial + + vary_texcoord0.xy -= 0.5; + vary_texcoord0.xy /= cloud_scale; + vary_texcoord0.xy += 0.5; + + vary_texcoord1 = vary_texcoord0; + vary_texcoord1.x += lightnorm.x * 0.0125; + vary_texcoord1.y += lightnorm.z * 0.0125; + + vary_texcoord2 = vary_texcoord0 * 16.; + vary_texcoord3 = vary_texcoord1 * 16.; + + // Get relative position + vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0); + + altitude_blend_factor = clamp((rel_pos.y + 512.0) / max_y, 0.0, 1.0); + + // Set altitude + if (rel_pos.y > 0) + { + rel_pos *= (max_y / rel_pos.y); + } + if (rel_pos.y < 0) + { + altitude_blend_factor = 0; // SL-11589 Fix clouds drooping below horizon + rel_pos *= (-32000. / rel_pos.y); + } + + // Can normalize then + vec3 rel_pos_norm = normalize(rel_pos); + float rel_pos_len = length(rel_pos); + + // Initialize temp variables + vec4 sunlight = sunlight_color; + vec4 light_atten; + + // Sunlight attenuation effect (hue and brightness) due to atmosphere + // this is used later for sunlight modulation at various altitudes + light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); + + // Calculate relative weights + vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density)); + vec4 blue_weight = blue_density / combined_haze; + vec4 haze_weight = haze_density / combined_haze; + + // Compute sunlight from rel_pos & lightnorm (for long rays like sky) + float off_axis = 1.0 / max(1e-6, max(0., rel_pos_norm.y) + lightnorm.y); + sunlight *= exp(-light_atten * off_axis); + + // Distance + float density_dist = rel_pos_len * density_multiplier; + + // Transparency (-> combined_haze) + // ATI Bugfix -- can't store combined_haze*density_dist in a variable because the ati + // compiler gets confused. + combined_haze = exp(-combined_haze * density_dist); + + // Compute haze glow + float haze_glow = 1.0 - dot(rel_pos_norm, lightnorm.xyz); + // haze_glow is 0 at the sun and increases away from sun + haze_glow = max(haze_glow, .001); + // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) + haze_glow *= glow.x; + // Higher glow.x gives dimmer glow (because next step is 1 / "angle") + haze_glow = pow(haze_glow, glow.z); + // glow.z should be negative, so we're doing a sort of (1 / "angle") function + + haze_glow *= sun_moon_glow_factor; + + // Add "minimum anti-solar illumination" + // For sun, add to glow. For moon, remove glow entirely. SL-13768 + haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (haze_glow + 0.25); + + // Increase ambient when there are more clouds + vec4 tmpAmbient = ambient_color; + tmpAmbient += (1. - tmpAmbient) * cloud_shadow * 0.5; + + // Dim sunlight by cloud shadow percentage + sunlight *= (1. - cloud_shadow); + + // Haze color below cloud + vec4 additiveColorBelowCloud = + (blue_horizon * blue_weight * (sunlight + tmpAmbient) + (haze_horizon * haze_weight) * (sunlight * haze_glow + tmpAmbient)); + + // CLOUDS + off_axis = 1.0 / max(1e-6, lightnorm.y * 2.); + sunlight *= exp(-light_atten * off_axis); + + // Cloud color out + vary_CloudColorSun = (sunlight * haze_glow) * cloud_color; + vary_CloudColorAmbient = tmpAmbient * cloud_color; + + // Attenuate cloud color by atmosphere + combined_haze = sqrt(combined_haze); // less atmos opacity (more transparency) below clouds + vary_CloudColorSun *= combined_haze; + vary_CloudColorAmbient *= combined_haze; + vec4 oHazeColorBelowCloud = additiveColorBelowCloud * (1. - combined_haze); + + // Make a nice cloud density based on the cloud_shadow value that was passed in. + vary_CloudDensity = 2. * (cloud_shadow - 0.25); + + // Combine these to minimize register use + vary_CloudColorAmbient += oHazeColorBelowCloud; + + // needs this to compile on mac + // vary_AtmosAttenuation = vec3(0.0,0.0,0.0); + + // END CLOUDS } - diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyF.glsl index bd0ad3bce8..6b36d00f97 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightShinyF.glsl @@ -42,6 +42,9 @@ VARYING vec4 vary_position; uniform samplerCube environmentMap; +// render_hud_attachments() -> HUD objects set LLShaderMgr::NO_ATMO; used in LLDrawPoolAlpha::beginRenderPass() +uniform int no_atmo; + vec3 fullbrightShinyAtmosTransport(vec3 light); vec3 fullbrightAtmosTransportFrag(vec3 light, vec3 additive, vec3 atten); vec3 fullbrightScaleSoftClip(vec3 light); @@ -51,7 +54,9 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou vec3 linear_to_srgb(vec3 c); vec3 srgb_to_linear(vec3 c); - +// See: +// class1\deferred\fullbrightShinyF.glsl +// class1\lighting\lightFullbrightShinyF.glsl void main() { #ifdef HAS_DIFFUSE_LOOKUP @@ -59,25 +64,39 @@ void main() #else vec4 color = texture2D(diffuseMap, vary_texcoord0.xy); #endif - + color.rgb *= vertex_color.rgb; - vec3 pos = vary_position.xyz/vary_position.w; - vec3 sunlit; - vec3 amblit; - vec3 additive; - vec3 atten; + // SL-9632 HUDs are affected by Atmosphere + if (no_atmo == 0) + { + vec3 sunlit; + vec3 amblit; + vec3 additive; + vec3 atten; + vec3 pos = vary_position.xyz/vary_position.w; + + calcAtmosphericVars(pos.xyz, vec3(0), 1.0, sunlit, amblit, additive, atten, false); - calcAtmosphericVars(pos.xyz, vec3(0), 1.0, sunlit, amblit, additive, atten, false); - - vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb; - float env_intensity = vertex_color.a; - color.rgb = mix(color.rgb, envColor.rgb, env_intensity); + vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb; + float env_intensity = vertex_color.a; //color.rgb = srgb_to_linear(color.rgb); - - color.rgb = fullbrightAtmosTransportFrag(color.rgb, additive, atten); - color.rgb = fullbrightScaleSoftClip(color.rgb); + color.rgb = mix(color.rgb, envColor.rgb, env_intensity); + + color.rgb = fullbrightAtmosTransportFrag(color.rgb, additive, atten); + color.rgb = fullbrightScaleSoftClip(color.rgb); + } + +/* + // NOTE: HUD objects will be full bright. Uncomment if you want "some" environment lighting effecting these HUD objects. + else + { + vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb; + float env_intensity = vertex_color.a; + color.rgb = mix(color.rgb, envColor.rgb, env_intensity); + } +*/ color.a = 1.0; diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl index 0afd1a9672..80d19102b6 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl @@ -1,439 +1,443 @@ -/**
-* @file materialF.glsl
-*
-* $LicenseInfo:firstyear=2007&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2007, 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$
-*/
-
-/*[EXTRA_CODE_HERE]*/
-
-//class1/deferred/materialF.glsl
-
-// This shader is used for both writing opaque/masked content to the gbuffer and writing blended content to the framebuffer during the alpha pass.
-
-#define DIFFUSE_ALPHA_MODE_NONE 0
-#define DIFFUSE_ALPHA_MODE_BLEND 1
-#define DIFFUSE_ALPHA_MODE_MASK 2
-#define DIFFUSE_ALPHA_MODE_EMISSIVE 3
-
-uniform float emissive_brightness; // fullbright flag, 1.0 == fullbright, 0.0 otherwise
-uniform int sun_up_factor;
-
-#ifdef WATER_FOG
-vec4 applyWaterFogView(vec3 pos, vec4 color);
-#endif
-
-vec3 atmosFragLighting(vec3 l, vec3 additive, vec3 atten);
-vec3 scaleSoftClipFrag(vec3 l);
-
-vec3 fullbrightAtmosTransportFrag(vec3 light, vec3 additive, vec3 atten);
-vec3 fullbrightScaleSoftClip(vec3 light);
-
-void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, out vec3 sunlit, out vec3 amblit, out vec3 additive, out vec3 atten, bool use_ao);
-
-vec3 srgb_to_linear(vec3 cs);
-vec3 linear_to_srgb(vec3 cs);
-
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
-
-#ifdef DEFINE_GL_FRAGCOLOR
-out vec4 frag_color;
-#else
-#define frag_color gl_FragColor
-#endif
-
-#ifdef HAS_SUN_SHADOW
-float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen);
-#endif
-
-uniform samplerCube environmentMap;
-uniform sampler2D lightFunc;
-
-// Inputs
-uniform vec4 morphFactor;
-uniform vec3 camPosLocal;
-uniform mat3 env_mat;
-
-uniform vec3 sun_dir;
-uniform vec3 moon_dir;
-VARYING vec2 vary_fragcoord;
-
-VARYING vec3 vary_position;
-
-uniform mat4 proj_mat;
-uniform mat4 inv_proj;
-uniform vec2 screen_res;
-
-uniform vec4 light_position[8];
-uniform vec3 light_direction[8];
-uniform vec4 light_attenuation[8];
-uniform vec3 light_diffuse[8];
-
-float getAmbientClamp();
-
-vec3 calcPointLightOrSpotLight(vec3 light_col, vec3 npos, vec3 diffuse, vec4 spec, vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight, inout float glare, float ambiance)
-{
- vec3 col = vec3(0);
-
- //get light vector
- vec3 lv = lp.xyz - v;
-
- //get distance
- float dist = length(lv);
- float da = 1.0;
-
- dist /= la;
-
- if (dist > 0.0 && la > 0.0)
- {
- //normalize light vector
- lv = normalize(lv);
-
- //distance attenuation
- float dist_atten = clamp(1.0 - (dist - 1.0*(1.0 - fa)) / fa, 0.0, 1.0);
- dist_atten *= dist_atten;
- dist_atten *= 2.0f;
-
- if (dist_atten <= 0.0)
- {
- return col;
- }
-
- // spotlight coefficient.
- float spot = max(dot(-ln, lv), is_pointlight);
- da *= spot*spot; // GL_SPOT_EXPONENT=2
-
- //angular attenuation
- da *= dot(n, lv);
-
- float lit = 0.0f;
-
- float amb_da = ambiance;
- if (da >= 0)
- {
- lit = max(da * dist_atten, 0.0);
- col = lit * light_col * diffuse;
- amb_da += (da*0.5 + 0.5) * ambiance;
- }
- amb_da += (da*da*0.5 + 0.5) * ambiance;
- amb_da *= dist_atten;
- amb_da = min(amb_da, 1.0f - lit);
-
- // SL-10969 need to see why these are blown out
- //col.rgb += amb_da * light_col * diffuse;
-
- if (spec.a > 0.0)
- {
- //vec3 ref = dot(pos+lv, norm);
- vec3 h = normalize(lv + npos);
- float nh = dot(n, h);
- float nv = dot(n, npos);
- float vh = dot(npos, h);
- float sa = nh;
- float fres = pow(1 - dot(h, npos), 5)*0.4 + 0.5;
-
- float gtdenom = 2 * nh;
- float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh));
-
- if (nh > 0.0)
- {
- float scol = fres*texture2D(lightFunc, vec2(nh, spec.a)).r*gt / (nh*da);
- vec3 speccol = lit*scol*light_col.rgb*spec.rgb;
- speccol = clamp(speccol, vec3(0), vec3(1));
- col += speccol;
-
- float cur_glare = max(speccol.r, speccol.g);
- cur_glare = max(cur_glare, speccol.b);
- glare = max(glare, speccol.r);
- glare += max(cur_glare, 0.0);
- }
- }
- }
-
- return max(col, vec3(0.0, 0.0, 0.0));
-}
-
-#else
-#ifdef DEFINE_GL_FRAGCOLOR
-out vec4 frag_data[3];
-#else
-#define frag_data gl_FragData
-#endif
-#endif
-
-uniform sampler2D diffuseMap; //always in sRGB space
-
-#ifdef HAS_NORMAL_MAP
-uniform sampler2D bumpMap;
-#endif
-
-#ifdef HAS_SPECULAR_MAP
-uniform sampler2D specularMap;
-
-VARYING vec2 vary_texcoord2;
-#endif
-
-uniform float env_intensity;
-uniform vec4 specular_color; // specular color RGB and specular exponent (glossiness) in alpha
-
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK)
-uniform float minimum_alpha;
-#endif
-
-#ifdef HAS_NORMAL_MAP
-VARYING vec3 vary_mat0;
-VARYING vec3 vary_mat1;
-VARYING vec3 vary_mat2;
-VARYING vec2 vary_texcoord1;
-#else
-VARYING vec3 vary_normal;
-#endif
-
-VARYING vec4 vertex_color;
-VARYING vec2 vary_texcoord0;
-
-vec2 encode_normal(vec3 n);
-
-void main()
-{
- vec2 pos_screen = vary_texcoord0.xy;
-
- vec4 diffcol = texture2D(diffuseMap, vary_texcoord0.xy);
- diffcol.rgb *= vertex_color.rgb;
-
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK)
-
- // Comparing floats cast from 8-bit values, produces acne right at the 8-bit transition points
- float bias = 0.001953125; // 1/512, or half an 8-bit quantization
- if (diffcol.a < minimum_alpha-bias)
- {
- discard;
- }
-#endif
-
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
- vec3 gamma_diff = diffcol.rgb;
- diffcol.rgb = srgb_to_linear(diffcol.rgb);
-#endif
-
-#ifdef HAS_SPECULAR_MAP
- vec4 spec = texture2D(specularMap, vary_texcoord2.xy);
- spec.rgb *= specular_color.rgb;
-#else
- vec4 spec = vec4(specular_color.rgb, 1.0);
-#endif
-
-#ifdef HAS_NORMAL_MAP
- vec4 norm = texture2D(bumpMap, vary_texcoord1.xy);
-
- norm.xyz = norm.xyz * 2 - 1;
-
- vec3 tnorm = vec3(dot(norm.xyz,vary_mat0),
- dot(norm.xyz,vary_mat1),
- dot(norm.xyz,vary_mat2));
-#else
- vec4 norm = vec4(0,0,0,1.0);
- vec3 tnorm = vary_normal;
-#endif
-
- norm.xyz = normalize(tnorm.xyz);
-
- vec2 abnormal = encode_normal(norm.xyz);
-
- vec4 final_color = diffcol;
-
-#if (DIFFUSE_ALPHA_MODE != DIFFUSE_ALPHA_MODE_EMISSIVE)
- final_color.a = emissive_brightness;
-#else
- final_color.a = max(final_color.a, emissive_brightness);
-#endif
-
- vec4 final_specular = spec;
-
-#ifdef HAS_SPECULAR_MAP
- vec4 final_normal = vec4(encode_normal(normalize(tnorm)), env_intensity * spec.a, 0.0);
- final_specular.a = specular_color.a * norm.a;
-#else
- vec4 final_normal = vec4(encode_normal(normalize(tnorm)), env_intensity, 0.0);
- final_specular.a = specular_color.a;
-#endif
-
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
-
- //forward rendering, output just lit sRGBA
- vec3 pos = vary_position;
-
- float shadow = 1.0f;
-
-#ifdef HAS_SUN_SHADOW
- shadow = sampleDirectionalShadow(pos.xyz, norm.xyz, pos_screen);
-#endif
-
- spec = final_specular;
- vec4 diffuse = final_color;
- float envIntensity = final_normal.z;
-
- vec3 color = vec3(0,0,0);
-
- vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir;
-
- float bloom = 0.0;
- vec3 sunlit;
- vec3 amblit;
- vec3 additive;
- vec3 atten;
-
- calcAtmosphericVars(pos.xyz, light_dir, 1.0, sunlit, amblit, additive, atten, false);
-
- // This call breaks the Mac GLSL compiler/linker for unknown reasons (17Mar2020)
- // The call is either a no-op or a pure (pow) gamma adjustment, depending on GPU level
- // TODO: determine if we want to re-apply the gamma adjustment, and if so understand & fix Mac breakage
- //color = fullbrightScaleSoftClip(color);
-
- vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
-
- //we're in sRGB space, so gamma correct this dot product so
- // lighting from the sun stays sharp
- float da = clamp(dot(normalize(norm.xyz), light_dir.xyz), 0.0, 1.0);
- da = pow(da, 1.0 / 1.3);
-
- color = amblit;
-
- //darken ambient for normals perpendicular to light vector so surfaces in shadow
- // and facing away from light still have some definition to them.
- // do NOT gamma correct this dot product so ambient lighting stays soft
- float ambient = min(abs(dot(norm.xyz, sun_dir.xyz)), 1.0);
- ambient *= 0.5;
- ambient *= ambient;
- ambient = (1.0 - ambient);
-
- vec3 sun_contrib = min(da, shadow) * sunlit;
-
- color *= ambient;
-
- color += sun_contrib;
-
- color *= gamma_diff.rgb;
-
- float glare = 0.0;
-
- if (spec.a > 0.0) // specular reflection
- {
-#if 1 //EEP
-
- vec3 npos = -normalize(pos.xyz);
-
- //vec3 ref = dot(pos+lv, norm);
- vec3 h = normalize(light_dir.xyz + npos);
- float nh = dot(norm.xyz, h);
- float nv = dot(norm.xyz, npos);
- float vh = dot(npos, h);
- float sa = nh;
- float fres = pow(1 - dot(h, npos), 5)*0.4 + 0.5;
-
- float gtdenom = 2 * nh;
- float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh));
-
- if (nh > 0.0)
- {
- float scol = fres*texture2D(lightFunc, vec2(nh, spec.a)).r*gt / (nh*da);
- vec3 sp = sun_contrib*scol / 6.0f;
- sp = clamp(sp, vec3(0), vec3(1));
- bloom = dot(sp, sp) / 4.0;
- color += sp * spec.rgb;
- }
-#else // PRODUCTION
- float sa = dot(refnormpersp, sun_dir.xyz);
- vec3 dumbshiny = sunlit*shadow*(texture2D(lightFunc, vec2(sa, spec.a)).r);
-
- // add the two types of shiny together
- vec3 spec_contrib = dumbshiny * spec.rgb;
- bloom = dot(spec_contrib, spec_contrib) / 6;
-
- glare = max(spec_contrib.r, spec_contrib.g);
- glare = max(glare, spec_contrib.b);
-
- color += spec_contrib;
-#endif
- }
-
- color = mix(color.rgb, diffcol.rgb, diffuse.a);
-
- if (envIntensity > 0.0)
- {
- //add environmentmap
- vec3 env_vec = env_mat * refnormpersp;
-
- vec3 reflected_color = textureCube(environmentMap, env_vec).rgb;
-
- color = mix(color, reflected_color, envIntensity);
-
- float cur_glare = max(reflected_color.r, reflected_color.g);
- cur_glare = max(cur_glare, reflected_color.b);
- cur_glare *= envIntensity*4.0;
- glare += cur_glare;
- }
-
- color = atmosFragLighting(color, additive, atten);
- color = scaleSoftClipFrag(color);
-
- //convert to linear before adding local lights
- color = srgb_to_linear(color);
-
- vec3 npos = normalize(-pos.xyz);
-
- vec3 light = vec3(0, 0, 0);
-
-#define LIGHT_LOOP(i) light.rgb += calcPointLightOrSpotLight(light_diffuse[i].rgb, npos, diffuse.rgb, final_specular, pos.xyz, norm.xyz, light_position[i], light_direction[i].xyz, light_attenuation[i].x, light_attenuation[i].y, light_attenuation[i].z, glare, light_attenuation[i].w );
-
- LIGHT_LOOP(1)
- LIGHT_LOOP(2)
- LIGHT_LOOP(3)
- LIGHT_LOOP(4)
- LIGHT_LOOP(5)
- LIGHT_LOOP(6)
- LIGHT_LOOP(7)
-
- color += light;
-
- glare = min(glare, 1.0);
- float al = max(diffcol.a, glare)*vertex_color.a;
-
- //convert to srgb as this color is being written post gamma correction
- color = linear_to_srgb(color);
-
-#ifdef WATER_FOG
- vec4 temp = applyWaterFogView(pos, vec4(color, al));
- color = temp.rgb;
- al = temp.a;
-#endif
-
- frag_color = vec4(color, al);
-
-#else // mode is not DIFFUSE_ALPHA_MODE_BLEND, encode to gbuffer
-
- // deferred path
- frag_data[0] = final_color; //gbuffer is sRGB
- frag_data[1] = final_specular; // XYZ = Specular color. W = Specular exponent.
- frag_data[2] = final_normal; // XY = Normal. Z = Env. intensity.
-#endif
-}
-
+/** +* @file materialF.glsl +* +* $LicenseInfo:firstyear=2007&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2007, 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$ +*/ + +/*[EXTRA_CODE_HERE]*/ + +//class1/deferred/materialF.glsl + +// This shader is used for both writing opaque/masked content to the gbuffer and writing blended content to the framebuffer during the alpha pass. + +#define DIFFUSE_ALPHA_MODE_NONE 0 +#define DIFFUSE_ALPHA_MODE_BLEND 1 +#define DIFFUSE_ALPHA_MODE_MASK 2 +#define DIFFUSE_ALPHA_MODE_EMISSIVE 3 + +uniform float emissive_brightness; // fullbright flag, 1.0 == fullbright, 0.0 otherwise +uniform int sun_up_factor; + +#ifdef WATER_FOG +vec4 applyWaterFogView(vec3 pos, vec4 color); +#endif + +vec3 atmosFragLighting(vec3 l, vec3 additive, vec3 atten); +vec3 scaleSoftClipFrag(vec3 l); + +vec3 fullbrightAtmosTransportFrag(vec3 light, vec3 additive, vec3 atten); +vec3 fullbrightScaleSoftClip(vec3 light); + +void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, out vec3 sunlit, out vec3 amblit, out vec3 additive, out vec3 atten, bool use_ao); + +vec3 srgb_to_linear(vec3 cs); +vec3 linear_to_srgb(vec3 cs); + +#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) + +#ifdef DEFINE_GL_FRAGCOLOR +out vec4 frag_color; +#else +#define frag_color gl_FragColor +#endif + +#ifdef HAS_SUN_SHADOW +float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen); +#endif + +uniform samplerCube environmentMap; +uniform sampler2D lightFunc; + +// Inputs +uniform vec4 morphFactor; +uniform vec3 camPosLocal; +uniform mat3 env_mat; + +uniform vec3 sun_dir; +uniform vec3 moon_dir; +VARYING vec2 vary_fragcoord; + +VARYING vec3 vary_position; + +uniform mat4 proj_mat; +uniform mat4 inv_proj; +uniform vec2 screen_res; + +uniform vec4 light_position[8]; +uniform vec3 light_direction[8]; +uniform vec4 light_attenuation[8]; +uniform vec3 light_diffuse[8]; + +float getAmbientClamp(); + +vec3 calcPointLightOrSpotLight(vec3 light_col, vec3 npos, vec3 diffuse, vec4 spec, vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight, inout float glare, float ambiance) +{ + vec3 col = vec3(0); + + //get light vector + vec3 lv = lp.xyz - v; + + //get distance + float dist = length(lv); + float da = 1.0; + + dist /= la; + + if (dist > 0.0 && la > 0.0) + { + //normalize light vector + lv = normalize(lv); + + //distance attenuation + float dist_atten = clamp(1.0 - (dist - 1.0*(1.0 - fa)) / fa, 0.0, 1.0); + dist_atten *= dist_atten; + dist_atten *= 2.0f; + + if (dist_atten <= 0.0) + { + return col; + } + + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 + + //angular attenuation + da *= dot(n, lv); + + float lit = 0.0f; + + float amb_da = ambiance; + if (da >= 0) + { + lit = max(da * dist_atten, 0.0); + col = lit * light_col * diffuse; + amb_da += (da*0.5 + 0.5) * ambiance; + } + amb_da += (da*da*0.5 + 0.5) * ambiance; + amb_da *= dist_atten; + amb_da = min(amb_da, 1.0f - lit); + + // SL-10969 need to see why these are blown out + //col.rgb += amb_da * light_col * diffuse; + + if (spec.a > 0.0) + { + //vec3 ref = dot(pos+lv, norm); + vec3 h = normalize(lv + npos); + float nh = dot(n, h); + float nv = dot(n, npos); + float vh = dot(npos, h); + float sa = nh; + float fres = pow(1 - dot(h, npos), 5)*0.4 + 0.5; + + float gtdenom = 2 * nh; + float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh)); + + if (nh > 0.0) + { + float scol = fres*texture2D(lightFunc, vec2(nh, spec.a)).r*gt / (nh*da); + vec3 speccol = lit*scol*light_col.rgb*spec.rgb; + speccol = clamp(speccol, vec3(0), vec3(1)); + col += speccol; + + float cur_glare = max(speccol.r, speccol.g); + cur_glare = max(cur_glare, speccol.b); + glare = max(glare, speccol.r); + glare += max(cur_glare, 0.0); + } + } + } + + return max(col, vec3(0.0, 0.0, 0.0)); +} + +#else +#ifdef DEFINE_GL_FRAGCOLOR +out vec4 frag_data[3]; +#else +#define frag_data gl_FragData +#endif +#endif + +uniform sampler2D diffuseMap; //always in sRGB space + +#ifdef HAS_NORMAL_MAP +uniform sampler2D bumpMap; +#endif + +#ifdef HAS_SPECULAR_MAP +uniform sampler2D specularMap; + +VARYING vec2 vary_texcoord2; +#endif + +uniform float env_intensity; +uniform vec4 specular_color; // specular color RGB and specular exponent (glossiness) in alpha + +#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK) +uniform float minimum_alpha; +#endif + +#ifdef HAS_NORMAL_MAP +VARYING vec3 vary_mat0; +VARYING vec3 vary_mat1; +VARYING vec3 vary_mat2; +VARYING vec2 vary_texcoord1; +#else +VARYING vec3 vary_normal; +#endif + +VARYING vec4 vertex_color; +VARYING vec2 vary_texcoord0; + +vec2 encode_normal(vec3 n); + +void main() +{ + vec2 pos_screen = vary_texcoord0.xy; + + vec4 diffcol = texture2D(diffuseMap, vary_texcoord0.xy); + diffcol.rgb *= vertex_color.rgb; + +#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK) + + // Comparing floats cast from 8-bit values, produces acne right at the 8-bit transition points + float bias = 0.001953125; // 1/512, or half an 8-bit quantization + if (diffcol.a < minimum_alpha-bias) + { + discard; + } +#endif + +#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) + vec3 gamma_diff = diffcol.rgb; + diffcol.rgb = srgb_to_linear(diffcol.rgb); +#endif + +#ifdef HAS_SPECULAR_MAP + vec4 spec = texture2D(specularMap, vary_texcoord2.xy); + spec.rgb *= specular_color.rgb; +#else + vec4 spec = vec4(specular_color.rgb, 1.0); +#endif + +#ifdef HAS_NORMAL_MAP + vec4 norm = texture2D(bumpMap, vary_texcoord1.xy); + + norm.xyz = norm.xyz * 2 - 1; + + vec3 tnorm = vec3(dot(norm.xyz,vary_mat0), + dot(norm.xyz,vary_mat1), + dot(norm.xyz,vary_mat2)); +#else + vec4 norm = vec4(0,0,0,1.0); + vec3 tnorm = vary_normal; +#endif + + norm.xyz = normalize(tnorm.xyz); + + vec2 abnormal = encode_normal(norm.xyz); + + vec4 final_color = diffcol; + +#if (DIFFUSE_ALPHA_MODE != DIFFUSE_ALPHA_MODE_EMISSIVE) + final_color.a = emissive_brightness; +#else + final_color.a = max(final_color.a, emissive_brightness); +#endif + + vec4 final_specular = spec; + +#ifdef HAS_SPECULAR_MAP + vec4 final_normal = vec4(encode_normal(normalize(tnorm)), env_intensity * spec.a, 0.0); + final_specular.a = specular_color.a * norm.a; +#else + vec4 final_normal = vec4(encode_normal(normalize(tnorm)), env_intensity, 0.0); + final_specular.a = specular_color.a; +#endif + +#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) + + //forward rendering, output just lit sRGBA + vec3 pos = vary_position; + + float shadow = 1.0f; + +#ifdef HAS_SUN_SHADOW + shadow = sampleDirectionalShadow(pos.xyz, norm.xyz, pos_screen); +#endif + + spec = final_specular; + vec4 diffuse = final_color; + float envIntensity = final_normal.z; + + vec3 color = vec3(0,0,0); + + vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir; + + float bloom = 0.0; + vec3 sunlit; + vec3 amblit; + vec3 additive; + vec3 atten; + + calcAtmosphericVars(pos.xyz, light_dir, 1.0, sunlit, amblit, additive, atten, false); + + // This call breaks the Mac GLSL compiler/linker for unknown reasons (17Mar2020) + // The call is either a no-op or a pure (pow) gamma adjustment, depending on GPU level + // TODO: determine if we want to re-apply the gamma adjustment, and if so understand & fix Mac breakage + //color = fullbrightScaleSoftClip(color); + + vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); + + //we're in sRGB space, so gamma correct this dot product so + // lighting from the sun stays sharp + float da = clamp(dot(normalize(norm.xyz), light_dir.xyz), 0.0, 1.0); + da = pow(da, 1.0 / 1.3); + + color = amblit; + + //darken ambient for normals perpendicular to light vector so surfaces in shadow + // and facing away from light still have some definition to them. + // do NOT gamma correct this dot product so ambient lighting stays soft + float ambient = min(abs(dot(norm.xyz, sun_dir.xyz)), 1.0); + ambient *= 0.5; + ambient *= ambient; + ambient = (1.0 - ambient); + + vec3 sun_contrib = min(da, shadow) * sunlit; + + color *= ambient; + + color += sun_contrib; + + color *= gamma_diff.rgb; + + float glare = 0.0; + + if (spec.a > 0.0) // specular reflection + { + /* // Reverting this specular calculation to previous 'dumbshiny' version - DJH 6/17/2020 + // Preserving the refactored version as a comment for potential reconsideration, + // overriding the general rule to avoid pollutiong the source with commented code. + // + // If you're reading this in 2021+, feel free to obliterate. + + vec3 npos = -normalize(pos.xyz); + + //vec3 ref = dot(pos+lv, norm); + vec3 h = normalize(light_dir.xyz + npos); + float nh = dot(norm.xyz, h); + float nv = dot(norm.xyz, npos); + float vh = dot(npos, h); + float sa = nh; + float fres = pow(1 - dot(h, npos), 5)*0.4 + 0.5; + + float gtdenom = 2 * nh; + float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh)); + + if (nh > 0.0) + { + float scol = fres*texture2D(lightFunc, vec2(nh, spec.a)).r*gt / (nh*da); + vec3 sp = sun_contrib*scol / 6.0f; + sp = clamp(sp, vec3(0), vec3(1)); + bloom = dot(sp, sp) / 4.0; + color += sp * spec.rgb; + } + */ + + float sa = dot(refnormpersp, sun_dir.xyz); + vec3 dumbshiny = sunlit * shadow * (texture2D(lightFunc, vec2(sa, spec.a)).r); + + // add the two types of shiny together + vec3 spec_contrib = dumbshiny * spec.rgb; + bloom = dot(spec_contrib, spec_contrib) / 6; + + glare = max(spec_contrib.r, spec_contrib.g); + glare = max(glare, spec_contrib.b); + + color += spec_contrib; + } + + color = mix(color.rgb, diffcol.rgb, diffuse.a); + + if (envIntensity > 0.0) + { + //add environmentmap + vec3 env_vec = env_mat * refnormpersp; + + vec3 reflected_color = textureCube(environmentMap, env_vec).rgb; + + color = mix(color, reflected_color, envIntensity); + + float cur_glare = max(reflected_color.r, reflected_color.g); + cur_glare = max(cur_glare, reflected_color.b); + cur_glare *= envIntensity*4.0; + glare += cur_glare; + } + + color = atmosFragLighting(color, additive, atten); + color = scaleSoftClipFrag(color); + + //convert to linear before adding local lights + color = srgb_to_linear(color); + + vec3 npos = normalize(-pos.xyz); + + vec3 light = vec3(0, 0, 0); + +#define LIGHT_LOOP(i) light.rgb += calcPointLightOrSpotLight(light_diffuse[i].rgb, npos, diffuse.rgb, final_specular, pos.xyz, norm.xyz, light_position[i], light_direction[i].xyz, light_attenuation[i].x, light_attenuation[i].y, light_attenuation[i].z, glare, light_attenuation[i].w ); + + LIGHT_LOOP(1) + LIGHT_LOOP(2) + LIGHT_LOOP(3) + LIGHT_LOOP(4) + LIGHT_LOOP(5) + LIGHT_LOOP(6) + LIGHT_LOOP(7) + + color += light; + + glare = min(glare, 1.0); + float al = max(diffcol.a, glare)*vertex_color.a; + + //convert to srgb as this color is being written post gamma correction + color = linear_to_srgb(color); + +#ifdef WATER_FOG + vec4 temp = applyWaterFogView(pos, vec4(color, al)); + color = temp.rgb; + al = temp.a; +#endif + + frag_color = vec4(color, al); + +#else // mode is not DIFFUSE_ALPHA_MODE_BLEND, encode to gbuffer + + // deferred path + frag_data[0] = final_color; //gbuffer is sRGB + frag_data[1] = final_specular; // XYZ = Specular color. W = Specular exponent. + frag_data[2] = final_normal; // XY = Normal. Z = Env. intensity. +#endif +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl b/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl index 80f232948a..35068899ee 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl @@ -1,9 +1,9 @@ /** - * @file moonF.glsl + * @file class1\deferred\moonF.glsl * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2005, Linden Research, Inc. + * Copyright (C) 2005, 2020 Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,34 +36,33 @@ out vec4 frag_data[3]; uniform vec4 color; uniform vec4 sunlight_color; uniform vec4 moonlight_color; -uniform vec3 lumWeights; +uniform vec3 moon_dir; uniform float moon_brightness; -uniform float minLuminance; uniform sampler2D diffuseMap; -uniform sampler2D altDiffuseMap; -uniform float blend_factor; // interp factor between moon A/B + VARYING vec2 vary_texcoord0; vec3 srgb_to_linear(vec3 c); -void main() -{ - vec4 moonA = texture2D(diffuseMap, vary_texcoord0.xy); - vec4 moonB = texture2D(altDiffuseMap, vary_texcoord0.xy); - vec4 c = mix(moonA, moonB, blend_factor); - c.rgb = srgb_to_linear(c.rgb); +/// Soft clips the light with a gamma correction +vec3 scaleSoftClip(vec3 light); - // mix factor which blends when sunlight is brighter - // and shows true moon color at night - vec3 luma_weights = vec3(0.3, 0.5, 0.3); +void main() +{ + // Restore Pre-EEP alpha fade moon near horizon + float fade = 1.0; + if( moon_dir.z > 0 ) + fade = clamp( moon_dir.z*moon_dir.z*4.0, 0.0, 1.0 ); - vec4 light_color = max(sunlight_color, moonlight_color); - float mix = 1.0 - dot(normalize(light_color.rgb), luma_weights); + vec4 c = texture2D(diffuseMap, vary_texcoord0.xy); +// c.rgb = srgb_to_linear(c.rgb); + c.rgb *= moonlight_color.rgb; + c.rgb *= moon_brightness; - vec3 exp = vec3(1.0 - mix * moon_brightness) * 2.0 - 1.0; - c.rgb = pow(c.rgb, exp); + c.rgb *= fade; + c.a *= fade; - //c.rgb *= moonlight_color.rgb; + c.rgb = scaleSoftClip(c.rgb); frag_data[0] = vec4(c.rgb, c.a); frag_data[1] = vec4(0.0); diff --git a/indra/newview/app_settings/shaders/class1/deferred/moonV.glsl b/indra/newview/app_settings/shaders/class1/deferred/moonV.glsl index e1bac4f248..c4922afd7d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/moonV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/moonV.glsl @@ -1,9 +1,9 @@ /** - * @file moonV.glsl + * @file class1\deferred\moonV.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2007, Linden Research, Inc. + * Copyright (C) 2007, 2020 Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -32,18 +32,13 @@ ATTRIBUTE vec2 texcoord0; VARYING vec2 vary_texcoord0; -void calcAtmospherics(vec3 eye_pos); - void main() { //transform vertex - vec3 offset = vec3(0, 0, 50); - vec4 vert = vec4(position.xyz - offset, 1.0); + vec4 vert = vec4(position.xyz, 1.0); vec4 pos = (modelview_matrix * vert); gl_Position = modelview_projection_matrix*vert; - calcAtmospherics(pos.xyz); - vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl index 0d1cc81786..8c402fcb54 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl @@ -1,24 +1,24 @@ -/** - * @file multiPointLightF.glsl +/** + * @file class1/deferred/multiPointLightF.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2007, 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$ */ @@ -36,119 +36,112 @@ out vec4 frag_color; uniform sampler2DRect depthMap; uniform sampler2DRect diffuseRect; uniform sampler2DRect specularRect; -uniform samplerCube environmentMap; -uniform sampler2D noiseMap; -uniform sampler2D lightFunc; +uniform samplerCube environmentMap; +uniform sampler2D noiseMap; +uniform sampler2D lightFunc; - -uniform vec3 env_mat[3]; +uniform vec3 env_mat[3]; uniform float sun_wash; +uniform int light_count; +uniform vec4 light[LIGHT_COUNT]; +uniform vec4 light_col[LIGHT_COUNT]; -uniform int light_count; - -uniform vec4 light[LIGHT_COUNT]; -uniform vec4 light_col[LIGHT_COUNT]; - -VARYING vec4 vary_fragcoord; -uniform vec2 screen_res; - +uniform vec2 screen_res; uniform float far_z; +uniform mat4 inv_proj; -uniform mat4 inv_proj; +VARYING vec4 vary_fragcoord; vec4 getPosition(vec2 pos_screen); vec3 getNorm(vec2 pos_screen); vec3 srgb_to_linear(vec3 c); -void main() +void main() { - vec3 out_col = vec3(0,0,0); - #if defined(LOCAL_LIGHT_KILL) - discard; -#else - vec2 frag = (vary_fragcoord.xy*0.5+0.5)*screen_res; - vec3 pos = getPosition(frag.xy).xyz; - if (pos.z < far_z) - { - discard; - } - - vec3 norm = getNorm(frag.xy); - - vec4 spec = texture2DRect(specularRect, frag.xy); - vec3 diff = texture2DRect(diffuseRect, frag.xy).rgb; - diff.rgb = srgb_to_linear(diff.rgb); - - float noise = texture2D(noiseMap, frag.xy/128.0).b; - vec3 npos = normalize(-pos); - - // As of OSX 10.6.7 ATI Apple's crash when using a variable size loop - for (int i = 0; i < LIGHT_COUNT; ++i) - { - vec3 lv = light[i].xyz-pos; - float dist = length(lv); - dist /= light[i].w; - if (dist <= 1.0) - { - float da = dot(norm, lv); - if (da > 0.0) - { - lv = normalize(lv); - da = dot(norm, lv); - - float fa = light_col[i].a+1.0; - float dist_atten = clamp(1.0-(dist-1.0*(1.0-fa))/fa, 0.0, 1.0); - dist_atten *= dist_atten; - - // Tweak falloff slightly to match pre-EEP attenuation - // NOTE: this magic number also shows up in a great many other places, search for dist_atten *= to audit - dist_atten *= 2.0; - - dist_atten *= noise; - - float lit = da * dist_atten; - - vec3 col = light_col[i].rgb*lit*diff; - - //vec3 col = vec3(dist2, light_col[i].a, lit); - - if (spec.a > 0.0) - { - lit = min(da*6.0, 1.0) * dist_atten; - //vec3 ref = dot(pos+lv, norm); - vec3 h = normalize(lv+npos); - float nh = dot(norm, h); - float nv = dot(norm, npos); - float vh = dot(npos, h); - float sa = nh; - float fres = pow(1 - dot(h, npos), 5)*0.4+0.5; - - float gtdenom = 2 * nh; - float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh)); - - if (nh > 0.0) - { - float scol = fres*texture2D(lightFunc, vec2(nh, spec.a)).r*gt/(nh*da); - col += lit*scol*light_col[i].rgb*spec.rgb; - //col += spec.rgb; - } - } - - out_col += col; - } - } - } + discard; // Bail immediately #endif - - frag_color.rgb = out_col; - frag_color.a = 0.0; + + vec3 out_col = vec3(0, 0, 0); + vec2 frag = (vary_fragcoord.xy * 0.5 + 0.5) * screen_res; + vec3 pos = getPosition(frag.xy).xyz; + if (pos.z < far_z) + { + discard; + } + + vec3 norm = getNorm(frag.xy); + + vec4 spec = texture2DRect(specularRect, frag.xy); + spec.rgb = srgb_to_linear(spec.rgb); + vec3 diff = texture2DRect(diffuseRect, frag.xy).rgb; + diff.rgb = srgb_to_linear(diff.rgb); + + float noise = texture2D(noiseMap, frag.xy / 128.0).b; + vec3 npos = normalize(-pos); + + // As of OSX 10.6.7 ATI Apple's crash when using a variable size loop + for (int i = 0; i < LIGHT_COUNT; ++i) + { + vec3 lv = light[i].xyz - pos; + float dist = length(lv); + dist /= light[i].w; + if (dist <= 1.0) + { + float da = dot(norm, lv); + if (da > 0.0) + { + lv = normalize(lv); + da = dot(norm, lv); + + float fa = light_col[i].a + 1.0; + float dist_atten = clamp(1.0 - (dist - 1.0 * (1.0 - fa)) / fa, 0.0, 1.0); + dist_atten *= dist_atten; + + // Tweak falloff slightly to match pre-EEP attenuation + // NOTE: this magic number also shows up in a great many other places, search for dist_atten *= to audit + dist_atten *= 2.0; + + dist_atten *= noise; + + float lit = da * dist_atten; + + vec3 col = light_col[i].rgb * lit * diff; + + if (spec.a > 0.0) + { + lit = min(da * 6.0, 1.0) * dist_atten; + vec3 h = normalize(lv + npos); + float nh = dot(norm, h); + float nv = dot(norm, npos); + float vh = dot(npos, h); + float sa = nh; + float fres = pow(1 - dot(h, npos), 5) * 0.4 + 0.5; + + float gtdenom = 2 * nh; + float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh)); + + if (nh > 0.0) + { + float scol = fres * texture2D(lightFunc, vec2(nh, spec.a)).r * gt / (nh * da); + col += lit * scol * light_col[i].rgb * spec.rgb; + } + } + + out_col += col; + } + } + } + + frag_color.rgb = out_col; + frag_color.a = 0.0; #ifdef IS_AMD_CARD - // If it's AMD make sure the GLSL compiler sees the arrays referenced once by static index. Otherwise it seems to optimise the storage awawy which leads to unfun crashes and artifacts. - vec4 dummy1 = light[0]; - vec4 dummy2 = light_col[0]; - vec4 dummy3 = light[LIGHT_COUNT-1]; - vec4 dummy4 = light_col[LIGHT_COUNT-1]; + // If it's AMD make sure the GLSL compiler sees the arrays referenced once by static index. Otherwise it seems to optimise the storage + // awawy which leads to unfun crashes and artifacts. + vec4 dummy1 = light[0]; + vec4 dummy2 = light_col[0]; + vec4 dummy3 = light[LIGHT_COUNT - 1]; + vec4 dummy4 = light_col[LIGHT_COUNT - 1]; #endif } diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl index ead754ec76..28a1faf24f 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl @@ -1,24 +1,24 @@ -/** +/** * @file WLSkyV.glsl * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2005, 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$ */ @@ -37,13 +37,13 @@ VARYING vec4 vary_HazeColor; // Inputs uniform vec3 camPosLocal; -uniform vec4 lightnorm; -uniform vec4 sunlight_color; -uniform vec4 moonlight_color; -uniform int sun_up_factor; -uniform vec4 ambient_color; -uniform vec4 blue_horizon; -uniform vec4 blue_density; +uniform vec4 lightnorm; +uniform vec4 sunlight_color; +uniform vec4 moonlight_color; +uniform int sun_up_factor; +uniform vec4 ambient_color; +uniform vec4 blue_horizon; +uniform vec4 blue_density; uniform float haze_horizon; uniform float haze_density; @@ -52,7 +52,7 @@ uniform float density_multiplier; uniform float distance_multiplier; uniform float max_y; -uniform vec4 glow; +uniform vec4 glow; uniform float sun_moon_glow_factor; uniform vec4 cloud_color; @@ -63,103 +63,91 @@ uniform vec4 cloud_color; // indra\newview\lllegacyatmospherics.cpp void main() { - - // World / view / projection + // World / view / projection vec4 pos = modelview_projection_matrix * vec4(position.xyz, 1.0); - gl_Position = pos; - - // Get relative position - vec3 P = position.xyz - camPosLocal.xyz + vec3(0,50,0); - - // Set altitude - if (P.y > 0.) - { - P *= (max_y / P.y); - } - else - { - P *= (-32000. / P.y); - } - - // Can normalize then - vec3 Pn = normalize(P); - - float Plen = length(P); - - // Initialize temp variables - vec4 temp1 = vec4(0.); - vec4 temp2 = vec4(0.); - vec4 blue_weight; - vec4 haze_weight; - vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - vec4 light_atten; - - float dens_mul = density_multiplier; - - // Sunlight attenuation effect (hue and brightness) due to atmosphere - // this is used later for sunlight modulation at various altitudes - light_atten = (blue_density + vec4(haze_density * 0.25)) * (dens_mul * max_y); - - // Calculate relative weights - temp1 = abs(blue_density) + vec4(abs(haze_density)); - blue_weight = blue_density / temp1; - haze_weight = haze_density / temp1; - - // Compute sunlight from P & lightnorm (for long rays like sky) - temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y ); - temp2.y = 1. / temp2.y; - sunlight *= exp( - light_atten * temp2.y); - - // Distance - temp2.z = Plen * dens_mul; - - // Transparency (-> temp1) - // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati - // compiler gets confused. - temp1 = exp(-temp1 * temp2.z); + gl_Position = pos; + + // Get relative position + vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0); + + // Adj position vector to clamp altitude + if (rel_pos.y > 0) + { + rel_pos *= (max_y / rel_pos.y); + } + if (rel_pos.y < 0) + { + rel_pos *= (-32000. / rel_pos.y); + } + + // Can normalize then + vec3 rel_pos_norm = normalize(rel_pos); - // Compute haze glow - temp2.x = dot(Pn, lightnorm.xyz); - temp2.x = 1. - temp2.x; - // temp2.x is 0 at the sun and increases away from sun - temp2.x = max(temp2.x, .001); - // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) - temp2.x *= glow.x; - // Higher glow.x gives dimmer glow (because next step is 1 / "angle") - temp2.x = pow(temp2.x, glow.z); - // glow.z should be negative, so we're doing a sort of (1 / "angle") function + float rel_pos_len = length(rel_pos); - // Add "minimum anti-solar illumination" - temp2.x += .25; + // Initialize temp variables + vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; + vec4 light_atten; - vec4 color = ( blue_horizon * blue_weight * (sunlight + ambient_color) - + (haze_horizon * haze_weight) * (sunlight * temp2.x + ambient_color) - ); + // Sunlight attenuation effect (hue and brightness) due to atmosphere + // this is used later for sunlight modulation at various altitudes + light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); + // Calculate relative weights + vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density)); + vec4 blue_weight = blue_density / combined_haze; + vec4 haze_weight = haze_density / combined_haze; + + // Compute sunlight from rel_pos & lightnorm (for long rays like sky) + float off_axis = 1.0 / max(1e-6, max(0., rel_pos_norm.y) + lightnorm.y); + sunlight *= exp(-light_atten * off_axis); + + // Distance + float density_dist = rel_pos_len * density_multiplier; + + // Transparency (-> combined_haze) + // ATI Bugfix -- can't store combined_haze*density_dist in a variable because the ati + // compiler gets confused. + combined_haze = exp(-combined_haze * density_dist); + + // Compute haze glow + float haze_glow = 1.0 - dot(rel_pos_norm, lightnorm.xyz); + // haze_glow is 0 at the sun and increases away from sun + haze_glow = max(haze_glow, .001); + // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) + haze_glow *= glow.x; + // Higher glow.x gives dimmer glow (because next step is 1 / "angle") + haze_glow = pow(haze_glow, glow.z); + // glow.z should be negative, so we're doing a sort of (1 / "angle") function + + // Add "minimum anti-solar illumination" + // For sun, add to glow. For moon, remove glow entirely. SL-13768 + haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (haze_glow + 0.25); + + vec4 color = + (blue_horizon * blue_weight * (sunlight + ambient_color) + (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient_color)); // Final atmosphere additive - color *= (1. - temp1); + color *= (1. - combined_haze); - // Increase ambient when there are more clouds - vec4 tmpAmbient = ambient_color; - tmpAmbient += max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; + // Increase ambient when there are more clouds + vec4 tmpAmbient = ambient_color; + tmpAmbient += max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; - // Dim sunlight by cloud shadow percentage - sunlight *= max(0.0, (1. - cloud_shadow)); + // Dim sunlight by cloud shadow percentage + sunlight *= max(0.0, (1. - cloud_shadow)); - // Haze color below cloud - vec4 additiveColorBelowCloud = ( blue_horizon * blue_weight * (sunlight + tmpAmbient) - + (haze_horizon * haze_weight) * (sunlight * temp2.x + tmpAmbient) - ); + // Haze color below cloud + vec4 additiveColorBelowCloud = + (blue_horizon * blue_weight * (sunlight + tmpAmbient) + (haze_horizon * haze_weight) * (sunlight * haze_glow + tmpAmbient)); - // Attenuate cloud color by atmosphere - temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds + // Attenuate cloud color by atmosphere + combined_haze = sqrt(combined_haze); // less atmos opacity (more transparency) below clouds - // At horizon, blend high altitude sky color towards the darker color below the clouds - color += (additiveColorBelowCloud - color) * (1. - sqrt(temp1)); + // At horizon, blend high altitude sky color towards the darker color below the clouds + color += (additiveColorBelowCloud - color) * (1. - sqrt(combined_haze)); // Haze color above cloud - vary_HazeColor = color; + vary_HazeColor = color; } - diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index a5804220bc..f80f1a985a 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -124,41 +124,15 @@ void main() if (spec.a > 0.0) // specular reflection { + float sa = dot(refnormpersp, light_dir.xyz); + vec3 dumbshiny = sunlit * (texture2D(lightFunc, vec2(sa, spec.a)).r); -#if 1 //EEP - vec3 npos = -normalize(pos.xyz); - - //vec3 ref = dot(pos+lv, norm); - vec3 h = normalize(light_dir.xyz+npos); - float nh = dot(norm.xyz, h); - float nv = dot(norm.xyz, npos); - float vh = dot(npos, h); - float sa = nh; - float fres = pow(1 - dot(h, npos), 5)*0.4+0.5; - - float gtdenom = 2 * nh; - float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh)); - - if (nh > 0.0) - { - float scontrib = fres*texture2D(lightFunc, vec2(nh, spec.a)).r*gt/(nh*da); - vec3 sp = sun_contrib*scontrib / 6.0; - sp = clamp(sp, vec3(0), vec3(1)); - bloom += dot(sp, sp) / 4.0; - color += sp * spec.rgb; - } -#else //PRODUCTION - float sa = dot(refnormpersp, light_dir.xyz); - vec3 dumbshiny = sunlit*(texture2D(lightFunc, vec2(sa, spec.a)).r); - // add the two types of shiny together vec3 spec_contrib = dumbshiny * spec.rgb; - bloom = dot(spec_contrib, spec_contrib) / 6; + bloom = dot(spec_contrib, spec_contrib) / 6; color.rgb += spec_contrib; -#endif - } - + color.rgb = mix(color.rgb, diffuse.rgb, diffuse.a); if (envIntensity > 0.0) diff --git a/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl b/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl index 50e781fa78..6cd2445522 100644 --- a/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl +++ b/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl @@ -23,6 +23,9 @@ * $/LicenseInfo$ */ +// Lambert Azimuthal Equal-Area projection +// See: https://aras-p.info/texts/CompactNormalStorage.html +// Also see: A_bit_more_deferred_-_CryEngine3.ppt vec2 encode_normal(vec3 n) { float f = sqrt(8 * n.z + 8); diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightAlphaMaskF.glsl index f665394b46..0bb48061e0 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightAlphaMaskF.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightAlphaMaskF.glsl @@ -31,6 +31,9 @@ out vec4 frag_color; uniform float minimum_alpha; +// render_hud_attachments() -> HUD objects set LLShaderMgr::NO_ATMO; +uniform int no_atmo; + vec3 atmosLighting(vec3 light); vec3 scaleSoftClip(vec3 light); @@ -41,16 +44,19 @@ void default_lighting() { vec4 color = diffuseLookup(vary_texcoord0.xy); - color *= vertex_color; - if (color.a < minimum_alpha) { discard; } + + color *= vertex_color; - color.rgb = atmosLighting(color.rgb); - - color.rgb = scaleSoftClip(color.rgb); + // SL-9632 HUDs are affected by Atmosphere + if (no_atmo == 0) + { + color.rgb = atmosLighting(color.rgb); + color.rgb = scaleSoftClip(color.rgb); + } frag_color = color; } diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightAlphaMaskF.glsl index 46390e4a0e..1855cfceeb 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightAlphaMaskF.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightAlphaMaskF.glsl @@ -30,7 +30,10 @@ out vec4 frag_color; #endif uniform float minimum_alpha; -uniform float texture_gamma; +uniform float texture_gamma; // either 1.0 or 2.2; see: "::TEXTURE_GAMMA" + +// render_hud_attachments() -> HUD objects set LLShaderMgr::NO_ATMO; used in LLDrawPoolAlpha::beginRenderPass() +uniform int no_atmo; vec3 fullbrightAtmosTransport(vec3 light); vec3 fullbrightScaleSoftClip(vec3 light); @@ -50,9 +53,17 @@ void fullbright_lighting() color.rgb *= vertex_color.rgb; color.rgb = pow(color.rgb, vec3(texture_gamma)); - color.rgb = fullbrightAtmosTransport(color.rgb); - - color.rgb = fullbrightScaleSoftClip(color.rgb); + + // SL-9632 HUDs are affected by Atmosphere + if (no_atmo == 0) + { + color.rgb = fullbrightAtmosTransport(color.rgb); + color.rgb = fullbrightScaleSoftClip(color.rgb); + } + + //*TODO: Are we missing an inverse pow() here? + // class1\lighting\lightFullbrightF.glsl has: + // color.rgb = pow(color.rgb, vec3(1.0/texture_gamma)); frag_color = color; } diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl index b967709c57..5fcdf3107c 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightF.glsl @@ -34,6 +34,9 @@ VARYING vec2 vary_texcoord0; uniform float texture_gamma; +// render_hud_attachments() -> HUD objects set LLShaderMgr::NO_ATMO; +uniform int no_atmo; + vec3 fullbrightAtmosTransport(vec3 light); vec3 fullbrightScaleSoftClip(vec3 light); @@ -43,9 +46,12 @@ void fullbright_lighting() color.rgb = pow(color.rgb, vec3(texture_gamma)); - color.rgb = fullbrightAtmosTransport(color.rgb); - - color.rgb = fullbrightScaleSoftClip(color.rgb); + // SL-9632 HUDs are affected by Atmosphere + if (no_atmo == 0) + { + color.rgb = fullbrightAtmosTransport(color.rgb); + color.rgb = fullbrightScaleSoftClip(color.rgb); + } color.rgb = pow(color.rgb, vec3(1.0/texture_gamma)); diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyF.glsl index 567811cd75..6f7e777d23 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyF.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyF.glsl @@ -35,20 +35,37 @@ VARYING vec3 vary_texcoord1; uniform samplerCube environmentMap; +// render_hud_attachments() -> HUD objects set LLShaderMgr::NO_ATMO; used in LLDrawPoolAlpha::beginRenderPass() +uniform int no_atmo; + vec3 fullbrightShinyAtmosTransport(vec3 light); vec3 fullbrightScaleSoftClip(vec3 light); +// See: +// class1\deferred\fullbrightShinyF.glsl +// class1\lighting\lightFullbrightShinyF.glsl void fullbright_shiny_lighting() { vec4 color = diffuseLookup(vary_texcoord0.xy); color.rgb *= vertex_color.rgb; - - vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb; - color.rgb = mix(color.rgb, envColor.rgb, vertex_color.a*0.75); // MAGIC NUMBER SL-12574; ALM: Off, Quality > Low - color.rgb = fullbrightShinyAtmosTransport(color.rgb); + // SL-9632 HUDs are affected by Atmosphere + if (no_atmo == 0) + { + vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb; + color.rgb = mix(color.rgb, envColor.rgb, vertex_color.a*0.75); // MAGIC NUMBER SL-12574; ALM: Off, Quality > Low - color.rgb = fullbrightScaleSoftClip(color.rgb); + color.rgb = fullbrightShinyAtmosTransport(color.rgb); + color.rgb = fullbrightScaleSoftClip(color.rgb); + } +/* + // NOTE: HUD objects will be full bright. Uncomment if you want "some" environment lighting effecting these HUD objects. + else + { + vec3 envColor = textureCube(environmentMap, vary_texcoord1.xyz).rgb; + color.rgb = mix(color.rgb, envColor.rgb, vertex_color.a*0.75); // MAGIC NUMBER SL-12574; ALM: Off, Quality > Low + } +*/ color.a = 1.0; diff --git a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl index 88959266c8..4bb588335a 100644 --- a/indra/newview/app_settings/shaders/class1/objects/previewV.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/previewV.glsl @@ -93,6 +93,5 @@ void main() col.rgb += light_diffuse[1].rgb * calcDirectionalLight(norm, light_position[1].xyz); col.rgb += light_diffuse[2].rgb*calcLocalLight(pos.xyz, norm, light_position[2], light_direction[2], light_attenuation[2].x, light_attenuation[2].z); col.rgb += light_diffuse[3].rgb*calcLocalLight(pos.xyz, norm, light_position[3], light_direction[3], light_attenuation[3].x, light_attenuation[3].z); - vertex_color = col*color; } diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl index a59bd9c0a6..9ef7704b70 100644 --- a/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl @@ -1,4 +1,4 @@ -/** +/** * @file simpleV.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ @@ -28,6 +28,9 @@ uniform mat4 texture_matrix0; uniform mat4 modelview_matrix; uniform mat4 modelview_projection_matrix; +// render_hud_attachments() -> HUD objects set LLShaderMgr::NO_ATMO; used in LLDrawPoolAlpha::beginRenderPass() +uniform int no_atmo; + ATTRIBUTE vec3 position; void passTextureIndex(); ATTRIBUTE vec2 texcoord0; @@ -46,19 +49,23 @@ void main() { //transform vertex vec4 vert = vec4(position.xyz,1.0); - passTextureIndex(); - vec4 pos = (modelview_matrix * vert); gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0); + + passTextureIndex(); vary_texcoord0 = (texture_matrix0 * vec4(texcoord0, 0, 1)).xy; - - - - vec3 norm = normalize(normal_matrix * normal); - calcAtmospherics(pos.xyz); + // SL-9632 HUDs are affected by Atmosphere + if (no_atmo == 1) + { + vertex_color = diffuse_color; + } + else + { + vec4 pos = (modelview_matrix * vert); + vec3 norm = normalize(normal_matrix * normal); - vec4 color = calcLighting(pos.xyz, norm, diffuse_color); - vertex_color = color; + calcAtmospherics(pos.xyz); - + vertex_color = calcLighting(pos.xyz, norm, diffuse_color); + } } diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl index dcb02bd1c1..ea2690ba09 100644 --- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl +++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl @@ -1,154 +1,134 @@ -/** +/** * @file class1\windlight\atmosphericsFuncs.glsl * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2019, 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$ */ -uniform vec4 lightnorm; -uniform vec4 sunlight_color; -uniform vec4 moonlight_color; -uniform int sun_up_factor; -uniform vec4 ambient_color; -uniform vec4 blue_horizon; -uniform vec4 blue_density; +uniform vec4 lightnorm; +uniform vec4 sunlight_color; +uniform vec4 moonlight_color; +uniform int sun_up_factor; +uniform vec4 ambient_color; +uniform vec4 blue_horizon; +uniform vec4 blue_density; uniform float haze_horizon; uniform float haze_density; uniform float cloud_shadow; uniform float density_multiplier; uniform float distance_multiplier; uniform float max_y; -uniform vec4 glow; +uniform vec4 glow; uniform float scene_light_strength; -uniform mat3 ssao_effect_mat; -uniform int no_atmo; +uniform mat3 ssao_effect_mat; +uniform int no_atmo; uniform float sun_moon_glow_factor; -float getAmbientClamp() +float getAmbientClamp() { return 1.0f; } + +void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, out vec3 sunlit, out vec3 amblit, out vec3 additive, + out vec3 atten, bool use_ao) { - return 1.0f; -} + vec3 rel_pos = inPositionEye; + //(TERRAIN) limit altitude + if (abs(rel_pos.y) > max_y) rel_pos *= (max_y / rel_pos.y); -void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, out vec3 sunlit, out vec3 amblit, out vec3 additive, out vec3 atten, bool use_ao) { + vec3 rel_pos_norm = normalize(rel_pos); + float rel_pos_len = length(rel_pos); + vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - vec3 P = inPositionEye; - - //(TERRAIN) limit altitude - if (P.y > max_y) P *= (max_y / P.y); - if (P.y < -max_y) P *= (-max_y / P.y); + // sunlight attenuation effect (hue and brightness) due to atmosphere + // this is used later for sunlight modulation at various altitudes + vec4 light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); + // I had thought blue_density and haze_density should have equal weighting, + // but attenuation due to haze_density tends to seem too strong - vec3 tmpLightnorm = lightnorm.xyz; + vec4 combined_haze = blue_density + vec4(haze_density); + vec4 blue_weight = blue_density / combined_haze; + vec4 haze_weight = vec4(haze_density) / combined_haze; - vec3 Pn = normalize(P); - float Plen = length(P); + //(TERRAIN) compute sunlight from lightnorm y component. Factor is roughly cosecant(sun elevation) (for short rays like terrain) + float above_horizon_factor = 1.0 / max(1e-6, lightnorm.y); + sunlight *= exp(-light_atten * above_horizon_factor); // for sun [horizon..overhead] this maps to an exp curve [0..1] + + // main atmospheric scattering line integral + float density_dist = rel_pos_len * density_multiplier; - vec4 temp1 = vec4(0); - vec3 temp2 = vec3(0); - vec4 blue_weight; - vec4 haze_weight; - vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - vec4 light_atten; + // Transparency (-> combined_haze) + // ATI Bugfix -- can't store combined_haze*density_dist*distance_multiplier in a variable because the ati + // compiler gets confused. + combined_haze = exp(-combined_haze * density_dist * distance_multiplier); - float dens_mul = density_multiplier; - float dist_mul = distance_multiplier; + // final atmosphere attenuation factor + atten = combined_haze.rgb; - //sunlight attenuation effect (hue and brightness) due to atmosphere - //this is used later for sunlight modulation at various altitudes - light_atten = (blue_density + vec4(haze_density * 0.25)) * (dens_mul * max_y); - //I had thought blue_density and haze_density should have equal weighting, - //but attenuation due to haze_density tends to seem too strong + // compute haze glow + float haze_glow = dot(rel_pos_norm, lightnorm.xyz); - temp1 = blue_density + vec4(haze_density); - blue_weight = blue_density / temp1; - haze_weight = vec4(haze_density) / temp1; + // dampen sun additive contrib when not facing it... + // SL-13539: This "if" clause causes an "additive" white artifact at roughly 77 degreees. + // if (length(light_dir) > 0.01) + haze_glow *= max(0.0f, dot(light_dir, rel_pos_norm)); - //(TERRAIN) compute sunlight from lightnorm only (for short rays like terrain) - temp2.y = max(0.0, tmpLightnorm.y); - if (abs(temp2.y) > 0.000001f) - { - temp2.y = 1. / abs(temp2.y); - } - temp2.y = max(0.0000001f, temp2.y); - sunlight *= exp(-light_atten * temp2.y); + haze_glow = 1. - haze_glow; + // haze_glow is 0 at the sun and increases away from sun + haze_glow = max(haze_glow, .001); // set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) + haze_glow *= glow.x; + // higher glow.x gives dimmer glow (because next step is 1 / "angle") + haze_glow = pow(haze_glow, glow.z); + // glow.z should be negative, so we're doing a sort of (1 / "angle") function - // main atmospheric scattering line integral - temp2.z = Plen * dens_mul; + // add "minimum anti-solar illumination" + haze_glow += .25; - // Transparency (-> temp1) - // ATI Bugfix -- can't store temp1*temp2.z*dist_mul in a variable because the ati - // compiler gets confused. - temp1 = exp(-temp1 * temp2.z * dist_mul); + haze_glow *= sun_moon_glow_factor; - //final atmosphere attenuation factor - atten = temp1.rgb; - - //compute haze glow - //(can use temp2.x as temp because we haven't used it yet) - temp2.x = dot(Pn, tmpLightnorm.xyz); + vec4 amb_color = ambient_color; - // dampen sun additive contrib when not facing it... - if (length(light_dir) > 0.01) - { - temp2.x *= max(0.0f, dot(light_dir, Pn)); - } - temp2.x = 1. - temp2.x; - //temp2.x is 0 at the sun and increases away from sun - temp2.x = max(temp2.x, .001); //was glow.y - //set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) - temp2.x *= glow.x; - //higher glow.x gives dimmer glow (because next step is 1 / "angle") - temp2.x = pow(temp2.x, glow.z); - //glow.z should be negative, so we're doing a sort of (1 / "angle") function - - //add "minimum anti-solar illumination" - temp2.x += .25; - - temp2.x *= sun_moon_glow_factor; - - vec4 amb_color = ambient_color; - - //increase ambient when there are more clouds + // increase ambient when there are more clouds vec4 tmpAmbient = amb_color + (vec4(1.) - amb_color) * cloud_shadow * 0.5; - + /* decrease value and saturation (that in HSV, not HSL) for occluded areas * // for HSV color/geometry used here, see http://gimp-savvy.com/BOOK/index.html?node52.html * // The following line of code performs the equivalent of: * float ambAlpha = tmpAmbient.a; * float ambValue = dot(vec3(tmpAmbient), vec3(0.577)); // projection onto <1/rt(3), 1/rt(3), 1/rt(3)>, the neutral white-black axis * vec3 ambHueSat = vec3(tmpAmbient) - vec3(ambValue); - * tmpAmbient = vec4(RenderSSAOEffect.valueFactor * vec3(ambValue) + RenderSSAOEffect.saturationFactor *(1.0 - ambFactor) * ambHueSat, ambAlpha); + * tmpAmbient = vec4(RenderSSAOEffect.valueFactor * vec3(ambValue) + RenderSSAOEffect.saturationFactor *(1.0 - ambFactor) * ambHueSat, + * ambAlpha); */ if (use_ao) { tmpAmbient = vec4(mix(ssao_effect_mat * tmpAmbient.rgb, tmpAmbient.rgb, ambFactor), tmpAmbient.a); } - //haze color - additive = - vec3(blue_horizon * blue_weight * (sunlight*(1.-cloud_shadow) + tmpAmbient) - + (haze_horizon * haze_weight) * (sunlight*(1.-cloud_shadow) * temp2.x - + tmpAmbient)); + // Similar/Shared Algorithms: + // indra\llinventory\llsettingssky.cpp -- LLSettingsSky::calculateLightSettings() + // indra\newview\app_settings\shaders\class1\windlight\atmosphericsFuncs.glsl -- calcAtmosphericVars() + // haze color + vec3 cs = sunlight.rgb * (1. - cloud_shadow); + additive = (blue_horizon.rgb * blue_weight.rgb) * (cs + tmpAmbient.rgb) + (haze_horizon * haze_weight.rgb) * (cs * haze_glow + tmpAmbient.rgb); - //brightness of surface both sunlight and ambient + // brightness of surface both sunlight and ambient sunlit = sunlight.rgb * 0.5; amblit = tmpAmbient.rgb * .25; - additive *= vec3(1.0 - temp1); + additive *= vec3(1.0 - combined_haze); } diff --git a/indra/newview/app_settings/shaders/class1/windlight/moonF.glsl b/indra/newview/app_settings/shaders/class1/windlight/moonF.glsl index 24f3992e32..2425a2ad04 100644 --- a/indra/newview/app_settings/shaders/class1/windlight/moonF.glsl +++ b/indra/newview/app_settings/shaders/class1/windlight/moonF.glsl @@ -3,7 +3,7 @@ * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2005, Linden Research, Inc. + * Copyright (C) 2005, 2020 Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,28 +36,30 @@ out vec4 frag_color; uniform vec4 color; uniform vec4 sunlight_color; uniform vec4 moonlight_color; -uniform vec3 lumWeights; +uniform vec3 moon_dir; uniform float moon_brightness; -uniform float minLuminance; uniform sampler2D diffuseMap; -uniform sampler2D altDiffuseMap; -uniform float blend_factor; // interp factor between moon A/B + VARYING vec2 vary_texcoord0; +vec3 scaleSoftClip(vec3 light); + void main() { - vec4 moonA = texture2D(diffuseMap, vary_texcoord0.xy); - vec4 moonB = texture2D(altDiffuseMap, vary_texcoord0.xy); - vec4 c = mix(moonA, moonB, blend_factor); + // Restore Pre-EEP alpha fade moon near horizon + float fade = 1.0; + if( moon_dir.z > 0 ) + fade = clamp( moon_dir.z*moon_dir.z*4.0, 0.0, 1.0 ); + + vec4 c = texture2D(diffuseMap, vary_texcoord0.xy); +// c.rgb = pow(c.rgb, vec3(0.7f)); // can't use "srgb_to_linear(color.rgb)" as that is a deferred only function + c.rgb *= moonlight_color.rgb; + c.rgb *= moon_brightness; - // mix factor which blends when sunlight is brighter - // and shows true moon color at night - vec3 luma_weights = vec3(0.3, 0.5, 0.3); - float mix = 1.0f - dot(normalize(sunlight_color.rgb), luma_weights); + c.rgb *= fade; + c.a *= fade; - vec3 exp = vec3(1.0 - mix * moon_brightness) * 2.0 - 1.0; - c.rgb = pow(c.rgb, exp); - //c.rgb *= moonlight_color.rgb; + c.rgb = scaleSoftClip(c.rgb); frag_color = vec4(c.rgb, c.a); } diff --git a/indra/newview/app_settings/shaders/class1/windlight/moonV.glsl b/indra/newview/app_settings/shaders/class1/windlight/moonV.glsl index 8cd4b2ef47..2fceb5f743 100644 --- a/indra/newview/app_settings/shaders/class1/windlight/moonV.glsl +++ b/indra/newview/app_settings/shaders/class1/windlight/moonV.glsl @@ -3,7 +3,7 @@ * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2007, Linden Research, Inc. + * Copyright (C) 2007, 2020 Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,20 +30,15 @@ uniform mat4 modelview_projection_matrix; ATTRIBUTE vec3 position; ATTRIBUTE vec2 texcoord0; -void calcAtmospherics(vec3 inPositionEye); - VARYING vec2 vary_texcoord0; void main() { //transform vertex - vec3 offset = vec3(0, 0, 50); - vec4 vert = vec4(position.xyz - offset, 1.0); + vec4 vert = vec4(position.xyz, 1.0); vec4 pos = (modelview_matrix * vert); gl_Position = modelview_projection_matrix*vert; vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy; - - calcAtmospherics(pos.xyz); } diff --git a/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl index 1dce85a83b..1485c515a4 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl @@ -1,24 +1,24 @@ -/** +/** * @file class2/deferred/skyF.glsl * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2005, 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$ */ @@ -32,13 +32,13 @@ uniform mat4 modelview_projection_matrix; // Inputs uniform vec3 camPosLocal; -uniform vec4 lightnorm; -uniform vec4 sunlight_color; -uniform vec4 moonlight_color; -uniform int sun_up_factor; -uniform vec4 ambient_color; -uniform vec4 blue_horizon; -uniform vec4 blue_density; +uniform vec4 lightnorm; +uniform vec4 sunlight_color; +uniform vec4 moonlight_color; +uniform int sun_up_factor; +uniform vec4 ambient_color; +uniform vec4 blue_horizon; +uniform vec4 blue_density; uniform float haze_horizon; uniform float haze_density; @@ -47,7 +47,7 @@ uniform float density_multiplier; uniform float distance_multiplier; uniform float max_y; -uniform vec4 glow; +uniform vec4 glow; uniform float sun_moon_glow_factor; uniform vec4 cloud_color; @@ -73,16 +73,16 @@ uniform float ice_level; vec3 rainbow(float d) { - d = clamp(d, -1.0, 0.0); - float rad = (droplet_radius - 5.0f) / 1024.0f; - return pow(texture2D(rainbow_map, vec2(rad, d)).rgb, vec3(1.8)) * moisture_level; + d = clamp(d, -1.0, 0.0); + float rad = (droplet_radius - 5.0f) / 1024.0f; + return pow(texture2D(rainbow_map, vec2(rad, d)).rgb, vec3(1.8)) * moisture_level; } vec3 halo22(float d) { - d = clamp(d, 0.1, 1.0); - float v = sqrt(clamp(1 - (d * d), 0, 1)); - return texture2D(halo_map, vec2(0, v)).rgb * ice_level; + d = clamp(d, 0.1, 1.0); + float v = sqrt(clamp(1 - (d * d), 0, 1)); + return texture2D(halo_map, vec2(0, v)).rgb * ice_level; } /// Soft clips the light with a gamma correction @@ -90,115 +90,96 @@ vec3 scaleSoftClip(vec3 light); void main() { - // World / view / projection - // Get relative position - vec3 P = pos.xyz - camPosLocal.xyz + vec3(0,50,0); + // Get relative position (offset why?) + vec3 rel_pos = pos.xyz - camPosLocal.xyz + vec3(0, 50, 0); - // Set altitude - if (P.y > 0.) + // Adj position vector to clamp altitude + if (rel_pos.y > 0.) { - P *= (max_y / P.y); + rel_pos *= (max_y / rel_pos.y); } - else + if (rel_pos.y < 0.) { - P *= (-32000. / P.y); + rel_pos *= (-32000. / rel_pos.y); } - // Can normalize then - vec3 Pn = normalize(P); - float Plen = length(P); + // Normalized + vec3 rel_pos_norm = normalize(rel_pos); + float rel_pos_len = length(rel_pos); // Initialize temp variables - vec4 temp1 = vec4(0.); - vec4 temp2 = vec4(0.); - vec4 blue_weight; - vec4 haze_weight; vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - vec4 light_atten; - - float dens_mul = density_multiplier; // Sunlight attenuation effect (hue and brightness) due to atmosphere // this is used later for sunlight modulation at various altitudes - light_atten = (blue_density + vec4(haze_density * 0.25)) * (dens_mul * max_y); + vec4 light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); // Calculate relative weights - temp1 = abs(blue_density) + vec4(abs(haze_density)); - blue_weight = blue_density / temp1; - haze_weight = haze_density / temp1; + vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density)); + vec4 blue_weight = blue_density / combined_haze; + vec4 haze_weight = haze_density / combined_haze; - // Compute sunlight from P & lightnorm (for long rays like sky) - temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y ); - temp2.y = 1. / temp2.y; - sunlight *= exp( - light_atten * temp2.y); + // Compute sunlight from rel_pos & lightnorm (for long rays like sky) + float off_axis = 1.0 / max(1e-6, max(0, rel_pos_norm.y) + lightnorm.y); + sunlight *= exp(-light_atten * off_axis); // Distance - temp2.z = Plen * dens_mul; + float density_dist = rel_pos_len * density_multiplier; - // Transparency (-> temp1) - // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati + // Transparency (-> combined_haze) + // ATI Bugfix -- can't store combined_haze*density_dist in a variable because the ati // compiler gets confused. - temp1 = exp(-temp1 * temp2.z); + combined_haze = exp(-combined_haze * density_dist); // Compute haze glow - temp2.x = dot(Pn, lightnorm.xyz); - temp2.x = 1. - temp2.x; - // temp2.x is 0 at the sun and increases away from sun - temp2.x = max(temp2.x, .001); - // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) - temp2.x *= glow.x; - // Higher glow.x gives dimmer glow (because next step is 1 / "angle") - temp2.x = pow(temp2.x, glow.z); - // glow.z should be negative, so we're doing a sort of (1 / "angle") function + float haze_glow = dot(rel_pos_norm, lightnorm.xyz); + haze_glow = 1. - haze_glow; + // haze_glow is 0 at the sun and increases away from sun + haze_glow = max(haze_glow, .001); + // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) + haze_glow *= glow.x; + // Higher glow.x gives dimmer glow (because next step is 1 / "angle") + haze_glow = pow(haze_glow, glow.z); + // glow.z should be negative, so we're doing a sort of (1 / "angle") function // Add "minimum anti-solar illumination" - temp2.x += .25; - - temp2.x *= sun_moon_glow_factor; + // For sun, add to glow. For moon, remove glow entirely. SL-13768 + haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (sun_moon_glow_factor * (haze_glow + 0.25)); // Haze color above cloud - vec4 color = ( blue_horizon * blue_weight * (sunlight + ambient_color) - + (haze_horizon * haze_weight) * (sunlight * temp2.x + ambient_color) - ); + vec4 color = blue_horizon * blue_weight * (sunlight + ambient_color) + + haze_horizon * haze_weight * (sunlight * haze_glow + ambient_color); // Final atmosphere additive - color *= (1. - temp1); + color *= (1. - combined_haze); // Increase ambient when there are more clouds - vec4 tmpAmbient = ambient_color; - tmpAmbient += max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; + // TODO 9/20: DJH what does this do? max(0,(1-ambient)) will change the color + vec4 ambient = ambient_color + max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; // Dim sunlight by cloud shadow percentage sunlight *= max(0.0, (1. - cloud_shadow)); // Haze color below cloud - vec4 additiveColorBelowCloud = (blue_horizon * blue_weight * (sunlight + tmpAmbient) - + (haze_horizon * haze_weight) * (sunlight * temp2.x + tmpAmbient) - ); + vec4 add_below_cloud = blue_horizon * blue_weight * (sunlight + ambient) + + haze_horizon * haze_weight * (sunlight * haze_glow + ambient); - - // Attenuate cloud color by atmosphere - temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds + combined_haze = sqrt(combined_haze); // less atmos opacity (more transparency) below clouds // At horizon, blend high altitude sky color towards the darker color below the clouds - color += (additiveColorBelowCloud - color) * (1. - sqrt(temp1)); - - float optic_d = dot(Pn, lightnorm.xyz); - - vec3 halo_22 = halo22(optic_d); + color += (add_below_cloud - color) * (1. - sqrt(combined_haze)); + float optic_d = dot(rel_pos_norm, lightnorm.xyz); + vec3 halo_22 = halo22(optic_d); color.rgb += rainbow(optic_d); - color.rgb += halo_22; - color.rgb *= 2.; color.rgb = scaleSoftClip(color.rgb); - /// Gamma correct for WL (soft clip effect). + // Gamma correct for WL (soft clip effect). frag_data[0] = vec4(color.rgb, 1.0); - frag_data[1] = vec4(0.0,0.0,0.0,0.0); - frag_data[2] = vec4(0.0,0.0,0.0,1.0); //1.0 in norm.w masks off fog + frag_data[1] = vec4(0.0, 0.0, 0.0, 0.0); + frag_data[2] = vec4(0.0, 0.0, 0.0, 1.0); // 1.0 in norm.w masks off fog } - diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index b0dff0c628..f4db53e0b7 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -1,24 +1,24 @@ -/** +/** * @file class2/deferred/softenLightF.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2007, 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$ */ @@ -39,7 +39,7 @@ uniform sampler2DRect specularRect; uniform sampler2DRect normalMap; uniform sampler2DRect lightMap; uniform sampler2DRect depthMap; -uniform samplerCube environmentMap; +uniform samplerCube environmentMap; uniform sampler2D lightFunc; uniform float blur_size; @@ -50,7 +50,7 @@ uniform mat3 env_mat; uniform vec3 sun_dir; uniform vec3 moon_dir; -uniform int sun_up_factor; +uniform int sun_up_factor; VARYING vec2 vary_fragcoord; uniform mat4 inv_proj; @@ -61,10 +61,10 @@ vec4 getPositionWithDepth(vec2 pos_screen, float depth); void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, out vec3 sunlit, out vec3 amblit, out vec3 additive, out vec3 atten, bool use_ao); float getAmbientClamp(); -vec3 atmosFragLighting(vec3 l, vec3 additive, vec3 atten); -vec3 scaleSoftClipFrag(vec3 l); -vec3 fullbrightAtmosTransportFrag(vec3 light, vec3 additive, vec3 atten); -vec3 fullbrightScaleSoftClip(vec3 light); +vec3 atmosFragLighting(vec3 l, vec3 additive, vec3 atten); +vec3 scaleSoftClipFrag(vec3 l); +vec3 fullbrightAtmosTransportFrag(vec3 light, vec3 additive, vec3 atten); +vec3 fullbrightScaleSoftClip(vec3 light); vec3 linear_to_srgb(vec3 c); vec3 srgb_to_linear(vec3 c); @@ -73,128 +73,85 @@ vec3 srgb_to_linear(vec3 c); vec4 applyWaterFogView(vec3 pos, vec4 color); #endif -void main() +void main() { - vec2 tc = vary_fragcoord.xy; - float depth = texture2DRect(depthMap, tc.xy).r; - vec4 pos = getPositionWithDepth(tc, depth); - vec4 norm = texture2DRect(normalMap, tc); + vec2 tc = vary_fragcoord.xy; + float depth = texture2DRect(depthMap, tc.xy).r; + vec4 pos = getPositionWithDepth(tc, depth); + vec4 norm = texture2DRect(normalMap, tc); float envIntensity = norm.z; - norm.xyz = getNorm(tc); - - vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir; - float da = clamp(dot(norm.xyz, light_dir.xyz), 0.0, 1.0); - float light_gamma = 1.0/1.3; - da = pow(da, light_gamma); - + norm.xyz = getNorm(tc); + + vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir; + float da = clamp(dot(norm.xyz, light_dir.xyz), 0.0, 1.0); + float light_gamma = 1.0 / 1.3; + da = pow(da, light_gamma); + vec4 diffuse = texture2DRect(diffuseRect, tc); - - vec4 spec = texture2DRect(specularRect, vary_fragcoord.xy); + vec4 spec = texture2DRect(specularRect, vary_fragcoord.xy); vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; - scol_ambocc = pow(scol_ambocc, vec2(light_gamma)); + scol_ambocc = pow(scol_ambocc, vec2(light_gamma)); + float scol = max(scol_ambocc.r, diffuse.a); + float ambocc = scol_ambocc.g; - float scol = max(scol_ambocc.r, diffuse.a); + vec3 color = vec3(0); + float bloom = 0.0; - float ambocc = scol_ambocc.g; + vec3 sunlit; + vec3 amblit; + vec3 additive; + vec3 atten; + calcAtmosphericVars(pos.xyz, light_dir, ambocc, sunlit, amblit, additive, atten, true); - vec3 color = vec3(0); - float bloom = 0.0; + color.rgb = amblit; + + float ambient = min(abs(dot(norm.xyz, sun_dir.xyz)), 1.0); + ambient *= 0.5; + ambient *= ambient; + ambient = (1.0 - ambient); + color.rgb *= ambient; + + vec3 sun_contrib = min(da, scol) * sunlit; + color.rgb += sun_contrib; + color.rgb *= diffuse.rgb; + + vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); + + if (spec.a > 0.0) // specular reflection { - vec3 sunlit; - vec3 amblit; - vec3 additive; - vec3 atten; - - calcAtmosphericVars(pos.xyz, light_dir, ambocc, sunlit, amblit, additive, atten, true); - - color.rgb = amblit; - - float ambient = min(abs(dot(norm.xyz, sun_dir.xyz)), 1.0); - ambient *= 0.5; - ambient *= ambient; - ambient = (1.0 - ambient); - - color.rgb *= ambient; - - vec3 sun_contrib = min(da, scol) * sunlit; - - color.rgb += sun_contrib; - - color.rgb *= diffuse.rgb; - - vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); - - if (spec.a > 0.0) // specular reflection - { - -#if 1 //EEP - vec3 npos = -normalize(pos.xyz); - - //vec3 ref = dot(pos+lv, norm); - vec3 h = normalize(light_dir.xyz+npos); - float nh = dot(norm.xyz, h); - float nv = dot(norm.xyz, npos); - float vh = dot(npos, h); - float sa = nh; - float fres = pow(1 - dot(h, npos), 5)*0.4+0.5; - - float gtdenom = 2 * nh; - float gt = max(0, min(gtdenom * nv / vh, gtdenom * da / vh)); - - if (nh > 0.0) - { - float scontrib = fres*texture2D(lightFunc, vec2(nh, spec.a)).r*gt/(nh*da); - vec3 sp = sun_contrib*scontrib / 6.0; - sp = clamp(sp, vec3(0), vec3(1)); - bloom += dot(sp, sp) / 4.0; - color += sp * spec.rgb; - } -#else //PRODUCTION - float sa = dot(refnormpersp, light_dir.xyz); - vec3 dumbshiny = sunlit*(texture2D(lightFunc, vec2(sa, spec.a)).r); - - // add the two types of shiny together - vec3 spec_contrib = dumbshiny * spec.rgb; - bloom = dot(spec_contrib, spec_contrib) / 6; - color.rgb += spec_contrib; -#endif + float sa = dot(refnormpersp, light_dir.xyz); + vec3 dumbshiny = sunlit * scol * (texture2D(lightFunc, vec2(sa, spec.a)).r); + + // add the two types of shiny together + vec3 spec_contrib = dumbshiny * spec.rgb; + bloom = dot(spec_contrib, spec_contrib) / 6; + color.rgb += spec_contrib; + } + + color.rgb = mix(color.rgb, diffuse.rgb, diffuse.a); - } - - color.rgb = mix(color.rgb, diffuse.rgb, diffuse.a); - - if (envIntensity > 0.0) - { //add environmentmap - vec3 env_vec = env_mat * refnormpersp; - vec3 reflected_color = textureCube(environmentMap, env_vec).rgb; - color = mix(color.rgb, reflected_color, envIntensity); - } - - if (norm.w < 0.5) - { - color = mix(atmosFragLighting(color, additive, atten), fullbrightAtmosTransportFrag(color, additive, atten), diffuse.a); - color = mix(scaleSoftClipFrag(color), fullbrightScaleSoftClip(color), diffuse.a); - } - - #ifdef WATER_FOG - vec4 fogged = applyWaterFogView(pos.xyz,vec4(color, bloom)); - color = fogged.rgb; - bloom = fogged.a; - #endif + if (envIntensity > 0.0) + { // add environmentmap + vec3 env_vec = env_mat * refnormpersp; + vec3 reflected_color = textureCube(environmentMap, env_vec).rgb; + color = mix(color.rgb, reflected_color, envIntensity); + } + if (norm.w < 0.5) + { + color = mix(atmosFragLighting(color, additive, atten), fullbrightAtmosTransportFrag(color, additive, atten), diffuse.a); + color = mix(scaleSoftClipFrag(color), fullbrightScaleSoftClip(color), diffuse.a); } -// linear debuggables -//color.rgb = vec3(final_da); -//color.rgb = vec3(ambient); -//color.rgb = vec3(scol); -//color.rgb = diffuse_srgb.rgb; +#ifdef WATER_FOG + vec4 fogged = applyWaterFogView(pos.xyz, vec4(color, bloom)); + color = fogged.rgb; + bloom = fogged.a; +#endif // convert to linear as fullscreen lights need to sum in linear colorspace // and will be gamma (re)corrected downstream... - frag_color.rgb = srgb_to_linear(color.rgb); - frag_color.a = bloom; + frag_color.a = bloom; } - diff --git a/indra/newview/app_settings/shaders/class2/windlight/cloudsV.glsl b/indra/newview/app_settings/shaders/class2/windlight/cloudsV.glsl index 2c1475d547..1f881eb44b 100644 --- a/indra/newview/app_settings/shaders/class2/windlight/cloudsV.glsl +++ b/indra/newview/app_settings/shaders/class2/windlight/cloudsV.glsl @@ -1,24 +1,24 @@ -/** +/** * @file class2\wl\cloudsV.glsl * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2005, 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$ */ @@ -33,25 +33,26 @@ ATTRIBUTE vec2 texcoord0; /////////////////////////////////////////////////////////////////////////////// // Output parameters -VARYING vec4 vary_CloudColorSun; -VARYING vec4 vary_CloudColorAmbient; +VARYING vec4 vary_CloudColorSun; +VARYING vec4 vary_CloudColorAmbient; VARYING float vary_CloudDensity; -VARYING vec2 vary_texcoord0; -VARYING vec2 vary_texcoord1; -VARYING vec2 vary_texcoord2; -VARYING vec2 vary_texcoord3; + +VARYING vec2 vary_texcoord0; +VARYING vec2 vary_texcoord1; +VARYING vec2 vary_texcoord2; +VARYING vec2 vary_texcoord3; VARYING float altitude_blend_factor; // Inputs uniform vec3 camPosLocal; -uniform vec4 lightnorm; -uniform vec4 sunlight_color; -uniform vec4 moonlight_color; -uniform int sun_up_factor; -uniform vec4 ambient_color; -uniform vec4 blue_horizon; -uniform vec4 blue_density; +uniform vec4 lightnorm; +uniform vec4 sunlight_color; +uniform vec4 moonlight_color; +uniform int sun_up_factor; +uniform vec4 ambient_color; +uniform vec4 blue_horizon; +uniform vec4 blue_density; uniform float haze_horizon; uniform float haze_density; @@ -59,141 +60,133 @@ uniform float cloud_shadow; uniform float density_multiplier; uniform float max_y; -uniform vec4 glow; +uniform vec4 glow; uniform float sun_moon_glow_factor; uniform vec4 cloud_color; uniform float cloud_scale; +// NOTE: Keep these in sync! +// indra\newview\app_settings\shaders\class1\deferred\skyV.glsl +// indra\newview\app_settings\shaders\class1\deferred\cloudsV.glsl +// indra\newview\app-settings\shaders\class2\windlight\cloudsV.glsl +// indra\newview\lllegacyatmospherics.cpp +// indra\newview\llsettingsvo.cpp void main() { + // World / view / projection + gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); + + // Texture coords + // SL-13084 EEP added support for custom cloud textures -- flip them horizontally to match the preview of Clouds > Cloud Scroll + vary_texcoord0 = vec2(-texcoord0.x, texcoord0.y); // See: LLSettingsVOSky::applySpecial - // World / view / projection - gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); + vary_texcoord0.xy -= 0.5; + vary_texcoord0.xy /= cloud_scale; + vary_texcoord0.xy += 0.5; - vary_texcoord0 = texcoord0; + vary_texcoord1 = vary_texcoord0; + vary_texcoord1.x += lightnorm.x * 0.0125; + vary_texcoord1.y += lightnorm.z * 0.0125; - // Get relative position - vec3 P = position.xyz - camPosLocal.xyz + vec3(0,50,0); + vary_texcoord2 = vary_texcoord0 * 16.; + vary_texcoord3 = vary_texcoord1 * 16.; + + // Get relative position + vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0); // fade clouds beyond a certain point so the bottom of the sky dome doesn't look silly at high altitude - altitude_blend_factor = clamp((P.y + 512.0) / max_y, 0.0, 1.0); - - // Set altitude - if (P.y > 0.) - { - P *= (max_y / P.y); - } - else - { - P *= (-32000. / P.y); - } - - // Can normalize then - vec3 Pn = normalize(P); - float Plen = length(P); - - // Initialize temp variables - vec4 temp1 = vec4(0.); - vec4 temp2 = vec4(0.); - vec4 blue_weight; - vec4 haze_weight; - //vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - vec4 sunlight = sunlight_color; - vec4 light_atten; - - float dens_mul = density_multiplier; - - // Sunlight attenuation effect (hue and brightness) due to atmosphere - // this is used later for sunlight modulation at various altitudes - light_atten = (blue_density + vec4(haze_density * 0.25)) * (dens_mul * max_y); - - // Calculate relative weights - temp1 = abs(blue_density) + vec4(abs(haze_density)); - blue_weight = blue_density / temp1; - haze_weight = haze_density / temp1; - - // Compute sunlight from P & lightnorm (for long rays like sky) - temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y ); - temp2.y = 1. / temp2.y; - sunlight *= exp( - light_atten * temp2.y); - - // Distance - temp2.z = Plen * dens_mul; - - // Transparency (-> temp1) - // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati - // compiler gets confused. - temp1 = exp(-temp1 * temp2.z); - - - // Compute haze glow - temp2.x = dot(Pn, lightnorm.xyz); - temp2.x = 1. - temp2.x; - // temp2.x is 0 at the sun and increases away from sun - temp2.x = max(temp2.x, .001); - // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) - temp2.x *= glow.x; - // Higher glow.x gives dimmer glow (because next step is 1 / "angle") - temp2.x = pow(temp2.x, glow.z); - // glow.z should be negative, so we're doing a sort of (1 / "angle") function - - temp2.x *= sun_moon_glow_factor; - - // Add "minimum anti-solar illumination" - temp2.x += .25; - - // Increase ambient when there are more clouds - vec4 tmpAmbient = ambient_color; - tmpAmbient += (1. - tmpAmbient) * cloud_shadow * 0.5; - - // Dim sunlight by cloud shadow percentage - sunlight *= (1. - cloud_shadow); - - // Haze color below cloud - vec4 additiveColorBelowCloud = ( blue_horizon * blue_weight * (sunlight + tmpAmbient) - + (haze_horizon * haze_weight) * (sunlight * temp2.x + tmpAmbient) - ); - - // CLOUDS - temp2.y = max(0., lightnorm.y * 2.); - temp2.y = 1. / temp2.y; - sunlight *= exp( - light_atten * temp2.y); - - // Cloud color out - vary_CloudColorSun = (sunlight * temp2.x) * cloud_color; - vary_CloudColorAmbient = tmpAmbient * cloud_color; - - // Attenuate cloud color by atmosphere - temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds - vary_CloudColorSun *= temp1; - vary_CloudColorAmbient *= temp1; - vec4 oHazeColorBelowCloud = additiveColorBelowCloud * (1. - temp1); - - // Make a nice cloud density based on the cloud_shadow value that was passed in. - vary_CloudDensity = 2. * (cloud_shadow - 0.25); - - - // Texture coords - vary_texcoord0 = texcoord0; - vary_texcoord0.xy -= 0.5; - vary_texcoord0.xy /= cloud_scale; - vary_texcoord0.xy += 0.5; - - vary_texcoord1 = vary_texcoord0; - vary_texcoord1.x += lightnorm.x * 0.0125; - vary_texcoord1.y += lightnorm.z * 0.0125; - - vary_texcoord2 = vary_texcoord0 * 16.; - vary_texcoord3 = vary_texcoord1 * 16.; - - // Combine these to minimize register use - vary_CloudColorAmbient += oHazeColorBelowCloud; - - // needs this to compile on mac - //vary_AtmosAttenuation = vec3(0.0,0.0,0.0); - - // END CLOUDS + altitude_blend_factor = clamp((rel_pos.y + 512.0) / max_y, 0.0, 1.0); + + // Adj position vector to clamp altitude + if (rel_pos.y > 0.) + { + rel_pos *= (max_y / rel_pos.y); + } + if (rel_pos.y < 0.) + { + rel_pos *= (-32000. / rel_pos.y); + } + + // Can normalize then + vec3 rel_pos_norm = normalize(rel_pos); + float rel_pos_len = length(rel_pos); + + // Initialize temp variables + vec4 sunlight = sunlight_color; + vec4 light_atten; + + // Sunlight attenuation effect (hue and brightness) due to atmosphere + // this is used later for sunlight modulation at various altitudes + light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); + + // Calculate relative weights + vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density)); + vec4 blue_weight = blue_density / combined_haze; + vec4 haze_weight = haze_density / combined_haze; + + // Compute sunlight from rel_pos & lightnorm (for long rays like sky) + float off_axis = 1.0 / max(1e-6, max(0., rel_pos_norm.y) + lightnorm.y); + sunlight *= exp(-light_atten * off_axis); + + // Distance + float density_dist = rel_pos_len * density_multiplier; + + // Transparency (-> combined_haze) + // ATI Bugfix -- can't store combined_haze*density_dist in a variable because the ati + // compiler gets confused. + combined_haze = exp(-combined_haze * density_dist); + + // Compute haze glow + float haze_glow = 1.0 - dot(rel_pos_norm, lightnorm.xyz); + // haze_glow is 0 at the sun and increases away from sun + haze_glow = max(haze_glow, .001); + // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) + haze_glow *= glow.x; + // Higher glow.x gives dimmer glow (because next step is 1 / "angle") + haze_glow = pow(haze_glow, glow.z); + // glow.z should be negative, so we're doing a sort of (1 / "angle") function + + haze_glow *= sun_moon_glow_factor; + + // Add "minimum anti-solar illumination" + // For sun, add to glow. For moon, remove glow entirely. SL-13768 + haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (haze_glow + 0.25); + + // Increase ambient when there are more clouds + vec4 tmpAmbient = ambient_color; + tmpAmbient += (1. - tmpAmbient) * cloud_shadow * 0.5; + + // Dim sunlight by cloud shadow percentage + sunlight *= (1. - cloud_shadow); + + // Haze color below cloud + vec4 additiveColorBelowCloud = + (blue_horizon * blue_weight * (sunlight + tmpAmbient) + (haze_horizon * haze_weight) * (sunlight * haze_glow + tmpAmbient)); + + // CLOUDS + off_axis = 1.0 / max(1e-6, lightnorm.y * 2.); + sunlight *= exp(-light_atten * off_axis); + + // Cloud color out + vary_CloudColorSun = (sunlight * haze_glow) * cloud_color; + vary_CloudColorAmbient = tmpAmbient * cloud_color; + + // Attenuate cloud color by atmosphere + combined_haze = sqrt(combined_haze); // less atmos opacity (more transparency) below clouds + vary_CloudColorSun *= combined_haze; + vary_CloudColorAmbient *= combined_haze; + vec4 oHazeColorBelowCloud = additiveColorBelowCloud * (1. - combined_haze); + + // Make a nice cloud density based on the cloud_shadow value that was passed in. + vary_CloudDensity = 2. * (cloud_shadow - 0.25); + + // Combine these to minimize register use + vary_CloudColorAmbient += oHazeColorBelowCloud; + + // needs this to compile on mac + // vary_AtmosAttenuation = vec3(0.0,0.0,0.0); + + // END CLOUDS } - diff --git a/indra/newview/app_settings/shaders/class2/windlight/skyV.glsl b/indra/newview/app_settings/shaders/class2/windlight/skyV.glsl index 0d141342ce..a0a33b8642 100644 --- a/indra/newview/app_settings/shaders/class2/windlight/skyV.glsl +++ b/indra/newview/app_settings/shaders/class2/windlight/skyV.glsl @@ -1,28 +1,28 @@ -/** +/** * @file class2\wl\skyV.glsl * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2005, 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$ */ - + uniform mat4 modelview_projection_matrix; ATTRIBUTE vec3 position; @@ -37,13 +37,13 @@ VARYING vec4 vary_HazeColor; // Inputs uniform vec3 camPosLocal; -uniform vec4 lightnorm; -uniform vec4 sunlight_color; -uniform vec4 moonlight_color; -uniform int sun_up_factor; -uniform vec4 ambient_color; -uniform vec4 blue_horizon; -uniform vec4 blue_density; +uniform vec4 lightnorm; +uniform vec4 sunlight_color; +uniform vec4 moonlight_color; +uniform int sun_up_factor; +uniform vec4 ambient_color; +uniform vec4 blue_horizon; +uniform vec4 blue_density; uniform float haze_horizon; uniform float haze_density; @@ -52,110 +52,98 @@ uniform float density_multiplier; uniform float distance_multiplier; uniform float max_y; -uniform vec4 glow; +uniform vec4 glow; uniform float sun_moon_glow_factor; uniform vec4 cloud_color; void main() { - - // World / view / projection + // World / view / projection vec4 pos = modelview_projection_matrix * vec4(position.xyz, 1.0); - gl_Position = pos; - - // Get relative position - vec3 P = position.xyz - camPosLocal.xyz + vec3(0,50,0); - - // Set altitude - if (P.y > 0.) - { - P *= (max_y / P.y); - } - else - { - P *= (-32000. / P.y); - } - - // Can normalize then - vec3 Pn = normalize(P); - - float Plen = length(P); - - // Initialize temp variables - vec4 temp1 = vec4(0.); - vec4 temp2 = vec4(0.); - vec4 blue_weight; - vec4 haze_weight; - vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; - vec4 light_atten; - - float dens_mul = density_multiplier; - - // Sunlight attenuation effect (hue and brightness) due to atmosphere - // this is used later for sunlight modulation at various altitudes - light_atten = (blue_density + vec4(haze_density * 0.25)) * (dens_mul * max_y); - - // Calculate relative weights - temp1 = abs(blue_density) + vec4(abs(haze_density)); - blue_weight = blue_density / temp1; - haze_weight = haze_density / temp1; - - // Compute sunlight from P & lightnorm (for long rays like sky) - temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y ); - temp2.y = 1. / temp2.y; - sunlight *= exp( - light_atten * temp2.y); - - // Distance - temp2.z = Plen * dens_mul; - - // Transparency (-> temp1) - // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati - // compiler gets confused. - temp1 = exp(-temp1 * temp2.z); + gl_Position = pos; + + // Get relative position + vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0); + + // Adj position vector to clamp altitude + if (rel_pos.y > 0.) + { + rel_pos *= (max_y / rel_pos.y); + } + if (rel_pos.y < 0.) + { + rel_pos *= (-32000. / rel_pos.y); + } - // Compute haze glow - temp2.x = dot(Pn, lightnorm.xyz); - temp2.x = 1. - temp2.x; - // temp2.x is 0 at the sun and increases away from sun - temp2.x = max(temp2.x, .001); - // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) - temp2.x *= glow.x; - // Higher glow.x gives dimmer glow (because next step is 1 / "angle") - temp2.x = pow(temp2.x, glow.z); - // glow.z should be negative, so we're doing a sort of (1 / "angle") function + // Can normalize then + vec3 rel_pos_norm = normalize(rel_pos); - // Add "minimum anti-solar illumination" - temp2.x += .25; + float rel_pos_len = length(rel_pos); - vec4 color = ( blue_horizon * blue_weight * (sunlight + ambient_color) - + (haze_horizon * haze_weight) * (sunlight * temp2.x + ambient_color) - ); + // Initialize temp variables + vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color; + vec4 light_atten; + // Sunlight attenuation effect (hue and brightness) due to atmosphere + // this is used later for sunlight modulation at various altitudes + light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y); + + // Calculate relative weights + vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density)); + vec4 blue_weight = blue_density / combined_haze; + vec4 haze_weight = haze_density / combined_haze; + + // Compute sunlight from rel_pos & lightnorm (for long rays like sky) + float off_axis = 1.0 / max(1e-6, max(0., rel_pos_norm.y) + lightnorm.y); + sunlight *= exp(-light_atten * off_axis); + + // Distance + float density_dist = rel_pos_len * density_multiplier; + + // Transparency (-> combined_haze) + // ATI Bugfix -- can't store combined_haze*density_dist in a variable because the ati + // compiler gets confused. + combined_haze = exp(-combined_haze * density_dist); + + // Compute haze glow + float haze_glow = 1.0 - dot(rel_pos_norm, lightnorm.xyz); + // haze_glow is 0 at the sun and increases away from sun + haze_glow = max(haze_glow, .001); + // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) + haze_glow *= glow.x; + // Higher glow.x gives dimmer glow (because next step is 1 / "angle") + haze_glow = pow(haze_glow, glow.z); + // glow.z should be negative, so we're doing a sort of (1 / "angle") function + + // Add "minimum anti-solar illumination" + // For sun, add to glow. For moon, remove glow entirely. SL-13768 + haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (haze_glow + 0.25); + + vec4 color = + (blue_horizon * blue_weight * (sunlight + ambient_color) + (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient_color)); // Final atmosphere additive - color *= (1. - temp1); + color *= (1. - combined_haze); - // Increase ambient when there are more clouds - vec4 tmpAmbient = ambient_color; - tmpAmbient += max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; + // Increase ambient when there are more clouds + vec4 tmpAmbient = ambient_color; + tmpAmbient += max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5; - // Dim sunlight by cloud shadow percentage - sunlight *= max(0.0, (1. - cloud_shadow)); + // Dim sunlight by cloud shadow percentage + sunlight *= max(0.0, (1. - cloud_shadow)); - // Haze color below cloud - vec4 additiveColorBelowCloud = ( blue_horizon * blue_weight * (sunlight + tmpAmbient) - + (haze_horizon * haze_weight) * (sunlight * temp2.x + tmpAmbient) - ); + // Haze color below cloud + vec4 additiveColorBelowCloud = + (blue_horizon * blue_weight * (sunlight + tmpAmbient) + (haze_horizon * haze_weight) * (sunlight * haze_glow + tmpAmbient)); - // Attenuate cloud color by atmosphere - temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds + // Attenuate cloud color by atmosphere + combined_haze = sqrt(combined_haze); // less atmos opacity (more transparency) below clouds - // At horizon, blend high altitude sky color towards the darker color below the clouds - color += (additiveColorBelowCloud - color) * (1. - sqrt(temp1)); + // At horizon, blend high altitude sky color towards the darker color below the clouds + color += (additiveColorBelowCloud - color) * (1. - sqrt(combined_haze)); // Haze color above cloud - vary_HazeColor = color; + vary_HazeColor = color; } - diff --git a/indra/newview/icons/release/secondlife.icns b/indra/newview/icons/release/secondlife.icns Binary files differindex e15e34140d..a30b51b67a 100644 --- a/indra/newview/icons/release/secondlife.icns +++ b/indra/newview/icons/release/secondlife.icns diff --git a/indra/newview/icons/release/secondlife.ico b/indra/newview/icons/release/secondlife.ico Binary files differindex 28bf1e7664..93d4fa54ba 100644 --- a/indra/newview/icons/release/secondlife.ico +++ b/indra/newview/icons/release/secondlife.ico diff --git a/indra/newview/icons/release/secondlife_128.png b/indra/newview/icons/release/secondlife_128.png Binary files differindex 4c9544f498..2f21c1c7fc 100644 --- a/indra/newview/icons/release/secondlife_128.png +++ b/indra/newview/icons/release/secondlife_128.png diff --git a/indra/newview/icons/release/secondlife_16.png b/indra/newview/icons/release/secondlife_16.png Binary files differindex bb3168b8be..68f1427309 100644 --- a/indra/newview/icons/release/secondlife_16.png +++ b/indra/newview/icons/release/secondlife_16.png diff --git a/indra/newview/icons/release/secondlife_256.BMP b/indra/newview/icons/release/secondlife_256.BMP Binary files differindex 74deedd7d3..dba2636803 100644 --- a/indra/newview/icons/release/secondlife_256.BMP +++ b/indra/newview/icons/release/secondlife_256.BMP diff --git a/indra/newview/icons/release/secondlife_256.png b/indra/newview/icons/release/secondlife_256.png Binary files differindex bece338a90..8f324910e7 100644 --- a/indra/newview/icons/release/secondlife_256.png +++ b/indra/newview/icons/release/secondlife_256.png diff --git a/indra/newview/icons/release/secondlife_32.png b/indra/newview/icons/release/secondlife_32.png Binary files differindex 736359c147..2b7cdef03d 100644 --- a/indra/newview/icons/release/secondlife_32.png +++ b/indra/newview/icons/release/secondlife_32.png diff --git a/indra/newview/icons/release/secondlife_48.png b/indra/newview/icons/release/secondlife_48.png Binary files differindex 07d39ae585..c2ef372dd7 100644 --- a/indra/newview/icons/release/secondlife_48.png +++ b/indra/newview/icons/release/secondlife_48.png diff --git a/indra/newview/icons/release/secondlife_512.png b/indra/newview/icons/release/secondlife_512.png Binary files differindex 53d1643f45..d8a0c2924d 100644 --- a/indra/newview/icons/release/secondlife_512.png +++ b/indra/newview/icons/release/secondlife_512.png diff --git a/indra/newview/installers/windows/install_icon.BMP b/indra/newview/installers/windows/install_icon.BMP Binary files differindex 09df573870..dba2636803 100644 --- a/indra/newview/installers/windows/install_icon.BMP +++ b/indra/newview/installers/windows/install_icon.BMP diff --git a/indra/newview/installers/windows/install_icon.ico b/indra/newview/installers/windows/install_icon.ico Binary files differindex efe6c4f323..93d4fa54ba 100644 --- a/indra/newview/installers/windows/install_icon.ico +++ b/indra/newview/installers/windows/install_icon.ico diff --git a/indra/newview/installers/windows/uninstall_icon.BMP b/indra/newview/installers/windows/uninstall_icon.BMP Binary files differindex 562b56676a..dba2636803 100644 --- a/indra/newview/installers/windows/uninstall_icon.BMP +++ b/indra/newview/installers/windows/uninstall_icon.BMP diff --git a/indra/newview/installers/windows/uninstall_icon.ico b/indra/newview/installers/windows/uninstall_icon.ico Binary files differindex 05e1546860..93d4fa54ba 100644 --- a/indra/newview/installers/windows/uninstall_icon.ico +++ b/indra/newview/installers/windows/uninstall_icon.ico diff --git a/indra/newview/llaccountingcostmanager.h b/indra/newview/llaccountingcostmanager.h index 55e1d19f05..d133c6437b 100644 --- a/indra/newview/llaccountingcostmanager.h +++ b/indra/newview/llaccountingcostmanager.h @@ -30,12 +30,6 @@ #include "llhandle.h" #include "llaccountingcost.h" -#include "httpcommon.h" -#include "llcoros.h" -#include "lleventcoro.h" -#include "httprequest.h" -#include "httpheaders.h" -#include "httpoptions.h" //=============================================================================== // An interface class for panels which display the parcel accounting information. diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index f3df79fb6b..c65bc0fa50 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -137,6 +137,10 @@ public: EStatus getStatus() const {return mStatus;}; void setStatus(EStatus pStatus) {mStatus = pStatus;}; + static std::map<S32, std::string> sTeleportStatusName; + static const std::string& statusName(EStatus status); + virtual void toOstream(std::ostream& os) const; + virtual bool canRestartTeleport(); virtual void startTeleport() = 0; @@ -148,12 +152,19 @@ private: EStatus mStatus; }; +std::map<S32, std::string> LLTeleportRequest::sTeleportStatusName = { { kPending, "kPending" }, + { kStarted, "kStarted" }, + { kFailed, "kFailed" }, + { kRestartPending, "kRestartPending"} }; + class LLTeleportRequestViaLandmark : public LLTeleportRequest { public: LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId); virtual ~LLTeleportRequestViaLandmark(); + virtual void toOstream(std::ostream& os) const; + virtual bool canRestartTeleport(); virtual void startTeleport(); @@ -172,6 +183,8 @@ public: LLTeleportRequestViaLure(const LLUUID &pLureId, BOOL pIsLureGodLike); virtual ~LLTeleportRequestViaLure(); + virtual void toOstream(std::ostream& os) const; + virtual bool canRestartTeleport(); virtual void startTeleport(); @@ -189,6 +202,8 @@ public: LLTeleportRequestViaLocation(const LLVector3d &pPosGlobal); virtual ~LLTeleportRequestViaLocation(); + virtual void toOstream(std::ostream& os) const; + virtual bool canRestartTeleport(); virtual void startTeleport(); @@ -208,6 +223,8 @@ public: LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal); virtual ~LLTeleportRequestViaLocationLookAt(); + virtual void toOstream(std::ostream& os) const; + virtual bool canRestartTeleport(); virtual void startTeleport(); @@ -492,6 +509,8 @@ void LLAgent::init() void LLAgent::cleanup() { mRegionp = NULL; + mTeleportRequest = NULL; + mTeleportCanceled = NULL; if (mTeleportFinishedSlot.connected()) { mTeleportFinishedSlot.disconnect(); @@ -858,6 +877,18 @@ boost::signals2::connection LLAgent::addParcelChangedCallback(parcel_changed_cal return mParcelChangedSignal.connect(cb); } +// static +void LLAgent::capabilityReceivedCallback(const LLUUID ®ion_id) +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region && region->getRegionID() == region_id) + { + region->requestSimulatorFeatures(); + LLAppViewer::instance()->updateNameLookupUrl(); + } +} + + //----------------------------------------------------------------------------- // setRegion() //----------------------------------------------------------------------------- @@ -867,9 +898,12 @@ void LLAgent::setRegion(LLViewerRegion *regionp) if (mRegionp != regionp) { - std::string ip = regionp->getHost().getString(); - LL_INFOS("AgentLocation") << "Moving agent into region: " << regionp->getName() - << " located at " << ip << LL_ENDL; + LL_INFOS("AgentLocation","Teleport") << "Moving agent into region: handle " << regionp->getHandle() + << " id " << regionp->getRegionID() + << " name " << regionp->getName() + << " previous region " + << (mRegionp ? mRegionp->getRegionID() : LLUUID::null) + << LL_ENDL; if (mRegionp) { // We've changed regions, we're now going to change our agent coordinate frame. @@ -901,10 +935,11 @@ void LLAgent::setRegion(LLViewerRegion *regionp) if (regionp->capabilitiesReceived()) { regionp->requestSimulatorFeatures(); + LLAppViewer::instance()->updateNameLookupUrl(); } else { - regionp->setCapabilitiesReceivedCallback(boost::bind(&LLViewerRegion::requestSimulatorFeatures, regionp)); + regionp->setCapabilitiesReceivedCallback(LLAgent::capabilityReceivedCallback); } } @@ -923,6 +958,15 @@ void LLAgent::setRegion(LLViewerRegion *regionp) // Update all of the regions. LLWorld::getInstance()->updateAgentOffset(mAgentOriginGlobal); + + if (regionp->capabilitiesReceived()) + { + LLAppViewer::instance()->updateNameLookupUrl(); + } + else + { + regionp->setCapabilitiesReceivedCallback([](const LLUUID ®ion_id) {LLAppViewer::instance()->updateNameLookupUrl(); }); + } } // Pass new region along to metrics components that care about this level of detail. @@ -2631,6 +2675,7 @@ void LLAgent::handlePreferredMaturityResult(U8 pServerMaturity) else { mMaturityPreferenceNumRetries = 0; + LL_WARNS() << "Too many retries for maturity preference" << LL_ENDL; reportPreferredMaturityError(); } } @@ -2682,6 +2727,7 @@ void LLAgent::reportPreferredMaturityError() mIsMaturityRatingChangingDuringTeleport = false; if (hasPendingTeleportRequest()) { + LL_WARNS("Teleport") << "Teleport failing due to preferred maturity error" << LL_ENDL; setTeleportState(LLAgent::TELEPORT_NONE); } @@ -3777,7 +3823,7 @@ void LLAgent::clearVisualParams(void *data) // protected bool LLAgent::teleportCore(bool is_local) { - LL_INFOS("Teleport") << "In teleport core!" << LL_ENDL; + LL_DEBUGS("Teleport") << "In teleport core" << LL_ENDL; if ((TELEPORT_NONE != mTeleportState) && (mTeleportState != TELEPORT_PENDING)) { LL_WARNS() << "Attempt to teleport when already teleporting." << LL_ENDL; @@ -3843,11 +3889,13 @@ bool LLAgent::teleportCore(bool is_local) add(LLStatViewer::TELEPORT, 1); if (is_local) { + LL_INFOS("Teleport") << "Setting teleport state to TELEPORT_LOCAL" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_LOCAL ); } else { gTeleportDisplay = TRUE; + LL_INFOS("Teleport") << "Non-local, setting teleport state to TELEPORT_START" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_START ); //release geometry from old location @@ -3914,6 +3962,7 @@ void LLAgent::startTeleportRequest() if (!isMaturityPreferenceSyncedWithServer()) { gTeleportDisplay = TRUE; + LL_INFOS("Teleport") << "Maturity preference not synced yet, setting teleport state to TELEPORT_PENDING" << LL_ENDL; setTeleportState(TELEPORT_PENDING); } else @@ -3957,10 +4006,19 @@ void LLAgent::handleTeleportFinished() { if (mRegionp->capabilitiesReceived()) { + LL_DEBUGS("Teleport") << "capabilities have been received for region handle " + << mRegionp->getHandle() + << " id " << mRegionp->getRegionID() + << ", calling onCapabilitiesReceivedAfterTeleport()" + << LL_ENDL; onCapabilitiesReceivedAfterTeleport(); } else { + LL_DEBUGS("Teleport") << "Capabilities not yet received for region handle " + << mRegionp->getHandle() + << " id " << mRegionp->getRegionID() + << LL_ENDL; mRegionp->setCapabilitiesReceivedCallback(boost::bind(&LLAgent::onCapabilitiesReceivedAfterTeleport)); } } @@ -3998,6 +4056,18 @@ void LLAgent::handleTeleportFailed() /*static*/ void LLAgent::onCapabilitiesReceivedAfterTeleport() { + if (gAgent.getRegion()) + { + LL_DEBUGS("Teleport") << "running after capabilities received callback has been triggered, agent region " + << gAgent.getRegion()->getHandle() + << " id " << gAgent.getRegion()->getRegionID() + << " name " << gAgent.getRegion()->getName() + << LL_ENDL; + } + else + { + LL_WARNS("Teleport") << "called when agent region is null!" << LL_ENDL; + } check_merchant_status(); } @@ -4011,8 +4081,8 @@ void LLAgent::teleportRequest( LLViewerRegion* regionp = getRegion(); if (regionp && teleportCore(region_handle == regionp->getHandle())) { - LL_INFOS("") << "TeleportLocationRequest: '" << region_handle << "':" - << pos_local << LL_ENDL; + LL_INFOS("Teleport") << "Sending TeleportLocationRequest: '" << region_handle << "':" + << pos_local << LL_ENDL; LLMessageSystem* msg = gMessageSystem; msg->newMessage("TeleportLocationRequest"); msg->nextBlockFast(_PREHASH_AgentData); @@ -4043,6 +4113,11 @@ void LLAgent::doTeleportViaLandmark(const LLUUID& landmark_asset_id) LLViewerRegion *regionp = getRegion(); if(regionp && teleportCore()) { + LL_INFOS("Teleport") << "Sending TeleportLandmarkRequest. Current region handle " << regionp->getHandle() + << " region id " << regionp->getRegionID() + << " requested landmark id " << landmark_asset_id + << LL_ENDL; + LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_TeleportLandmarkRequest); msg->nextBlockFast(_PREHASH_Info); @@ -4075,6 +4150,11 @@ void LLAgent::doTeleportViaLure(const LLUUID& lure_id, BOOL godlike) teleport_flags |= TELEPORT_FLAGS_VIA_LURE; } + LL_INFOS("Teleport") << "Sending TeleportLureRequest." + << " Current region handle " << regionp->getHandle() + << " region id " << regionp->getRegionID() + << " lure id " << lure_id + << LL_ENDL; // send the message LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_TeleportLureRequest); @@ -4097,6 +4177,8 @@ void LLAgent::teleportCancel() LLViewerRegion* regionp = getRegion(); if(regionp) { + LL_INFOS("Teleport") << "Sending TeleportCancel" << LL_ENDL; + // send the message LLMessageSystem* msg = gMessageSystem; msg->newMessage("TeleportCancel"); @@ -4109,13 +4191,14 @@ void LLAgent::teleportCancel() } clearTeleportRequest(); gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); - gPipeline.resetVertexBuffers(); + gPipeline.resetVertexBuffers(); } void LLAgent::restoreCanceledTeleportRequest() { if (mTeleportCanceled != NULL) { + LL_INFOS() << "Restoring canceled teleport request, setting state to TELEPORT_REQUESTED" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_REQUESTED ); mTeleportRequest = mTeleportCanceled; mTeleportCanceled.reset(); @@ -4153,7 +4236,6 @@ void LLAgent::doTeleportViaLocation(const LLVector3d& pos_global) else if(regionp && teleportCore(regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY]))) { - LL_WARNS() << "Using deprecated teleportlocationrequest." << LL_ENDL; // send the message LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_TeleportLocationRequest); @@ -4173,6 +4255,14 @@ void LLAgent::doTeleportViaLocation(const LLVector3d& pos_global) msg->addVector3Fast(_PREHASH_Position, pos); pos.mV[VX] += 1; msg->addVector3Fast(_PREHASH_LookAt, pos); + + LL_WARNS("Teleport") << "Sending deprecated(?) TeleportLocationRequest." + << " pos_global " << pos_global + << " region_x " << region_x + << " region_y " << region_y + << " region_handle " << region_handle + << LL_ENDL; + sendReliableMessage(); } } @@ -4214,7 +4304,11 @@ void LLAgent::setTeleportState(ETeleportState state) " for previously failed teleport. Ignore!" << LL_ENDL; return; } - LL_DEBUGS("Teleport") << "Setting teleport state to " << state << " Previous state: " << mTeleportState << LL_ENDL; + LL_DEBUGS("Teleport") << "Setting teleport state to " + << LLAgent::teleportStateName(state) << "(" << state << ")" + << " Previous state: " + << teleportStateName(mTeleportState) << "(" << mTeleportState << ")" + << LL_ENDL; mTeleportState = state; if (mTeleportState > TELEPORT_NONE && gSavedSettings.getBOOL("FreezeTime")) { @@ -4551,6 +4645,34 @@ void LLAgent::observeFriends() } } +std::map<S32, std::string> LLAgent::sTeleportStateName = { { TELEPORT_NONE, "TELEPORT_NONE" }, + { TELEPORT_START, "TELEPORT_START" }, + { TELEPORT_REQUESTED, "TELEPORT_REQUESTED" }, + { TELEPORT_MOVING, "TELEPORT_MOVING" }, + { TELEPORT_START_ARRIVAL, "TELEPORT_START_ARRIVAL" }, + { TELEPORT_ARRIVING, "TELEPORT_ARRIVING" }, + { TELEPORT_LOCAL, "TELEPORT_LOCAL" }, + { TELEPORT_PENDING, "TELEPORT_PENDING" } }; + +const std::string& LLAgent::teleportStateName(S32 state) +{ + static std::string invalid_state_str("INVALID"); + auto iter = LLAgent::sTeleportStateName.find(state); + if (iter != LLAgent::sTeleportStateName.end()) + { + return iter->second; + } + else + { + return invalid_state_str; + } +} + +const std::string& LLAgent::getTeleportStateName() const +{ + return teleportStateName(getTeleportState()); +} + void LLAgent::parseTeleportMessages(const std::string& xml_filename) { LLXMLNodePtr root; @@ -4674,40 +4796,70 @@ void LLTeleportRequest::restartTeleport() llassert(0); } +// TODO this enum -> name idiom should be in a common class rather than repeated various places. +const std::string& LLTeleportRequest::statusName(EStatus status) +{ + static std::string invalid_status_str("INVALID"); + auto iter = LLTeleportRequest::sTeleportStatusName.find(status); + if (iter != LLTeleportRequest::sTeleportStatusName.end()) + { + return iter->second; + } + else + { + return invalid_status_str; + } +} + +std::ostream& operator<<(std::ostream& os, const LLTeleportRequest& req) +{ + req.toOstream(os); + return os; +} + +void LLTeleportRequest::toOstream(std::ostream& os) const +{ + os << "status " << statusName(mStatus) << "(" << mStatus << ")"; +} + //----------------------------------------------------------------------------- // LLTeleportRequestViaLandmark //----------------------------------------------------------------------------- - LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId) : LLTeleportRequest(), mLandmarkId(pLandmarkId) { - LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created." << LL_ENDL; + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL; } LLTeleportRequestViaLandmark::~LLTeleportRequestViaLandmark() { - LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark" << LL_ENDL; + LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL; +} + +void LLTeleportRequestViaLandmark::toOstream(std::ostream& os) const +{ + os << "landmark " << mLandmarkId << " "; + LLTeleportRequest::toOstream(os); } bool LLTeleportRequestViaLandmark::canRestartTeleport() { - LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::canRestartTeleport? -> true" << LL_ENDL; + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::canRestartTeleport? -> true, " << *this << LL_ENDL; return true; } void LLTeleportRequestViaLandmark::startTeleport() { - LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::startTeleport" << LL_ENDL; + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::startTeleport, " << *this << LL_ENDL; gAgent.doTeleportViaLandmark(getLandmarkId()); } void LLTeleportRequestViaLandmark::restartTeleport() { - LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::restartTeleport" << LL_ENDL; + LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark::restartTeleport, " << *this << LL_ENDL; gAgent.doTeleportViaLandmark(getLandmarkId()); } - //----------------------------------------------------------------------------- // LLTeleportRequestViaLure //----------------------------------------------------------------------------- @@ -4724,6 +4876,12 @@ LLTeleportRequestViaLure::~LLTeleportRequestViaLure() LL_INFOS("Teleport") << "~LLTeleportRequestViaLure" << LL_ENDL; } +void LLTeleportRequestViaLure::toOstream(std::ostream& os) const +{ + os << "mIsLureGodLike " << (S32) mIsLureGodLike << " "; + LLTeleportRequestViaLandmark::toOstream(os); +} + bool LLTeleportRequestViaLure::canRestartTeleport() { // stinson 05/17/2012 : cannot restart a teleport via lure because of server-side restrictions @@ -4764,6 +4922,12 @@ LLTeleportRequestViaLocation::~LLTeleportRequestViaLocation() LL_INFOS("Teleport") << "~LLTeleportRequestViaLocation" << LL_ENDL; } +void LLTeleportRequestViaLocation::toOstream(std::ostream& os) const +{ + os << "mPosGlobal " << mPosGlobal << " "; + LLTeleportRequest::toOstream(os); +} + bool LLTeleportRequestViaLocation::canRestartTeleport() { LL_INFOS("Teleport") << "LLTeleportRequestViaLocation::canRestartTeleport -> true" << LL_ENDL; @@ -4797,6 +4961,11 @@ LLTeleportRequestViaLocationLookAt::~LLTeleportRequestViaLocationLookAt() LL_INFOS("Teleport") << "~LLTeleportRequestViaLocationLookAt" << LL_ENDL; } +void LLTeleportRequestViaLocationLookAt::toOstream(std::ostream& os) const +{ + LLTeleportRequestViaLocation::toOstream(os); +} + bool LLTeleportRequestViaLocationLookAt::canRestartTeleport() { LL_INFOS("Teleport") << "LLTeleportRequestViaLocationLookAt::canRestartTeleport -> true" << LL_ENDL; diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 88cce0b911..d46c99db8c 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -47,20 +47,15 @@ extern const BOOL ANIMATE; extern const U8 AGENT_STATE_TYPING; // Typing indication extern const U8 AGENT_STATE_EDITING; // Set when agent has objects selected -class LLChat; class LLViewerRegion; class LLMotion; -class LLToolset; class LLMessageSystem; class LLPermissions; class LLHost; class LLFriendObserver; -class LLPickInfo; -class LLViewerObject; class LLAgentDropGroupViewerNode; class LLAgentAccess; class LLSLURL; -class LLPauseRequestHandle; class LLUIColor; class LLTeleportRequest; @@ -91,8 +86,6 @@ struct LLGroupData class LLAgentListener; -class LLAgentImpl; - //------------------------------------------------------------------------ // LLAgent //------------------------------------------------------------------------ @@ -261,6 +254,8 @@ public: boost::signals2::connection addParcelChangedCallback(parcel_changed_callback_t); private: + static void capabilityReceivedCallback(const LLUUID ®ion_id); + typedef boost::signals2::signal<void()> parcel_changed_signal_t; parcel_changed_signal_t mParcelChangedSignal; @@ -619,6 +614,10 @@ public: TELEPORT_PENDING = 7 }; + static std::map<S32, std::string> sTeleportStateName; + static const std::string& teleportStateName(S32); + const std::string& getTeleportStateName() const; + public: static void parseTeleportMessages(const std::string& xml_filename); const void getTeleportSourceSLURL(LLSLURL& slurl) const; diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 9e65409256..ed6c3c307f 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -354,6 +354,18 @@ void LLAgentCamera::resetView(BOOL reset_camera, BOOL change_camera) resetPanDiff(); resetOrbitDiff(); mHUDTargetZoom = 1.f; + + if (LLSelectMgr::getInstance()->mAllowSelectAvatar) + { + // resetting camera also resets position overrides in debug mode 'AllowSelectAvatar' + LLObjectSelectionHandle selected_handle = LLSelectMgr::getInstance()->getSelection(); + if (selected_handle->getObjectCount() == 1 + && selected_handle->getFirstObject() != NULL + && selected_handle->getFirstObject()->isAvatar()) + { + LLSelectMgr::getInstance()->resetObjectOverrides(selected_handle); + } + } } // Allow camera to be moved somewhere other than behind avatar. @@ -2690,10 +2702,19 @@ void LLAgentCamera::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate, BOOL re LLVector3 at_axis; if (!isAgentAvatarValid() || !gAgentAvatarp->getParent()) { - at_axis = LLViewerCamera::getInstance()->getAtAxis(); - at_axis.mV[VZ] = 0.f; - at_axis.normalize(); - gAgent.resetAxes(at_axis); + // In case of front view rotate agent to look into direction opposite to camera + // In case of rear view rotate agent into diraction same as camera, e t c + LLVector3 vect = getCameraOffsetInitial(); + F32 rotxy = F32(atan2(vect.mV[VY], vect.mV[VX])); + + LLCoordFrame frameCamera = *((LLCoordFrame*)LLViewerCamera::getInstance()); + // front view angle rotxy is zero, rear view rotxy angle is 180, compensate + frameCamera.yaw((180 * DEG_TO_RAD) - rotxy); + at_axis = frameCamera.getAtAxis(); + at_axis.mV[VZ] = 0.f; + at_axis.normalize(); + gAgent.resetAxes(at_axis); + gAgent.yaw(0); } } } diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 2411f0f86d..7f18ea6fe2 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -881,13 +881,6 @@ void LLAgentWearables::addWearableToAgentInventory(LLPointer<LLInventoryCallback void LLAgentWearables::removeWearable(const LLWearableType::EType type, bool do_remove_all, U32 index) { - if (gAgent.isTeen() && - (type == LLWearableType::WT_UNDERSHIRT || type == LLWearableType::WT_UNDERPANTS)) - { - // Can't take off underclothing in simple UI mode or on PG accounts - // TODO: enable the removing of a single undershirt/underpants if multiple are worn. - Nyx - return; - } if (getWearableCount(type) == 0) { // no wearables to remove diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index ee49125711..005259bcb8 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -44,6 +44,10 @@ const std::string AISAPI::INVENTORY_CAP_NAME("InventoryAPIv3"); const std::string AISAPI::LIBRARY_CAP_NAME("LibraryAPIv3"); +std::list<AISAPI::ais_query_item_t> AISAPI::sPostponedQuery; + +const S32 MAX_SIMULTANEOUS_COROUTINES = 2048; + //------------------------------------------------------------------------- /*static*/ bool AISAPI::isAvailable() @@ -366,9 +370,51 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t /*static*/ void AISAPI::EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc) { + LLCoprocedureManager &inst = LLCoprocedureManager::instance(); + S32 pending_in_pool = inst.countPending("AIS"); std::string procFullName = "AIS(" + procName + ")"; - LLCoprocedureManager::instance().enqueueCoprocedure("AIS", procFullName, proc); + if (pending_in_pool < MAX_SIMULTANEOUS_COROUTINES) + { + inst.enqueueCoprocedure("AIS", procFullName, proc); + } + else + { + // As I understand it, coroutines have built-in 'pending' pool + // but unfortunately it has limited size which inventory often goes over + // so this is a workaround to not overfill it. + if (sPostponedQuery.empty()) + { + sPostponedQuery.push_back(ais_query_item_t(procFullName, proc)); + gIdleCallbacks.addFunction(onIdle, NULL); + } + else + { + sPostponedQuery.push_back(ais_query_item_t(procFullName, proc)); + } + } +} +/*static*/ +void AISAPI::onIdle(void *userdata) +{ + if (!sPostponedQuery.empty()) + { + LLCoprocedureManager &inst = LLCoprocedureManager::instance(); + S32 pending_in_pool = inst.countPending("AIS"); + while (pending_in_pool < MAX_SIMULTANEOUS_COROUTINES && !sPostponedQuery.empty()) + { + ais_query_item_t &item = sPostponedQuery.front(); + inst.enqueueCoprocedure("AIS", item.first, item.second); + sPostponedQuery.pop_front(); + pending_in_pool++; + } + } + + if (sPostponedQuery.empty()) + { + // Nothing to do anymore + gIdleCallbacks.deleteFunction(onIdle, NULL); + } } /*static*/ diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h index e97059014b..856f3fc180 100644 --- a/indra/newview/llaisapi.h +++ b/indra/newview/llaisapi.h @@ -31,7 +31,6 @@ #include <map> #include <set> #include <string> -#include "llhttpretrypolicy.h" #include "llviewerinventory.h" #include "llcorehttputil.h" #include "llcoproceduremanager.h" @@ -72,6 +71,7 @@ private: const std::string, LLSD, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t) > invokationFn_t; static void EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc); + static void onIdle(void *userdata); // launches postponed AIS commands static std::string getInvCap(); static std::string getLibCap(); @@ -80,6 +80,8 @@ private: invokationFn_t invoke, std::string url, LLUUID targetId, LLSD body, completion_t callback, COMMAND_TYPE type); + typedef std::pair<std::string, LLCoprocedureManager::CoProcedure_t> ais_query_item_t; + static std::list<ais_query_item_t> sPostponedQuery; }; class AISUpdate diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index afa4414968..134a34137b 100644 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -522,20 +522,20 @@ void LLAppCoreHttp::refreshSettings(bool initial) LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, const LLCore::HttpHandler::ptr_t &handler, void *appdata) { - X509_STORE_CTX *ctx = static_cast<X509_STORE_CTX *>(appdata); - LLCore::HttpStatus result; - LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(""); - LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); - LLSD validation_params = LLSD::emptyMap(); - LLURI uri(url); + LLCore::HttpStatus result; + try + { + X509_STORE_CTX *ctx = static_cast<X509_STORE_CTX *>(appdata); + LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(""); + LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); + LLSD validation_params = LLSD::emptyMap(); + LLURI uri(url); - validation_params[CERT_HOSTNAME] = uri.hostName(); + validation_params[CERT_HOSTNAME] = uri.hostName(); - // *TODO: In the case of an exception while validating the cert, we need a way - // to pass the offending(?) cert back out. *Rider* + // *TODO: In the case of an exception while validating the cert, we need a way + // to pass the offending(?) cert back out. *Rider* - try - { // don't validate hostname. Let libcurl do it instead. That way, it'll handle redirects store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params); } diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index ccab9173a9..aeb3294f53 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -136,6 +136,7 @@ // called again. Since it returned false, do not yet cancel // frameTimer. handleQuit(); + [[NSApplication sharedApplication] stopModal]; return NSTerminateCancel; } else { // pumpMainLoop() returned true: it's done. Okay, done with frameTimer. @@ -296,6 +297,7 @@ struct AttachmentInfo std::vector<AttachmentInfo> info{ AttachmentInfo(metadata.logFilePathname, "text/plain"), AttachmentInfo(metadata.userSettingsPathname, "text/xml"), + AttachmentInfo(metadata.accountSettingsPathname, "text/xml"), AttachmentInfo(metadata.staticDebugPathname, "text/xml") }; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 1a33059188..e8a3305645 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1957,7 +1957,7 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id) } // Moved from LLWearableList::ContextMenu for wider utility. -bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) +bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) const { // TODO: investigate wearables may not be loaded at this point EXT-8231 @@ -1967,7 +1967,7 @@ bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) // Count given clothes (by wearable type) and objects. for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it) { - LLViewerInventoryItem* item = gInventory.getItem(*it); + const LLViewerInventoryItem* item = gInventory.getItem(*it); if (!item) { return false; @@ -3278,6 +3278,50 @@ void update_base_outfit_after_ordering() bool copy_folder_links = false; app_mgr.slamCategoryLinks(app_mgr.getCOF(), base_outfit_id, copy_folder_links, dirty_state_updater); + if (base_outfit_id.notNull()) + { + LLIsValidItemLink collector; + + LLInventoryModel::cat_array_t cof_cats; + LLInventoryModel::item_array_t cof_item_array; + gInventory.collectDescendentsIf(app_mgr.getCOF(), cof_cats, cof_item_array, + LLInventoryModel::EXCLUDE_TRASH, collector); + + for (U32 i = 0; i < outfit_item_array.size(); ++i) + { + LLViewerInventoryItem* linked_item = outfit_item_array.at(i)->getLinkedItem(); + if (linked_item != NULL && linked_item->getActualType() == LLAssetType::AT_TEXTURE) + { + outfit_item_array.erase(outfit_item_array.begin() + i); + break; + } + } + + if (outfit_item_array.size() != cof_item_array.size()) + { + return; + } + + std::sort(cof_item_array.begin(), cof_item_array.end(), sort_by_linked_uuid); + std::sort(outfit_item_array.begin(), outfit_item_array.end(), sort_by_linked_uuid); + + for (U32 i = 0; i < cof_item_array.size(); ++i) + { + LLViewerInventoryItem *cof_it = cof_item_array.at(i); + LLViewerInventoryItem *base_it = outfit_item_array.at(i); + + if (cof_it->getActualDescription() != base_it->getActualDescription()) + { + if (cof_it->getLinkedUUID() == base_it->getLinkedUUID()) + { + base_it->setDescription(cof_it->getActualDescription()); + gInventory.updateItem(base_it); + } + } + } + LLAppearanceMgr::getInstance()->updateIsDirty(); + } + } // Save COF changes - update the contents of the current base outfit @@ -3582,6 +3626,10 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd } llcoro::suspend(); + if (LLApp::isQuitting()) + { + return; + } S32 retryCount(0); bool bRetry; do @@ -3645,6 +3693,11 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData); + if (LLApp::isQuitting()) + { + return; + } + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); @@ -3680,6 +3733,10 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd LL_WARNS("Avatar") << "Bake retry #" << retryCount << " in " << timeout << " seconds." << LL_ENDL; llcoro::suspendUntilTimeout(timeout); + if (LLApp::isQuitting()) + { + return; + } bRetry = true; continue; } diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index c274a8b049..8a55a848db 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -101,7 +101,7 @@ public: bool getCanReplaceCOF(const LLUUID& outfit_cat_id); // Can we add all referenced items to the avatar? - bool canAddWearables(const uuid_vec_t& item_ids); + bool canAddWearables(const uuid_vec_t& item_ids) const; // Copy all items in a category. void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 581e69ec40..c44690ac25 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -49,6 +49,7 @@ #include "llwindow.h" #include "llviewerstats.h" #include "llviewerstatsrecorder.h" +#include "llkeyconflict.h" // for legacy keybinding support, remove later #include "llmarketplacefunctions.h" #include "llmarketplacenotifications.h" #include "llmd5.h" @@ -73,6 +74,7 @@ #include "llviewermedia.h" #include "llviewerparcelaskplay.h" #include "llviewerparcelmedia.h" +#include "llviewershadermgr.h" #include "llviewermediafocus.h" #include "llviewermessage.h" #include "llviewerobjectlist.h" @@ -149,7 +151,7 @@ #include "llapr.h" #include <boost/lexical_cast.hpp> -#include "llviewerkeyboard.h" +#include "llviewerinput.h" #include "lllfsthread.h" #include "llworkerthread.h" #include "lltexturecache.h" @@ -285,7 +287,7 @@ extern BOOL gHiDPISupport; //////////////////////////////////////////////////////////// // All from the last globals push... -F32 gSimLastTime; // Used in LLAppViewer::init and send_stats() +F32 gSimLastTime; // Used in LLAppViewer::init and send_viewer_stats() F32 gSimFrames; BOOL gShowObjectUpdates = FALSE; @@ -526,7 +528,8 @@ bool create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base) LLIconCtrl* icon; - if(gAgent.isInGroup(match_id, TRUE)) + if( match->getMenuName() == "menu_url_group.xml" // See LLUrlEntryGroup constructor + || gAgent.isInGroup(match_id, TRUE)) //This check seems unfiting, urls are either /agent or /group { LLGroupIconCtrl::Params icon_params; icon_params.group_id = match_id; @@ -604,8 +607,9 @@ static void settings_to_globals() static void settings_modify() { LLRenderTarget::sUseFBO = gSavedSettings.getBOOL("RenderDeferred"); + LLPipeline::sRenderTransparentWater = gSavedSettings.getBOOL("RenderTransparentWater"); LLPipeline::sRenderBump = gSavedSettings.getBOOL("RenderObjectBump"); - LLPipeline::sRenderDeferred = LLPipeline::sRenderBump && gSavedSettings.getBOOL("RenderDeferred"); + LLPipeline::sRenderDeferred = LLPipeline::sRenderTransparentWater && LLPipeline::sRenderBump && gSavedSettings.getBOOL("RenderDeferred"); LLVOSurfacePatch::sLODFactor = gSavedSettings.getF32("RenderTerrainLODFactor"); LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4] gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession; @@ -676,6 +680,7 @@ LLAppViewer::LLAppViewer() mPurgeCacheOnExit(false), mPurgeUserDataOnExit(false), mSecondInstance(false), + mUpdaterNotFound(false), mSavedFinalSnapshot(false), mSavePerAccountSettings(false), // don't save settings on logout unless login succeeded. mQuitRequested(false), @@ -755,7 +760,6 @@ public: } }; -static S32 sQAModeTermCode = 0; // set from QAModeTermCode to specify exit code for LL_ERRS bool LLAppViewer::init() { @@ -771,7 +775,7 @@ bool LLAppViewer::init() // initialize the LLSettingsType translation bridge. LLTranslationBridge::ptr_t trans = std::make_shared<LLUITranslationBridge>(); - LLSettingsType::initClass(trans); + LLSettingsType::initParamSingleton(trans); // initialize SSE options LLVector4a::initClass(); @@ -799,8 +803,18 @@ bool LLAppViewer::init() initMaxHeapSize() ; LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize")); - // if a return code is set for error exit, save it here for use in fatalErrorHandler - sQAModeTermCode = gSavedSettings.getS32("QAModeTermCode"); + + // Although initLoggingAndGetLastDuration() is the right place to mess with + // setFatalFunction(), we can't query gSavedSettings until after + // initConfiguration(). + S32 rc(gSavedSettings.getS32("QAModeTermCode")); + if (rc >= 0) + { + // QAModeTermCode set, terminate with that rc on LL_ERRS. Use + // _exit() rather than exit() because normal cleanup depends too + // much on successful startup! + LLError::setFatalFunction([rc](const std::string&){ _exit(rc); }); + } mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); @@ -982,35 +996,32 @@ bool LLAppViewer::init() gGLManager.getGLInfo(gDebugInfo); gGLManager.printGLInfoString(); - // Load Default bindings - std::string key_bindings_file = gDirUtilp->findFile("keys.xml", - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - - - if (!gViewerKeyboard.loadBindingsXML(key_bindings_file)) - { - std::string key_bindings_file = gDirUtilp->findFile("keys.ini", - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - if (!gViewerKeyboard.loadBindings(key_bindings_file)) - { - LL_ERRS("InitInfo") << "Unable to open keys.ini" << LL_ENDL; - } - } - // If we don't have the right GL requirements, exit. if (!gGLManager.mHasRequirements) { // can't use an alert here since we're exiting and // all hell breaks lose. + LLUIString details = LLNotifications::instance().getGlobalString("UnsupportedGLRequirements"); OSMessageBox( - LLNotifications::instance().getGlobalString("UnsupportedGLRequirements"), + details.getString(), LLStringUtil::null, OSMB_OK); return 0; } + // If we don't have the right shader requirements. + if (!gGLManager.mHasShaderObjects + || !gGLManager.mHasVertexShader + || !gGLManager.mHasFragmentShader) + { + LLUIString details = LLNotifications::instance().getGlobalString("UnsupportedShaderRequirements"); + OSMessageBox( + details.getString(), + LLStringUtil::null, + OSMB_OK); + return 0; + } + // Without SSE2 support we will crash almost immediately, warn here. if (!gSysCPU.hasSSE2()) { @@ -1126,7 +1137,7 @@ bool LLAppViewer::init() gGLActive = FALSE; -#if LL_RELEASE_FOR_DOWNLOAD +#if LL_RELEASE_FOR_DOWNLOAD if (!gSavedSettings.getBOOL("CmdLineSkipUpdater")) { LLProcess::Params updater; @@ -1134,19 +1145,32 @@ bool LLAppViewer::init() // Because it's the updater, it MUST persist beyond the lifespan of the // viewer itself. updater.autokill = false; + std::string updater_file; #if LL_WINDOWS - updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "SLVersionChecker.exe"); + updater_file = "SLVersionChecker.exe"; + updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file); #elif LL_DARWIN // explicitly run the system Python interpreter on SLVersionChecker.py updater.executable = "python"; - updater.args.add(gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", "SLVersionChecker.py")); + updater_file = "SLVersionChecker.py"; + updater.args.add(gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", updater_file)); #else - updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "SLVersionChecker"); + updater_file = "SLVersionChecker"; + updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file); #endif // add LEAP mode command-line argument to whichever of these we selected updater.args.add("leap"); // UpdaterServiceSettings - updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting"))); + if (gSavedSettings.getBOOL("FirstLoginThisInstall")) + { + // Befor first login, treat this as 'manual' updates, + // updater won't install anything, but required updates + updater.args.add("0"); + } + else + { + updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting"))); + } // channel updater.args.add(LLVersionInfo::instance().getChannel()); // testok @@ -1154,37 +1178,58 @@ bool LLAppViewer::init() // ForceAddressSize updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize"))); - // Run the updater. An exception from launching the updater should bother us. - LLLeap::create(updater, true); + try + { + // Run the updater. An exception from launching the updater should bother us. + LLLeap::create(updater, true); + mUpdaterNotFound = false; + } + catch (...) + { + LLUIString details = LLNotifications::instance().getGlobalString("LLLeapUpdaterFailure"); + details.setArg("[UPDATER_APP]", updater_file); + OSMessageBox( + details.getString(), + LLStringUtil::null, + OSMB_OK); + mUpdaterNotFound = true; + } } else { LL_WARNS("InitInfo") << "Skipping updater check." << LL_ENDL; } - // Iterate over --leap command-line options. But this is a bit tricky: if - // there's only one, it won't be an array at all. - LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand")); - LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL; - if (LeapCommand.isDefined() && ! LeapCommand.isArray()) - { - // If LeapCommand is actually a scalar value, make an array of it. - // Have to do it in two steps because LeapCommand.append(LeapCommand) - // trashes content! :-P - LLSD item(LeapCommand); - LeapCommand.append(item); - } - BOOST_FOREACH(const std::string& leap, llsd::inArray(LeapCommand)) - { - LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL; - // We don't have any better description of this plugin than the - // user-specified command line. Passing "" causes LLLeap to derive a - // description from the command line itself. - // Suppress LLLeap::Error exception: trust LLLeap's own logging. We - // don't consider any one --leap command mission-critical, so if one - // fails, log it, shrug and carry on. - LLLeap::create("", leap, false); // exception=false - } + if (mUpdaterNotFound) + { + LL_WARNS("InitInfo") << "Failed to launch updater. Skipping Leap commands." << LL_ENDL; + } + else + { + // Iterate over --leap command-line options. But this is a bit tricky: if + // there's only one, it won't be an array at all. + LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand")); + LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL; + if (LeapCommand.isDefined() && !LeapCommand.isArray()) + { + // If LeapCommand is actually a scalar value, make an array of it. + // Have to do it in two steps because LeapCommand.append(LeapCommand) + // trashes content! :-P + LLSD item(LeapCommand); + LeapCommand.append(item); + } + BOOST_FOREACH(const std::string& leap, llsd::inArray(LeapCommand)) + { + LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL; + // We don't have any better description of this plugin than the + // user-specified command line. Passing "" causes LLLeap to derive a + // description from the command line itself. + // Suppress LLLeap::Error exception: trust LLLeap's own logging. We + // don't consider any one --leap command mission-critical, so if one + // fails, log it, shrug and carry on. + LLLeap::create("", leap, false); // exception=false + } + } if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) { @@ -1192,7 +1237,7 @@ bool LLAppViewer::init() << "lleventhost no longer supported as a dynamic library" << LL_ENDL; } -#endif +#endif //LL_RELEASE_FOR_DOWNLOAD LLTextUtil::TextHelpers::iconCallbackCreationFunction = create_text_segment_icon_from_url_match; @@ -1243,6 +1288,9 @@ bool LLAppViewer::init() gSavedSettings.getControl("FramePerSecondLimit")->getSignal()->connect(boost::bind(&LLAppViewer::onChangeFrameLimit, this, _2)); onChangeFrameLimit(gSavedSettings.getLLSD("FramePerSecondLimit")); + // Load User's bindings + loadKeyBindings(); + return true; } @@ -1432,6 +1480,7 @@ bool LLAppViewer::doFrame() { joystick->scanJoystick(); gKeyboard->scanKeyboard(); + gViewerInput.scanMouse(); } // Update state based on messages, user input, object idle. @@ -1462,9 +1511,9 @@ bool LLAppViewer::doFrame() display(); static U64 last_call = 0; - if (!gTeleportDisplay || gGLManager.mIsIntel) // SL-10625...throttle early, throttle often with Intel + if (!gTeleportDisplay) { - // Frame/draw throttling + // Frame/draw throttling, controlled by FramePerSecondLimit U64 elapsed_time = LLTimer::getTotalTime() - last_call; if (elapsed_time < mMinMicroSecPerFrame) { @@ -1500,8 +1549,10 @@ bool LLAppViewer::doFrame() } // yield cooperatively when not running as foreground window - if ( (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) - || !gFocusMgr.getAppHasFocus()) + // and when not quiting (causes trouble at mac's cleanup stage) + if (!LLApp::isExiting() + && ((gViewerWindow && !gViewerWindow->getWindow()->getVisible()) + || !gFocusMgr.getAppHasFocus())) { // Sleep if we're not rendering, or the window is minimized. static LLCachedControl<S32> s_bacground_yeild_time(gSavedSettings, "BackgroundYieldTime", 40); @@ -1601,6 +1652,7 @@ bool LLAppViewer::doFrame() } delete gServicePump; + gServicePump = NULL; destroyMainloopTimeout(); @@ -1658,7 +1710,11 @@ bool LLAppViewer::cleanup() //dump scene loading monitor results if (LLSceneMonitor::instanceExists()) { - LLSceneMonitor::instance().dumpToFile(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "scene_monitor_results.csv")); + if (!isSecondInstance()) + { + LLSceneMonitor::instance().dumpToFile(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "scene_monitor_results.csv")); + } + LLSceneMonitor::deleteSingleton(); } // There used to be an 'if (LLFastTimerView::sAnalyzePerformance)' block @@ -1843,8 +1899,6 @@ bool LLAppViewer::cleanup() SUBSYSTEM_CLEANUP(LLAvatarAppearance); - SUBSYSTEM_CLEANUP(LLAvatarAppearance); - SUBSYSTEM_CLEANUP(LLPostProcess); LLTracker::cleanupInstance(); @@ -2046,6 +2100,7 @@ bool LLAppViewer::cleanup() LLUIImageList::getInstance()->cleanUp(); // This should eventually be done in LLAppViewer + SUBSYSTEM_CLEANUP(LLImage); SUBSYSTEM_CLEANUP(LLVFSThread); SUBSYSTEM_CLEANUP(LLLFSThread); @@ -2089,6 +2144,7 @@ bool LLAppViewer::cleanup() LLWeb::loadURLExternal( gLaunchFileOnQuit, false ); LL_INFOS() << "File launched." << LL_ENDL; } + // make sure nothing uses applyProxySettings by this point. LL_INFOS() << "Cleaning up LLProxy." << LL_ENDL; SUBSYSTEM_CLEANUP(LLProxy); LLCore::LLHttp::cleanup(); @@ -2099,8 +2155,6 @@ bool LLAppViewer::cleanup() LLError::LLCallStacks::cleanup(); - removeMarkerFiles(); - // It's not at first obvious where, in this long sequence, a generic cleanup // call OUGHT to go. So let's say this: as we migrate cleanup from // explicit hand-placed calls into the generic mechanism, eventually @@ -2108,14 +2162,12 @@ bool LLAppViewer::cleanup() // still see above are calls that MUST happen before the generic cleanup // kicks in. - // The logging subsystem depends on an LLSingleton. Any logging after - // LLSingletonBase::deleteAll() won't be recorded. - LL_INFOS() << "Goodbye!" << LL_ENDL; - // This calls every remaining LLSingleton's cleanupSingleton() and // deleteSingleton() methods. LLSingletonBase::deleteAll(); + LL_INFOS() << "Goodbye!" << LL_ENDL; + removeDumpDir(); // return 0; @@ -2126,7 +2178,7 @@ bool LLAppViewer::initThreads() { static const bool enable_threads = true; - LLImage::initParamSingleton(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange")); + LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange")); LLVFSThread::initClass(enable_threads && false); LLLFSThread::initClass(enable_threads && false); @@ -2156,26 +2208,23 @@ bool LLAppViewer::initThreads() return true; } -LLError::ErrFatalHookResult fatalErrorHook(const std::string &error_string) +void errorCallback(LLError::ELevel level, const std::string &error_string) { + if (level == LLError::LEVEL_ERROR) + { #ifndef LL_RELEASE_FOR_DOWNLOAD - OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_OK); + OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_OK); #endif - //Set the ErrorActivated global so we know to create a marker file - gLLErrorActivated = true; - - gDebugInfo["FatalMessage"] = error_string; - // We're not already crashing -- we simply *intend* to crash. Since we - // haven't actually trashed anything yet, we can afford to write the whole - // static info file. - LLAppViewer::instance()->writeDebugInfo(); + //Set the ErrorActivated global so we know to create a marker file + gLLErrorActivated = true; -#ifdef SHADER_CRASH_NONFATAL - return LLError::ERR_DO_NOT_CRASH; -#else - return LLError::ERR_CRASH; -#endif + gDebugInfo["FatalMessage"] = error_string; + // We're not already crashing -- we simply *intend* to crash. Since we + // haven't actually trashed anything yet, we can afford to write the whole + // static info file. + LLAppViewer::instance()->writeDebugInfo(); + } } void LLAppViewer::initLoggingAndGetLastDuration() @@ -2186,8 +2235,8 @@ void LLAppViewer::initLoggingAndGetLastDuration() LLError::initForApplication( gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "") ,gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "") ); - - // TBD fatal hook belongs here + LLError::addGenericRecorder(&errorCallback); + //LLError::setTimeFunction(getRuntime); // Remove the last ".old" log file. std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, @@ -2957,7 +3006,8 @@ bool LLAppViewer::initWindow() if (use_watchdog) { - LLWatchdog::getInstance()->init(); + LLWatchdog::getInstance()->init( + [](){ LL_ERRS() << "Watchdog killer event" << LL_ENDL; }); } LLNotificationsUI::LLNotificationManager::getInstance(); @@ -3091,8 +3141,9 @@ LLSD LLAppViewer::getViewerInfo() const info["POSITION"] = ll_sd_from_vector3d(pos); info["POSITION_LOCAL"] = ll_sd_from_vector3(gAgent.getPosAgentFromGlobal(pos)); info["REGION"] = gAgent.getRegion()->getName(); - info["HOSTNAME"] = gAgent.getRegion()->getHost().getHostName(); - info["HOSTIP"] = gAgent.getRegion()->getHost().getString(); + + boost::regex regex("\\.(secondlife|lindenlab)\\..*"); + info["HOSTNAME"] = boost::regex_replace(gAgent.getRegion()->getSimHostName(), regex, ""); info["SERVER_VERSION"] = gLastVersionChannel; LLSLURL slurl; LLAgentUI::buildSLURL(slurl); @@ -3450,6 +3501,12 @@ void LLAppViewer::writeSystemInfo() gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall"); gDebugInfo["StartupState"] = LLStartUp::getStartupStateString(); + std::vector<std::string> resolutions = gViewerWindow->getWindow()->getDisplaysResolutionList(); + for (auto res_iter : resolutions) + { + gDebugInfo["DisplayInfo"].append(res_iter); + } + writeDebugInfo(); // Save out debug_info.log early, in case of crash. } @@ -3554,7 +3611,7 @@ void LLAppViewer::handleViewerCrash() if(gAgent.getRegion()) { - gDebugInfo["Dynamic"]["CurrentSimHost"] = gAgent.getRegionHost().getHostName(); + gDebugInfo["Dynamic"]["CurrentSimHost"] = gAgent.getRegion()->getSimHostName(); gDebugInfo["Dynamic"]["CurrentRegion"] = gAgent.getRegion()->getName(); const LLVector3& loc = gAgent.getPositionAgent(); @@ -3947,7 +4004,9 @@ void LLAppViewer::requestQuit() gFloaterView->closeAllChildren(true); } - send_stats(); + // Send preferences once, when exiting + bool include_preferences = true; + send_viewer_stats(include_preferences); gLogoutTimer.reset(); mQuitRequested = true; @@ -3967,6 +4026,7 @@ static LLNotificationFunctorRegistration finish_quit_reg("ConfirmQuit", finish_q void LLAppViewer::userQuit() { + LL_INFOS() << "User requested quit" << LL_ENDL; if (gDisconnected || !gViewerWindow || !gViewerWindow->getProgressView() @@ -4368,6 +4428,134 @@ void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb) LLDeferredTaskList::instance().addTask(cb); } +void LLAppViewer::loadKeyBindings() +{ + std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "key_bindings.xml"); +#if 1 + // Legacy support + // Remove #if-#endif section half a year after DRTVWR-501 releases. + // Mouse actions are part of keybinding file since DRTVWR-501 instead of being stored in + // settings.xml. To support legacy viewers that were storing in settings.xml we need to + // transfer old variables to new format. + // Also part of backward compatibility is present in LLKeyConflictHandler to modify + // legacy variables on changes in new system (to make sure we won't enforce + // legacy values again if user dropped to defaults in new system) + if (LLVersionInfo::getInstance()->getChannelAndVersion() != gLastRunVersion + || !gDirUtilp->fileExists(key_bindings_file)) // if file is missing, assume that there were no changes by user yet + { + // copy mouse actions and voice key changes to new file + LL_INFOS("InitInfo") << "Converting legacy mouse bindings to new format" << LL_ENDL; + // Load settings from file + LLKeyConflictHandler third_person_view(LLKeyConflictHandler::MODE_THIRD_PERSON); + LLKeyConflictHandler sitting_view(LLKeyConflictHandler::MODE_SITTING); + + // Since we are only modifying keybindings if personal file doesn't exist yet, + // it should be safe to just overwrite the value + // If key is already in use somewhere by default, LLKeyConflictHandler should resolve it. + BOOL value = gSavedSettings.getBOOL("DoubleClickAutoPilot"); + third_person_view.registerControl("walk_to", + 0, + value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, + KEY_NONE, + MASK_NONE, + value); + + U32 index = value ? 1 : 0; // we can store multiple combinations per action, so if first is in use by doubleclick, go to second + value = gSavedSettings.getBOOL("ClickToWalk"); + third_person_view.registerControl("walk_to", + index, + value ? EMouseClickType::CLICK_LEFT : EMouseClickType::CLICK_NONE, + KEY_NONE, + MASK_NONE, + value); + + value = gSavedSettings.getBOOL("DoubleClickTeleport"); + third_person_view.registerControl("teleport_to", + 0, + value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, + KEY_NONE, + MASK_NONE, + value); + + // sitting also supports teleport + sitting_view.registerControl("teleport_to", + 0, + value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, + KEY_NONE, + MASK_NONE, + value); + + std::string key_string = gSavedSettings.getString("PushToTalkButton"); + EMouseClickType mouse = EMouseClickType::CLICK_NONE; + KEY key = KEY_NONE; + if (key_string == "MiddleMouse") + { + mouse = EMouseClickType::CLICK_MIDDLE; + } + else if (key_string == "MouseButton4") + { + mouse = EMouseClickType::CLICK_BUTTON4; + } + else if (key_string == "MouseButton5") + { + mouse = EMouseClickType::CLICK_BUTTON5; + } + else + { + LLKeyboard::keyFromString(key_string, &key); + } + + value = gSavedSettings.getBOOL("PushToTalkToggle"); + std::string control_name = value ? "toggle_voice" : "voice_follow_key"; + third_person_view.registerControl(control_name, 0, mouse, key, MASK_NONE, true); + sitting_view.registerControl(control_name, 0, mouse, key, MASK_NONE, true); + + if (third_person_view.hasUnsavedChanges()) + { + // calls loadBindingsXML() + third_person_view.saveToSettings(); + } + + if (sitting_view.hasUnsavedChanges()) + { + // calls loadBindingsXML() + sitting_view.saveToSettings(); + } + + // in case of voice we need to repeat this in other modes + + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + // edit and first person modes; MODE_SAVED_SETTINGS not in use at the moment + if (i != LLKeyConflictHandler::MODE_THIRD_PERSON && i != LLKeyConflictHandler::MODE_SITTING) + { + LLKeyConflictHandler handler((LLKeyConflictHandler::ESourceMode)i); + + handler.registerControl(control_name, 0, mouse, key, MASK_NONE, true); + + if (handler.hasUnsavedChanges()) + { + // calls loadBindingsXML() + handler.saveToSettings(); + } + } + } + } + // since something might have gone wrong or there might have been nothing to save + // (and because otherwise following code will have to be encased in else{}), + // load everything one last time +#endif + if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) + { + // Failed to load custom bindings, try default ones + key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "key_bindings.xml"); + if (!gViewerInput.loadBindingsXML(key_bindings_file)) + { + LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL; + } + } +} + void LLAppViewer::purgeCache() { LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << LL_ENDL; @@ -4745,7 +4933,8 @@ void LLAppViewer::idle() if (viewer_stats_timer.getElapsedTimeF32() >= SEND_STATS_PERIOD && !gDisconnected) { LL_INFOS() << "Transmitting sessions stats" << LL_ENDL; - send_stats(); + bool include_preferences = false; + send_viewer_stats(include_preferences); viewer_stats_timer.reset(); } @@ -4827,13 +5016,14 @@ void LLAppViewer::idle() { return; } + + gViewerWindow->updateUI(); + if (gTeleportDisplay) { return; } - gViewerWindow->updateUI(); - /////////////////////////////////////// // Agent and camera movement // @@ -5155,11 +5345,56 @@ void LLAppViewer::sendLogoutRequest() } } +void LLAppViewer::updateNameLookupUrl() +{ + LLViewerRegion* region = gAgent.getRegion(); + if (!region || !region->capabilitiesReceived()) + { + return; + } + + LLAvatarNameCache *name_cache = LLAvatarNameCache::getInstance(); + bool had_capability = LLAvatarNameCache::getInstance()->hasNameLookupURL(); + std::string name_lookup_url; + name_lookup_url.reserve(128); // avoid a memory allocation below + name_lookup_url = region->getCapability("GetDisplayNames"); + bool have_capability = !name_lookup_url.empty(); + if (have_capability) + { + // we have support for display names, use it + U32 url_size = name_lookup_url.size(); + // capabilities require URLs with slashes before query params: + // https://<host>:<port>/cap/<uuid>/?ids=<blah> + // but the caps are granted like: + // https://<host>:<port>/cap/<uuid> + if (url_size > 0 && name_lookup_url[url_size - 1] != '/') + { + name_lookup_url += '/'; + } + name_cache->setNameLookupURL(name_lookup_url); + } + else + { + // Display names not available on this region + name_cache->setNameLookupURL(std::string()); + } + + // Error recovery - did we change state? + if (had_capability != have_capability) + { + // name tags are persistant on screen, so make sure they refresh + LLVOAvatar::invalidateNameTags(); + } +} + void LLAppViewer::idleNameCache() { // Neither old nor new name cache can function before agent has a region LLViewerRegion* region = gAgent.getRegion(); - if (!region) return; + if (!region) + { + return; + } // deal with any queued name requests and replies. gCacheName->processPending(); @@ -5167,47 +5402,12 @@ void LLAppViewer::idleNameCache() // Can't run the new cache until we have the list of capabilities // for the agent region, and can therefore decide whether to use // display names or fall back to the old name system. - if (!region->capabilitiesReceived()) return; - - // Agent may have moved to a different region, so need to update cap URL - // for name lookups. Can't do this in the cap grant code, as caps are - // granted to neighbor regions before the main agent gets there. Can't - // do it in the move-into-region code because cap not guaranteed to be - // granted yet, for example on teleport. - LLAvatarNameCache *name_cache = LLAvatarNameCache::getInstance(); - bool had_capability = LLAvatarNameCache::getInstance()->hasNameLookupURL(); - std::string name_lookup_url; - name_lookup_url.reserve(128); // avoid a memory allocation below - name_lookup_url = region->getCapability("GetDisplayNames"); - bool have_capability = !name_lookup_url.empty(); - if (have_capability) - { - // we have support for display names, use it - U32 url_size = name_lookup_url.size(); - // capabilities require URLs with slashes before query params: - // https://<host>:<port>/cap/<uuid>/?ids=<blah> - // but the caps are granted like: - // https://<host>:<port>/cap/<uuid> - if (url_size > 0 && name_lookup_url[url_size-1] != '/') - { - name_lookup_url += '/'; - } - name_cache->setNameLookupURL(name_lookup_url); - } - else - { - // Display names not available on this region - name_cache->setNameLookupURL( std::string() ); - } - - // Error recovery - did we change state? - if (had_capability != have_capability) - { - // name tags are persistant on screen, so make sure they refresh - LLVOAvatar::invalidateNameTags(); - } + if (!region->capabilitiesReceived()) + { + return; + } - name_cache->idle(); + LLAvatarNameCache::getInstance()->idle(); } // @@ -5554,7 +5754,7 @@ void LLAppViewer::handleLoginComplete() if(gAgent.getRegion()) { - gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName(); + gDebugInfo["CurrentSimHost"] = gAgent.getRegion()->getSimHostName(); gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName(); } diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index e8b3464c6e..d24cdcedc7 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -95,6 +95,7 @@ public: bool quitRequested() { return mQuitRequested; } bool logoutRequestSent() { return mLogoutRequestSent; } bool isSecondInstance() { return mSecondInstance; } + bool isUpdaterMissing() { return mUpdaterNotFound; } void writeDebugInfo(bool isStatic=true); @@ -194,6 +195,8 @@ public: void purgeCache(); // Clear the local cache. void purgeCacheImmediate(); //clear local cache immediately. S32 updateTextureThreads(F32 max_time); + + void loadKeyBindings(); // mute/unmute the system's master audio virtual void setMasterSystemAudioMute(bool mute); @@ -205,7 +208,9 @@ public: // llcorehttp init/shutdown/config information. LLAppCoreHttp & getAppCoreHttp() { return mAppCoreHttp; } - + + void updateNameLookupUrl(); + protected: virtual bool initWindow(); // Initialize the viewer's window. virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system @@ -262,6 +267,7 @@ private: static LLAppViewer* sInstance; bool mSecondInstance; // Is this a second instance of the app? + bool mUpdaterNotFound; // True when attempt to start updater failed std::string mMarkerFileName; LLAPRFile mMarkerFile; // A file created to indicate the app is running. diff --git a/indra/newview/llappviewerlistener.cpp b/indra/newview/llappviewerlistener.cpp index 94250f1fc2..2380a8ebf0 100644 --- a/indra/newview/llappviewerlistener.cpp +++ b/indra/newview/llappviewerlistener.cpp @@ -52,10 +52,12 @@ LLAppViewerListener::LLAppViewerListener(const LLAppViewerGetter& getter): void LLAppViewerListener::requestQuit(const LLSD& event) { + LL_INFOS() << "Listener requested quit" << LL_ENDL; mAppViewerGetter()->requestQuit(); } void LLAppViewerListener::forceQuit(const LLSD& event) { + LL_INFOS() << "Listener requested force quit" << LL_ENDL; mAppViewerGetter()->forceQuit(); } diff --git a/indra/newview/llappviewermacosx-for-objc.h b/indra/newview/llappviewermacosx-for-objc.h index 37e8a3917a..79c3efff91 100644 --- a/indra/newview/llappviewermacosx-for-objc.h +++ b/indra/newview/llappviewermacosx-for-objc.h @@ -41,6 +41,7 @@ struct CrashMetadata { std::string logFilePathname; std::string userSettingsPathname; + std::string accountSettingsPathname; std::string staticDebugPathname; std::string OSInfo; std::string agentFullname; diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index 784a104573..42946e4415 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -183,6 +183,7 @@ CrashMetadataSingleton::CrashMetadataSingleton() LL_INFOS("Bugsplat") << "Previous run metadata from '" << staticDebugPathname << "':" << LL_ENDL; logFilePathname = get_metadata(info, "SLLog"); userSettingsPathname = get_metadata(info, "SettingsFilename"); + accountSettingsPathname = get_metadata(info, "PerAccountSettingsFilename"); OSInfo = get_metadata(info, "OSInfo"); agentFullname = get_metadata(info, "LoginName"); // Translate underscores back to spaces diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 9f8b1745d4..c9c675eee9 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -139,6 +139,9 @@ namespace { // user name, when we have it sBugSplatSender->setDefaultUserName(WCSTR(gAgentAvatarp->getFullname())); + + sBugSplatSender->sendAdditionalFile( + WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "settings_per_account.xml"))); } // LL_ERRS message, when there is one diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index d3e66289d1..0fd6009074 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -248,6 +248,7 @@ void LLAttachmentsMgr::linkRecentlyArrivedAttachments() { if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(*it) && + !gAgentAvatarp->getWornAttachment(*it)->isTempAttachment() && // Don't link temp attachments in COF! !LLAppearanceMgr::instance().isLinkedInCOF(*it)) { LLUUID item_id = *it; diff --git a/indra/newview/llattachmentsmgr.h b/indra/newview/llattachmentsmgr.h index a4ef762e8b..90aeff3032 100644 --- a/indra/newview/llattachmentsmgr.h +++ b/indra/newview/llattachmentsmgr.h @@ -30,8 +30,6 @@ #include "llsingleton.h" -class LLViewerInventoryItem; - //-------------------------------------------------------------------------------- // LLAttachmentsMgr // diff --git a/indra/newview/llavatariconctrl.h b/indra/newview/llavatariconctrl.h index a1dacd1a27..c510e86958 100644 --- a/indra/newview/llavatariconctrl.h +++ b/indra/newview/llavatariconctrl.h @@ -31,7 +31,6 @@ #include "lliconctrl.h" #include "llavatarpropertiesprocessor.h" -#include "llviewermenu.h" class LLAvatarName; diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index 36d18114aa..b95cd68526 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -30,7 +30,6 @@ #include <boost/signals2.hpp> #include "llpanel.h" -#include "lloutputmonitorctrl.h" #include "llbutton.h" #include "lltextbox.h" #include "llstyle.h" @@ -38,6 +37,7 @@ #include "llcallingcard.h" // for LLFriendObserver class LLAvatarIconCtrl; +class LLOutputMonitorCtrl; class LLAvatarName; class LLIconCtrl; diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 54c6c985d6..3ab5c669c4 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -57,7 +57,6 @@ #include "llinventorymodel.h" #include "llmultigesture.h" #include "llui.h" -#include "llviewermenu.h" #include "lluictrlfactory.h" // diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 431a8c60be..2ba2c6d8b5 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -64,6 +64,7 @@ #include "llstring.h" #include "llurlaction.h" #include "llviewercontrol.h" +#include "llviewermenu.h" #include "llviewerobjectlist.h" static LLDefaultChildRegistry::Register<LLChatHistory> r("chat_history"); diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 4f42868f1a..1c22e055bb 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llchatitemscontainerctrl.h" +#include "llchatmsgbox.h" #include "lltextbox.h" #include "llavataractions.h" diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h index f66670ec8c..ebff9ca298 100644 --- a/indra/newview/llchatitemscontainerctrl.h +++ b/indra/newview/llchatitemscontainerctrl.h @@ -28,12 +28,13 @@ #define LL_LLCHATITEMSCONTAINERCTRL_H_ #include "llchat.h" -#include "llchatmsgbox.h" #include "llpanel.h" #include "llscrollbar.h" #include "llviewerchat.h" #include "lltoastpanel.h" +class LLChatMsgBox; + typedef enum e_show_item_header { CHATITEMHEADER_SHOW_ONLY_NAME = 0, diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index dedb06c945..0f187b0ecf 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -36,6 +36,7 @@ #include "llsingleton.h" #include "llsyswellwindow.h" #include "llfloaternotificationstabbed.h" +#include "llviewermenu.h" static LLDefaultChildRegistry::Register<LLChicletPanel> t1("chiclet_panel"); static LLDefaultChildRegistry::Register<LLNotificationChiclet> t2("chiclet_notification"); @@ -1092,6 +1093,10 @@ void LLScriptChiclet::onMenuItemClicked(const LLSD& user_data) { LLScriptFloaterManager::instance().removeNotification(getSessionId()); } + else if ("close all" == action) + { + LLIMWellWindow::getInstance()->closeAll(); + } } void LLScriptChiclet::createPopupMenu() diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index 1caefd58ab..b31981b235 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -140,10 +140,31 @@ protected: { LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("Attachment.Touch", boost::bind(handleMultiple, handle_attachment_touch, mUUIDs)); + registrar.add("Attachment.Edit", boost::bind(handleMultiple, handle_item_edit, mUUIDs)); registrar.add("Attachment.Detach", boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), mUUIDs)); + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + enable_registrar.add("Attachment.OnEnable", boost::bind(&CofAttachmentContextMenu::onEnable, this, _2)); + return createFromFile("menu_cof_attachment.xml"); } + + bool onEnable(const LLSD& userdata) + { + const std::string event_name = userdata.asString(); + + if ("touch" == event_name) + { + return (1 == mUUIDs.size()) && (enable_attachment_touch(mUUIDs.front())); + } + else if ("edit" == event_name) + { + return (1 == mUUIDs.size()) && (get_is_item_editable(mUUIDs.front())); + } + + return true; + } }; ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h index 380fdccfa3..a17cab486a 100644 --- a/indra/newview/llcolorswatch.h +++ b/indra/newview/llcolorswatch.h @@ -36,7 +36,6 @@ // Classes // class LLColor4; -class LLFloaterColorPicker; class LLColorSwatchCtrl : public LLUICtrl diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index fe14bc081f..06d959ba3c 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -402,23 +402,30 @@ bool LLCommandLineParser::parseCommandLineString(const std::string& str) } } - // Split the string content into tokens - const char* escape_chars = "\\"; - const char* separator_chars = "\r\n "; - const char* quote_chars = "\"'"; - boost::escaped_list_separator<char> sep(escape_chars, separator_chars, quote_chars); - boost::tokenizer< boost::escaped_list_separator<char> > tok(cmd_line_string, sep); std::vector<std::string> tokens; - // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens)); - for(boost::tokenizer< boost::escaped_list_separator<char> >::iterator i = tok.begin(); - i != tok.end(); - ++i) + try { - if(0 != i->size()) + // Split the string content into tokens + const char* escape_chars = "\\"; + const char* separator_chars = "\r\n "; + const char* quote_chars = "\"'"; + boost::escaped_list_separator<char> sep(escape_chars, separator_chars, quote_chars); + boost::tokenizer< boost::escaped_list_separator<char> > tok(cmd_line_string, sep); + // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens)); + for (boost::tokenizer< boost::escaped_list_separator<char> >::iterator i = tok.begin(); + i != tok.end(); + ++i) { - tokens.push_back(*i); + if (0 != i->size()) + { + tokens.push_back(*i); + } } } + catch (...) + { + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("Unexpected crash while parsing: " << str)); + } po::command_line_parser clp(tokens); return parseAndStoreResults(clp); diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 76e16f5a1f..3aaaaf52f5 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -347,6 +347,13 @@ void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID paren bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { + if (LLApp::isQuitting()) + { + // Reply from coroutine came on shutdown + // We are quiting, don't start any more coroutines! + return true; + } + LLSD result; LLCheckedHandle<LLFloaterCompileQueue> floater(hfloater); // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. @@ -381,6 +388,8 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, LLSDMap("timeout", LLSD::Boolean(true))); + floater.check(); + if (result.has("timeout")) { // A timeout filed in the result will always be true if present. LLStringUtil::format_map_t args; @@ -404,6 +413,12 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat } + if (!gAssetStorage) + { + // viewer likely is shutting down + return true; + } + { HandleScriptUserData userData(pump.getName()); @@ -468,6 +483,8 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, LLSDMap("timeout", LLSD::Boolean(true))); + floater.check(); + if (result.has("timeout")) { // A timeout filed in the result will always be true if present. LLStringUtil::format_map_t args; @@ -797,6 +814,7 @@ void LLFloaterScriptQueue::objectScriptProcessingQueueCoro(std::string action, L // but offers no guarantee of doing so. llcoro::suspend(); } + floater.check(); } floater->addStringMessage("Done"); diff --git a/indra/newview/llcompilequeue.h b/indra/newview/llcompilequeue.h index 1b3d8f83a0..adb854875a 100644 --- a/indra/newview/llcompilequeue.h +++ b/indra/newview/llcompilequeue.h @@ -29,14 +29,11 @@ #include "llinventory.h" #include "llviewerobject.h" -#include "llvoinventorylistener.h" #include "lluuid.h" #include "llfloater.h" #include "llscrolllistctrl.h" -#include "llviewerinventory.h" - #include "llevents.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp index 5a6b66df52..fab249f988 100644 --- a/indra/newview/llcontrolavatar.cpp +++ b/indra/newview/llcontrolavatar.cpp @@ -78,6 +78,24 @@ void LLControlAvatar::initInstance() mInitFlags |= 1<<4; } +const LLVOAvatar *LLControlAvatar::getAttachedAvatar() const +{ + if (mRootVolp && mRootVolp->isAttachment()) + { + return mRootVolp->getAvatarAncestor(); + } + return NULL; +} + +LLVOAvatar *LLControlAvatar::getAttachedAvatar() +{ + if (mRootVolp && mRootVolp->isAttachment()) + { + return mRootVolp->getAvatarAncestor(); + } + return NULL; +} + void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_scale_fixup) const { @@ -165,7 +183,7 @@ void LLControlAvatar::matchVolumeTransform() if (mRootVolp->isAttachment()) { - LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor(); + LLVOAvatar *attached_av = getAttachedAvatar(); if (attached_av) { LLViewerJointAttachment *attach = attached_av->getTargetAttachmentPoint(mRootVolp); @@ -360,7 +378,34 @@ void LLControlAvatar::idleUpdate(LLAgent &agent, const F64 &time) } } -BOOL LLControlAvatar::updateCharacter(LLAgent &agent) +bool LLControlAvatar::computeNeedsUpdate() +{ + computeUpdatePeriod(); + + // Animesh attachments are a special case. Should have the same update cadence as their attached parent avatar. + LLVOAvatar *attached_av = getAttachedAvatar(); + if (attached_av) + { + // Have to run computeNeedsUpdate() for attached av in + // case it hasn't run updateCharacter() already this + // frame. Note this means that the attached av will + // run computeNeedsUpdate() multiple times per frame + // if it has animesh attachments. Results will be + // consistent except for the corner case of exceeding + // MAX_IMPOSTOR_INTERVAL in one call but not another, + // which should be rare. + attached_av->computeNeedsUpdate(); + mNeedsImpostorUpdate = attached_av->mNeedsImpostorUpdate; + if (mNeedsImpostorUpdate) + { + mLastImpostorUpdateReason = 12; + } + return mNeedsImpostorUpdate; + } + return LLVOAvatar::computeNeedsUpdate(); +} + +bool LLControlAvatar::updateCharacter(LLAgent &agent) { return LLVOAvatar::updateCharacter(agent); } @@ -634,29 +679,23 @@ std::string LLControlAvatar::getFullname() const // virtual bool LLControlAvatar::shouldRenderRigged() const { - if (mRootVolp && mRootVolp->isAttachment()) - { - LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor(); - if (attached_av) - { - return attached_av->shouldRenderRigged(); - } - } + const LLVOAvatar *attached_av = getAttachedAvatar(); + if (attached_av) + { + return attached_av->shouldRenderRigged(); + } return true; } // virtual BOOL LLControlAvatar::isImpostor() { - if (mRootVolp && mRootVolp->isAttachment()) - { - // Attached animated objects should match state of their attached av. - LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor(); - if (attached_av) - { - return attached_av->isImpostor(); - } - } + // Attached animated objects should match state of their attached av. + LLVOAvatar *attached_av = getAttachedAvatar(); + if (attached_av) + { + return attached_av->isImpostor(); + } return LLVOAvatar::isImpostor(); } diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h index 288d07cd48..8e87299f3e 100644 --- a/indra/newview/llcontrolavatar.h +++ b/indra/newview/llcontrolavatar.h @@ -40,6 +40,10 @@ public: virtual void initInstance(); // Called after construction to initialize the class. virtual ~LLControlAvatar(); + // If this is an attachment, return the avatar it is attached to. Otherwise NULL. + virtual const LLVOAvatar *getAttachedAvatar() const; + virtual LLVOAvatar *getAttachedAvatar(); + void getNewConstraintFixups(LLVector3& new_pos_constraint, F32& new_scale_constraint) const; void matchVolumeTransform(); void updateVolumeGeom(); @@ -53,7 +57,8 @@ public: void markForDeath(); virtual void idleUpdate(LLAgent &agent, const F64 &time); - virtual BOOL updateCharacter(LLAgent &agent); + virtual bool computeNeedsUpdate(); + virtual bool updateCharacter(LLAgent &agent); void getAnimatedVolumes(std::vector<LLVOVolume*>& volumes); void updateAnimations(); diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index 5539fa75dd..a696c99a82 100644 --- a/indra/newview/llconversationlog.cpp +++ b/indra/newview/llconversationlog.cpp @@ -398,6 +398,24 @@ void LLConversationLog::deleteBackupLogs() } } +void LLConversationLog::verifyFilename(const LLUUID& session_id, const std::string &expected_filename, const std::string &new_session_name) +{ + conversations_vec_t::iterator conv_it = mConversations.begin(); + for (; conv_it != mConversations.end(); ++conv_it) + { + if (conv_it->getSessionID() == session_id) + { + if (conv_it->getHistoryFileName() != expected_filename) + { + LLLogChat::renameLogFile(conv_it->getHistoryFileName(), expected_filename); + conv_it->updateHistoryFileName(expected_filename); + conv_it->setConversationName(new_session_name); + } + break; + } + } +} + bool LLConversationLog::moveLog(const std::string &originDirectory, const std::string &targetDirectory) { @@ -482,6 +500,10 @@ bool LLConversationLog::saveToFile(const std::string& filename) conv_it->getSessionID().toString(conversation_id); conv_it->getParticipantID().toString(participant_id); + bool is_adhoc = (conv_it->getConversationType() == LLIMModel::LLIMSession::ADHOC_SESSION); + std::string conv_name = is_adhoc ? conv_it->getConversationName() : LLURI::escape(conv_it->getConversationName()); + std::string file_name = is_adhoc ? conv_it->getHistoryFileName() : LLURI::escape(conv_it->getHistoryFileName()); + // examples of two file entries // [1343221177] 0 1 0 John Doe| 7e4ec5be-783f-49f5-71dz-16c58c64c145 4ec62a74-c246-0d25-2af6-846beac2aa55 john.doe| // [1343222639] 2 0 0 Ad-hoc Conference| c3g67c89-c479-4c97-b21d-32869bcfe8rc 68f1c33e-4135-3e3e-a897-8c9b23115c09 Ad-hoc Conference hash597394a0-9982-766d-27b8-c75560213b9a| @@ -490,10 +512,10 @@ bool LLConversationLog::saveToFile(const std::string& filename) (S32)conv_it->getConversationType(), (S32)0, (S32)conv_it->hasOfflineMessages(), - LLURI::escape(conv_it->getConversationName()).c_str(), + conv_name.c_str(), participant_id.c_str(), conversation_id.c_str(), - LLURI::escape(conv_it->getHistoryFileName()).c_str()); + file_name.c_str()); } fclose(fp); return true; @@ -514,7 +536,9 @@ bool LLConversationLog::loadFromFile(const std::string& filename) } bool purge_required = false; - char buffer[MAX_STRING]; + static constexpr int UTF_BUFFER{ 1024 }; // long enough to handle the most extreme Unicode nonsense and some to spare + + char buffer[UTF_BUFFER]; char conv_name_buffer[MAX_STRING]; char part_id_buffer[MAX_STRING]; char conv_id_buffer[MAX_STRING]; @@ -525,11 +549,14 @@ bool LLConversationLog::loadFromFile(const std::string& filename) // before CHUI-348 it was a flag of conversation voice state int prereserved_unused; - while (!feof(fp) && fgets(buffer, MAX_STRING, fp)) + memset(buffer, '\0', UTF_BUFFER); + while (!feof(fp) && fgets(buffer, UTF_BUFFER, fp)) { - conv_name_buffer[0] = '\0'; - part_id_buffer[0] = '\0'; - conv_id_buffer[0] = '\0'; + // force blank for added safety + memset(conv_name_buffer, '\0', MAX_STRING); + memset(part_id_buffer, '\0', MAX_STRING); + memset(conv_id_buffer, '\0', MAX_STRING); + memset(history_file_name, '\0', MAX_STRING); sscanf(buffer, "[%lld] %d %d %d %[^|]| %s %s %[^|]|", &time, @@ -541,14 +568,18 @@ bool LLConversationLog::loadFromFile(const std::string& filename) conv_id_buffer, history_file_name); + bool is_adhoc = ((SessionType)stype == LLIMModel::LLIMSession::ADHOC_SESSION); + std::string conv_name = is_adhoc ? conv_name_buffer : LLURI::unescape(conv_name_buffer); + std::string file_name = is_adhoc ? history_file_name : LLURI::unescape(history_file_name); + ConversationParams params; params.time(LLUnits::Seconds::fromValue(time)) .conversation_type((SessionType)stype) .has_offline_ims(has_offline_ims) - .conversation_name(LLURI::unescape(conv_name_buffer)) + .conversation_name(conv_name) .participant_id(LLUUID(part_id_buffer)) .session_id(LLUUID(conv_id_buffer)) - .history_filename(LLURI::unescape(history_file_name)); + .history_filename(file_name); LLConversation conversation(params); @@ -562,6 +593,7 @@ bool LLConversationLog::loadFromFile(const std::string& filename) } mConversations.push_back(conversation); + memset(buffer, '\0', UTF_BUFFER); } fclose(fp); diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h index 46e46a3278..820a5db491 100644 --- a/indra/newview/llconversationlog.h +++ b/indra/newview/llconversationlog.h @@ -59,7 +59,7 @@ public: getTime() const { return mTime; } bool hasOfflineMessages() const { return mHasOfflineIMs; } - void setConversationName(std::string conv_name) { mConversationName = conv_name; } + void setConversationName(const std::string &conv_name) { mConversationName = conv_name; } void setOfflineMessages(bool new_messages) { mHasOfflineIMs = new_messages; } bool isOlderThan(U32Days days) const; @@ -68,6 +68,8 @@ public: */ void updateTimestamp(); + void updateHistoryFileName(const std::string &new_name) { mHistoryFileName = new_name; } + /* * Resets flag of unread offline message to false when im floater with this conversation is opened. */ @@ -137,6 +139,8 @@ public: * public method which is called on viewer exit to save conversation log */ void cache(); + // will check if current name is edentical with the one on disk and will rename the one on disk if it isn't + void verifyFilename(const LLUUID& session_id, const std::string &expected_filename, const std::string &new_session_name); bool moveLog(const std::string &originDirectory, const std::string &targetDirectory); void getListOfBackupLogs(std::vector<std::string>& list_of_backup_logs); void deleteBackupLogs(); diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index c258136889..4cfde21e32 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -92,6 +92,23 @@ LLConversationItem::~LLConversationItem() } } +//virtual +void LLConversationItem::addChild(LLFolderViewModelItem* child) +{ + // Avoid duplicates: bail out if that child is already present in the list + // Note: this happens when models are created and 'parented' before views + // This is performance unfriendly, but conversation can addToFolder multiple times + child_list_t::const_iterator iter; + for (iter = mChildren.begin(); iter != mChildren.end(); iter++) + { + if (child == *iter) + { + return; + } + } + LLFolderViewModelItemCommon::addChild(child); +} + void LLConversationItem::postEvent(const std::string& event_type, LLConversationItemSession* session, LLConversationItemParticipant* participant) { LLUUID session_id = (session ? session->getUUID() : LLUUID()); @@ -324,11 +341,36 @@ void LLConversationItemSession::removeParticipant(const LLUUID& participant_id) void LLConversationItemSession::clearParticipants() { + // clearParticipants function potentially is malfunctioning since it only cleans children of models, + // it does nothing to views that own those models (listeners) + // probably needs to post some kind of 'remove all participants' event clearChildren(); mIsLoaded = false; mNeedsRefresh = true; } + +void LLConversationItemSession::clearAndDeparentModels() +{ + std::for_each(mChildren.begin(), mChildren.end(), + [](LLFolderViewModelItem* c) + { + if (c->getNumRefs() == 0) + { + // LLConversationItemParticipant can be created but not assigned to any view, + // it was waiting for an "add_participant" event to be processed + delete c; + } + else + { + // Model is still assigned to some view/widget + c->setParent(NULL); + } + } + ); + mChildren.clear(); +} + LLConversationItemParticipant* LLConversationItemSession::findParticipant(const LLUUID& participant_id) { // This is *not* a general tree parsing algorithm. It assumes that a session contains only diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index 80385fad5f..787deeb594 100644 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -96,6 +96,7 @@ public: virtual void buildContextMenu(LLMenuGL& menu, U32 flags) { } virtual BOOL isUpToDate() const { return TRUE; } virtual bool hasChildren() const { return FALSE; } + virtual void addChild(LLFolderViewModelItem* child); virtual bool potentiallyVisible() { return true; } virtual bool filter( LLFolderViewFilter& filter) { return false; } @@ -164,6 +165,7 @@ public: void removeParticipant(LLConversationItemParticipant* participant); void removeParticipant(const LLUUID& participant_id); void clearParticipants(); + void clearAndDeparentModels(); // will delete unowned models and deparent owned ones LLConversationItemParticipant* findParticipant(const LLUUID& participant_id); void setParticipantIsMuted(const LLUUID& participant_id, bool is_muted); diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index 60a5204547..df16868132 100644 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -31,6 +31,7 @@ #include <boost/bind.hpp> #include "llagentdata.h" +#include "llavataractions.h" #include "llconversationmodel.h" #include "llfloaterimsession.h" #include "llfloaterimnearbychat.h" @@ -102,6 +103,56 @@ LLConversationViewSession::~LLConversationViewSession() mFlashTimer->unset(); } +void LLConversationViewSession::destroyView() +{ + // Chat can create and parent models(listeners) to session's model before creating + // coresponding views, such participant's models normally will wait for idle cycles + // but since we are deleting session and won't be processing any more events, make + // sure unowned LLConversationItemParticipant models are removed as well. + + LLConversationItemSession* vmi = dynamic_cast<LLConversationItemSession*>(getViewModelItem()); + + // CONV_SESSION_1_ON_1 stores participants as two models that belong to views independent + // from session (nasty! These views are widgets in LLFloaterIMSessionTab, see buildConversationViewParticipant) + if (vmi && vmi->getType() != LLConversationItem::CONV_SESSION_1_ON_1) + { + // Destroy existing views + while (!mItems.empty()) + { + LLFolderViewItem *itemp = mItems.back(); + mItems.pop_back(); + + LLFolderViewModelItem* item_vmi = itemp->getViewModelItem(); + if (item_vmi) // supposed to exist + { + // unparent to remove from child list + vmi->removeChild(item_vmi); + } + itemp->destroyView(); + } + + // Not needed in scope of sessions, but just in case + while (!mFolders.empty()) + { + LLFolderViewFolder *folderp = mFolders.back(); + mFolders.pop_back(); + + LLFolderViewModelItem* folder_vmi = folderp->getViewModelItem(); + if (folder_vmi) + { + vmi->removeChild(folder_vmi); + } + folderp->destroyView(); + } + + // Now everything that is left in model(listener) is not owned by views, + // only by sessions, deparent so it won't point to soon to be dead model + vmi->clearAndDeparentModels(); + } + + LLFolderViewFolder::destroyView(); +} + void LLConversationViewSession::setFlashState(bool flash_state) { if (flash_state && !mFlashStateOn) @@ -430,10 +481,15 @@ void LLConversationViewSession::refresh() // Refresh the session view from its model data LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); vmi->resetRefresh(); - + if (mSessionTitle) - { - mSessionTitle->setText(vmi->getDisplayName()); + { + if (!highlightFriendTitle(vmi)) + { + LLStyle::Params title_style; + title_style.color = LLUIColorTable::instance().getColor("LabelTextColor"); + mSessionTitle->setText(vmi->getDisplayName(), title_style); + } } // Update all speaking indicators @@ -478,6 +534,22 @@ void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& sessi } } +bool LLConversationViewSession::highlightFriendTitle(LLConversationItem* vmi) +{ + if(vmi->getType() == LLConversationItem::CONV_PARTICIPANT || vmi->getType() == LLConversationItem::CONV_SESSION_1_ON_1) + { + LLIMModel::LLIMSession* session= LLIMModel::instance().findIMSession(vmi->getUUID()); + if (session && LLAvatarActions::isFriend(session->mOtherParticipantID)) + { + LLStyle::Params title_style; + title_style.color = LLUIColorTable::instance().getColor("ConversationFriendColor"); + mSessionTitle->setText(vmi->getDisplayName(), title_style); + return true; + } + } + return false; +} + // // Implementation of conversations list participant (avatar) widgets // @@ -545,7 +617,9 @@ BOOL LLConversationViewParticipant::postBuild() } updateChildren(); - return LLFolderViewItem::postBuild(); + LLFolderViewItem::postBuild(); + refresh(); + return TRUE; } void LLConversationViewParticipant::draw() @@ -576,7 +650,14 @@ void LLConversationViewParticipant::draw() } else { - color = mIsSelected ? sHighlightFgColor : sFgColor; + if (LLAvatarActions::isFriend(mUUID)) + { + color = LLUIColorTable::instance().getColor("ConversationFriendColor"); + } + else + { + color = mIsSelected ? sHighlightFgColor : sFgColor; + } } LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(getViewModelItem()); @@ -619,7 +700,7 @@ void LLConversationViewParticipant::refresh() // *TODO: We should also do something with vmi->isModerator() to echo that state in the UI somewhat mSpeakingIndicator->setIsModeratorMuted(participant_model->isModeratorMuted()); - + // Do the regular upstream refresh LLFolderViewItem::refresh(); } diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index 420c250dfe..0932d24dfe 100644 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -34,7 +34,9 @@ #include "lloutputmonitorctrl.h" class LLTextBox; +class LLFloater; class LLFloaterIMContainer; +class LLConversationItem; class LLConversationViewSession; class LLConversationViewParticipant; @@ -65,6 +67,8 @@ protected: public: virtual ~LLConversationViewSession(); + /*virtual*/ void destroyView(); + /*virtual*/ BOOL postBuild(); /*virtual*/ void draw(); /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); @@ -92,6 +96,8 @@ public: LLFloater* getSessionFloater(); bool isInActiveVoiceChannel() { return mIsInActiveVoiceChannel; } + bool highlightFriendTitle(LLConversationItem* vmi); + private: void onCurrentVoiceSessionChanged(const LLUUID& session_id); diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index df94e337da..d4fc1fe64d 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -603,6 +603,11 @@ bool LLCurrencyUIManager::bought() return impl.mBought; } +void LLCurrencyUIManager::clearError() +{ + impl.clearError(); +} + bool LLCurrencyUIManager::hasError() { return impl.mError; diff --git a/indra/newview/llcurrencyuimanager.h b/indra/newview/llcurrencyuimanager.h index 3f37db8db1..7c05acbb05 100644 --- a/indra/newview/llcurrencyuimanager.h +++ b/indra/newview/llcurrencyuimanager.h @@ -80,6 +80,8 @@ public: bool buying(); // are we in the process of buying? bool bought(); // did the buy() transaction complete successfully + void clearError(); + bool hasError(); std::string errorMessage(); std::string errorURI(); diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 8c6cbc020b..5034bd1c5e 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -1178,11 +1178,33 @@ LLSpatialPartition* LLDrawable::getSpatialPartition() } else if (isRoot()) { - if (mSpatialBridge && (mSpatialBridge->asPartition()->mPartitionType == LLViewerRegion::PARTITION_HUD) != mVObjp->isHUDAttachment()) + if (mSpatialBridge) { - // remove obsolete bridge - mSpatialBridge->markDead(); - setSpatialBridge(NULL); + U32 partition_type = mSpatialBridge->asPartition()->mPartitionType; + bool is_hud = mVObjp->isHUDAttachment(); + bool is_animesh = mVObjp->isAnimatedObject() && mVObjp->getControlAvatar() != NULL; + bool is_attachment = mVObjp->isAttachment() && !is_hud && !is_animesh; + if ((partition_type == LLViewerRegion::PARTITION_HUD) != is_hud) + { + // Was/became HUD + // remove obsolete bridge + mSpatialBridge->markDead(); + setSpatialBridge(NULL); + } + else if ((partition_type == LLViewerRegion::PARTITION_CONTROL_AV) != is_animesh) + { + // Was/became part of animesh + // remove obsolete bridge + mSpatialBridge->markDead(); + setSpatialBridge(NULL); + } + else if ((partition_type == LLViewerRegion::PARTITION_AVATAR) != is_attachment) + { + // Was/became part of avatar + // remove obsolete bridge + mSpatialBridge->markDead(); + setSpatialBridge(NULL); + } } //must be an active volume if (!mSpatialBridge) @@ -1191,6 +1213,15 @@ LLSpatialPartition* LLDrawable::getSpatialPartition() { setSpatialBridge(new LLHUDBridge(this, getRegion())); } + else if (mVObjp->isAnimatedObject() && mVObjp->getControlAvatar()) + { + setSpatialBridge(new LLControlAVBridge(this, getRegion())); + } + // check HUD first, because HUD is also attachment + else if (mVObjp->isAttachment()) + { + setSpatialBridge(new LLAvatarBridge(this, getRegion())); + } else { setSpatialBridge(new LLVolumeBridge(this, getRegion())); @@ -1454,7 +1485,7 @@ void LLSpatialBridge::setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* LLVOAvatar* avatarp = (LLVOAvatar*) objparent; if (avatarp->isVisible()) { - impostor = objparent->isAvatar() && ((LLVOAvatar*) objparent)->isImpostor(); + impostor = objparent->isAvatar() && !LLPipeline::sImpostorRender && ((LLVOAvatar*) objparent)->isImpostor(); loaded = objparent->isAvatar() && ((LLVOAvatar*) objparent)->isFullyLoaded(); } else @@ -1538,7 +1569,8 @@ void LLSpatialBridge::updateDistance(LLCamera& camera_in, bool force_update) if (mDrawable->getVObj()) { - if (mDrawable->getVObj()->isAttachment()) + // Don't update if we are part of impostor, unles it's an impostor pass + if (!LLPipeline::sImpostorRender && mDrawable->getVObj()->isAttachment()) { LLDrawable* parent = mDrawable->getParent(); if (parent && parent->getVObj()) @@ -1698,12 +1730,26 @@ void LLDrawable::updateFaceSize(S32 idx) LLBridgePartition::LLBridgePartition(LLViewerRegion* regionp) : LLSpatialPartition(0, FALSE, 0, regionp) { - mDrawableType = LLPipeline::RENDER_TYPE_AVATAR; + mDrawableType = LLPipeline::RENDER_TYPE_VOLUME; mPartitionType = LLViewerRegion::PARTITION_BRIDGE; mLODPeriod = 16; mSlopRatio = 0.25f; } +LLAvatarPartition::LLAvatarPartition(LLViewerRegion* regionp) + : LLBridgePartition(regionp) +{ + mDrawableType = LLPipeline::RENDER_TYPE_AVATAR; + mPartitionType = LLViewerRegion::PARTITION_AVATAR; +} + +LLControlAVPartition::LLControlAVPartition(LLViewerRegion* regionp) + : LLBridgePartition(regionp) +{ + mDrawableType = LLPipeline::RENDER_TYPE_CONTROL_AV; + mPartitionType = LLViewerRegion::PARTITION_CONTROL_AV; +} + LLHUDBridge::LLHUDBridge(LLDrawable* drawablep, LLViewerRegion* regionp) : LLVolumeBridge(drawablep, regionp) { diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index 2aee7b450a..d583a692f9 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -86,7 +86,8 @@ LLDrawPool *LLDrawPool::createPool(const U32 type, LLViewerTexture *tex0) poolp = new LLDrawPoolAlpha(); break; case POOL_AVATAR: - poolp = new LLDrawPoolAvatar(); + case POOL_CONTROL_AV: + poolp = new LLDrawPoolAvatar(type); break; case POOL_TREE: poolp = new LLDrawPoolTree(tex0); @@ -383,16 +384,6 @@ LLRenderPass::~LLRenderPass() } -LLDrawPool* LLRenderPass::instancePool() -{ -#if LL_RELEASE_FOR_DOWNLOAD - LL_WARNS() << "Attempting to instance a render pass. Invalid operation." << LL_ENDL; -#else - LL_ERRS() << "Attempting to instance a render pass. Invalid operation." << LL_ENDL; -#endif - return NULL; -} - void LLRenderPass::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture) { LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[type]; @@ -449,7 +440,7 @@ void LLRenderPass::applyModelMatrix(const LLDrawInfo& params) if (params.mModelMatrix != gGLLastMatrix) { gGLLastMatrix = params.mModelMatrix; - gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.loadMatrix(gGLModelView); if (params.mModelMatrix) { diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h index 4eb9a4151d..ecd9bd034f 100644 --- a/indra/newview/lldrawpool.h +++ b/indra/newview/lldrawpool.h @@ -60,6 +60,7 @@ public: POOL_GRASS, POOL_INVISIBLE, // see below * POOL_AVATAR, + POOL_CONTROL_AV, // Animesh POOL_VOIDWATER, POOL_WATER, POOL_GLOW, @@ -110,7 +111,6 @@ public: virtual S32 getShaderLevel() const { return mShaderLevel; } static LLDrawPool* createPool(const U32 type, LLViewerTexture *tex0 = NULL); - virtual LLDrawPool *instancePool() = 0; // Create an empty new instance of the pool. virtual LLViewerTexture* getTexture() = 0; virtual BOOL isFacePool() { return FALSE; } virtual void resetDrawOrders() = 0; @@ -138,31 +138,30 @@ public: PASS_POST_BUMP, PASS_MATERIAL, PASS_MATERIAL_ALPHA, - PASS_MATERIAL_ALPHA_MASK, + PASS_MATERIAL_ALPHA_MASK, // Diffuse texture used as alpha mask PASS_MATERIAL_ALPHA_EMISSIVE, PASS_SPECMAP, PASS_SPECMAP_BLEND, - PASS_SPECMAP_MASK, + PASS_SPECMAP_MASK, // Diffuse texture used as alpha mask and specular texture(map) PASS_SPECMAP_EMISSIVE, PASS_NORMMAP, PASS_NORMMAP_BLEND, - PASS_NORMMAP_MASK, + PASS_NORMMAP_MASK, // Diffuse texture used as alpha mask and normal map PASS_NORMMAP_EMISSIVE, PASS_NORMSPEC, PASS_NORMSPEC_BLEND, - PASS_NORMSPEC_MASK, + PASS_NORMSPEC_MASK, // Diffuse texture used as alpha mask with normal and specular map PASS_NORMSPEC_EMISSIVE, PASS_GLOW, PASS_ALPHA, PASS_ALPHA_MASK, - PASS_FULLBRIGHT_ALPHA_MASK, + PASS_FULLBRIGHT_ALPHA_MASK, // Diffuse texture used as alpha mask and fullbright PASS_ALPHA_INVISIBLE, NUM_RENDER_TYPES, }; LLRenderPass(const U32 type); virtual ~LLRenderPass(); - /*virtual*/ LLDrawPool* instancePool(); /*virtual*/ LLViewerTexture* getDebugTexture() { return NULL; } LLViewerTexture* getTexture() { return NULL; } BOOL isDead() { return FALSE; } diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index da0467315f..4ee08e869a 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -314,11 +314,15 @@ void LLDrawPoolAlpha::render(S32 pass) LLVertexBuffer::MAP_TEXCOORD0); pushBatches(LLRenderPass::PASS_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); - pushBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); pushBatches(LLRenderPass::PASS_ALPHA_INVISIBLE, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); + // Material alpha mask gGL.diffuseColor4f(0, 0, 1, 1); pushBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); + pushBatches(LLRenderPass::PASS_NORMMAP_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); + pushBatches(LLRenderPass::PASS_SPECMAP_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); + pushBatches(LLRenderPass::PASS_NORMSPEC_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); + pushBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); gGL.diffuseColor4f(0, 1, 0, 1); pushBatches(LLRenderPass::PASS_INVISIBLE, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 789a254389..55b3864b6d 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -38,7 +38,6 @@ #include "lldrawable.h" #include "lldrawpoolbump.h" #include "llface.h" -#include "llvolume.h" #include "llmeshrepository.h" #include "llsky.h" #include "llviewercamera.h" @@ -106,8 +105,8 @@ S32 cube_channel = -1; static LLTrace::BlockTimerStatHandle FTM_SHADOW_AVATAR("Avatar Shadow"); -LLDrawPoolAvatar::LLDrawPoolAvatar() : - LLFacePool(POOL_AVATAR) +LLDrawPoolAvatar::LLDrawPoolAvatar(U32 type) : + LLFacePool(type) { } @@ -136,15 +135,6 @@ BOOL LLDrawPoolAvatar::isDead() } return TRUE; } - -//----------------------------------------------------------------------------- -// instancePool() -//----------------------------------------------------------------------------- -LLDrawPool *LLDrawPoolAvatar::instancePool() -{ - return new LLDrawPoolAvatar(); -} - S32 LLDrawPoolAvatar::getShaderLevel() const { @@ -582,12 +572,12 @@ void LLDrawPoolAvatar::renderShadow(S32 pass) { return; } - - BOOL impostor = avatarp->isImpostor(); - if (impostor - && LLVOAvatar::AV_DO_NOT_RENDER != avatarp->getVisualMuteSettings() - && LLVOAvatar::AV_ALWAYS_RENDER != avatarp->getVisualMuteSettings()) + LLVOAvatar::AvatarOverallAppearance oa = avatarp->getOverallAppearance(); + BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor(); + if (oa == LLVOAvatar::AOA_INVISIBLE || + (impostor && oa == LLVOAvatar::AOA_JELLYDOLL)) { + // No shadows for jellydolled or invisible avs. return; } @@ -1474,7 +1464,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) return; } - LLVOAvatar *avatarp; + LLVOAvatar *avatarp = NULL; if (single_avatar) { @@ -1520,11 +1510,12 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) return; } - BOOL impostor = avatarp->isImpostor() && !single_avatar; + BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor() && !single_avatar; if (( avatarp->isInMuteList() || impostor - || (LLVOAvatar::AV_DO_NOT_RENDER == avatarp->getVisualMuteSettings() && !avatarp->needsImpostorUpdate()) ) && pass != 0) + || (LLVOAvatar::AOA_NORMAL != avatarp->getOverallAppearance() && !avatarp->needsImpostorUpdate()) ) && pass != 0) +// || (LLVOAvatar::AV_DO_NOT_RENDER == avatarp->getVisualMuteSettings() && !avatarp->needsImpostorUpdate()) ) && pass != 0) { //don't draw anything but the impostor for impostored avatars return; } @@ -1534,6 +1525,13 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) return; } + LLVOAvatar *attached_av = avatarp->getAttachedAvatar(); + if (attached_av && LLVOAvatar::AOA_NORMAL != attached_av->getOverallAppearance()) + { + // Animesh attachment of a jellydolled or invisible parent - don't show + return; + } + if (pass == 0) { if (!LLPipeline::sReflectionRender) @@ -1541,7 +1539,8 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) LLVOAvatar::sNumVisibleAvatars++; } - if (impostor || (LLVOAvatar::AV_DO_NOT_RENDER == avatarp->getVisualMuteSettings() && !avatarp->needsImpostorUpdate())) +// if (impostor || (LLVOAvatar::AV_DO_NOT_RENDER == avatarp->getVisualMuteSettings() && !avatarp->needsImpostorUpdate())) + if (impostor || (LLVOAvatar::AOA_NORMAL != avatarp->getOverallAppearance() && !avatarp->needsImpostorUpdate())) { if (LLPipeline::sRenderDeferred && !LLPipeline::sReflectionRender && avatarp->mImpostor.isComplete()) { @@ -1741,11 +1740,16 @@ void LLDrawPoolAvatar::getRiggedGeometry( LLVolume* volume, const LLVolumeFace& vol_face) { - face->setGeomIndex(0); - face->setIndicesIndex(0); - - //rigged faces do not batch textures - face->setTextureIndex(255); + face->setGeomIndex(0); + face->setIndicesIndex(0); + + if (face->getTextureIndex() != FACE_DO_NOT_BATCH_TEXTURES) + { + face->setDrawInfo(NULL); + } + + //rigged faces do not batch textures + face->setTextureIndex(FACE_DO_NOT_BATCH_TEXTURES); if (buffer.isNull() || buffer->getTypeMask() != data_mask || !buffer->isWriteable()) { @@ -1810,7 +1814,7 @@ void LLDrawPoolAvatar::getRiggedGeometry( } else { - face->setPoolType(LLDrawPool::POOL_AVATAR); + face->setPoolType(mType); // either POOL_AVATAR or POOL_CONTROL_AV } //LL_INFOS() << "Rebuilt face " << face->getTEOffset() << " of " << face->getDrawable() << " at " << gFrameTimeSeconds << LL_ENDL; @@ -2125,7 +2129,7 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow) if (mat) { - switch (LLMaterial::eDiffuseAlphaMode(mat->getDiffuseAlphaMode())) + switch (LLMaterial::eDiffuseAlphaMode(mat->getDiffuseAlphaModeRender())) { case LLMaterial::DIFFUSE_ALPHA_MODE_MASK: { @@ -2263,7 +2267,7 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow) sVertexProgram->uniform4f(LLShaderMgr::SPECULAR_COLOR, col.mV[0], col.mV[1], col.mV[2], spec); sVertexProgram->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, env); - if (mat->getDiffuseAlphaMode() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) + if (mat->getDiffuseAlphaModeRender() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) { F32 cutoff = mat->getAlphaMaskCutoff()/255.f; sVertexProgram->setMinimumAlpha(cutoff); @@ -2494,7 +2498,7 @@ LLColor3 LLDrawPoolAvatar::getDebugColor() const void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type) { llassert (facep->isState(LLFace::RIGGED)); - llassert(getType() == LLDrawPool::POOL_AVATAR); + llassert(getType() == LLDrawPool::POOL_AVATAR || getType() == LLDrawPool::POOL_CONTROL_AV); if (facep->getPool() && facep->getPool() != this) { LL_ERRS() << "adding rigged face that's already in another pool" << LL_ENDL; @@ -2516,7 +2520,7 @@ void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type) void LLDrawPoolAvatar::removeRiggedFace(LLFace* facep) { llassert (facep->isState(LLFace::RIGGED)); - llassert(getType() == LLDrawPool::POOL_AVATAR); + llassert(getType() == LLDrawPool::POOL_AVATAR || getType() == LLDrawPool::POOL_CONTROL_AV); if (facep->getPool() != this) { LL_ERRS() << "Tried to remove a rigged face from the wrong pool" << LL_ENDL; diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h index cb09eb18e2..92a8538958 100644 --- a/indra/newview/lldrawpoolavatar.h +++ b/indra/newview/lldrawpoolavatar.h @@ -178,12 +178,10 @@ typedef enum virtual S32 getShaderLevel() const; - LLDrawPoolAvatar(); + LLDrawPoolAvatar(U32 type); static LLMatrix4& getModelView(); - /*virtual*/ LLDrawPool *instancePool(); - /*virtual*/ S32 getNumPasses(); /*virtual*/ void beginRenderPass(S32 pass); /*virtual*/ void endRenderPass(S32 pass); diff --git a/indra/newview/lldrawpoolground.cpp b/indra/newview/lldrawpoolground.cpp index 6bd2631d3b..5b74264dab 100644 --- a/indra/newview/lldrawpoolground.cpp +++ b/indra/newview/lldrawpoolground.cpp @@ -46,11 +46,6 @@ LLDrawPoolGround::LLDrawPoolGround() : { } -LLDrawPool *LLDrawPoolGround::instancePool() -{ - return new LLDrawPoolGround(); -} - void LLDrawPoolGround::prerender() { mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_ENVIRONMENT); diff --git a/indra/newview/lldrawpoolground.h b/indra/newview/lldrawpoolground.h index a4f8a3fcf5..15b1dc60a2 100644 --- a/indra/newview/lldrawpoolground.h +++ b/indra/newview/lldrawpoolground.h @@ -43,8 +43,6 @@ public: LLDrawPoolGround(); - /*virtual*/ LLDrawPool *instancePool(); - /*virtual*/ void prerender(); /*virtual*/ void render(S32 pass = 0); }; diff --git a/indra/newview/lldrawpoolsky.cpp b/indra/newview/lldrawpoolsky.cpp index dbe8724088..b6f55e800a 100644 --- a/indra/newview/lldrawpoolsky.cpp +++ b/indra/newview/lldrawpoolsky.cpp @@ -48,11 +48,6 @@ LLDrawPoolSky::LLDrawPoolSky() { } -LLDrawPool *LLDrawPoolSky::instancePool() -{ - return new LLDrawPoolSky(); -} - void LLDrawPoolSky::prerender() { mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_ENVIRONMENT); @@ -128,23 +123,12 @@ void LLDrawPoolSky::renderSkyFace(U8 index) return; } - F32 interp_val = gSky.mVOSkyp ? gSky.mVOSkyp->getInterpVal() : 0.0f; - if (index < 6) // sky tex...interp { llassert(mSkyTex); mSkyTex[index].bindTexture(true); // bind the current tex face->renderIndexed(); - - if (interp_val > 0.01f) // iff, we've got enough info to lerp (a to and a from) - { - LLGLEnable blend(GL_BLEND); - llassert(mSkyTex); - mSkyTex[index].bindTexture(false); // bind the "other" texture - gGL.diffuseColor4f(1, 1, 1, interp_val); // lighting is disabled - face->renderIndexed(); - } } else // heavenly body faces, no interp... { diff --git a/indra/newview/lldrawpoolsky.h b/indra/newview/lldrawpoolsky.h index 916d8c1cbe..d1dcd6b22e 100644 --- a/indra/newview/lldrawpoolsky.h +++ b/indra/newview/lldrawpoolsky.h @@ -49,8 +49,6 @@ public: LLDrawPoolSky(); - /*virtual*/ LLDrawPool *instancePool(); - /*virtual*/ S32 getNumPostDeferredPasses() { return getNumPasses(); } /*virtual*/ void beginPostDeferredPass(S32 pass) { beginRenderPass(pass); } /*virtual*/ void endPostDeferredPass(S32 pass) { endRenderPass(pass); } diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 33a11631fe..37dc80e2b7 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -87,13 +87,6 @@ LLDrawPoolTerrain::~LLDrawPoolTerrain() llassert( gPipeline.findPool( getType(), getTexture() ) == NULL ); } - -LLDrawPool *LLDrawPoolTerrain::instancePool() -{ - return new LLDrawPoolTerrain(mTexturep); -} - - U32 LLDrawPoolTerrain::getVertexDataMask() { if (LLPipeline::sShadowRender) diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h index 04e27d9370..5b4558020d 100644 --- a/indra/newview/lldrawpoolterrain.h +++ b/indra/newview/lldrawpoolterrain.h @@ -49,8 +49,6 @@ public: LLDrawPoolTerrain(LLViewerTexture *texturep); virtual ~LLDrawPoolTerrain(); - /*virtual*/ LLDrawPool *instancePool(); - /*virtual*/ S32 getNumDeferredPasses() { return 1; } /*virtual*/ void beginDeferredPass(S32 pass); /*virtual*/ void endDeferredPass(S32 pass); diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index b885008cae..0d5195bdbf 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -51,11 +51,6 @@ LLDrawPoolTree::LLDrawPoolTree(LLViewerTexture *texturep) : mTexturep->setAddressMode(LLTexUnit::TAM_WRAP); } -LLDrawPool *LLDrawPoolTree::instancePool() -{ - return new LLDrawPoolTree(mTexturep); -} - void LLDrawPoolTree::prerender() { mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT); diff --git a/indra/newview/lldrawpooltree.h b/indra/newview/lldrawpooltree.h index 9c1e60f5eb..13f9ec8dce 100644 --- a/indra/newview/lldrawpooltree.h +++ b/indra/newview/lldrawpooltree.h @@ -45,8 +45,6 @@ public: LLDrawPoolTree(LLViewerTexture *texturep); - /*virtual*/ LLDrawPool *instancePool(); - /*virtual*/ void prerender(); /*virtual*/ S32 getNumDeferredPasses() { return 1; } diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp index 073adfb627..aa426cd785 100644 --- a/indra/newview/lldrawpoolwater.cpp +++ b/indra/newview/lldrawpoolwater.cpp @@ -104,13 +104,6 @@ void LLDrawPoolWater::restoreGL() }*/ } -LLDrawPool *LLDrawPoolWater::instancePool() -{ - LL_ERRS() << "Should never be calling instancePool on a water pool!" << LL_ENDL; - return NULL; -} - - void LLDrawPoolWater::prerender() { mShaderLevel = (gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps) ? LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_WATER) : 0; @@ -170,7 +163,7 @@ void LLDrawPoolWater::render(S32 pass) std::sort(mDrawFace.begin(), mDrawFace.end(), LLFace::CompareDistanceGreater()); // See if we are rendering water as opaque or not - if (!gSavedSettings.getBOOL("RenderTransparentWater")) + if (!LLPipeline::sRenderTransparentWater) { // render water for low end hardware renderOpaqueLegacyWater(); diff --git a/indra/newview/lldrawpoolwater.h b/indra/newview/lldrawpoolwater.h index d436557e1c..a5d163e0d7 100644 --- a/indra/newview/lldrawpoolwater.h +++ b/indra/newview/lldrawpoolwater.h @@ -61,7 +61,6 @@ public: LLDrawPoolWater(); /*virtual*/ ~LLDrawPoolWater(); - /*virtual*/ LLDrawPool *instancePool(); static void restoreGL(); /*virtual*/ S32 getNumPostDeferredPasses() { return 0; } //getNumPasses(); } diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index 961d72c62e..d4e7f1600e 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -216,10 +216,11 @@ void LLDrawPoolWLSky::renderSkyHaze(const LLVector3& camPosLocal, F32 camHeightL if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY)) { + LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); LLGLSPipelineDepthTestSkyBox sky(true, false); sky_shader->bind(); sky_shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, 1); - sky_shader->uniform1f(LLShaderMgr::SUN_MOON_GLOW_FACTOR, 1.0f); + sky_shader->uniform1f(LLShaderMgr::SUN_MOON_GLOW_FACTOR, psky->getSunMoonGlowFactor()); renderDome(origin, camHeightLocal, sky_shader); sky_shader->unbind(); } @@ -531,8 +532,6 @@ void LLDrawPoolWLSky::renderHeavenlyBodies() } } - blend_factor = LLEnvironment::instance().getCurrentSky()->getBlendFactor(); - face = gSky.mVOSkyp->mFace[LLVOSky::FACE_MOON]; if (gSky.mVOSkyp->getMoon().getDraw() && face && face->getTexture(LLRender::DIFFUSE_MAP) && face->getGeomCount() && moon_shader) @@ -550,17 +549,17 @@ void LLDrawPoolWLSky::renderHeavenlyBodies() { // Bind current and next sun textures moon_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, tex_a, LLTexUnit::TT_TEXTURE); - blend_factor = 0; + //blend_factor = 0; } else if (tex_b && !tex_a) { moon_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, tex_b, LLTexUnit::TT_TEXTURE); - blend_factor = 0; + //blend_factor = 0; } else if (tex_b != tex_a) { moon_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, tex_a, LLTexUnit::TT_TEXTURE); - moon_shader->bindTexture(LLShaderMgr::ALTERNATE_DIFFUSE_MAP, tex_b, LLTexUnit::TT_TEXTURE); + //moon_shader->bindTexture(LLShaderMgr::ALTERNATE_DIFFUSE_MAP, tex_b, LLTexUnit::TT_TEXTURE); } LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); @@ -570,7 +569,8 @@ void LLDrawPoolWLSky::renderHeavenlyBodies() moon_shader->uniform1f(LLShaderMgr::MOON_BRIGHTNESS, moon_brightness); moon_shader->uniform4fv(LLShaderMgr::MOONLIGHT_COLOR, 1, gSky.mVOSkyp->getMoon().getColor().mV); moon_shader->uniform4fv(LLShaderMgr::DIFFUSE_COLOR, 1, color.mV); - moon_shader->uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor); + //moon_shader->uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor); + moon_shader->uniform3fv(LLShaderMgr::DEFERRED_MOON_DIR, 1, psky->getMoonDirection().mV); // shader: moon_dir face->renderIndexed(); @@ -632,11 +632,6 @@ void LLDrawPoolWLSky::prerender() //LL_INFOS() << "wlsky prerendering pass." << LL_ENDL; } -LLDrawPoolWLSky *LLDrawPoolWLSky::instancePool() -{ - return new LLDrawPoolWLSky(); -} - LLViewerTexture* LLDrawPoolWLSky::getTexture() { return NULL; diff --git a/indra/newview/lldrawpoolwlsky.h b/indra/newview/lldrawpoolwlsky.h index 3acfda4eee..a4f176d6db 100644 --- a/indra/newview/lldrawpoolwlsky.h +++ b/indra/newview/lldrawpoolwlsky.h @@ -62,8 +62,6 @@ public: //static LLDrawPool* createPool(const U32 type, LLViewerTexture *tex0 = NULL); - // Create an empty new instance of the pool. - /*virtual*/ LLDrawPoolWLSky *instancePool(); ///< covariant override /*virtual*/ LLViewerTexture* getTexture(); /*virtual*/ BOOL isFacePool() { return FALSE; } /*virtual*/ void resetDrawOrders(); diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index 1e8c57ac6a..89c20904c1 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -125,11 +125,11 @@ BOOL LLViewerDynamicTexture::render() //----------------------------------------------------------------------------- void LLViewerDynamicTexture::preRender(BOOL clear_depth) { - //only images up to 1024*1024 are supported - llassert(mFullHeight <= 512); - llassert(mFullWidth <= 512); + gPipeline.allocatePhysicsBuffer(); + llassert(mFullWidth <= static_cast<S32>(gPipeline.mPhysicsDisplay.getWidth())); + llassert(mFullHeight <= static_cast<S32>(gPipeline.mPhysicsDisplay.getHeight())); - if (gGLManager.mHasFramebufferObject && gPipeline.mBake.isComplete()) + if (gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsATI) { //using offscreen render target, just use the bottom left corner mOrigin.set(0, 0); } @@ -216,7 +216,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances() return TRUE; } - bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mBake.isComplete(); + bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mBake.isComplete() && !gGLManager.mIsATI; if (use_fbo) { diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index 342ee3ccf5..74c1b99e4d 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -806,6 +806,25 @@ const F32 LLEnvironment::SUN_DELTA_YAW(F_PI); // 180deg const U32 LLEnvironment::DayInstance::NO_ANIMATE_SKY(0x01); const U32 LLEnvironment::DayInstance::NO_ANIMATE_WATER(0x02); +std::string env_selection_to_string(LLEnvironment::EnvSelection_t sel) +{ +#define RTNENUM(E) case LLEnvironment::E: return #E + switch (sel){ + RTNENUM(ENV_EDIT); + RTNENUM(ENV_LOCAL); + RTNENUM(ENV_PUSH); + RTNENUM(ENV_PARCEL); + RTNENUM(ENV_REGION); + RTNENUM(ENV_DEFAULT); + RTNENUM(ENV_END); + RTNENUM(ENV_CURRENT); + RTNENUM(ENV_NONE); + default: + return llformat("Unknown(%d)", sel); + } +#undef RTNENUM +} + //------------------------------------------------------------------------- LLEnvironment::LLEnvironment(): @@ -981,7 +1000,7 @@ bool LLEnvironment::canAgentUpdateRegionEnvironment() const if (gAgent.isGodlike()) return true; - return gAgent.getRegion()->canManageEstate(); + return gAgent.canManageEstate(); } bool LLEnvironment::isExtendedEnvironmentEnabled() const @@ -1010,7 +1029,7 @@ void LLEnvironment::onRegionChange() } if (!cur_region->capabilitiesReceived()) { - cur_region->setCapabilitiesReceivedCallback([](LLUUID region_id) { LLEnvironment::instance().requestRegion(); }); + cur_region->setCapabilitiesReceivedCallback([](const LLUUID ®ion_id) { LLEnvironment::instance().requestRegion(); }); return; } requestRegion(); @@ -1037,7 +1056,8 @@ F32 LLEnvironment::getCamHeight() const F32 LLEnvironment::getWaterHeight() const { - return gAgent.getRegion()->getWaterHeight(); + LLViewerRegion* cur_region = gAgent.getRegion(); + return cur_region ? cur_region->getWaterHeight() : DEFAULT_WATER_HEIGHT; } bool LLEnvironment::getIsSunUp() const @@ -1059,6 +1079,7 @@ void LLEnvironment::setSelectedEnvironment(LLEnvironment::EnvSelection_t env, LL { mSelectedEnvironment = env; updateEnvironment(transition, forced); + LL_DEBUGS("ENVIRONMENT") << "Setting environment " << env_selection_to_string(env) << " with transition: " << transition << LL_ENDL; } bool LLEnvironment::hasEnvironment(LLEnvironment::EnvSelection_t env) @@ -1095,11 +1116,13 @@ LLEnvironment::DayInstance::ptr_t LLEnvironment::getEnvironmentInstance(LLEnviro void LLEnvironment::setEnvironment(LLEnvironment::EnvSelection_t env, const LLSettingsDay::ptr_t &pday, LLSettingsDay::Seconds daylength, LLSettingsDay::Seconds dayoffset, S32 env_version) { if ((env < ENV_EDIT) || (env >= ENV_DEFAULT)) - { - LL_WARNS("ENVIRONMENT") << "Attempt to change invalid environment selection." << LL_ENDL; + { + LL_WARNS("ENVIRONMENT") << "Attempt to change invalid environment selection (" << env_selection_to_string(env) << ")." << LL_ENDL; return; } + logEnvironment(env, pday, env_version); + DayInstance::ptr_t environment = getEnvironmentInstance(env, true); environment->clear(); @@ -1116,7 +1139,7 @@ void LLEnvironment::setEnvironment(LLEnvironment::EnvSelection_t env, LLEnvironm { if ((env < ENV_EDIT) || (env >= ENV_DEFAULT)) { - LL_WARNS("ENVIRONMENT") << "Attempt to change invalid environment selection." << LL_ENDL; + LL_WARNS("ENVIRONMENT") << "Attempt to change invalid environment selection (" << env_selection_to_string(env) << ")." << LL_ENDL; return; } @@ -1125,30 +1148,32 @@ void LLEnvironment::setEnvironment(LLEnvironment::EnvSelection_t env, LLEnvironm if (fixed.first) { + logEnvironment(env, fixed.first, env_version); environment->setSky(fixed.first); environment->setFlags(DayInstance::NO_ANIMATE_SKY); } else if (!environment->getSky()) { + LL_DEBUGS("ENVIRONMENT") << "Blank sky for " << env_selection_to_string(env) << ". Reusing environment for sky." << LL_ENDL; environment->setSky(mCurrentEnvironment->getSky()); environment->setFlags(DayInstance::NO_ANIMATE_SKY); } if (fixed.second) { + logEnvironment(env, fixed.second, env_version); environment->setWater(fixed.second); environment->setFlags(DayInstance::NO_ANIMATE_WATER); } else if (!environment->getWater()) { + LL_DEBUGS("ENVIRONMENT") << "Blank water for " << env_selection_to_string(env) << ". Reusing environment for water." << LL_ENDL; environment->setWater(mCurrentEnvironment->getWater()); environment->setFlags(DayInstance::NO_ANIMATE_WATER); } if (!mSignalEnvChanged.empty()) mSignalEnvChanged(env, env_version); - - /*TODO: readjust environment*/ } void LLEnvironment::setEnvironment(LLEnvironment::EnvSelection_t env, const LLSettingsBase::ptr_t &settings, S32 env_version) @@ -1221,10 +1246,12 @@ void LLEnvironment::onSetEnvAssetLoaded(EnvSelection_t env, if (!settings || status) { LLSD args; - args["DESC"] = asset_id.asString(); + args["NAME"] = asset_id.asString(); LLNotificationsUtil::add("FailedToFindSettings", args); + LL_DEBUGS("ENVIRONMENT") << "Failed to find settings for " << env_selection_to_string(env) << ", asset_id: " << asset_id << LL_ENDL; return; } + LL_DEBUGS("ENVIRONMENT") << "Loaded asset: " << asset_id << LL_ENDL; setEnvironment(env, settings); updateEnvironment(transition); @@ -1238,19 +1265,48 @@ void LLEnvironment::clearEnvironment(LLEnvironment::EnvSelection_t env) return; } + LL_DEBUGS("ENVIRONMENT") << "Cleaning environment " << env_selection_to_string(env) << LL_ENDL; + mEnvironments[env].reset(); if (!mSignalEnvChanged.empty()) mSignalEnvChanged(env, VERSION_CLEANUP); +} - /*TODO: readjust environment*/ +void LLEnvironment::logEnvironment(EnvSelection_t env, const LLSettingsBase::ptr_t &settings, S32 env_version) +{ + LL_DEBUGS("ENVIRONMENT") << "Setting Day environment " << env_selection_to_string(env) << " with version(update type): " << env_version << LL_NEWLINE; + // code between LL_DEBUGS and LL_ENDL won't execute unless log is enabled + if (settings) + { + LLUUID asset_id = settings->getAssetId(); + if (asset_id.notNull()) + { + LL_CONT << "Asset id: " << asset_id << LL_NEWLINE; + } + + LLUUID id = settings->getId(); // Not in use? + if (id.notNull()) + { + LL_CONT << "Settings id: " << id << LL_NEWLINE; + } + + LL_CONT << "Name: " << settings->getName() << LL_NEWLINE + << "Type: " << settings->getSettingsType() << LL_NEWLINE + << "Flags: " << settings->getFlags(); // Not in use? + } + else + { + LL_CONT << "Empty settings!"; + } + LL_CONT << LL_ENDL; } LLSettingsDay::ptr_t LLEnvironment::getEnvironmentDay(LLEnvironment::EnvSelection_t env) { if ((env < ENV_EDIT) || (env > ENV_DEFAULT)) { - LL_WARNS("ENVIRONMENT") << "Attempt to retrieve invalid environment selection." << LL_ENDL; + LL_WARNS("ENVIRONMENT") << "Attempt to retrieve invalid environment selection (" << env_selection_to_string(env) << ")." << LL_ENDL; return LLSettingsDay::ptr_t(); } @@ -1266,7 +1322,7 @@ LLSettingsDay::Seconds LLEnvironment::getEnvironmentDayLength(EnvSelection_t env { if ((env < ENV_EDIT) || (env > ENV_DEFAULT)) { - LL_WARNS("ENVIRONMENT") << "Attempt to retrieve invalid environment selection." << LL_ENDL; + LL_WARNS("ENVIRONMENT") << "Attempt to retrieve invalid environment selection (" << env_selection_to_string(env) << ")." << LL_ENDL; return LLSettingsDay::Seconds(0); } @@ -1282,7 +1338,7 @@ LLSettingsDay::Seconds LLEnvironment::getEnvironmentDayOffset(EnvSelection_t env { if ((env < ENV_EDIT) || (env > ENV_DEFAULT)) { - LL_WARNS("ENVIRONMENT") << "Attempt to retrieve invalid environment selection." << LL_ENDL; + LL_WARNS("ENVIRONMENT") << "Attempt to retrieve invalid environment selection (" << env_selection_to_string(env) << ")." << LL_ENDL; return LLSettingsDay::Seconds(0); } @@ -1325,7 +1381,7 @@ LLEnvironment::fixedEnvironment_t LLEnvironment::getEnvironmentFixed(LLEnvironme if ((env < ENV_EDIT) || (env > ENV_DEFAULT)) { - LL_WARNS("ENVIRONMENT") << "Attempt to retrieve invalid environment selection." << LL_ENDL; + LL_WARNS("ENVIRONMENT") << "Attempt to retrieve invalid environment selection (" << env_selection_to_string(env) << ")." << LL_ENDL; return fixedEnvironment_t(); } @@ -1612,6 +1668,17 @@ void LLEnvironment::updateShaderUniforms(LLGLSLShader *shader) void LLEnvironment::recordEnvironment(S32 parcel_id, LLEnvironment::EnvironmentInfo::ptr_t envinfo, LLSettingsBase::Seconds transition) { + if (!gAgent.getRegion()) + { + return; + } + // mRegionId id can be null, no specification as to why and if it's valid so check valid ids only + if (gAgent.getRegion()->getRegionID() != envinfo->mRegionId && envinfo->mRegionId.notNull()) + { + LL_INFOS("ENVIRONMENT") << "Requested environmend region id: " << envinfo->mRegionId << " agent is on: " << gAgent.getRegion()->getRegionID() << LL_ENDL; + return; + } + if (envinfo->mParcelId == INVALID_PARCEL_ID) { // the returned info applies to an entire region. @@ -2361,7 +2428,7 @@ void LLEnvironment::onSetExperienceEnvAssetLoaded(LLUUID experience_id, LLSettin if (!settings || status) { LLSD args; - args["DESC"] = experience_id.asString(); + args["NAME"] = experience_id.asString(); LLNotificationsUtil::add("FailedToFindSettings", args); return; } @@ -3332,7 +3399,7 @@ namespace return; } - LL_WARNS("PUSHENV") << "Underlying environment has changed (" << env << ")! Base env is type " << base_env << LL_ENDL; + LL_WARNS("PUSHENV", "ENVIRONMENT") << "Underlying environment has changed (" << env << ")! Base env is type " << base_env << LL_ENDL; LLEnvironment::DayInstance::ptr_t trans = std::make_shared<InjectedTransition>(std::static_pointer_cast<DayInjection>(shared_from_this()), mBaseDayInstance->getSky(), mBaseDayInstance->getWater(), nextbase, LLEnvironment::TRANSITION_DEFAULT); diff --git a/indra/newview/llenvironment.h b/indra/newview/llenvironment.h index 91c4b85135..6ab0db7501 100644 --- a/indra/newview/llenvironment.h +++ b/indra/newview/llenvironment.h @@ -148,8 +148,11 @@ public: void setEnvironment(EnvSelection_t env, const LLUUID &assetId, S32 env_version = NO_VERSION); void setSharedEnvironment(); - void clearEnvironment(EnvSelection_t env); + + static void logEnvironment(EnvSelection_t env, const LLSettingsBase::ptr_t &settings, S32 env_version = NO_VERSION); + + LLSettingsDay::ptr_t getEnvironmentDay(EnvSelection_t env); LLSettingsDay::Seconds getEnvironmentDayLength(EnvSelection_t env); LLSettingsDay::Seconds getEnvironmentDayOffset(EnvSelection_t env); diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index 299fffd9ab..4b5fd8a758 100644 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -166,6 +166,14 @@ namespace Details // LL_DEBUGS("LLEventPollImpl::eventPollCoro") << "<" << counter << "> result = " // << LLSDXMLStreamer(result) << LL_ENDL; + if (gDisconnected) + { + // Lost connection or disconnected during quit, don't process sim/region update + // messages, they might populate some cleaned up classes (LLWorld, region and object list) + LL_INFOS("LLEventPollImpl") << "Dropping event messages" << LL_ENDL; + break; + } + LLSD httpResults = result["http_result"]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); @@ -210,7 +218,7 @@ namespace Details llcoro::suspendUntilTimeout(waitToRetry); - if (mDone) + if (mDone || gDisconnected) break; LL_INFOS("LLEventPollImpl") << "<" << counter << "> About to retry request." << LL_ENDL; continue; diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 18ea184da6..01d8b6775b 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -153,7 +153,7 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp) } mTEOffset = -1; - mTextureIndex = 255; + mTextureIndex = FACE_DO_NOT_BATCH_TEXTURES; setDrawable(drawablep); mVObjp = objp; @@ -184,6 +184,7 @@ void LLFace::destroy() if(mTexture[i].notNull()) { mTexture[i]->removeFace(i, this) ; + mTexture[i] = NULL; } } @@ -195,7 +196,7 @@ void LLFace::destroy() if (mDrawPoolp) { - if (this->isState(LLFace::RIGGED) && mDrawPoolp->getType() == LLDrawPool::POOL_AVATAR) + if (this->isState(LLFace::RIGGED) && (mDrawPoolp->getType() == LLDrawPool::POOL_CONTROL_AV || mDrawPoolp->getType() == LLDrawPool::POOL_AVATAR)) { ((LLDrawPoolAvatar*) mDrawPoolp)->removeRiggedFace(this); } @@ -203,7 +204,6 @@ void LLFace::destroy() { mDrawPoolp->removeFace(this); } - mDrawPoolp = NULL; } @@ -212,7 +212,7 @@ void LLFace::destroy() delete mTextureMatrix; mTextureMatrix = NULL; - if (mDrawablep.notNull()) + if (mDrawablep) { LLSpatialGroup* group = mDrawablep->getSpatialGroup(); if (group) @@ -224,7 +224,7 @@ void LLFace::destroy() } setDrawInfo(NULL); - + mDrawablep = NULL; mVObjp = NULL; } @@ -456,13 +456,13 @@ void LLFace::setTextureIndex(U8 index) { mTextureIndex = index; - if (mTextureIndex != 255) + if (mTextureIndex != FACE_DO_NOT_BATCH_TEXTURES) { mDrawablep->setState(LLDrawable::REBUILD_POSITION); } else { - if (mDrawInfo && mDrawInfo->mTextureList.size() <= 1) + if (mDrawInfo && !mDrawInfo->mTextureList.empty()) { LL_ERRS() << "Face with no texture index references indexed texture draw info." << LL_ENDL; } @@ -535,7 +535,7 @@ void LLFace::updateCenterAgent() void LLFace::renderSelected(LLViewerTexture *imagep, const LLColor4& color) { - if (mDrawablep->getSpatialGroup() == NULL) + if (mDrawablep == NULL || mDrawablep->getSpatialGroup() == NULL) { return; } @@ -543,7 +543,7 @@ void LLFace::renderSelected(LLViewerTexture *imagep, const LLColor4& color) mDrawablep->getSpatialGroup()->rebuildGeom(); mDrawablep->getSpatialGroup()->rebuildMesh(); - if(mDrawablep.isNull() || mVertexBuffer.isNull()) + if(mVertexBuffer.isNull()) { return; } @@ -1165,7 +1165,7 @@ bool LLFace::canRenderAsMask() } LLMaterial* mat = te->getMaterialParams(); - if (mat && mat->getDiffuseAlphaMode() == LLMaterial::DIFFUSE_ALPHA_MODE_BLEND) + if (mat && mat->getDiffuseAlphaModeRender() == LLMaterial::DIFFUSE_ALPHA_MODE_BLEND) { return false; } @@ -1412,7 +1412,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, } else { - if (!mat || mat->getDiffuseAlphaMode() != LLMaterial::DIFFUSE_ALPHA_MODE_MASK) + if (!mat || mat->getDiffuseAlphaModeRender() != LLMaterial::DIFFUSE_ALPHA_MODE_MASK) { shiny_in_alpha = true; } @@ -1539,7 +1539,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, mVertexBuffer->bindForFeedback(0, LLVertexBuffer::TYPE_VERTEX, mGeomIndex, mGeomCount); - U8 index = mTextureIndex < 255 ? mTextureIndex : 0; + U8 index = mTextureIndex < FACE_DO_NOT_BATCH_TEXTURES ? mTextureIndex : 0; S32 val = 0; U8* vp = (U8*) &val; @@ -2072,7 +2072,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLVector4a texIdx; - S32 index = mTextureIndex < 255 ? mTextureIndex : 0; + S32 index = mTextureIndex < FACE_DO_NOT_BATCH_TEXTURES ? mTextureIndex : 0; F32 val = 0.f; S32* vp = (S32*) &val; @@ -2673,7 +2673,7 @@ S32 LLFace::renderElements(const U16 *index_array) const S32 LLFace::renderIndexed() { - if(mDrawablep.isNull() || mDrawPoolp == NULL) + if(mDrawablep == NULL || mDrawPoolp == NULL) { return 0; } diff --git a/indra/newview/llface.h b/indra/newview/llface.h index c74d4e3fa8..3611539ff8 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -52,6 +52,7 @@ class LLDrawInfo; const F32 MIN_ALPHA_SIZE = 1024.f; const F32 MIN_TEX_ANIM_SIZE = 512.f; +const U8 FACE_DO_NOT_BATCH_TEXTURES = 255; class LLFace : public LLTrace::MemTrackableNonVirtual<LLFace, 16> { @@ -279,8 +280,13 @@ private: LLXformMatrix* mXform; LLPointer<LLViewerTexture> mTexture[LLRender::NUM_TEXTURE_CHANNELS]; - - LLPointer<LLDrawable> mDrawablep; + + // mDrawablep is not supposed to be null, don't use LLPointer because + // mDrawablep owns LLFace and LLPointer is a good way to either cause a + // memory leak or a 'delete each other' situation if something deletes + // drawable wrongly. + LLDrawable* mDrawablep; + // LLViewerObject technically owns drawable, but also it should be strictly managed LLPointer<LLViewerObject> mVObjp; S32 mTEOffset; diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 347997a69a..7a887a2549 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -384,6 +384,8 @@ LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) mUpdateDropDownItems(true), mRestoreOverflowMenu(false), mGetPrevItems(true), + mMouseX(0), + mMouseY(0), mItemsChangedTimer() { // Register callback for menus with current registrar (will be parent panel's registrar) @@ -399,7 +401,7 @@ LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) //make chevron button LLTextBox::Params more_button_params(p.more_button); mMoreTextBox = LLUICtrlFactory::create<LLTextBox> (more_button_params); - mMoreTextBox->setClickedCallback(boost::bind(&LLFavoritesBarCtrl::showDropDownMenu, this)); + mMoreTextBox->setClickedCallback(boost::bind(&LLFavoritesBarCtrl::onMoreTextBoxClicked, this)); addChild(mMoreTextBox); mDropDownItemsCount = 0; @@ -975,6 +977,12 @@ BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &it return TRUE; } +void LLFavoritesBarCtrl::onMoreTextBoxClicked() +{ + LLUI::getInstance()->getMousePositionScreen(&mMouseX, &mMouseY); + showDropDownMenu(); +} + void LLFavoritesBarCtrl::showDropDownMenu() { if (mOverflowMenuHandle.isDead()) @@ -1130,7 +1138,7 @@ void LLFavoritesBarCtrl::positionAndShowMenu(LLToggleableMenu* menu) } } - LLMenuGL::showPopup(this, menu, menu_x, menu_y); + LLMenuGL::showPopup(this, menu, menu_x, menu_y, mMouseX, mMouseY); } void LLFavoritesBarCtrl::onButtonClick(LLUUID item_id) diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 571208aa31..868f1c83c8 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -96,6 +96,8 @@ protected: void showDropDownMenu(); + void onMoreTextBoxClicked(); + LLHandle<LLView> mOverflowMenuHandle; LLHandle<LLView> mContextMenuHandle; @@ -163,6 +165,9 @@ private: BOOL mTabsHighlightEnabled; + S32 mMouseX; + S32 mMouseY; + boost::signals2::connection mEndDragConnection; }; diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index d915a9fd26..dc9816c9f7 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -379,22 +379,6 @@ F32 gpu_benchmark(); #if LL_WINDOWS -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific - -U32 exception_benchmark_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) -{ - if (code == STATUS_MSC_EXCEPTION) - { - // C++ exception, go on - return EXCEPTION_CONTINUE_SEARCH; - } - else - { - // handle it - return EXCEPTION_EXECUTE_HANDLER; - } -} - F32 logExceptionBenchmark() { // Todo: make a wrapper/class for SEH exceptions @@ -403,7 +387,7 @@ F32 logExceptionBenchmark() { gbps = gpu_benchmark(); } - __except (exception_benchmark_filter(GetExceptionCode(), GetExceptionInformation())) + __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation())) { // convert to C++ styled exception char integer_string[32]; diff --git a/indra/newview/llfloateravatartextures.cpp b/indra/newview/llfloateravatartextures.cpp index 8e654a53c4..ebb73fc1f7 100644 --- a/indra/newview/llfloateravatartextures.cpp +++ b/indra/newview/llfloateravatartextures.cpp @@ -55,7 +55,7 @@ BOOL LLFloaterAvatarTextures::postBuild() { for (U32 i=0; i < TEX_NUM_INDICES; i++) { - const std::string tex_name = LLAvatarAppearanceDictionary::getInstance()->getTexture(ETextureIndex(i))->mName; + const std::string tex_name = LLAvatarAppearance::getDictionary()->getTexture(ETextureIndex(i))->mName; mTextures[i] = getChild<LLTextureCtrl>(tex_name); } mTitle = getTitle(); @@ -77,7 +77,7 @@ static void update_texture_ctrl(LLVOAvatar* avatarp, ETextureIndex te) { LLUUID id = IMG_DEFAULT_AVATAR; - const LLAvatarAppearanceDictionary::TextureEntry* tex_entry = LLAvatarAppearanceDictionary::getInstance()->getTexture(te); + const LLAvatarAppearanceDictionary::TextureEntry* tex_entry = LLAvatarAppearance::getDictionary()->getTexture(te); if (tex_entry && tex_entry->mIsLocalTexture) { if (avatarp->isSelf()) @@ -165,14 +165,14 @@ void LLFloaterAvatarTextures::onClickDump(void* data) const LLTextureEntry* te = avatarp->getTE(i); if (!te) continue; - const LLAvatarAppearanceDictionary::TextureEntry* tex_entry = LLAvatarAppearanceDictionary::getInstance()->getTexture((ETextureIndex)(i)); + const LLAvatarAppearanceDictionary::TextureEntry* tex_entry = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)(i)); if (!tex_entry) continue; if (LLVOAvatar::isIndexLocalTexture((ETextureIndex)i)) { LLUUID id = IMG_DEFAULT_AVATAR; - LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getInstance()->getTEWearableType((ETextureIndex)i); + LLWearableType::EType wearable_type = LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex)i); if (avatarp->isSelf()) { LLViewerWearable *wearable = gAgentWearables.getViewerWearable(wearable_type, 0); diff --git a/indra/newview/llfloaterbuy.cpp b/indra/newview/llfloaterbuy.cpp index 4d3ebcda1e..ea93d3bfaa 100644 --- a/indra/newview/llfloaterbuy.cpp +++ b/indra/newview/llfloaterbuy.cpp @@ -49,7 +49,8 @@ #include "lltrans.h" LLFloaterBuy::LLFloaterBuy(const LLSD& key) -: LLFloater(key) +: LLFloater(key), + mSelectionUpdateSlot() { } @@ -179,12 +180,19 @@ void LLFloaterBuy::show(const LLSaleInfo& sale_info) floater->getChild<LLUICtrl>("buy_text")->setTextArg("[AMOUNT]", llformat("%d", sale_info.getSalePrice())); floater->getChild<LLUICtrl>("buy_name_text")->setTextArg("[NAME]", owner_name); + floater->showViews(true); + // Must do this after the floater is created, because // sometimes the inventory is already there and // the callback is called immediately. LLViewerObject* obj = selection->getFirstRootObject(); floater->registerVOInventoryListener(obj,NULL); floater->requestVOInventory(); + + if (!floater->mSelectionUpdateSlot.connected()) + { + floater->mSelectionUpdateSlot = LLSelectMgr::getInstance()->mUpdateSignal.connect(boost::bind(&LLFloaterBuy::onSelectionChanged, floater)); + } } void LLFloaterBuy::inventoryChanged(LLViewerObject* obj, @@ -280,6 +288,30 @@ void LLFloaterBuy::inventoryChanged(LLViewerObject* obj, removeVOInventoryListener(); } +void LLFloaterBuy::onSelectionChanged() +{ + + if (LLSelectMgr::getInstance()->getEditSelection()->getRootObjectCount() == 0) + { + removeVOInventoryListener(); + closeFloater(); + } + else if (LLSelectMgr::getInstance()->getEditSelection()->getRootObjectCount() > 1) + { + removeVOInventoryListener(); + showViews(false); + reset(); + setTitle(getString("mupliple_selected")); + } +} + +void LLFloaterBuy::showViews(bool show) +{ + getChild<LLUICtrl>("buy_btn")->setEnabled(show); + getChild<LLUICtrl>("buy_text")->setVisible(show); + getChild<LLUICtrl>("buy_name_text")->setVisible(show); +} + void LLFloaterBuy::onClickBuy() { // Put the items where we put new folders. @@ -303,5 +335,10 @@ void LLFloaterBuy::onClickCancel() // virtual void LLFloaterBuy::onClose(bool app_quitting) { + if (mSelectionUpdateSlot.connected()) + { + mSelectionUpdateSlot.disconnect(); + } + mObjectSelection.clear(); } diff --git a/indra/newview/llfloaterbuy.h b/indra/newview/llfloaterbuy.h index 3ec642dee1..e83b3c6ba6 100644 --- a/indra/newview/llfloaterbuy.h +++ b/indra/newview/llfloaterbuy.h @@ -63,12 +63,17 @@ protected: S32 serial_num, void* data); + void onSelectionChanged(); + void showViews(bool show); + void onClickBuy(); void onClickCancel(); private: LLSafeHandle<LLObjectSelection> mObjectSelection; LLSaleInfo mSaleInfo; + + boost::signals2::connection mSelectionUpdateSlot; }; #endif diff --git a/indra/newview/llfloaterbuycurrency.cpp b/indra/newview/llfloaterbuycurrency.cpp index 91436e52fe..1751d54b5a 100644 --- a/indra/newview/llfloaterbuycurrency.cpp +++ b/indra/newview/llfloaterbuycurrency.cpp @@ -74,7 +74,6 @@ public: void onClickBuy(); void onClickCancel(); - void onClickErrorWeb(); }; LLFloater* LLFloaterBuyCurrency::buildFloater(const LLSD& key) @@ -132,7 +131,6 @@ BOOL LLFloaterBuyCurrencyUI::postBuild() getChild<LLUICtrl>("buy_btn")->setCommitCallback( boost::bind(&LLFloaterBuyCurrencyUI::onClickBuy, this)); getChild<LLUICtrl>("cancel_btn")->setCommitCallback( boost::bind(&LLFloaterBuyCurrencyUI::onClickCancel, this)); - getChild<LLUICtrl>("error_web")->setCommitCallback( boost::bind(&LLFloaterBuyCurrencyUI::onClickErrorWeb, this)); center(); @@ -173,7 +171,6 @@ void LLFloaterBuyCurrencyUI::updateUI() // hide most widgets - we'll turn them on as needed next getChildView("info_buying")->setVisible(FALSE); - getChildView("info_cannot_buy")->setVisible(FALSE); getChildView("info_need_more")->setVisible(FALSE); getChildView("purchase_warning_repurchase")->setVisible(FALSE); getChildView("purchase_warning_notenough")->setVisible(FALSE); @@ -183,32 +180,17 @@ void LLFloaterBuyCurrencyUI::updateUI() if (hasError) { // display an error from the server - getChildView("normal_background")->setVisible(FALSE); - getChildView("error_background")->setVisible(TRUE); - getChildView("info_cannot_buy")->setVisible(TRUE); - getChildView("cannot_buy_message")->setVisible(TRUE); - getChildView("balance_label")->setVisible(FALSE); - getChildView("balance_amount")->setVisible(FALSE); - getChildView("buying_label")->setVisible(FALSE); - getChildView("buying_amount")->setVisible(FALSE); - getChildView("total_label")->setVisible(FALSE); - getChildView("total_amount")->setVisible(FALSE); - - LLTextBox* message = getChild<LLTextBox>("cannot_buy_message"); - if (message) - { - message->setText(mManager.errorMessage()); - } - - getChildView("error_web")->setVisible( !mManager.errorURI().empty()); + LLSD args; + args["TITLE"] = getString("info_cannot_buy"); + args["MESSAGE"] = mManager.errorMessage(); + LLNotificationsUtil::add("CouldNotBuyCurrency", args); + mManager.clearError(); + closeFloater(); } else { // display the main Buy L$ interface getChildView("normal_background")->setVisible(TRUE); - getChildView("error_background")->setVisible(FALSE); - getChildView("cannot_buy_message")->setVisible(FALSE); - getChildView("error_web")->setVisible(FALSE); if (mHasTarget) { @@ -278,14 +260,6 @@ void LLFloaterBuyCurrencyUI::onClickCancel() LLStatusBar::sendMoneyBalanceRequest(); } -void LLFloaterBuyCurrencyUI::onClickErrorWeb() -{ - LLWeb::loadURL(mManager.errorURI()); - closeFloater(); - // Update L$ balance - LLStatusBar::sendMoneyBalanceRequest(); -} - // static void LLFloaterBuyCurrency::buyCurrency() { diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp index d574f1433f..3b192ff81b 100644 --- a/indra/newview/llfloatercamera.cpp +++ b/indra/newview/llfloatercamera.cpp @@ -457,6 +457,7 @@ void LLFloaterCamera::switchMode(ECameraControlMode mode) switch (mode) { + case CAMERA_CTRL_MODE_PRESETS: case CAMERA_CTRL_MODE_PAN: sFreeCamera = false; clear_camera_tool(); @@ -467,13 +468,6 @@ void LLFloaterCamera::switchMode(ECameraControlMode mode) activate_camera_tool(); break; - case CAMERA_CTRL_MODE_PRESETS: - if(sFreeCamera) - { - switchMode(CAMERA_CTRL_MODE_FREE_CAMERA); - } - break; - default: //normally we won't occur here llassert_always(FALSE); @@ -528,7 +522,6 @@ void LLFloaterCamera::onClickCameraItem(const LLSD& param) { camera_floater->switchMode(CAMERA_CTRL_MODE_FREE_CAMERA); camera_floater->updateItemsSelection(); - camera_floater->fromFreeToPresets(); } } else @@ -586,15 +579,7 @@ void LLFloaterCamera::switchToPreset(const std::string& name) if (camera_floater) { camera_floater->updateItemsSelection(); - camera_floater->fromFreeToPresets(); - } -} - -void LLFloaterCamera::fromFreeToPresets() -{ - if (!sFreeCamera && mCurrMode == CAMERA_CTRL_MODE_FREE_CAMERA && mPrevMode == CAMERA_CTRL_MODE_PRESETS) - { - switchMode(CAMERA_CTRL_MODE_PRESETS); + camera_floater->switchMode(CAMERA_CTRL_MODE_PRESETS); } } diff --git a/indra/newview/llfloatercamera.h b/indra/newview/llfloatercamera.h index 9440f50c3f..a69b87ad16 100644 --- a/indra/newview/llfloatercamera.h +++ b/indra/newview/llfloatercamera.h @@ -69,10 +69,6 @@ public: /*switch to one of the camera presets (front, rear, side)*/ static void switchToPreset(const std::string& name); - /* move to CAMERA_CTRL_MODE_PRESETS from CAMERA_CTRL_MODE_FREE_CAMERA if we are on presets panel and - are not in free camera mode*/ - void fromFreeToPresets(); - virtual void onOpen(const LLSD& key); virtual void onClose(bool app_quitting); diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp index 44725cab70..580a3f2610 100644 --- a/indra/newview/llfloaterconversationpreview.cpp +++ b/indra/newview/llfloaterconversationpreview.cpp @@ -84,7 +84,7 @@ BOOL LLFloaterConversationPreview::postBuild() file = "chat"; } mChatHistoryFileName = file; - if (mIsGroup) + if (mIsGroup && !LLStringUtil::endsWith(mChatHistoryFileName, GROUP_CHAT_SUFFIX)) { mChatHistoryFileName += GROUP_CHAT_SUFFIX; } diff --git a/indra/newview/llfloaterdeleteprefpreset.cpp b/indra/newview/llfloaterdeleteprefpreset.cpp index 0765756b43..819b2bcee2 100644 --- a/indra/newview/llfloaterdeleteprefpreset.cpp +++ b/indra/newview/llfloaterdeleteprefpreset.cpp @@ -59,8 +59,17 @@ BOOL LLFloaterDeletePrefPreset::postBuild() void LLFloaterDeletePrefPreset::onOpen(const LLSD& key) { mSubdirectory = key.asString(); - std::string floater_title = getString(std::string("title_") + mSubdirectory); - setTitle(floater_title); + std::string title_type = std::string("title_") + mSubdirectory; + if (hasString(title_type)) + { + std::string floater_title = getString(title_type); + setTitle(floater_title); + } + else + { + LL_WARNS() << title_type << " not found" << LL_ENDL; + setTitle(title_type); + } LLComboBox* combo = getChild<LLComboBox>("preset_combo"); EDefaultOptions option = DEFAULT_HIDE; diff --git a/indra/newview/llfloatereditenvironmentbase.cpp b/indra/newview/llfloatereditenvironmentbase.cpp new file mode 100644 index 0000000000..e888144b6a --- /dev/null +++ b/indra/newview/llfloatereditenvironmentbase.cpp @@ -0,0 +1,479 @@ +/** + * @file llfloatereditenvironmentbase.cpp + * @brief Floaters to create and edit fixed settings for sky and water. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatereditenvironmentbase.h" + +#include <boost/make_shared.hpp> + +// libs +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llfilepicker.h" +#include "llsettingspicker.h" +#include "llviewerparcelmgr.h" + +// newview +#include "llsettingssky.h" +#include "llsettingswater.h" + +#include "llenvironment.h" +#include "llagent.h" +#include "llparcel.h" + +#include "llsettingsvo.h" +#include "llinventorymodel.h" + +namespace +{ + const std::string ACTION_APPLY_LOCAL("apply_local"); + const std::string ACTION_APPLY_PARCEL("apply_parcel"); + const std::string ACTION_APPLY_REGION("apply_region"); +} + +//========================================================================= +const std::string LLFloaterEditEnvironmentBase::KEY_INVENTORY_ID("inventory_id"); + + +//========================================================================= + +class LLFixedSettingCopiedCallback : public LLInventoryCallback +{ +public: + LLFixedSettingCopiedCallback(LLHandle<LLFloater> handle) : mHandle(handle) {} + + virtual void fire(const LLUUID& inv_item_id) + { + if (!mHandle.isDead()) + { + LLViewerInventoryItem* item = gInventory.getItem(inv_item_id); + if (item) + { + LLFloaterEditEnvironmentBase* floater = (LLFloaterEditEnvironmentBase*)mHandle.get(); + floater->onInventoryCreated(item->getAssetUUID(), inv_item_id); + } + } + } + +private: + LLHandle<LLFloater> mHandle; +}; + +//========================================================================= +LLFloaterEditEnvironmentBase::LLFloaterEditEnvironmentBase(const LLSD &key) : + LLFloater(key), + mInventoryId(), + mInventoryItem(nullptr), + mIsDirty(false), + mCanCopy(false), + mCanMod(false), + mCanTrans(false), + mCanSave(false) +{ +} + +LLFloaterEditEnvironmentBase::~LLFloaterEditEnvironmentBase() +{ +} + +void LLFloaterEditEnvironmentBase::onFocusReceived() +{ + if (isInVisibleChain()) + { + updateEditEnvironment(); + LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_EDIT, LLEnvironment::TRANSITION_FAST); + } +} + +void LLFloaterEditEnvironmentBase::onFocusLost() +{ +} + +void LLFloaterEditEnvironmentBase::loadInventoryItem(const LLUUID &inventoryId, bool can_trans) +{ + if (inventoryId.isNull()) + { + mInventoryItem = nullptr; + mInventoryId.setNull(); + mCanMod = true; + mCanCopy = true; + mCanTrans = true; + return; + } + + mInventoryId = inventoryId; + LL_INFOS("SETTINGS") << "Setting edit inventory item to " << mInventoryId << "." << LL_ENDL; + mInventoryItem = gInventory.getItem(mInventoryId); + + if (!mInventoryItem) + { + LL_WARNS("SETTINGS") << "Could not find inventory item with Id = " << mInventoryId << LL_ENDL; + LLNotificationsUtil::add("CantFindInvItem"); + closeFloater(); + + mInventoryId.setNull(); + mInventoryItem = nullptr; + return; + } + + if (mInventoryItem->getAssetUUID().isNull()) + { + LL_WARNS("ENVIRONMENT") << "Asset ID in inventory item is NULL (" << mInventoryId << ")" << LL_ENDL; + LLNotificationsUtil::add("UnableEditItem"); + closeFloater(); + + mInventoryId.setNull(); + mInventoryItem = nullptr; + return; + } + + mCanSave = true; + mCanCopy = mInventoryItem->getPermissions().allowCopyBy(gAgent.getID()); + mCanMod = mInventoryItem->getPermissions().allowModifyBy(gAgent.getID()); + mCanTrans = can_trans && mInventoryItem->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + + mExpectingAssetId = mInventoryItem->getAssetUUID(); + LLSettingsVOBase::getSettingsAsset(mInventoryItem->getAssetUUID(), + [this](LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status, LLExtStat) { onAssetLoaded(asset_id, settings, status); }); +} + + +void LLFloaterEditEnvironmentBase::checkAndConfirmSettingsLoss(LLFloaterEditEnvironmentBase::on_confirm_fn cb) +{ + if (isDirty()) + { + LLSD args(LLSDMap("TYPE", getEditSettings()->getSettingsType()) + ("NAME", getEditSettings()->getName())); + + // create and show confirmation textbox + LLNotificationsUtil::add("SettingsConfirmLoss", args, LLSD(), + [cb](const LLSD¬if, const LLSD&resp) + { + S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); + if (opt == 0) + cb(); + }); + } + else if (cb) + { + cb(); + } +} + +void LLFloaterEditEnvironmentBase::onAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status) +{ + if (asset_id != mExpectingAssetId) + { + LL_WARNS("ENVDAYEDIT") << "Expecting {" << mExpectingAssetId << "} got {" << asset_id << "} - throwing away." << LL_ENDL; + return; + } + mExpectingAssetId.setNull(); + clearDirtyFlag(); + + if (!settings || status) + { + LLSD args; + args["NAME"] = (mInventoryItem) ? mInventoryItem->getName() : asset_id.asString(); + LLNotificationsUtil::add("FailedToFindSettings", args); + closeFloater(); + return; + } + + if (settings->getFlag(LLSettingsBase::FLAG_NOSAVE)) + { + mCanSave = false; + mCanCopy = false; + mCanMod = false; + mCanTrans = false; + } + else + { + if (mInventoryItem) + settings->setName(mInventoryItem->getName()); + + if (mCanCopy) + settings->clearFlag(LLSettingsBase::FLAG_NOCOPY); + else + settings->setFlag(LLSettingsBase::FLAG_NOCOPY); + + if (mCanMod) + settings->clearFlag(LLSettingsBase::FLAG_NOMOD); + else + settings->setFlag(LLSettingsBase::FLAG_NOMOD); + + if (mCanTrans) + settings->clearFlag(LLSettingsBase::FLAG_NOTRANS); + else + settings->setFlag(LLSettingsBase::FLAG_NOTRANS); + } + + setEditSettingsAndUpdate(settings); +} + +void LLFloaterEditEnvironmentBase::onButtonImport() +{ + checkAndConfirmSettingsLoss([this](){ doImportFromDisk(); }); +} + +void LLFloaterEditEnvironmentBase::onSaveAsCommit(const LLSD& notification, const LLSD& response, const LLSettingsBase::ptr_t &settings) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + std::string settings_name = response["message"].asString(); + + LLInventoryObject::correctInventoryName(settings_name); + if (settings_name.empty()) + { + // Ideally notification should disable 'OK' button if name won't fit our requirements, + // for now either display notification, or use some default name + settings_name = "Unnamed"; + } + + if (mCanMod) + { + doApplyCreateNewInventory(settings_name, settings); + } + else if (mInventoryItem) + { + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + LLUUID parent_id = mInventoryItem->getParentUUID(); + if (marketplacelistings_id == parent_id) + { + parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); + } + + LLPointer<LLInventoryCallback> cb = new LLFixedSettingCopiedCallback(getHandle()); + copy_inventory_item( + gAgent.getID(), + mInventoryItem->getPermissions().getOwner(), + mInventoryItem->getUUID(), + parent_id, + settings_name, + cb); + } + else + { + LL_WARNS() << "Failed to copy fixed env setting" << LL_ENDL; + } + } +} + +void LLFloaterEditEnvironmentBase::onClickCloseBtn(bool app_quitting) +{ + if (!app_quitting) + checkAndConfirmSettingsLoss([this](){ closeFloater(); clearDirtyFlag(); }); + else + closeFloater(); +} + +void LLFloaterEditEnvironmentBase::doApplyCreateNewInventory(std::string settings_name, const LLSettingsBase::ptr_t &settings) +{ + if (mInventoryItem) + { + LLUUID parent_id = mInventoryItem->getParentUUID(); + U32 next_owner_perm = mInventoryItem->getPermissions().getMaskNextOwner(); + LLSettingsVOBase::createInventoryItem(settings, next_owner_perm, parent_id, settings_name, + [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); + } + else + { + LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); + // This method knows what sort of settings object to create. + LLSettingsVOBase::createInventoryItem(settings, parent_id, settings_name, + [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); + } +} + +void LLFloaterEditEnvironmentBase::doApplyUpdateInventory(const LLSettingsBase::ptr_t &settings) +{ + LL_DEBUGS("ENVEDIT") << "Update inventory for " << mInventoryId << LL_ENDL; + if (mInventoryId.isNull()) + { + LLSettingsVOBase::createInventoryItem(settings, gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS), std::string(), + [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); + } + else + { + LLSettingsVOBase::updateInventoryItem(settings, mInventoryId, + [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryUpdated(asset_id, inventory_id, results); }); + } +} + +void LLFloaterEditEnvironmentBase::doApplyEnvironment(const std::string &where, const LLSettingsBase::ptr_t &settings) +{ + U32 flags(0); + + if (mInventoryItem) + { + if (!mInventoryItem->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) + flags |= LLSettingsBase::FLAG_NOMOD; + if (!mInventoryItem->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) + flags |= LLSettingsBase::FLAG_NOTRANS; + } + + flags |= settings->getFlags(); + settings->setFlag(flags); + + if (where == ACTION_APPLY_LOCAL) + { + settings->setName("Local"); // To distinguish and make sure there is a name. Safe, because this is a copy. + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, settings); + } + else if (where == ACTION_APPLY_PARCEL) + { + LLParcel *parcel(LLViewerParcelMgr::instance().getAgentOrSelectedParcel()); + + if ((!parcel) || (parcel->getLocalID() == INVALID_PARCEL_ID)) + { + LL_WARNS("ENVIRONMENT") << "Can not identify parcel. Not applying." << LL_ENDL; + LLNotificationsUtil::add("WLParcelApplyFail"); + return; + } + + if (mInventoryItem && !isDirty()) + { + LLEnvironment::instance().updateParcel(parcel->getLocalID(), mInventoryItem->getAssetUUID(), mInventoryItem->getName(), LLEnvironment::NO_TRACK, -1, -1, flags); + } + else if (settings->getSettingsType() == "sky") + { + LLEnvironment::instance().updateParcel(parcel->getLocalID(), std::static_pointer_cast<LLSettingsSky>(settings), -1, -1); + } + else if (settings->getSettingsType() == "water") + { + LLEnvironment::instance().updateParcel(parcel->getLocalID(), std::static_pointer_cast<LLSettingsWater>(settings), -1, -1); + } + else if (settings->getSettingsType() == "day") + { + LLEnvironment::instance().updateParcel(parcel->getLocalID(), std::static_pointer_cast<LLSettingsDay>(settings), -1, -1); + } + } + else if (where == ACTION_APPLY_REGION) + { + if (mInventoryItem && !isDirty()) + { + LLEnvironment::instance().updateRegion(mInventoryItem->getAssetUUID(), mInventoryItem->getName(), LLEnvironment::NO_TRACK, -1, -1, flags); + } + else if (settings->getSettingsType() == "sky") + { + LLEnvironment::instance().updateRegion(std::static_pointer_cast<LLSettingsSky>(settings), -1, -1); + } + else if (settings->getSettingsType() == "water") + { + LLEnvironment::instance().updateRegion(std::static_pointer_cast<LLSettingsWater>(settings), -1, -1); + } + else if (settings->getSettingsType() == "day") + { + LLEnvironment::instance().updateRegion(std::static_pointer_cast<LLSettingsDay>(settings), -1, -1); + } + } + else + { + LL_WARNS("ENVIRONMENT") << "Unknown apply '" << where << "'" << LL_ENDL; + return; + } + +} + +void LLFloaterEditEnvironmentBase::doCloseInventoryFloater(bool quitting) +{ + LLFloater* floaterp = mInventoryFloater.get(); + + if (floaterp) + { + floaterp->closeFloater(quitting); + } +} + +void LLFloaterEditEnvironmentBase::onInventoryCreated(LLUUID asset_id, LLUUID inventory_id, LLSD results) +{ + LL_WARNS("ENVIRONMENT") << "Inventory item " << inventory_id << " has been created with asset " << asset_id << " results are:" << results << LL_ENDL; + + if (inventory_id.isNull() || !results["success"].asBoolean()) + { + LLNotificationsUtil::add("CantCreateInventory"); + return; + } + onInventoryCreated(asset_id, inventory_id); +} + +void LLFloaterEditEnvironmentBase::onInventoryCreated(LLUUID asset_id, LLUUID inventory_id) +{ + bool can_trans = true; + if (mInventoryItem) + { + LLPermissions perms = mInventoryItem->getPermissions(); + + LLInventoryItem *created_item = gInventory.getItem(mInventoryId); + + if (created_item) + { + can_trans = perms.allowOperationBy(PERM_TRANSFER, gAgent.getID()); + created_item->setPermissions(perms); + created_item->updateServer(false); + } + } + + clearDirtyFlag(); + setFocus(TRUE); // Call back the focus... + loadInventoryItem(inventory_id, can_trans); +} + +void LLFloaterEditEnvironmentBase::onInventoryUpdated(LLUUID asset_id, LLUUID inventory_id, LLSD results) +{ + LL_WARNS("ENVIRONMENT") << "Inventory item " << inventory_id << " has been updated with asset " << asset_id << " results are:" << results << LL_ENDL; + + clearDirtyFlag(); + if (inventory_id != mInventoryId) + { + loadInventoryItem(inventory_id); + } +} + +void LLFloaterEditEnvironmentBase::onPanelDirtyFlagChanged(bool value) +{ + if (value) + setDirtyFlag(); +} + +//------------------------------------------------------------------------- +bool LLFloaterEditEnvironmentBase::canUseInventory() const +{ + return LLEnvironment::instance().isInventoryEnabled(); +} + +bool LLFloaterEditEnvironmentBase::canApplyRegion() const +{ + return gAgent.canManageEstate(); +} + +bool LLFloaterEditEnvironmentBase::canApplyParcel() const +{ + return LLEnvironment::instance().canAgentUpdateParcelEnvironment(); +} + +//========================================================================= diff --git a/indra/newview/llfloatereditenvironmentbase.h b/indra/newview/llfloatereditenvironmentbase.h new file mode 100644 index 0000000000..7c7cf5bdcd --- /dev/null +++ b/indra/newview/llfloatereditenvironmentbase.h @@ -0,0 +1,148 @@ +/** + * @file llfloatereditenvironmentbase.h + * @brief Floaters to create and edit fixed settings for sky and water. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_FLOATEREDITENVIRONMENTBASE_H +#define LL_FLOATEREDITENVIRONMENTBASE_H + +#include "llfloater.h" +#include "llsettingsbase.h" +#include "llflyoutcombobtn.h" +#include "llinventory.h" + +#include "boost/signals2.hpp" + +class LLTabContainer; +class LLButton; +class LLLineEditor; +class LLFloaterSettingsPicker; +class LLFixedSettingCopiedCallback; + +class LLFloaterEditEnvironmentBase : public LLFloater +{ + LOG_CLASS(LLFloaterEditEnvironmentBase); + + friend class LLFixedSettingCopiedCallback; + +public: + static const std::string KEY_INVENTORY_ID; + + LLFloaterEditEnvironmentBase(const LLSD &key); + ~LLFloaterEditEnvironmentBase(); + + virtual void onFocusReceived() override; + virtual void onFocusLost() override; + + virtual LLSettingsBase::ptr_t getEditSettings() const = 0; + + virtual BOOL isDirty() const override { return getIsDirty(); } + +protected: + typedef std::function<void()> on_confirm_fn; + + virtual void setEditSettingsAndUpdate(const LLSettingsBase::ptr_t &settings) = 0; + virtual void updateEditEnvironment() = 0; + + virtual LLFloaterSettingsPicker *getSettingsPicker() = 0; + + void loadInventoryItem(const LLUUID &inventoryId, bool can_trans = true); + + void checkAndConfirmSettingsLoss(on_confirm_fn cb); + + virtual void doImportFromDisk() = 0; + virtual void doApplyCreateNewInventory(std::string settings_name, const LLSettingsBase::ptr_t &settings); + virtual void doApplyUpdateInventory(const LLSettingsBase::ptr_t &settings); + virtual void doApplyEnvironment(const std::string &where, const LLSettingsBase::ptr_t &settings); + void doCloseInventoryFloater(bool quitting = false); + + bool canUseInventory() const; + bool canApplyRegion() const; + bool canApplyParcel() const; + + LLUUID mInventoryId; + LLInventoryItem * mInventoryItem; + LLHandle<LLFloater> mInventoryFloater; + bool mCanCopy; + bool mCanMod; + bool mCanTrans; + bool mCanSave; + + bool mIsDirty; + + void onInventoryCreated(LLUUID asset_id, LLUUID inventory_id); + void onInventoryCreated(LLUUID asset_id, LLUUID inventory_id, LLSD results); + void onInventoryUpdated(LLUUID asset_id, LLUUID inventory_id, LLSD results); + + bool getIsDirty() const { return mIsDirty; } + void setDirtyFlag() { mIsDirty = true; } + virtual void clearDirtyFlag() = 0; + + void onPanelDirtyFlagChanged(bool); + + virtual void onClickCloseBtn(bool app_quitting = false) override; + void onSaveAsCommit(const LLSD& notification, const LLSD& response, const LLSettingsBase::ptr_t &settings); + void onButtonImport(); + + void onAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settins, S32 status); + +private: + LLUUID mExpectingAssetId; // for asset load confirmation +}; + +class LLSettingsEditPanel : public LLPanel +{ +public: + virtual void setSettings(const LLSettingsBase::ptr_t &) = 0; + + typedef boost::signals2::signal<void(LLPanel *, bool)> on_dirty_charged_sg; + typedef boost::signals2::connection connection_t; + + inline bool getIsDirty() const { return mIsDirty; } + inline void setIsDirty() { mIsDirty = true; if (!mOnDirtyChanged.empty()) mOnDirtyChanged(this, mIsDirty); } + inline void clearIsDirty() { mIsDirty = false; if (!mOnDirtyChanged.empty()) mOnDirtyChanged(this, mIsDirty); } + + inline bool getCanChangeSettings() const { return mCanEdit; } + inline void setCanChangeSettings(bool flag) { mCanEdit = flag; } + + inline connection_t setOnDirtyFlagChanged(on_dirty_charged_sg::slot_type cb) { return mOnDirtyChanged.connect(cb); } + + +protected: + LLSettingsEditPanel() : + LLPanel(), + mIsDirty(false), + mOnDirtyChanged() + {} + +private: + void onTextureChanged(LLUUID &inventory_item_id); + + bool mIsDirty; + bool mCanEdit; + + on_dirty_charged_sg mOnDirtyChanged; +}; + +#endif // LL_FLOATERENVIRONMENTBASE_H diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp index ea22043de8..0501c287ad 100644 --- a/indra/newview/llfloatereditextdaycycle.cpp +++ b/indra/newview/llfloatereditextdaycycle.cpp @@ -125,7 +125,6 @@ namespace { } //========================================================================= -const std::string LLFloaterEditExtDayCycle::KEY_INVENTORY_ID("inventory_id"); const std::string LLFloaterEditExtDayCycle::KEY_EDIT_CONTEXT("edit_context"); const std::string LLFloaterEditExtDayCycle::KEY_DAY_LENGTH("day_length"); const std::string LLFloaterEditExtDayCycle::KEY_CANMOD("canmod"); @@ -133,7 +132,7 @@ const std::string LLFloaterEditExtDayCycle::KEY_CANMOD("canmod"); const std::string LLFloaterEditExtDayCycle::VALUE_CONTEXT_INVENTORY("inventory"); const std::string LLFloaterEditExtDayCycle::VALUE_CONTEXT_PARCEL("parcel"); const std::string LLFloaterEditExtDayCycle::VALUE_CONTEXT_REGION("region"); - +/* //========================================================================= class LLDaySettingCopiedCallback : public LLInventoryCallback @@ -156,12 +155,12 @@ public: private: LLHandle<LLFloater> mHandle; -}; +};*/ //========================================================================= LLFloaterEditExtDayCycle::LLFloaterEditExtDayCycle(const LLSD &key) : - LLFloater(key), + LLFloaterEditEnvironmentBase(key), mFlyoutControl(nullptr), mDayLength(0), mCurrentTrack(1), @@ -170,19 +169,12 @@ LLFloaterEditExtDayCycle::LLFloaterEditExtDayCycle(const LLSD &key) : mFramesSlider(nullptr), mCurrentTimeLabel(nullptr), mImportButton(nullptr), - mInventoryId(), - mInventoryItem(nullptr), mLoadFrame(nullptr), mSkyBlender(), mWaterBlender(), mScratchSky(), mScratchWater(), mIsPlaying(false), - mIsDirty(false), - mCanSave(false), - mCanCopy(false), - mCanMod(false), - mCanTrans(false), mCloneTrack(nullptr), mLoadTrack(nullptr), mClearTrack(nullptr) @@ -425,19 +417,6 @@ void LLFloaterEditExtDayCycle::onClose(bool app_quitting) } } -void LLFloaterEditExtDayCycle::onFocusReceived() -{ - if (isInVisibleChain()) - { - updateEditEnvironment(); - LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_EDIT, LLEnvironment::TRANSITION_FAST); - } -} - -void LLFloaterEditExtDayCycle::onFocusLost() -{ -} - void LLFloaterEditExtDayCycle::onVisibilityChange(BOOL new_visibility) { @@ -488,6 +467,10 @@ void LLFloaterEditExtDayCycle::refresh() LLFloater::refresh(); } +void LLFloaterEditExtDayCycle::setEditSettingsAndUpdate(const LLSettingsBase::ptr_t &settings) +{ + setEditDayCycle(std::dynamic_pointer_cast<LLSettingsDay>(settings)); +} void LLFloaterEditExtDayCycle::setEditDayCycle(const LLSettingsDay::ptr_t &pday) { @@ -700,63 +683,6 @@ void LLFloaterEditExtDayCycle::onButtonApply(LLUICtrl *ctrl, const LLSD &data) } } -void LLFloaterEditExtDayCycle::onSaveAsCommit(const LLSD& notification, const LLSD& response, const LLSettingsDay::ptr_t &day) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) - { - std::string settings_name = response["message"].asString(); - - LLInventoryObject::correctInventoryName(settings_name); - if (settings_name.empty()) - { - // Ideally notification should disable 'OK' button if name won't fit our requirements, - // for now either display notification, or use some default name - settings_name = "Unnamed"; - } - - if (mCanMod) - { - doApplyCreateNewInventory(day, settings_name); - } - else if (mInventoryItem) - { - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - LLUUID parent_id = mInventoryItem->getParentUUID(); - if (marketplacelistings_id == parent_id) - { - parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); - } - - LLPointer<LLInventoryCallback> cb = new LLDaySettingCopiedCallback(getHandle()); - copy_inventory_item( - gAgent.getID(), - mInventoryItem->getPermissions().getOwner(), - mInventoryItem->getUUID(), - parent_id, - settings_name, - cb); - } - else - { - LL_WARNS() << "Failed to copy day setting" << LL_ENDL; - } - } -} - -void LLFloaterEditExtDayCycle::onClickCloseBtn(bool app_quitting /*= false*/) -{ - if (!app_quitting) - checkAndConfirmSettingsLoss([this](){ closeFloater(); clearDirtyFlag(); }); - else - closeFloater(); -} - -void LLFloaterEditExtDayCycle::onButtonImport() -{ - checkAndConfirmSettingsLoss([this]() { doImportFromDisk(); }); -} - void LLFloaterEditExtDayCycle::onButtonLoadFrame() { doOpenInventoryFloater((mCurrentTrack == LLSettingsDay::TRACK_WATER) ? LLSettingsType::ST_WATER : LLSettingsType::ST_SKY, LLUUID::null); @@ -1053,35 +979,6 @@ void LLFloaterEditExtDayCycle::onFrameSliderMouseUp(S32 x, S32 y, MASK mask) selectFrame(sliderpos, LLSettingsDay::DEFAULT_FRAME_SLOP_FACTOR); } - -void LLFloaterEditExtDayCycle::onPanelDirtyFlagChanged(bool value) -{ - if (value) - setDirtyFlag(); -} - -void LLFloaterEditExtDayCycle::checkAndConfirmSettingsLoss(on_confirm_fn cb) -{ - if (isDirty()) - { - LLSD args(LLSDMap("TYPE", mEditDay->getSettingsType()) - ("NAME", mEditDay->getName())); - - // create and show confirmation textbox - LLNotificationsUtil::add("SettingsConfirmLoss", args, LLSD(), - [cb](const LLSD¬if, const LLSD&resp) - { - S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); - if (opt == 0) - cb(); - }); - } - else if (cb) - { - cb(); - } -} - void LLFloaterEditExtDayCycle::onTimeSliderCallback() { stopPlay(); @@ -1435,106 +1332,6 @@ LLFloaterEditExtDayCycle::connection_t LLFloaterEditExtDayCycle::setEditCommitSi return mCommitSignal.connect(cb); } -void LLFloaterEditExtDayCycle::loadInventoryItem(const LLUUID &inventoryId, bool can_trans) -{ - if (inventoryId.isNull()) - { - mInventoryItem = nullptr; - mInventoryId.setNull(); - mCanSave = true; - mCanCopy = true; - mCanMod = true; - mCanTrans = true; - return; - } - - mInventoryId = inventoryId; - LL_INFOS("ENVDAYEDIT") << "Setting edit inventory item to " << mInventoryId << "." << LL_ENDL; - mInventoryItem = gInventory.getItem(mInventoryId); - - if (!mInventoryItem) - { - LL_WARNS("ENVDAYEDIT") << "Could not find inventory item with Id = " << mInventoryId << LL_ENDL; - - LLNotificationsUtil::add("CantFindInvItem"); - closeFloater(); - mInventoryId.setNull(); - mInventoryItem = nullptr; - return; - } - - if (mInventoryItem->getAssetUUID().isNull()) - { - LL_WARNS("ENVDAYEDIT") << "Asset ID in inventory item is NULL (" << mInventoryId << ")" << LL_ENDL; - - LLNotificationsUtil::add("UnableEditItem"); - closeFloater(); - - mInventoryId.setNull(); - mInventoryItem = nullptr; - return; - } - - mCanSave = true; - mCanCopy = mInventoryItem->getPermissions().allowCopyBy(gAgent.getID()); - mCanMod = mInventoryItem->getPermissions().allowModifyBy(gAgent.getID()); - mCanTrans = can_trans && mInventoryItem->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); - - mExpectingAssetId = mInventoryItem->getAssetUUID(); - LLSettingsVOBase::getSettingsAsset(mInventoryItem->getAssetUUID(), - [this](LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status, LLExtStat) { onAssetLoaded(asset_id, settings, status); }); -} - -void LLFloaterEditExtDayCycle::onAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status) -{ - if (asset_id != mExpectingAssetId) - { - LL_WARNS("ENVDAYEDIT") << "Expecting {" << mExpectingAssetId << "} got {" << asset_id << "} - throwing away." << LL_ENDL; - return; - } - mExpectingAssetId.setNull(); - clearDirtyFlag(); - - if (!settings || status) - { - LLSD args; - args["NAME"] = (mInventoryItem) ? mInventoryItem->getName() : "Unknown"; - LLNotificationsUtil::add("FailedToFindSettings", args); - closeFloater(); - return; - } - - if (settings->getFlag(LLSettingsBase::FLAG_NOSAVE)) - { - mCanSave = false; - mCanCopy = false; - mCanMod = false; - mCanTrans = false; - } - else - { - if (mCanCopy) - settings->clearFlag(LLSettingsBase::FLAG_NOCOPY); - else - settings->setFlag(LLSettingsBase::FLAG_NOCOPY); - - if (mCanMod) - settings->clearFlag(LLSettingsBase::FLAG_NOMOD); - else - settings->setFlag(LLSettingsBase::FLAG_NOMOD); - - if (mCanTrans) - settings->clearFlag(LLSettingsBase::FLAG_NOTRANS); - else - settings->setFlag(LLSettingsBase::FLAG_NOTRANS); - - if (mInventoryItem) - settings->setName(mInventoryItem->getName()); - } - - setEditDayCycle(std::dynamic_pointer_cast<LLSettingsDay>(settings)); -} - void LLFloaterEditExtDayCycle::updateEditEnvironment(void) { if (!mEditDay) @@ -1670,93 +1467,6 @@ void LLFloaterEditExtDayCycle::reblendSettings() mWaterBlender->setPosition(position); } -void LLFloaterEditExtDayCycle::doApplyCreateNewInventory(const LLSettingsDay::ptr_t &day, std::string settings_name) -{ - if (mInventoryItem) - { - LLUUID parent_id = mInventoryItem->getParentUUID(); - U32 next_owner_perm = mInventoryItem->getPermissions().getMaskNextOwner(); - LLSettingsVOBase::createInventoryItem(day, next_owner_perm, parent_id, settings_name, - [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); - } - else - { - LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); - // This method knows what sort of settings object to create. - LLSettingsVOBase::createInventoryItem(day, parent_id, settings_name, - [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); - } -} - -void LLFloaterEditExtDayCycle::doApplyUpdateInventory(const LLSettingsDay::ptr_t &day) -{ - if (mInventoryId.isNull()) - LLSettingsVOBase::createInventoryItem(day, gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS), std::string(), - [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); - else - LLSettingsVOBase::updateInventoryItem(day, mInventoryId, - [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryUpdated(asset_id, inventory_id, results); }); -} - -void LLFloaterEditExtDayCycle::doApplyEnvironment(const std::string &where, const LLSettingsDay::ptr_t &day) -{ - U32 flags(0); - - if (mInventoryItem) - { - if (!mInventoryItem->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) - flags |= LLSettingsBase::FLAG_NOMOD; - if (!mInventoryItem->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) - flags |= LLSettingsBase::FLAG_NOTRANS; - } - - flags |= day->getFlags(); - day->setFlag(flags); - - if (where == ACTION_APPLY_LOCAL) - { - day->setName("Local"); // To distinguish and make sure there is a name. Safe, because this is a copy. - LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, day); - } - else if (where == ACTION_APPLY_PARCEL) - { - LLParcel *parcel(LLViewerParcelMgr::instance().getAgentOrSelectedParcel()); - - if ((!parcel) || (parcel->getLocalID() == INVALID_PARCEL_ID)) - { - LL_WARNS("ENVDAYEDIT") << "Can not identify parcel. Not applying." << LL_ENDL; - LLNotificationsUtil::add("WLParcelApplyFail"); - return; - } - - if (mInventoryItem && !isDirty()) - { - LLEnvironment::instance().updateParcel(parcel->getLocalID(), mInventoryItem->getAssetUUID(), mInventoryItem->getName(), LLEnvironment::NO_TRACK, -1, -1, flags); - } - else - { - LLEnvironment::instance().updateParcel(parcel->getLocalID(), day, -1, -1); - } - } - else if (where == ACTION_APPLY_REGION) - { - if (mInventoryItem && !isDirty()) - { - LLEnvironment::instance().updateRegion(mInventoryItem->getAssetUUID(), mInventoryItem->getName(), LLEnvironment::NO_TRACK, -1, -1, flags); - } - else - { - LLEnvironment::instance().updateRegion(day, -1, -1); - } - } - else - { - LL_WARNS("ENVDAYEDIT") << "Unknown apply '" << where << "'" << LL_ENDL; - return; - } - -} - void LLFloaterEditExtDayCycle::doApplyCommit(LLSettingsDay::ptr_t day) { if (!mCommitSignal.empty()) @@ -1793,51 +1503,6 @@ bool LLFloaterEditExtDayCycle::isAddingFrameAllowed() return mFramesSlider->canAddSliders(); } -void LLFloaterEditExtDayCycle::onInventoryCreated(LLUUID asset_id, LLUUID inventory_id, LLSD results) -{ - LL_INFOS("ENVDAYEDIT") << "Inventory item " << inventory_id << " has been created with asset " << asset_id << " results are:" << results << LL_ENDL; - - if (inventory_id.isNull() || !results["success"].asBoolean()) - { - LLNotificationsUtil::add("CantCreateInventory"); - return; - } - onInventoryCreated(asset_id, inventory_id); -} - -void LLFloaterEditExtDayCycle::onInventoryCreated(LLUUID asset_id, LLUUID inventory_id) -{ - bool can_trans = true; - if (mInventoryItem) - { - LLPermissions perms = mInventoryItem->getPermissions(); - - LLInventoryItem *created_item = gInventory.getItem(mInventoryId); - - if (created_item) - { - can_trans = perms.allowOperationBy(PERM_TRANSFER, gAgent.getID()); - created_item->setPermissions(perms); - created_item->updateServer(false); - } - } - - clearDirtyFlag(); - setFocus(TRUE); // Call back the focus... - loadInventoryItem(inventory_id, can_trans); -} - -void LLFloaterEditExtDayCycle::onInventoryUpdated(LLUUID asset_id, LLUUID inventory_id, LLSD results) -{ - LL_WARNS("ENVDAYEDIT") << "Inventory item " << inventory_id << " has been updated with asset " << asset_id << " results are:" << results << LL_ENDL; - - clearDirtyFlag(); - if (inventory_id != mInventoryId) - { - loadInventoryItem(inventory_id); - } -} - void LLFloaterEditExtDayCycle::doImportFromDisk() { // Load a a legacy Windlight XML from disk. (new LLFilePickerReplyThread(boost::bind(&LLFloaterEditExtDayCycle::loadSettingFromFile, this, _1), LLFilePicker::FFLOAD_XML, false))->getFile(); @@ -1864,21 +1529,6 @@ void LLFloaterEditExtDayCycle::loadSettingFromFile(const std::vector<std::string setEditDayCycle(legacyday); } -bool LLFloaterEditExtDayCycle::canUseInventory() const -{ - return LLEnvironment::instance().isInventoryEnabled(); -} - -bool LLFloaterEditExtDayCycle::canApplyRegion() const -{ - return gAgent.canManageEstate(); -} - -bool LLFloaterEditExtDayCycle::canApplyParcel() const -{ - return LLEnvironment::instance().canAgentUpdateParcelEnvironment(); -} - void LLFloaterEditExtDayCycle::startPlay() { doCloseInventoryFloater(); @@ -1983,14 +1633,8 @@ void LLFloaterEditExtDayCycle::doCloseTrackFloater(bool quitting) } } -void LLFloaterEditExtDayCycle::onPickerCommitTrackId(U32 track_id) +LLFloaterSettingsPicker * LLFloaterEditExtDayCycle::getSettingsPicker() { - cloneTrack(track_id, mCurrentTrack); -} - -void LLFloaterEditExtDayCycle::doOpenInventoryFloater(LLSettingsType::type_e type, LLUUID curritem) -{ -// LLUI::sWindow->setCursor(UI_CURSOR_WAIT); LLFloaterSettingsPicker *picker = static_cast<LLFloaterSettingsPicker *>(mInventoryFloater.get()); // Show the dialog @@ -2003,7 +1647,17 @@ void LLFloaterEditExtDayCycle::doOpenInventoryFloater(LLSettingsType::type_e typ picker->setCommitCallback([this](LLUICtrl *, const LLSD &data){ onPickerCommitSetting(data["ItemId"].asUUID(), data["Track"].asInteger()); }); } + return picker; +} + +void LLFloaterEditExtDayCycle::onPickerCommitTrackId(U32 track_id) +{ + cloneTrack(track_id, mCurrentTrack); +} +void LLFloaterEditExtDayCycle::doOpenInventoryFloater(LLSettingsType::type_e type, LLUUID curritem) +{ + LLFloaterSettingsPicker *picker = getSettingsPicker(); picker->setSettingsFilter(type); picker->setSettingsItemId(curritem); if (type == LLSettingsType::ST_DAYCYCLE) @@ -2018,16 +1672,6 @@ void LLFloaterEditExtDayCycle::doOpenInventoryFloater(LLSettingsType::type_e typ picker->setFocus(TRUE); } -void LLFloaterEditExtDayCycle::doCloseInventoryFloater(bool quitting) -{ - LLFloater* floaterp = mInventoryFloater.get(); - - if (floaterp) - { - floaterp->closeFloater(quitting); - } -} - void LLFloaterEditExtDayCycle::onPickerCommitSetting(LLUUID item_id, S32 track) { LLSettingsBase::TrackPosition frame(mTimeSlider->getCurSliderValue()); @@ -2118,7 +1762,9 @@ void LLFloaterEditExtDayCycle::onAssetLoadedForInsertion(LLUUID item_id, LLUUID LLInventoryItem *inv_item = gInventory.getItem(item_id); - if (inv_item && !inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) + if (inv_item + && (!inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()) + || !inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()))) { // Need to check if item is already no-transfer, otherwise make it no-transfer bool no_transfer = false; diff --git a/indra/newview/llfloatereditextdaycycle.h b/indra/newview/llfloatereditextdaycycle.h index b6e9fdb14f..9a30fb199f 100644 --- a/indra/newview/llfloatereditextdaycycle.h +++ b/indra/newview/llfloatereditextdaycycle.h @@ -32,6 +32,7 @@ #include <boost/signals2.hpp> #include "llenvironment.h" +#include "llfloatereditenvironmentbase.h" class LLCheckBoxCtrl; class LLComboBox; @@ -50,14 +51,13 @@ typedef std::shared_ptr<LLSettingsBase> LLSettingsBasePtr_t; /** * Floater for creating or editing a day cycle. */ -class LLFloaterEditExtDayCycle : public LLFloater +class LLFloaterEditExtDayCycle : public LLFloaterEditEnvironmentBase { LOG_CLASS(LLFloaterEditExtDayCycle); friend class LLDaySettingCopiedCallback; public: - static const std::string KEY_INVENTORY_ID; static const std::string KEY_EDIT_CONTEXT; static const std::string KEY_DAY_LENGTH; static const std::string KEY_CANMOD; @@ -82,8 +82,8 @@ public: virtual BOOL postBuild() override; virtual void onOpen(const LLSD& key) override; virtual void onClose(bool app_quitting) override; - virtual void onFocusReceived() override; - virtual void onFocusLost() override; + //virtual void onFocusReceived() override; + //virtual void onFocusLost() override; virtual void onVisibilityChange(BOOL new_visibility) override; connection_t setEditCommitSignal(edit_commit_signal_t::slot_type cb); @@ -97,10 +97,13 @@ public: LLUUID getEditingAssetId() { return mEditDay ? mEditDay->getAssetId() : LLUUID::null; } LLUUID getEditingInventoryId() { return mInventoryId; } + virtual LLSettingsBase::ptr_t getEditSettings() const override { return mEditDay; } + BOOL handleKeyUp(KEY key, MASK mask, BOOL called_from_parent) override; - BOOL isDirty() const override { return getIsDirty(); } +protected: + virtual void setEditSettingsAndUpdate(const LLSettingsBase::ptr_t &settings) override; private: typedef std::function<void()> on_confirm_fn; @@ -108,8 +111,8 @@ private: // flyout response/click void onButtonApply(LLUICtrl *ctrl, const LLSD &data); - virtual void onClickCloseBtn(bool app_quitting = false) override; - void onButtonImport(); + //virtual void onClickCloseBtn(bool app_quitting = false) override; + //void onButtonImport(); void onButtonLoadFrame(); void onAddFrame(); void onRemoveFrame(); @@ -119,7 +122,6 @@ private: void onCommitName(class LLLineEditor* caller, void* user_data); void onTrackSelectionCallback(const LLSD& user_data); void onPlayActionCallback(const LLSD& user_data); - void onSaveAsCommit(const LLSD& notification, const LLSD& response, const LLSettingsDay::ptr_t &day); // time slider clicked void onTimeSliderCallback(); // a frame moved or frame selection changed @@ -128,10 +130,6 @@ private: void onFrameSliderMouseDown(S32 x, S32 y, MASK mask); void onFrameSliderMouseUp(S32 x, S32 y, MASK mask); - void onPanelDirtyFlagChanged(bool); - - void checkAndConfirmSettingsLoss(on_confirm_fn cb); - void cloneTrack(U32 source_index, U32 dest_index); void cloneTrack(const LLSettingsDay::ptr_t &source_day, U32 source_index, U32 dest_index); void selectTrack(U32 track_index, bool force = false); @@ -148,25 +146,21 @@ private: void removeCurrentSliderFrame(); void removeSliderFrame(F32 frame); - void loadInventoryItem(const LLUUID &inventoryId, bool can_trans = true); - void onAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status); - - void doImportFromDisk(); + virtual void doImportFromDisk() override; void loadSettingFromFile(const std::vector<std::string>& filenames); - void doApplyCreateNewInventory(const LLSettingsDay::ptr_t &day, std::string settings_name); - void doApplyUpdateInventory(const LLSettingsDay::ptr_t &day); - void doApplyEnvironment(const std::string &where, const LLSettingsDay::ptr_t &day); void doApplyCommit(LLSettingsDay::ptr_t day); void onInventoryCreated(LLUUID asset_id, LLUUID inventory_id); void onInventoryCreated(LLUUID asset_id, LLUUID inventory_id, LLSD results); void onInventoryUpdated(LLUUID asset_id, LLUUID inventory_id, LLSD results); + void doOpenTrackFloater(const LLSD &args); void doCloseTrackFloater(bool quitting = false); + virtual LLFloaterSettingsPicker* getSettingsPicker() override; void onPickerCommitTrackId(U32 track_id); void doOpenInventoryFloater(LLSettingsType::type_e type, LLUUID curritem); - void doCloseInventoryFloater(bool quitting = false); + //void doCloseInventoryFloater(bool quitting = false); void onPickerCommitSetting(LLUUID item_id, S32 track); void onAssetLoadedForInsertion(LLUUID item_id, LLUUID asset_id, @@ -176,11 +170,7 @@ private: S32 dest_track, LLSettingsBase::TrackPosition dest_frame); - bool canUseInventory() const; - bool canApplyRegion() const; - bool canApplyParcel() const; - - void updateEditEnvironment(); + virtual void updateEditEnvironment() override; void synchronizeTabs(); void reblendSettings(); @@ -193,7 +183,7 @@ private: bool getIsDirty() const { return mIsDirty; } void setDirtyFlag() { mIsDirty = true; } - virtual void clearDirtyFlag(); + virtual void clearDirtyFlag() override; bool isRemovingFrameAllowed(); bool isAddingFrameAllowed(); @@ -218,11 +208,8 @@ private: LLView* mSkyTabLayoutContainer; LLView* mWaterTabLayoutContainer; LLTextBox* mCurrentTimeLabel; - LLUUID mInventoryId; - LLInventoryItem * mInventoryItem; LLFlyoutComboBtnCtrl * mFlyoutControl; - LLHandle<LLFloater> mInventoryFloater; LLHandle<LLFloater> mTrackFloater; LLTrackBlenderLoopingManual::ptr_t mSkyBlender; @@ -236,11 +223,6 @@ private: LLFrameTimer mPlayTimer; F32 mPlayStartFrame; // an env frame bool mIsPlaying; - bool mIsDirty; - bool mCanCopy; - bool mCanMod; - bool mCanTrans; - bool mCanSave; edit_commit_signal_t mCommitSignal; diff --git a/indra/newview/llfloaterfixedenvironment.cpp b/indra/newview/llfloaterfixedenvironment.cpp index 37e162b249..41bbd5e8f9 100644 --- a/indra/newview/llfloaterfixedenvironment.cpp +++ b/indra/newview/llfloaterfixedenvironment.cpp @@ -81,44 +81,11 @@ namespace const std::string XML_FLYOUTMENU_FILE("menu_save_settings.xml"); } -//========================================================================= -const std::string LLFloaterFixedEnvironment::KEY_INVENTORY_ID("inventory_id"); - - -//========================================================================= - -class LLFixedSettingCopiedCallback : public LLInventoryCallback -{ -public: - LLFixedSettingCopiedCallback(LLHandle<LLFloater> handle) : mHandle(handle) {} - - virtual void fire(const LLUUID& inv_item_id) - { - if (!mHandle.isDead()) - { - LLViewerInventoryItem* item = gInventory.getItem(inv_item_id); - if (item) - { - LLFloaterFixedEnvironment* floater = (LLFloaterFixedEnvironment*)mHandle.get(); - floater->onInventoryCreated(item->getAssetUUID(), inv_item_id); - } - } - } - -private: - LLHandle<LLFloater> mHandle; -}; //========================================================================= LLFloaterFixedEnvironment::LLFloaterFixedEnvironment(const LLSD &key) : - LLFloater(key), - mFlyoutControl(nullptr), - mInventoryId(), - mInventoryItem(nullptr), - mIsDirty(false), - mCanCopy(false), - mCanMod(false), - mCanTrans(false) + LLFloaterEditEnvironmentBase(key), + mFlyoutControl(nullptr) { } @@ -176,19 +143,6 @@ void LLFloaterFixedEnvironment::onClose(bool app_quitting) syncronizeTabs(); } -void LLFloaterFixedEnvironment::onFocusReceived() -{ - if (isInVisibleChain()) - { - updateEditEnvironment(); - LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_EDIT, LLEnvironment::TRANSITION_FAST); - } -} - -void LLFloaterFixedEnvironment::onFocusLost() -{ -} - void LLFloaterFixedEnvironment::refresh() { if (!mSettings) @@ -220,6 +174,15 @@ void LLFloaterFixedEnvironment::refresh() } } +void LLFloaterFixedEnvironment::setEditSettingsAndUpdate(const LLSettingsBase::ptr_t &settings) +{ + mSettings = settings; // shouldn't this do buildDeepCloneAndUncompress() ? + updateEditEnvironment(); + syncronizeTabs(); + refresh(); + LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_FAST); +} + void LLFloaterFixedEnvironment::syncronizeTabs() { S32 count = mTab->getTabCount(); @@ -250,131 +213,9 @@ LLFloaterSettingsPicker * LLFloaterFixedEnvironment::getSettingsPicker() return picker; } -void LLFloaterFixedEnvironment::loadInventoryItem(const LLUUID &inventoryId, bool can_trans) -{ - if (inventoryId.isNull()) - { - mInventoryItem = nullptr; - mInventoryId.setNull(); - mCanMod = true; - mCanCopy = true; - mCanTrans = true; - return; - } - - mInventoryId = inventoryId; - LL_INFOS("SETTINGS") << "Setting edit inventory item to " << mInventoryId << "." << LL_ENDL; - mInventoryItem = gInventory.getItem(mInventoryId); - - if (!mInventoryItem) - { - LL_WARNS("SETTINGS") << "Could not find inventory item with Id = " << mInventoryId << LL_ENDL; - LLNotificationsUtil::add("CantFindInvItem"); - closeFloater(); - - mInventoryId.setNull(); - mInventoryItem = nullptr; - return; - } - - if (mInventoryItem->getAssetUUID().isNull()) - { - LL_WARNS("ENVIRONMENT") << "Asset ID in inventory item is NULL (" << mInventoryId << ")" << LL_ENDL; - LLNotificationsUtil::add("UnableEditItem"); - closeFloater(); - - mInventoryId.setNull(); - mInventoryItem = nullptr; - return; - } - - mCanCopy = mInventoryItem->getPermissions().allowCopyBy(gAgent.getID()); - mCanMod = mInventoryItem->getPermissions().allowModifyBy(gAgent.getID()); - mCanTrans = can_trans && mInventoryItem->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); - - LLSettingsVOBase::getSettingsAsset(mInventoryItem->getAssetUUID(), - [this](LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status, LLExtStat) { onAssetLoaded(asset_id, settings, status); }); -} - - -void LLFloaterFixedEnvironment::checkAndConfirmSettingsLoss(LLFloaterFixedEnvironment::on_confirm_fn cb) -{ - if (isDirty()) - { - LLSD args(LLSDMap("TYPE", mSettings->getSettingsType()) - ("NAME", mSettings->getName())); - - // create and show confirmation textbox - LLNotificationsUtil::add("SettingsConfirmLoss", args, LLSD(), - [cb](const LLSD¬if, const LLSD&resp) - { - S32 opt = LLNotificationsUtil::getSelectedOption(notif, resp); - if (opt == 0) - cb(); - }); - } - else if (cb) - { - cb(); - } -} - void LLFloaterFixedEnvironment::onPickerCommitSetting(LLUUID item_id) { loadInventoryItem(item_id); -// mInventoryId = item_id; -// mInventoryItem = gInventory.getItem(mInventoryId); -// -// mCanCopy = mInventoryItem->getPermissions().allowCopyBy(gAgent.getID()); -// mCanMod = mInventoryItem->getPermissions().allowModifyBy(gAgent.getID()); -// mCanTrans = mInventoryItem->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); -// -// LLSettingsVOBase::getSettingsAsset(mInventoryItem->getAssetUUID(), -// [this](LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status, LLExtStat) { onAssetLoaded(asset_id, settings, status); }); -} - -void LLFloaterFixedEnvironment::onAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status) -{ - if (mInventoryItem && mInventoryItem->getAssetUUID() != asset_id) - { - LL_WARNS("ENVIRONMENT") << "Discarding obsolete asset callback" << LL_ENDL; - return; - } - - clearDirtyFlag(); - - if (!settings || status) - { - LLSD args; - args["NAME"] = (mInventoryItem) ? mInventoryItem->getName() : "Unknown"; - LLNotificationsUtil::add("FailedToFindSettings", args); - closeFloater(); - return; - } - - mSettings = settings; - if (mInventoryItem) - mSettings->setName(mInventoryItem->getName()); - - if (mCanCopy) - settings->clearFlag(LLSettingsBase::FLAG_NOCOPY); - else - settings->setFlag(LLSettingsBase::FLAG_NOCOPY); - - if (mCanMod) - settings->clearFlag(LLSettingsBase::FLAG_NOMOD); - else - settings->setFlag(LLSettingsBase::FLAG_NOMOD); - - if (mCanTrans) - settings->clearFlag(LLSettingsBase::FLAG_NOTRANS); - else - settings->setFlag(LLSettingsBase::FLAG_NOTRANS); - - updateEditEnvironment(); - syncronizeTabs(); - refresh(); - LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_FAST); } void LLFloaterFixedEnvironment::onNameChanged(const std::string &name) @@ -473,50 +314,6 @@ void LLFloaterFixedEnvironment::onButtonApply(LLUICtrl *ctrl, const LLSD &data) } } -void LLFloaterFixedEnvironment::onSaveAsCommit(const LLSD& notification, const LLSD& response, const LLSettingsBase::ptr_t &settings) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) - { - std::string settings_name = response["message"].asString(); - - LLInventoryObject::correctInventoryName(settings_name); - if (settings_name.empty()) - { - // Ideally notification should disable 'OK' button if name won't fit our requirements, - // for now either display notification, or use some default name - settings_name = "Unnamed"; - } - - if (mCanMod) - { - doApplyCreateNewInventory(settings_name, settings); - } - else if (mInventoryItem) - { - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - LLUUID parent_id = mInventoryItem->getParentUUID(); - if (marketplacelistings_id == parent_id) - { - parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); - } - - LLPointer<LLInventoryCallback> cb = new LLFixedSettingCopiedCallback(getHandle()); - copy_inventory_item( - gAgent.getID(), - mInventoryItem->getPermissions().getOwner(), - mInventoryItem->getUUID(), - parent_id, - settings_name, - cb); - } - else - { - LL_WARNS() << "Failed to copy fixed env setting" << LL_ENDL; - } - } -} - void LLFloaterFixedEnvironment::onClickCloseBtn(bool app_quitting) { if (!app_quitting) @@ -530,116 +327,6 @@ void LLFloaterFixedEnvironment::onButtonLoad() checkAndConfirmSettingsLoss([this](){ doSelectFromInventory(); }); } -void LLFloaterFixedEnvironment::doApplyCreateNewInventory(std::string settings_name, const LLSettingsBase::ptr_t &settings) -{ - if (mInventoryItem) - { - LLUUID parent_id = mInventoryItem->getParentUUID(); - U32 next_owner_perm = mInventoryItem->getPermissions().getMaskNextOwner(); - LLSettingsVOBase::createInventoryItem(settings, next_owner_perm, parent_id, settings_name, - [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); - } - else - { - LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); - // This method knows what sort of settings object to create. - LLSettingsVOBase::createInventoryItem(settings, parent_id, settings_name, - [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); - } -} - -void LLFloaterFixedEnvironment::doApplyUpdateInventory(const LLSettingsBase::ptr_t &settings) -{ - LL_DEBUGS("ENVEDIT") << "Update inventory for " << mInventoryId << LL_ENDL; - if (mInventoryId.isNull()) - { - LLSettingsVOBase::createInventoryItem(settings, gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS), std::string(), - [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryCreated(asset_id, inventory_id, results); }); - } - else - { - LLSettingsVOBase::updateInventoryItem(settings, mInventoryId, - [this](LLUUID asset_id, LLUUID inventory_id, LLUUID, LLSD results) { onInventoryUpdated(asset_id, inventory_id, results); }); - } -} - -void LLFloaterFixedEnvironment::doApplyEnvironment(const std::string &where, const LLSettingsBase::ptr_t &settings) -{ - U32 flags(0); - - if (mInventoryItem) - { - if (!mInventoryItem->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) - flags |= LLSettingsBase::FLAG_NOMOD; - if (!mInventoryItem->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) - flags |= LLSettingsBase::FLAG_NOTRANS; - } - - flags |= settings->getFlags(); - settings->setFlag(flags); - - if (where == ACTION_APPLY_LOCAL) - { - settings->setName("Local"); // To distinguish and make sure there is a name. Safe, because this is a copy. - LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, settings); - } - else if (where == ACTION_APPLY_PARCEL) - { - LLParcel *parcel(LLViewerParcelMgr::instance().getAgentOrSelectedParcel()); - - if ((!parcel) || (parcel->getLocalID() == INVALID_PARCEL_ID)) - { - LL_WARNS("ENVIRONMENT") << "Can not identify parcel. Not applying." << LL_ENDL; - LLNotificationsUtil::add("WLParcelApplyFail"); - return; - } - - if (mInventoryItem && !isDirty()) - { - LLEnvironment::instance().updateParcel(parcel->getLocalID(), mInventoryItem->getAssetUUID(), mInventoryItem->getName(), LLEnvironment::NO_TRACK, -1, -1, flags); - } - else if (settings->getSettingsType() == "sky") - { - LLEnvironment::instance().updateParcel(parcel->getLocalID(), std::static_pointer_cast<LLSettingsSky>(settings), -1, -1); - } - else if (settings->getSettingsType() == "water") - { - LLEnvironment::instance().updateParcel(parcel->getLocalID(), std::static_pointer_cast<LLSettingsWater>(settings), -1, -1); - } - } - else if (where == ACTION_APPLY_REGION) - { - if (mInventoryItem && !isDirty()) - { - LLEnvironment::instance().updateRegion(mInventoryItem->getAssetUUID(), mInventoryItem->getName(), LLEnvironment::NO_TRACK, -1, -1, flags); - } - else if (settings->getSettingsType() == "sky") - { - LLEnvironment::instance().updateRegion(std::static_pointer_cast<LLSettingsSky>(settings), -1, -1); - } - else if (settings->getSettingsType() == "water") - { - LLEnvironment::instance().updateRegion(std::static_pointer_cast<LLSettingsWater>(settings), -1, -1); - } - } - else - { - LL_WARNS("ENVIRONMENT") << "Unknown apply '" << where << "'" << LL_ENDL; - return; - } - -} - -void LLFloaterFixedEnvironment::doCloseInventoryFloater(bool quitting) -{ - LLFloater* floaterp = mInventoryFloater.get(); - - if (floaterp) - { - floaterp->closeFloater(quitting); - } -} - void LLFloaterFixedEnvironment::onInventoryCreated(LLUUID asset_id, LLUUID inventory_id, LLSD results) { LL_WARNS("ENVIRONMENT") << "Inventory item " << inventory_id << " has been created with asset " << asset_id << " results are:" << results << LL_ENDL; @@ -709,28 +396,6 @@ void LLFloaterFixedEnvironment::doSelectFromInventory() picker->setFocus(TRUE); } -void LLFloaterFixedEnvironment::onPanelDirtyFlagChanged(bool value) -{ - if (value) - setDirtyFlag(); -} - -//------------------------------------------------------------------------- -bool LLFloaterFixedEnvironment::canUseInventory() const -{ - return LLEnvironment::instance().isInventoryEnabled(); -} - -bool LLFloaterFixedEnvironment::canApplyRegion() const -{ - return gAgent.canManageEstate(); -} - -bool LLFloaterFixedEnvironment::canApplyParcel() const -{ - return LLEnvironment::instance().canAgentUpdateParcelEnvironment(); -} - //========================================================================= LLFloaterFixedEnvironmentWater::LLFloaterFixedEnvironmentWater(const LLSD &key): LLFloaterFixedEnvironment(key) diff --git a/indra/newview/llfloaterfixedenvironment.h b/indra/newview/llfloaterfixedenvironment.h index 513996c4a3..f35f4a4368 100644 --- a/indra/newview/llfloaterfixedenvironment.h +++ b/indra/newview/llfloaterfixedenvironment.h @@ -27,7 +27,7 @@ #ifndef LL_FLOATERFIXEDENVIRONMENT_H #define LL_FLOATERFIXEDENVIRONMENT_H -#include "llfloater.h" +#include "llfloatereditenvironmentbase.h" #include "llsettingsbase.h" #include "llflyoutcombobtn.h" #include "llinventory.h" @@ -43,15 +43,10 @@ class LLFixedSettingCopiedCallback; /** * Floater container for creating and editing fixed environment settings. */ -class LLFloaterFixedEnvironment : public LLFloater +class LLFloaterFixedEnvironment : public LLFloaterEditEnvironmentBase { LOG_CLASS(LLFloaterFixedEnvironment); - - friend class LLFixedSettingCopiedCallback; - public: - static const std::string KEY_INVENTORY_ID; - LLFloaterFixedEnvironment(const LLSD &key); ~LLFloaterFixedEnvironment(); @@ -59,64 +54,35 @@ public: virtual void onOpen(const LLSD& key) override; virtual void onClose(bool app_quitting) override; - virtual void onFocusReceived() override; - virtual void onFocusLost() override; - void setEditSettings(const LLSettingsBase::ptr_t &settings) { mSettings = settings; clearDirtyFlag(); syncronizeTabs(); refresh(); } - LLSettingsBase::ptr_t getEditSettings() const { return mSettings; } - - virtual BOOL isDirty() const override { return getIsDirty(); } + virtual LLSettingsBase::ptr_t getEditSettings() const override { return mSettings; } protected: typedef std::function<void()> on_confirm_fn; - virtual void updateEditEnvironment() = 0; virtual void refresh() override; + void setEditSettingsAndUpdate(const LLSettingsBase::ptr_t &settings) override; virtual void syncronizeTabs(); - LLFloaterSettingsPicker *getSettingsPicker(); - - void loadInventoryItem(const LLUUID &inventoryId, bool can_trans = true); - - void checkAndConfirmSettingsLoss(on_confirm_fn cb); + virtual LLFloaterSettingsPicker *getSettingsPicker() override; LLTabContainer * mTab; LLLineEditor * mTxtName; LLSettingsBase::ptr_t mSettings; - virtual void doImportFromDisk() = 0; - virtual void doApplyCreateNewInventory(std::string settings_name, const LLSettingsBase::ptr_t &settings); - virtual void doApplyUpdateInventory(const LLSettingsBase::ptr_t &settings); - virtual void doApplyEnvironment(const std::string &where, const LLSettingsBase::ptr_t &settings); - void doCloseInventoryFloater(bool quitting = false); - - bool canUseInventory() const; - bool canApplyRegion() const; - bool canApplyParcel() const; - LLFlyoutComboBtnCtrl * mFlyoutControl; - LLUUID mInventoryId; - LLInventoryItem * mInventoryItem; - LLHandle<LLFloater> mInventoryFloater; - bool mCanCopy; - bool mCanMod; - bool mCanTrans; - void onInventoryCreated(LLUUID asset_id, LLUUID inventory_id); void onInventoryCreated(LLUUID asset_id, LLUUID inventory_id, LLSD results); void onInventoryUpdated(LLUUID asset_id, LLUUID inventory_id, LLSD results); - bool getIsDirty() const { return mIsDirty; } - void setDirtyFlag() { mIsDirty = true; } - virtual void clearDirtyFlag(); + virtual void clearDirtyFlag() override; + void updatePermissionFlags(); void doSelectFromInventory(); - void onPanelDirtyFlagChanged(bool); virtual void onClickCloseBtn(bool app_quitting = false) override; - void onSaveAsCommit(const LLSD& notification, const LLSD& response, const LLSettingsBase::ptr_t &settings); private: void onNameChanged(const std::string &name); @@ -126,9 +92,6 @@ private: void onButtonLoad(); void onPickerCommitSetting(LLUUID item_id); - void onAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settins, S32 status); - - bool mIsDirty; }; class LLFloaterFixedEnvironmentWater : public LLFloaterFixedEnvironment @@ -172,36 +135,4 @@ protected: private: }; -class LLSettingsEditPanel : public LLPanel -{ -public: - virtual void setSettings(const LLSettingsBase::ptr_t &) = 0; - - typedef boost::signals2::signal<void(LLPanel *, bool)> on_dirty_charged_sg; - typedef boost::signals2::connection connection_t; - - inline bool getIsDirty() const { return mIsDirty; } - inline void setIsDirty() { mIsDirty = true; if (!mOnDirtyChanged.empty()) mOnDirtyChanged(this, mIsDirty); } - inline void clearIsDirty() { mIsDirty = false; if (!mOnDirtyChanged.empty()) mOnDirtyChanged(this, mIsDirty); } - - inline bool getCanChangeSettings() const { return mCanEdit; } - inline void setCanChangeSettings(bool flag) { mCanEdit = flag; } - - inline connection_t setOnDirtyFlagChanged(on_dirty_charged_sg::slot_type cb) { return mOnDirtyChanged.connect(cb); } - - -protected: - LLSettingsEditPanel() : - LLPanel(), - mIsDirty(false), - mOnDirtyChanged() - {} - -private: - bool mIsDirty; - bool mCanEdit; - - on_dirty_charged_sg mOnDirtyChanged; -}; - #endif // LL_FLOATERFIXEDENVIRONMENT_H diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index 696f748613..028c922a4d 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -35,6 +35,7 @@ #include "llagent.h" #include "llbutton.h" +#include "llcheckboxctrl.h" #include "llcombobox.h" #include "lldrawable.h" #include "lldrawpoolavatar.h" @@ -115,8 +116,14 @@ BOOL LLFloaterImagePreview::postBuild() mSculptedPreview = new LLImagePreviewSculpted(256, 256); mSculptedPreview->setPreviewTarget(mRawImagep, 2.0f); - if (mRawImagep->getWidth() * mRawImagep->getHeight () <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF) - getChildView("lossless_check")->setEnabled(TRUE); + if (mRawImagep->getWidth() * mRawImagep->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF) + { + // We want "lossless_check" to be unchecked when it is disabled, regardless of + // LosslessJ2CUpload state, so only assign control when enabling checkbox + LLCheckBoxCtrl* check_box = getChild<LLCheckBoxCtrl>("lossless_check"); + check_box->setEnabled(TRUE); + check_box->setControlVariable(gSavedSettings.getControl("LosslessJ2CUpload")); + } } else { diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 21420b122b..8d38a5743b 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -54,9 +54,13 @@ #include "llcallbacklist.h" #include "llworld.h" #include "llsdserialize.h" +#include "llviewermenu.h" // is_agent_mappable #include "llviewerobjectlist.h" #include "boost/foreach.hpp" + +const S32 EVENTS_PER_IDLE_LOOP = 100; + // // LLFloaterIMContainer // @@ -66,7 +70,8 @@ LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& param mConversationsRoot(NULL), mConversationsEventStream("ConversationsEvents"), mInitialized(false), - mIsFirstLaunch(true) + mIsFirstLaunch(true), + mConversationEventQueue() { mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLFloaterIMContainer::isActionChecked, this, _2)); mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLFloaterIMContainer::onCustomAction, this, _2)); @@ -304,12 +309,15 @@ void LLFloaterIMContainer::addFloater(LLFloater* floaterp, LLIconCtrl* icon = 0; + bool is_in_group = gAgent.isInGroup(session_id, TRUE); + LLUUID icon_id; - if(gAgent.isInGroup(session_id, TRUE)) + if (is_in_group) { LLGroupIconCtrl::Params icon_params; icon_params.group_id = session_id; icon = LLUICtrlFactory::instance().create<LLGroupIconCtrl>(icon_params); + icon_id = session_id; mSessions[session_id] = floaterp; floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id)); @@ -321,11 +329,18 @@ void LLFloaterIMContainer::addFloater(LLFloater* floaterp, LLAvatarIconCtrl::Params icon_params; icon_params.avatar_id = avatar_id; icon = LLUICtrlFactory::instance().create<LLAvatarIconCtrl>(icon_params); + icon_id = avatar_id; mSessions[session_id] = floaterp; floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id)); } + LLFloaterIMSessionTab* floater = LLFloaterIMSessionTab::getConversation(session_id); + if (floater) + { + floater->updateChatIcon(icon_id); + } + // forced resize of the floater LLRect wrapper_rect = this->mTabContainer->getLocalRect(); floaterp->setRect(wrapper_rect); @@ -424,7 +439,9 @@ void LLFloaterIMContainer::idle(void* user_data) { LLFloaterIMContainer* self = static_cast<LLFloaterIMContainer*>(user_data); - if (!self->getVisible() || self->isMinimized()) + self->idleProcessEvents(); + + if (!self->getVisible() || self->isMinimized()) { return; } @@ -447,19 +464,23 @@ void LLFloaterIMContainer::idleUpdate() const LLConversationItem *current_session = getCurSelectedViewModelItem(); if (current_session) { - // Update moderator options visibility - LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = current_session->getChildrenBegin(); - LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = current_session->getChildrenEnd(); - bool is_moderator = isGroupModerator(); - bool can_ban = haveAbilityToBan(); - while (current_participant_model != end_participant_model) + if (current_session->getType() == LLConversationItem::CONV_SESSION_GROUP) { - LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model); - participant_model->setModeratorOptionsVisible(is_moderator && participant_model->getUUID() != gAgentID); - participant_model->setGroupBanVisible(can_ban && participant_model->getUUID() != gAgentID); - - current_participant_model++; + // Update moderator options visibility + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = current_session->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = current_session->getChildrenEnd(); + bool is_moderator = isGroupModerator(); + bool can_ban = haveAbilityToBan(); + while (current_participant_model != end_participant_model) + { + LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model); + participant_model->setModeratorOptionsVisible(is_moderator); + participant_model->setGroupBanVisible(can_ban && participant_model->getUUID() != gAgentID); + + current_participant_model++; + } } + // Update floater's title as required by the currently selected session or use the default title LLFloaterIMSession * conversation_floaterp = LLFloaterIMSession::findInstance(current_session->getUUID()); setTitle(conversation_floaterp && conversation_floaterp->needsTitleOverwrite() ? conversation_floaterp->getTitle() : mGeneralTitle); @@ -485,13 +506,28 @@ void LLFloaterIMContainer::idleUpdate() } } +void LLFloaterIMContainer::idleProcessEvents() +{ + if (!mConversationEventQueue.empty()) + { + S32 events_to_handle = llmin((S32)mConversationEventQueue.size(), EVENTS_PER_IDLE_LOOP); + for (S32 i = 0; i < events_to_handle; i++) + { + handleConversationModelEvent(mConversationEventQueue.back()); + mConversationEventQueue.pop_back(); + } + } +} + bool LLFloaterIMContainer::onConversationModelEvent(const LLSD& event) { - // For debug only - //std::ostringstream llsd_value; - //llsd_value << LLSDOStreamer<LLSDNotationFormatter>(event) << std::endl; - //LL_INFOS() << "LLFloaterIMContainer::onConversationModelEvent, event = " << llsd_value.str() << LL_ENDL; - // end debug + mConversationEventQueue.push_front(event); + return true; +} + + +void LLFloaterIMContainer::handleConversationModelEvent(const LLSD& event) +{ // Note: In conversations, the model is not responsible for creating the view, which is a good thing. This means that // the model could change substantially and the view could echo only a portion of this model (though currently the @@ -508,7 +544,7 @@ bool LLFloaterIMContainer::onConversationModelEvent(const LLSD& event) if (!session_view) { // We skip events that are not associated with a session - return false; + return; } LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id); LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ? @@ -535,9 +571,9 @@ bool LLFloaterIMContainer::onConversationModelEvent(const LLSD& event) { LLConversationItemSession* session_model = dynamic_cast<LLConversationItemSession*>(mConversationsItems[session_id]); LLConversationItemParticipant* participant_model = (session_model ? session_model->findParticipant(participant_id) : NULL); + LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id); if (!participant_view && session_model && participant_model) - { - LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id); + { if (session_id.isNull() || (im_sessionp && !im_sessionp->isP2PSessionType())) { participant_view = createConversationViewParticipant(participant_model); @@ -548,7 +584,8 @@ bool LLFloaterIMContainer::onConversationModelEvent(const LLSD& event) // Add a participant view to the conversation floater if (conversation_floater && participant_model) { - conversation_floater->addConversationViewParticipant(participant_model); + bool skip_updating = im_sessionp && im_sessionp->isGroupChat(); + conversation_floater->addConversationViewParticipant(participant_model, !skip_updating); } } else if (type == "update_participant") @@ -571,12 +608,6 @@ bool LLFloaterIMContainer::onConversationModelEvent(const LLSD& event) mConversationViewModel.requestSortAll(); mConversationsRoot->arrangeAll(); - if (conversation_floater) - { - conversation_floater->refreshConversation(); - } - - return false; } void LLFloaterIMContainer::draw() @@ -1409,12 +1440,21 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v { return is_single_select; } - - // Beyond that point, if only the user agent is selected, everything is disabled - if (is_single_select && (single_id == gAgentID)) - { - return false; - } + + bool is_moderator_option = ("can_moderate_voice" == item) || ("can_allow_text_chat" == item) || ("can_mute" == item) || ("can_unmute" == item); + + // Beyond that point, if only the user agent is selected, everything is disabled + if (is_single_select && (single_id == gAgentID)) + { + if (is_moderator_option) + { + return enableModerateContextMenuItem(item, true); + } + else + { + return false; + } + } // If the user agent is selected with others, everything is disabled for (uuid_vec_t::const_iterator id = uuids.begin(); id != uuids.end(); ++id) @@ -1480,11 +1520,11 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v { return canBanSelectedMember(single_id); } - else if (("can_moderate_voice" == item) || ("can_allow_text_chat" == item) || ("can_mute" == item) || ("can_unmute" == item)) - { - // *TODO : get that out of here... - return enableModerateContextMenuItem(item); - } + else if (is_moderator_option) + { + // *TODO : get that out of here... + return enableModerateContextMenuItem(item); + } // By default, options that not explicitely disabled are enabled return true; @@ -1786,6 +1826,8 @@ bool LLFloaterIMContainer::removeConversationListItem(const LLUUID& uuid, bool c { new_selection = mConversationsRoot->getPreviousFromChild(widget, FALSE); } + + // Will destroy views and delete models that are not assigned to any views widget->destroyView(); } @@ -1854,7 +1896,7 @@ LLConversationViewParticipant* LLFloaterIMContainer::createConversationViewParti return LLUICtrlFactory::create<LLConversationViewParticipant>(params); } -bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& userdata) +bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& userdata, bool is_self) { // only group moderators can perform actions related to this "enable callback" if (!isGroupModerator()) @@ -1874,7 +1916,7 @@ bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& user { return voice_channel; } - else if ("can_mute" == userdata) + else if (("can_mute" == userdata) && !is_self) { return voice_channel && !isMuted(getCurSelectedViewModelItem()->getUUID()); } @@ -1884,7 +1926,7 @@ bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& user } // The last invoke is used to check whether the "can_allow_text_chat" will enabled - return LLVoiceClient::getInstance()->isParticipantAvatar(getCurSelectedViewModelItem()->getUUID()); + return LLVoiceClient::getInstance()->isParticipantAvatar(getCurSelectedViewModelItem()->getUUID()) && !is_self; } bool LLFloaterIMContainer::isGroupModerator() diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h index 78b3572111..468b47f1f1 100644 --- a/indra/newview/llfloaterimcontainer.h +++ b/indra/newview/llfloaterimcontainer.h @@ -164,7 +164,7 @@ private: void doToSelectedGroup(const LLSD& userdata); static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); - bool enableModerateContextMenuItem(const std::string& userdata); + bool enableModerateContextMenuItem(const std::string& userdata, bool is_self = false); LLSpeaker * getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp); LLSpeakerMgr * getSpeakerMgrForSelectedParticipant(); bool isGroupModerator(); @@ -181,6 +181,7 @@ private: bool isParticipantListExpanded(); void idleUpdate(); // for convenience (self) from static idle + void idleProcessEvents(); LLButton* mExpandCollapseBtn; LLButton* mStubCollapseBtn; @@ -220,6 +221,7 @@ private: LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); LLConversationViewParticipant* createConversationViewParticipant(LLConversationItem* item); bool onConversationModelEvent(const LLSD& event); + void handleConversationModelEvent(const LLSD& event); // Conversation list data LLPanel* mConversationsListPanel; // This is the main widget we add conversation widget to @@ -229,6 +231,8 @@ private: LLFolderView* mConversationsRoot; LLEventStream mConversationsEventStream; + std::deque<LLSD> mConversationEventQueue; + LLTimer mParticipantRefreshTimer; }; diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index a4ab1af9a8..48e2b8dc14 100644 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -160,7 +160,6 @@ void LLFloaterIMSession::onClickCloseBtn(bool) else { LL_WARNS() << "Empty session with id: " << (mSessionID.asString()) << LL_ENDL; - return; } LLFloaterIMSessionTab::onClickCloseBtn(); diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 3aee08482b..e7f428c06a 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -32,6 +32,8 @@ #include "llagent.h" #include "llagentcamera.h" #include "llavataractions.h" +#include "llavatariconctrl.h" +#include "llgroupiconctrl.h" #include "llchatentry.h" #include "llchathistory.h" #include "llchiclet.h" @@ -45,6 +47,9 @@ #include "llfloaterimnearbychat.h" const F32 REFRESH_INTERVAL = 1.0f; +const std::string ICN_GROUP("group_chat_icon"); +const std::string ICN_NEARBY("nearby_chat_icon"); +const std::string ICN_AVATAR("avatar_icon"); void cb_group_do_nothing() { @@ -465,9 +470,10 @@ void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD &args) } } - +static LLTrace::BlockTimerStatHandle FTM_BUILD_CONVERSATION_VIEW_PARTICIPANT("Build Conversation View"); void LLFloaterIMSessionTab::buildConversationViewParticipant() { + LL_RECORD_BLOCK_TIME(FTM_BUILD_CONVERSATION_VIEW_PARTICIPANT); // Clear the widget list since we are rebuilding afresh from the model conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); while (widget_it != mConversationsWidgets.end()) @@ -496,14 +502,20 @@ void LLFloaterIMSessionTab::buildConversationViewParticipant() } } -void LLFloaterIMSessionTab::addConversationViewParticipant(LLConversationItem* participant_model) +void LLFloaterIMSessionTab::addConversationViewParticipant(LLConversationItem* participant_model, bool update_view) { + if (!participant_model) + { + // Nothing to do if the model is inexistent + return; + } + // Check if the model already has an associated view LLUUID uuid = participant_model->getUUID(); LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid); // If not already present, create the participant view and attach it to the root, otherwise, just refresh it - if (widget) + if (widget && update_view) { updateConversationViewParticipant(uuid); // overkill? } @@ -522,10 +534,9 @@ void LLFloaterIMSessionTab::removeConversationViewParticipant(const LLUUID& part LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); if (widget) { - mConversationsRoot->extractItem(widget); - delete widget; - mConversationsWidgets.erase(participant_id); + widget->destroyView(); } + mConversationsWidgets.erase(participant_id); } void LLFloaterIMSessionTab::updateConversationViewParticipant(const LLUUID& participant_id) @@ -693,6 +704,39 @@ void LLFloaterIMSessionTab::updateSessionName(const std::string& name) mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + name); } +void LLFloaterIMSessionTab::updateChatIcon(const LLUUID& id) +{ + if (mSession) + { + if (mSession->isP2PSessionType()) + { + LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>(ICN_AVATAR); + icon->setVisible(true); + icon->setValue(id); + } + if (mSession->isAdHocSessionType()) + { + LLGroupIconCtrl* icon = getChild<LLGroupIconCtrl>(ICN_GROUP); + icon->setVisible(true); + } + if (mSession->isGroupSessionType()) + { + LLGroupIconCtrl* icon = getChild<LLGroupIconCtrl>(ICN_GROUP); + icon->setVisible(true); + icon->setValue(id); + } + } + else + { + if (mIsNearbyChat) + { + LLIconCtrl* icon = getChild<LLIconCtrl>(ICN_NEARBY); + icon->setVisible(true); + } + } + +} + void LLFloaterIMSessionTab::hideAllStandardButtons() { for (S32 i = 0; i < BUTTON_COUNT; i++) diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index 1b4922fd73..375461cfc1 100644 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -84,7 +84,7 @@ public: /*virtual*/ void setFocus(BOOL focus); // Handle the left hand participant list widgets - void addConversationViewParticipant(LLConversationItem* item); + void addConversationViewParticipant(LLConversationItem* item, bool update_view = true); void removeConversationViewParticipant(const LLUUID& participant_id); void updateConversationViewParticipant(const LLUUID& participant_id); void refreshConversation(); @@ -103,6 +103,8 @@ public: void restoreFloater(); void saveCollapsedState(); + void updateChatIcon(const LLUUID& id); + LLView* getChatHistory(); protected: diff --git a/indra/newview/llfloaterjoystick.cpp b/indra/newview/llfloaterjoystick.cpp index 2b672bc890..93a26f31cc 100644 --- a/indra/newview/llfloaterjoystick.cpp +++ b/indra/newview/llfloaterjoystick.cpp @@ -40,7 +40,17 @@ #include "llviewercontrol.h" #include "llappviewer.h" #include "llviewerjoystick.h" +#include "llviewerwindow.h" +#include "llwindow.h" #include "llcheckboxctrl.h" +#include "llcombobox.h" + +#if LL_WINDOWS && !LL_MESA_HEADLESS +// Require DirectInput version 8 +#define DIRECTINPUT_VERSION 0x0800 + +#include <dinput.h> +#endif static LLTrace::SampleStatHandle<> sJoystickAxis0("Joystick axis 0"), sJoystickAxis1("Joystick axis 1"), @@ -58,8 +68,33 @@ static LLTrace::SampleStatHandle<>* sJoystickAxes[6] = &sJoystickAxis5 }; + +#if LL_WINDOWS && !LL_MESA_HEADLESS + +BOOL CALLBACK di8_list_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, LPVOID pvRef) +{ + // Note: If a single device can function as more than one DirectInput + // device type, it is enumerated as each device type that it supports. + // Capable of detecting devices like Oculus Rift + if (device_instance_ptr && pvRef) + { + std::string product_name = utf16str_to_utf8str(llutf16string(device_instance_ptr->tszProductName)); + S32 size = sizeof(GUID); + LLSD::Binary data; //just an std::vector + data.resize(size); + memcpy(&data[0], &device_instance_ptr->guidInstance /*POD _GUID*/, size); + + LLFloaterJoystick * floater = (LLFloaterJoystick*)pvRef; + LLSD value = data; + floater->addDevice(product_name, value); + } + return DIENUM_CONTINUE; +} +#endif + LLFloaterJoystick::LLFloaterJoystick(const LLSD& data) - : LLFloater(data) + : LLFloater(data), + mHasDeviceList(false) { if (!LLViewerJoystick::getInstance()->isJoystickInitialized()) { @@ -71,14 +106,13 @@ LLFloaterJoystick::LLFloaterJoystick(const LLSD& data) void LLFloaterJoystick::draw() { - bool joystick_inited = LLViewerJoystick::getInstance()->isJoystickInitialized(); - getChildView("enable_joystick")->setEnabled(joystick_inited); - getChildView("joystick_type")->setEnabled(joystick_inited); - std::string desc = LLViewerJoystick::getInstance()->getDescription(); - if (desc.empty()) desc = getString("NoDevice"); - getChild<LLUICtrl>("joystick_type")->setValue(desc); - - LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); + LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); + bool joystick_inited = joystick->isJoystickInitialized(); + if (joystick_inited != mHasDeviceList) + { + refreshListOfDevices(); + } + for (U32 i = 0; i < 6; i++) { F32 value = joystick->getJoystickAxis(i); @@ -115,8 +149,8 @@ BOOL LLFloaterJoystick::postBuild() } } - mCheckJoystickEnabled = getChild<LLCheckBoxCtrl>("enable_joystick"); - childSetCommitCallback("enable_joystick",onCommitJoystickEnabled,this); + mJoysticksCombo = getChild<LLComboBox>("joystick_combo"); + childSetCommitCallback("joystick_combo",onCommitJoystickEnabled,this); mCheckFlycamEnabled = getChild<LLCheckBoxCtrl>("JoystickFlycamEnabled"); childSetCommitCallback("JoystickFlycamEnabled",onCommitJoystickEnabled,this); @@ -125,6 +159,7 @@ BOOL LLFloaterJoystick::postBuild() childSetAction("ok_btn", onClickOK, this); refresh(); + refreshListOfDevices(); return TRUE; } @@ -141,6 +176,7 @@ void LLFloaterJoystick::apply() void LLFloaterJoystick::initFromSettings() { mJoystickEnabled = gSavedSettings.getBOOL("JoystickEnabled"); + mJoystickId = gSavedSettings.getLLSD("JoystickDeviceUUID"); mJoystickAxis[0] = gSavedSettings.getS32("JoystickAxis0"); mJoystickAxis[1] = gSavedSettings.getS32("JoystickAxis1"); @@ -210,12 +246,80 @@ void LLFloaterJoystick::initFromSettings() void LLFloaterJoystick::refresh() { LLFloater::refresh(); + initFromSettings(); } +void LLFloaterJoystick::addDevice(std::string &name, LLSD& value) +{ + mJoysticksCombo->add(name, value, ADD_BOTTOM, 1); +} + +void LLFloaterJoystick::refreshListOfDevices() +{ + mJoysticksCombo->removeall(); + std::string no_device = getString("JoystickDisabled"); + LLSD value = LLSD::Integer(0); + addDevice(no_device, value); + + mHasDeviceList = false; + + // di8_devices_callback callback is immediate and happens in scope of getInputDevices() +#if LL_WINDOWS && !LL_MESA_HEADLESS + // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib + U32 device_type = DI8DEVCLASS_GAMECTRL; + void* callback = &di8_list_devices_callback; +#else + // MAC doesn't support device search yet + // On MAC there is an ndof_idsearch and it is possible to specify product + // and manufacturer in NDOF_Device for ndof_init_first to pick specific one + U32 device_type = 0; + void* callback = NULL; +#endif + if (gViewerWindow->getWindow()->getInputDevices(device_type, callback, this)) + { + mHasDeviceList = true; + } + + bool is_device_id_set = LLViewerJoystick::getInstance()->isDeviceUUIDSet(); + + if (LLViewerJoystick::getInstance()->isJoystickInitialized() && + (!mHasDeviceList || !is_device_id_set)) + { +#if LL_WINDOWS && !LL_MESA_HEADLESS + LL_WARNS() << "NDOF connected to device without using SL provided handle" << LL_ENDL; +#endif + std::string desc = LLViewerJoystick::getInstance()->getDescription(); + if (!desc.empty()) + { + LLSD value = LLSD::Integer(0); + addDevice(desc, value); + mHasDeviceList = true; + } + } + + if (gSavedSettings.getBOOL("JoystickEnabled") && mHasDeviceList) + { + if (is_device_id_set) + { + LLSD guid = LLViewerJoystick::getInstance()->getDeviceUUID(); + mJoysticksCombo->selectByValue(guid); + } + else + { + mJoysticksCombo->selectByValue(LLSD::Integer(1)); + } + } + else + { + mJoysticksCombo->selectByValue(LLSD::Integer(0)); + } +} + void LLFloaterJoystick::cancel() { gSavedSettings.setBOOL("JoystickEnabled", mJoystickEnabled); + gSavedSettings.setLLSD("JoystickDeviceUUID", mJoystickId); gSavedSettings.setS32("JoystickAxis0", mJoystickAxis[0]); gSavedSettings.setS32("JoystickAxis1", mJoystickAxis[1]); @@ -285,7 +389,21 @@ void LLFloaterJoystick::cancel() void LLFloaterJoystick::onCommitJoystickEnabled(LLUICtrl*, void *joy_panel) { LLFloaterJoystick* self = (LLFloaterJoystick*)joy_panel; - BOOL joystick_enabled = self->mCheckJoystickEnabled->get(); + + LLSD value = self->mJoysticksCombo->getValue(); + bool joystick_enabled = true; + if (value.isInteger()) + { + // ndof already has a device selected, we are just setting it enabled or disabled + joystick_enabled = value.asInteger(); + } + else + { + LLViewerJoystick::getInstance()->initDevice(value); + // else joystick is enabled, because combobox holds id of device + joystick_enabled = true; + } + gSavedSettings.setBOOL("JoystickEnabled", joystick_enabled); BOOL flycam_enabled = self->mCheckFlycamEnabled->get(); if (!joystick_enabled || !flycam_enabled) @@ -297,6 +415,12 @@ void LLFloaterJoystick::onCommitJoystickEnabled(LLUICtrl*, void *joy_panel) joystick->toggleFlycam(); } } + + std::string device_id = LLViewerJoystick::getInstance()->getDeviceUUIDString(); + gSavedSettings.setString("JoystickDeviceUUID", device_id); + LL_DEBUGS("Joystick") << "Selected " << device_id << " as joystick." << LL_ENDL; + + self->refreshListOfDevices(); } void LLFloaterJoystick::onClickRestoreSNDefaults(void *joy_panel) diff --git a/indra/newview/llfloaterjoystick.h b/indra/newview/llfloaterjoystick.h index a1b5951389..1d46efd3f6 100644 --- a/indra/newview/llfloaterjoystick.h +++ b/indra/newview/llfloaterjoystick.h @@ -31,6 +31,7 @@ #include "llstatview.h" class LLCheckBoxCtrl; +class LLComboBox; class LLFloaterJoystick : public LLFloater { @@ -45,8 +46,11 @@ public: virtual void draw(); static void setSNDefaults(); + void addDevice(std::string &name, LLSD& value); + protected: + void refreshListOfDevices(); void onClose(bool app_quitting); void onClickCloseBtn(bool app_quitting); @@ -65,6 +69,7 @@ private: private: // Device prefs bool mJoystickEnabled; + LLSD mJoystickId; S32 mJoystickAxis[7]; bool m3DCursor; bool mAutoLeveling; @@ -85,8 +90,10 @@ private: F32 mFlycamFeathering; // Controls that can disable the flycam - LLCheckBoxCtrl *mCheckJoystickEnabled; LLCheckBoxCtrl *mCheckFlycamEnabled; + LLComboBox *mJoysticksCombo; + + bool mHasDeviceList; // stats view LLStatBar* mAxisStatsBar[6]; diff --git a/indra/newview/llfloaterloadprefpreset.cpp b/indra/newview/llfloaterloadprefpreset.cpp index fa17a9d40e..8ed76b1df4 100644 --- a/indra/newview/llfloaterloadprefpreset.cpp +++ b/indra/newview/llfloaterloadprefpreset.cpp @@ -58,14 +58,27 @@ BOOL LLFloaterLoadPrefPreset::postBuild() void LLFloaterLoadPrefPreset::onOpen(const LLSD& key) { mSubdirectory = key.asString(); - std::string floater_title = getString(std::string("title_") + mSubdirectory); - - setTitle(floater_title); + std::string title_type = std::string("title_") + mSubdirectory; + if (hasString(title_type)) + { + std::string floater_title = getString(title_type); + setTitle(floater_title); + } + else + { + LL_WARNS() << title_type << " not found" << LL_ENDL; + setTitle(title_type); + } LLComboBox* combo = getChild<LLComboBox>("preset_combo"); EDefaultOptions option = DEFAULT_TOP; LLPresetsManager::getInstance()->setPresetNamesInComboBox(mSubdirectory, combo, option); + std::string preset_graphic_active = gSavedSettings.getString("PresetGraphicActive"); + if (!preset_graphic_active.empty()) + { + combo->setSimple(preset_graphic_active); + } } void LLFloaterLoadPrefPreset::onPresetsListChange() @@ -74,6 +87,11 @@ void LLFloaterLoadPrefPreset::onPresetsListChange() EDefaultOptions option = DEFAULT_TOP; LLPresetsManager::getInstance()->setPresetNamesInComboBox(mSubdirectory, combo, option); + std::string preset_graphic_active = gSavedSettings.getString("PresetGraphicActive"); + if (!preset_graphic_active.empty()) + { + combo->setSimple(preset_graphic_active); + } } void LLFloaterLoadPrefPreset::onBtnCancel() diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp index 889d017389..524162ba51 100644 --- a/indra/newview/llfloatermarketplacelistings.cpp +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -103,14 +103,17 @@ void LLPanelMarketplaceListings::buildAllPanels() panel = buildInventoryPanel("Active Items", "panel_marketplace_listings_listed.xml"); panel->getFilter().setFilterMarketplaceActiveFolders(); panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().setDefaultEmptyLookupMessage("MarketplaceNoListing"); panel->getFilter().markDefault(); panel = buildInventoryPanel("Inactive Items", "panel_marketplace_listings_unlisted.xml"); panel->getFilter().setFilterMarketplaceInactiveFolders(); panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().setDefaultEmptyLookupMessage("MarketplaceNoListing"); panel->getFilter().markDefault(); panel = buildInventoryPanel("Unassociated Items", "panel_marketplace_listings_unassociated.xml"); panel->getFilter().setFilterMarketplaceUnassociatedFolders(); panel->getFilter().setEmptyLookupMessage("MarketplaceNoMatchingItems"); + panel->getFilter().setDefaultEmptyLookupMessage("MarketplaceNoListing"); panel->getFilter().markDefault(); // Set the tab panel diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index bc44e37c5a..999e4a9582 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -27,7 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llmodelloader.h" -#include "lldaeloader.h" +#include "llmodelpreview.h" #include "llfloatermodelpreview.h" @@ -40,15 +40,7 @@ #include "llagent.h" #include "llbutton.h" #include "llcombobox.h" -#include "lldatapacker.h" -#include "lldrawable.h" -#include "llrender.h" -#include "llface.h" #include "llfocusmgr.h" -#include "llfloaterperms.h" -#include "lliconctrl.h" -#include "llmatrix4a.h" -#include "llmenubutton.h" #include "llmeshrepository.h" #include "llnotificationsutil.h" #include "llsdutil_math.h" @@ -56,44 +48,26 @@ #include "lltextbox.h" #include "lltoolmgr.h" #include "llui.h" -#include "llvector4a.h" -#include "llviewercamera.h" #include "llviewerwindow.h" -#include "llvoavatar.h" -#include "llvoavatarself.h" #include "pipeline.h" -#include "lluictrlfactory.h" #include "llviewercontrol.h" -#include "llviewermenu.h" -#include "llviewermenufile.h" -#include "llviewerregion.h" -#include "llviewertexturelist.h" +#include "llviewermenufile.h" //LLFilePickerThread #include "llstring.h" #include "llbutton.h" #include "llcheckboxctrl.h" -#include "llradiogroup.h" -#include "llsdserialize.h" #include "llsliderctrl.h" #include "llspinctrl.h" -#include "lltoggleablemenu.h" +#include "lltabcontainer.h" #include "lltrans.h" -#include "llvfile.h" -#include "llvfs.h" #include "llcallbacklist.h" -#include "llviewerobjectlist.h" -#include "llanimationstates.h" +#include "llviewertexteditor.h" #include "llviewernetwork.h" -#include "llviewershadermgr.h" -#include "glod/glod.h" -#include <boost/algorithm/string.hpp> //static S32 LLFloaterModelPreview::sUploadAmount = 10; LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL; -bool LLModelPreview::sIgnoreLoadedCallback = false; - // "Retain%" decomp parameter has values from 0.0 to 1.0 by 0.01 // But according to the UI spec for upload model floater, this parameter // should be represented by Retain spinner with values from 1 to 100 by 1. @@ -106,114 +80,20 @@ const double RETAIN_COEFFICIENT = 100; // should be represented by Smooth combobox with only 10 values. // So this const is used as a size of Smooth combobox list. const S32 SMOOTH_VALUES_NUMBER = 10; +const S32 PREVIEW_RENDER_SIZE = 1024; +const F32 PREVIEW_CAMERA_DISTANCE = 16.f; -// mCameraDistance -// Also see: mCameraZoom -const F32 MODEL_PREVIEW_CAMERA_DISTANCE = 16.f; - -void drawBoxOutline(const LLVector3& pos, const LLVector3& size); - - -std::string lod_name[NUM_LOD+1] = -{ - "lowest", - "low", - "medium", - "high", - "I went off the end of the lod_name array. Me so smart." -}; - -std::string lod_triangles_name[NUM_LOD+1] = -{ - "lowest_triangles", - "low_triangles", - "medium_triangles", - "high_triangles", - "I went off the end of the lod_triangles_name array. Me so smart." -}; - -std::string lod_vertices_name[NUM_LOD+1] = +class LLMeshFilePicker : public LLFilePickerThread { - "lowest_vertices", - "low_vertices", - "medium_vertices", - "high_vertices", - "I went off the end of the lod_vertices_name array. Me so smart." -}; +public: + LLMeshFilePicker(LLModelPreview* mp, S32 lod); + virtual void notify(const std::vector<std::string>& filenames); -std::string lod_status_name[NUM_LOD+1] = -{ - "lowest_status", - "low_status", - "medium_status", - "high_status", - "I went off the end of the lod_status_name array. Me so smart." +private: + LLModelPreview* mMP; + S32 mLOD; }; -std::string lod_icon_name[NUM_LOD+1] = -{ - "status_icon_lowest", - "status_icon_low", - "status_icon_medium", - "status_icon_high", - "I went off the end of the lod_status_name array. Me so smart." -}; - -std::string lod_status_image[NUM_LOD+1] = -{ - "ModelImport_Status_Good", - "ModelImport_Status_Warning", - "ModelImport_Status_Error", - "I went off the end of the lod_status_image array. Me so smart." -}; - -std::string lod_label_name[NUM_LOD+1] = -{ - "lowest_label", - "low_label", - "medium_label", - "high_label", - "I went off the end of the lod_label_name array. Me so smart." -}; - -BOOL stop_gloderror() -{ - GLuint error = glodGetError(); - - if (error != GLOD_NO_ERROR) - { - LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; - return TRUE; - } - - return FALSE; -} - -LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material) -{ - LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); - - if (texture) - { - if (texture->getDiscardLevel() > -1) - { - gGL.getTexUnit(0)->bind(texture, true); - return texture; - } - } - - return NULL; -} - -std::string stripSuffix(std::string name) -{ - if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1)) - { - return name.substr(0, name.rfind('_')); - } - return name; -} - LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod) : LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA) { @@ -223,6 +103,11 @@ LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod) void LLMeshFilePicker::notify(const std::vector<std::string>& filenames) { + if(LLAppViewer::instance()->quitRequested()) + { + return; + } + if (filenames.size() > 0) { mMP->loadModel(filenames[0], mLOD); @@ -234,37 +119,16 @@ void LLMeshFilePicker::notify(const std::vector<std::string>& filenames) } } -void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut) -{ - LLModelLoader::scene::iterator base_iter = scene.begin(); - bool found = false; - while (!found && (base_iter != scene.end())) - { - matOut = base_iter->first; - - LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); - while (!found && (base_instance_iter != base_iter->second.end())) - { - LLModelInstance& base_instance = *base_instance_iter++; - LLModel* base_model = base_instance.mModel; - - if (base_model && (base_model->mLabel == name_to_match)) - { - baseModelOut = base_model; - return; - } - } - base_iter++; - } -} - //----------------------------------------------------------------------------- // LLFloaterModelPreview() //----------------------------------------------------------------------------- LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) : LLFloaterModelUploadBase(key), mUploadBtn(NULL), -mCalculateBtn(NULL) +mCalculateBtn(NULL), +mUploadLogText(NULL), +mTabContainer(NULL), +mAvatarTabIndex(0) { sInstance = this; mLastMouseX = 0; @@ -307,10 +171,11 @@ BOOL LLFloaterModelPreview::postBuild() getChild<LLSpinCtrl>("lod_triangle_limit_" + lod_name[lod])->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLODParamCommit, this, lod, true)); } - childSetCommitCallback("upload_skin", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL); - childSetCommitCallback("upload_joints", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL); - childSetCommitCallback("lock_scale_if_joint_position", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL); - childSetCommitCallback("upload_textures", boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this), NULL); + // Upload/avatar options, they need to refresh errors/notifications + childSetCommitCallback("upload_skin", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL); + childSetCommitCallback("upload_joints", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL); + childSetCommitCallback("lock_scale_if_joint_position", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL); + childSetCommitCallback("upload_textures", boost::bind(&LLFloaterModelPreview::onUploadOptionChecked, this, _1), NULL); childSetTextArg("status", "[STATUS]", getString("status_idle")); @@ -321,10 +186,6 @@ BOOL LLFloaterModelPreview::postBuild() childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this); - childSetCommitCallback("upload_skin", onUploadSkinCommit, this); - childSetCommitCallback("upload_joints", onUploadJointsCommit, this); - childSetCommitCallback("lock_scale_if_joint_position", onUploadJointsCommit, this); - childSetCommitCallback("import_scale", onImportScaleCommit, this); childSetCommitCallback("pelvis_offset", onPelvisOffsetCommit, this); @@ -333,13 +194,20 @@ BOOL LLFloaterModelPreview::postBuild() getChild<LLCheckBoxCtrl>("show_edges")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1)); getChild<LLCheckBoxCtrl>("show_physics")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1)); getChild<LLCheckBoxCtrl>("show_textures")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1)); - getChild<LLCheckBoxCtrl>("show_skin_weight")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1)); + getChild<LLCheckBoxCtrl>("show_skin_weight")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onShowSkinWeightChecked, this, _1)); + getChild<LLCheckBoxCtrl>("show_joint_overrides")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1)); getChild<LLCheckBoxCtrl>("show_joint_positions")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _1)); childDisable("upload_skin"); childDisable("upload_joints"); childDisable("lock_scale_if_joint_position"); + childSetVisible("skin_too_many_joints", false); + childSetVisible("skin_unknown_joint", false); + + childSetVisible("warning_title", false); + childSetVisible("warning_message", false); + initDecompControls(); LLView* preview_panel = getChild<LLView>("preview_panel"); @@ -395,6 +263,12 @@ BOOL LLFloaterModelPreview::postBuild() mUploadBtn = getChild<LLButton>("ok_btn"); mCalculateBtn = getChild<LLButton>("calculate_btn"); + mUploadLogText = getChild<LLViewerTextEditor>("log_text"); + mTabContainer = getChild<LLTabContainer>("import_tab"); + + LLPanel *panel = mTabContainer->getPanelByName("rigging_panel"); + mAvatarTabIndex = mTabContainer->getIndexForPanel(panel); + panel->getChild<LLScrollListCtrl>("joints_list")->setCommitCallback(boost::bind(&LLFloaterModelPreview::onJointListSelection, this)); if (LLConvexDecomposition::getInstance() != NULL) { @@ -411,6 +285,24 @@ BOOL LLFloaterModelPreview::postBuild() } //----------------------------------------------------------------------------- +// reshape() +//----------------------------------------------------------------------------- + +void LLFloaterModelPreview::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLFloaterModelUploadBase::reshape(width, height, called_from_parent); + + LLView* preview_panel = getChild<LLView>("preview_panel"); + LLRect rect = preview_panel->getRect(); + + if (rect != mPreviewRect) + { + mModelPreview->refresh(); + mPreviewRect = preview_panel->getRect(); + } +} + +//----------------------------------------------------------------------------- // LLFloaterModelPreview() //----------------------------------------------------------------------------- LLFloaterModelPreview::~LLFloaterModelPreview() @@ -433,18 +325,95 @@ void LLFloaterModelPreview::initModelPreview() delete mModelPreview; } - mModelPreview = new LLModelPreview(512, 512, this ); - mModelPreview->setPreviewTarget(MODEL_PREVIEW_CAMERA_DISTANCE); + S32 tex_width = 512; + S32 tex_height = 512; + + S32 max_width = llmin(PREVIEW_RENDER_SIZE, (S32)gPipeline.mScreenWidth); + S32 max_height = llmin(PREVIEW_RENDER_SIZE, (S32)gPipeline.mScreenHeight); + + while ((tex_width << 1) < max_width) + { + tex_width <<= 1; + } + while ((tex_height << 1) < max_height) + { + tex_height <<= 1; + } + + mModelPreview = new LLModelPreview(tex_width, tex_height, this); + mModelPreview->setPreviewTarget(PREVIEW_CAMERA_DISTANCE); mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5)); mModelPreview->setModelUpdatedCallback(boost::bind(&LLFloaterModelPreview::modelUpdated, this, _1)); } +void LLFloaterModelPreview::onUploadOptionChecked(LLUICtrl* ctrl) +{ + if (mModelPreview) + { + auto name = ctrl->getName(); + bool value = ctrl->getValue().asBoolean(); + // update the option and notifications + // (this is a bit convoluted, because of the current structure of mModelPreview) + if (name == "upload_skin") + { + childSetValue("show_skin_weight", value); + mModelPreview->mViewOption["show_skin_weight"] = value; + if (!value) + { + mModelPreview->mViewOption["show_joint_overrides"] = false; + mModelPreview->mViewOption["show_joint_positions"] = false; + childSetValue("show_joint_overrides", false); + childSetValue("show_joint_positions", false); + } + } + else if (name == "upload_joints") + { + if (mModelPreview->mViewOption["show_skin_weight"]) + { + childSetValue("show_joint_overrides", value); + mModelPreview->mViewOption["show_joint_overrides"] = value; + } + } + else if (name == "upload_textures") + { + childSetValue("show_textures", value); + mModelPreview->mViewOption["show_textures"] = value; + } + else if (name == "lock_scale_if_joint_position") + { + mModelPreview->mViewOption["lock_scale_if_joint_position"] = value; + } + + mModelPreview->refresh(); // a 'dirty' flag for render + mModelPreview->resetPreviewTarget(); + mModelPreview->clearBuffers(); + mModelPreview->mDirty = true; + } + // set the button visible, it will be refreshed later + toggleCalculateButton(true); +} + +void LLFloaterModelPreview::onShowSkinWeightChecked(LLUICtrl* ctrl) +{ + if (mModelPreview) + { + mModelPreview->mCameraOffset.clearVec(); + onViewOptionChecked(ctrl); + } +} + void LLFloaterModelPreview::onViewOptionChecked(LLUICtrl* ctrl) { if (mModelPreview) { - mModelPreview->mViewOption[ctrl->getName()] = !mModelPreview->mViewOption[ctrl->getName()]; - + auto name = ctrl->getName(); + mModelPreview->mViewOption[name] = !mModelPreview->mViewOption[name]; + if (name == "show_physics") + { + auto enabled = mModelPreview->mViewOption[name]; + childSetEnabled("physics_explode", enabled); + childSetVisible("physics_explode", enabled); + } mModelPreview->refresh(); } } @@ -479,6 +448,12 @@ void LLFloaterModelPreview::disableViewOption(const std::string& option) setViewOptionEnabled(option, false); } +void LLFloaterModelPreview::loadHighLodModel() +{ + mModelPreview->mLookUpLodFiles = true; + loadModel(3); +} + void LLFloaterModelPreview::loadModel(S32 lod) { mModelPreview->mLoading = true; @@ -500,19 +475,14 @@ void LLFloaterModelPreview::loadModel(S32 lod, const std::string& file_name, boo void LLFloaterModelPreview::onClickCalculateBtn() { + clearLogTab(); + addStringToLog("Calculating model data.", false); mModelPreview->rebuildUploadData(); bool upload_skinweights = childGetValue("upload_skin").asBoolean(); bool upload_joint_positions = childGetValue("upload_joints").asBoolean(); bool lock_scale_if_joint_position = childGetValue("lock_scale_if_joint_position").asBoolean(); - if (upload_joint_positions) - { - // Diagnostic message showing list of joints for which joint offsets are defined. - // FIXME - given time, would be much better to put this in the UI, in updateStatusMessages(). - mModelPreview->getPreviewAvatar()->showAttachmentOverrides(); - } - mUploadModelUrl.clear(); mModelPhysicsFee.clear(); @@ -526,6 +496,132 @@ void LLFloaterModelPreview::onClickCalculateBtn() mUploadBtn->setEnabled(false); } +// Modified cell_params, make sure to clear values if you have to reuse cell_params outside of this function +void add_row_to_list(LLScrollListCtrl *listp, + LLScrollListCell::Params &cell_params, + const LLSD &item_value, + const std::string &name, + const LLSD &vx, + const LLSD &vy, + const LLSD &vz) +{ + LLScrollListItem::Params item_params; + item_params.value = item_value; + + cell_params.column = "model_name"; + cell_params.value = name; + + item_params.columns.add(cell_params); + + cell_params.column = "axis_x"; + cell_params.value = vx; + item_params.columns.add(cell_params); + + cell_params.column = "axis_y"; + cell_params.value = vy; + item_params.columns.add(cell_params); + + cell_params.column = "axis_z"; + cell_params.value = vz; + + item_params.columns.add(cell_params); + + listp->addRow(item_params); +} + +void populate_list_with_overrides(LLScrollListCtrl *listp, const LLJointOverrideData &data, bool include_overrides) +{ + if (data.mModelsNoOverrides.empty() && data.mPosOverrides.empty()) + { + return; + } + + static const std::string no_override_placeholder = "-"; + + S32 count = 0; + LLScrollListCell::Params cell_params; + cell_params.font = LLFontGL::getFontSansSerif(); + // Start out right justifying numeric displays + cell_params.font_halign = LLFontGL::HCENTER; + + std::map<std::string, LLVector3>::const_iterator map_iter = data.mPosOverrides.begin(); + std::map<std::string, LLVector3>::const_iterator map_end = data.mPosOverrides.end(); + while (map_iter != map_end) + { + if (include_overrides) + { + add_row_to_list(listp, + cell_params, + LLSD::Integer(count), + map_iter->first, + LLSD::Real(map_iter->second.mV[VX]), + LLSD::Real(map_iter->second.mV[VY]), + LLSD::Real(map_iter->second.mV[VZ])); + } + else + { + add_row_to_list(listp, + cell_params, + LLSD::Integer(count), + map_iter->first, + no_override_placeholder, + no_override_placeholder, + no_override_placeholder); + } + count++; + map_iter++; + } + + std::set<std::string>::const_iterator set_iter = data.mModelsNoOverrides.begin(); + std::set<std::string>::const_iterator set_end = data.mModelsNoOverrides.end(); + while (set_iter != set_end) + { + add_row_to_list(listp, + cell_params, + LLSD::Integer(count), + *set_iter, + no_override_placeholder, + no_override_placeholder, + no_override_placeholder); + count++; + set_iter++; + } +} + +void LLFloaterModelPreview::onJointListSelection() +{ + S32 display_lod = mModelPreview->mPreviewLOD; + LLPanel *panel = mTabContainer->getPanelByName("rigging_panel"); + LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list"); + LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list"); + LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list"); + LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr"); + + joints_pos->deleteAllItems(); + joints_scale->deleteAllItems(); + + LLScrollListItem *selected = joints_list->getFirstSelected(); + if (selected) + { + std::string label = selected->getValue().asString(); + LLJointOverrideData &data = mJointOverrides[display_lod][label]; + bool upload_joint_positions = childGetValue("upload_joints").asBoolean(); + populate_list_with_overrides(joints_pos, data, upload_joint_positions); + + joint_pos_descr->setTextArg("[JOINT]", label); + mSelectedJointName = label; + } + else + { + // temporary value (shouldn't happen) + std::string label = "mPelvis"; + joint_pos_descr->setTextArg("[JOINT]", label); + mSelectedJointName.clear(); + } + + // Note: We can make a version of renderBones() to highlight selected joint +} + void LLFloaterModelPreview::onDescriptionKeystroke(LLUICtrl* ctrl) { // Workaround for SL-4186, server doesn't allow name changes after 'calculate' stage @@ -570,33 +666,6 @@ void LLFloaterModelPreview::onPelvisOffsetCommit( LLUICtrl*, void* userdata ) } //static -void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata) -{ - LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; - - if (!fp->mModelPreview) - { - return; - } - - fp->mModelPreview->refresh(); -} - -//static -void LLFloaterModelPreview::onUploadSkinCommit(LLUICtrl*,void* userdata) -{ - LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; - - if (!fp->mModelPreview) - { - return; - } - fp->mModelPreview->refresh(); - fp->mModelPreview->resetPreviewTarget(); - fp->mModelPreview->clearBuffers(); -} - -//static void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata) { LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; @@ -626,6 +695,7 @@ void LLFloaterModelPreview::onGenerateNormalsCommit(LLUICtrl* ctrl, void* userda void LLFloaterModelPreview::toggleGenarateNormals() { bool enabled = childGetValue("gen_normals").asBoolean(); + mModelPreview->mViewOption["gen_normals"] = enabled; childSetEnabled("crease_angle", enabled); if(enabled) { mModelPreview->generateNormals(); @@ -669,6 +739,27 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) } } +void LLFloaterModelPreview::draw3dPreview() +{ + gGL.color3f(1.f, 1.f, 1.f); + + gGL.getTexUnit(0)->bind(mModelPreview); + + gGL.begin( LLRender::QUADS ); + { + gGL.texCoord2f(0.f, 1.f); + gGL.vertex2i(mPreviewRect.mLeft+1, mPreviewRect.mTop-1); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(mPreviewRect.mLeft+1, mPreviewRect.mBottom+1); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom+1); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mTop-1); + } + gGL.end(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); +} //----------------------------------------------------------------------------- // draw() @@ -715,36 +806,9 @@ void LLFloaterModelPreview::draw() childSetTextArg("prim_cost", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost)); childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size())); - if (mModelPreview->lodsReady()) + if (!isMinimized() && mModelPreview->lodsReady()) { - gGL.color3f(1.f, 1.f, 1.f); - - gGL.getTexUnit(0)->bind(mModelPreview); - - - LLView* preview_panel = getChild<LLView>("preview_panel"); - - LLRect rect = preview_panel->getRect(); - if (rect != mPreviewRect) - { - mModelPreview->refresh(); - mPreviewRect = preview_panel->getRect(); - } - - gGL.begin( LLRender::QUADS ); - { - gGL.texCoord2f(0.f, 1.f); - gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop-1); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mBottom); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex2i(mPreviewRect.mRight-1, mPreviewRect.mTop-1); - } - gGL.end(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + draw3dPreview(); } } @@ -843,8 +907,11 @@ BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) mModelPreview->zoom((F32)clicks * -0.2f); mModelPreview->refresh(); } - - return TRUE; + else + { + LLFloaterModelUploadBase::handleScrollWheel(x, y, clicks); + } + return TRUE; } /*virtual*/ @@ -1104,7 +1171,8 @@ void LLFloaterModelPreview::initDecompControls() float max = param[i].mDetails.mRange.mHigh.mFloat; float delta = param[i].mDetails.mRange.mDelta.mFloat; - if ("Cosine%" == name) + bool is_smooth_cb = ("Cosine%" == name); + if (is_smooth_cb) { createSmoothComboBox(combo_box, min, max); } @@ -1115,10 +1183,8 @@ void LLFloaterModelPreview::initDecompControls() std::string label = llformat("%.1f", value); combo_box->add(label, value, ADD_BOTTOM, true); } - combo_box->setValue(param[i].mDefault.mFloat); - } - + combo_box->setValue(is_smooth_cb ? 0: param[i].mDefault.mFloat); combo_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); } } @@ -1190,7 +1256,7 @@ void LLFloaterModelPreview::initDecompControls() //LL_INFOS() << "-----------------------------" << LL_ENDL; } } - + mDefaultDecompParams = mDecompParams; childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this); } @@ -1220,3089 +1286,279 @@ void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handl } //----------------------------------------------------------------------------- -// LLModelPreview +// addStringToLog() //----------------------------------------------------------------------------- - -LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) -: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex() -, mLodsQuery() -, mLodsWithParsingError() -, mPelvisZOffset( 0.0f ) -, mLegacyRigValid( false ) -, mRigValidJointUpload( false ) -, mPhysicsSearchLOD( LLModel::LOD_PHYSICS ) -, mResetJoints( false ) -, mModelNoErrors( true ) -, mLastJointUpdate( false ) -{ - mNeedsUpdate = TRUE; - mCameraDistance = 0.f; - mCameraYaw = 0.f; - mCameraPitch = 0.f; - mCameraZoom = 1.f; - mTextureName = 0; - mPreviewLOD = 0; - mModelLoader = NULL; - mMaxTriangleLimit = 0; - mDirty = false; - mGenLOD = false; - mLoading = false; - mLoadState = LLModelLoader::STARTING; - mGroup = 0; - mLODFrozen = false; - mBuildShareTolerance = 0.f; - mBuildQueueMode = GLOD_QUEUE_GREEDY; - mBuildBorderMode = GLOD_BORDER_UNLOCK; - mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE; - - for (U32 i = 0; i < LLModel::NUM_LODS; ++i) - { - mRequestedTriangleCount[i] = 0; - mRequestedCreaseAngle[i] = -1.f; - mRequestedLoDMode[i] = 0; - mRequestedErrorThreshold[i] = 0.f; - mRequestedBuildOperator[i] = 0; - mRequestedQueueMode[i] = 0; - mRequestedBorderMode[i] = 0; - mRequestedShareTolerance[i] = 0.f; - } - - mViewOption["show_textures"] = false; - - mFMP = fmp; - - mHasPivot = false; - mModelPivot = LLVector3( 0.0f, 0.0f, 0.0f ); - - glodInit(); - - createPreviewAvatar(); -} - -LLModelPreview::~LLModelPreview() -{ - // glod apparently has internal mem alignment issues that are angering - // the heap-check code in windows, these should be hunted down in that - // TP code, if possible - // - // kernel32.dll!HeapFree() + 0x14 bytes - // msvcr100.dll!free(void * pBlock) Line 51 C - // glod.dll!glodGetGroupParameteriv() + 0x119 bytes - // glod.dll!glodShutdown() + 0x77 bytes - // - //glodShutdown(); - if(mModelLoader) - { - mModelLoader->shutdown(); - } -} - -U32 LLModelPreview::calcResourceCost() -{ - assert_main_thread(); - - rebuildUploadData(); - - //Upload skin is selected BUT check to see if the joints coming in from the asset were malformed. - if ( mFMP && mFMP->childGetValue("upload_skin").asBoolean() ) - { - bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); - if ( uploadingJointPositions && !isRigValidForJointPositionUpload() ) - { - mFMP->childDisable("ok_btn"); - } - } - - std::set<LLModel*> accounted; - U32 num_points = 0; - U32 num_hulls = 0; - - F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f; - mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f; - - if ( mFMP && mFMP->childGetValue("upload_joints").asBoolean() ) - { - // FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail. - // see also call to addAttachmentPosOverride. - LLUUID fake_mesh_id; - fake_mesh_id.generate(); - getPreviewAvatar()->addPelvisFixup( mPelvisZOffset, fake_mesh_id ); - } - - F32 streaming_cost = 0.f; - F32 physics_cost = 0.f; - for (U32 i = 0; i < mUploadData.size(); ++i) - { - LLModelInstance& instance = mUploadData[i]; - - if (accounted.find(instance.mModel) == accounted.end()) - { - accounted.insert(instance.mModel); - - LLModel::Decomposition& decomp = - instance.mLOD[LLModel::LOD_PHYSICS] ? - instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : - instance.mModel->mPhysics; - - //update instance skin info for each lods pelvisZoffset - for ( int j=0; j<LLModel::NUM_LODS; ++j ) - { - if ( instance.mLOD[j] ) - { - instance.mLOD[j]->mSkinInfo.mPelvisOffset = mPelvisZOffset; - } - } - - std::stringstream ostr; - LLSD ret = LLModel::writeModel(ostr, - instance.mLOD[4], - instance.mLOD[3], - instance.mLOD[2], - instance.mLOD[1], - instance.mLOD[0], - decomp, - mFMP->childGetValue("upload_skin").asBoolean(), - mFMP->childGetValue("upload_joints").asBoolean(), - mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(), - TRUE, - FALSE, - instance.mModel->mSubmodelID); - - num_hulls += decomp.mHull.size(); - for (U32 i = 0; i < decomp.mHull.size(); ++i) - { - num_points += decomp.mHull[i].size(); - } - - //calculate streaming cost - LLMatrix4 transformation = instance.mTransform; - - LLVector3 position = LLVector3(0, 0, 0) * transformation; - - LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - LLVector3 scale = LLVector3(x_length, y_length, z_length); - - F32 radius = scale.length()*0.5f*debug_scale; - - LLMeshCostData costs; - if (gMeshRepo.getCostData(ret, costs)) - { - streaming_cost += costs.getRadiusBasedStreamingCost(radius); - } - } - } - - F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f; - - mDetailsSignal(mPreviewScale[0]*scale, mPreviewScale[1]*scale, mPreviewScale[2]*scale, streaming_cost, physics_cost); - - updateStatusMessages(); - - return (U32) streaming_cost; -} - -void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost) -{ - assert_main_thread(); - childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x)); - childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", y)); - childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", z)); -} - -void LLFloaterModelPreview::setPreviewLOD(S32 lod) -{ - if (mModelPreview) - { - mModelPreview->setPreviewLOD(lod); - } -} - - -void LLModelPreview::rebuildUploadData() -{ - assert_main_thread(); - - mUploadData.clear(); - mTextureSet.clear(); - - //fill uploaddata instance vectors from scene data - - std::string requested_name = mFMP->getChild<LLUICtrl>("description_form")->getValue().asString(); - - LLSpinCtrl* scale_spinner = mFMP->getChild<LLSpinCtrl>("import_scale"); - - F32 scale = scale_spinner->getValue().asReal(); - - LLMatrix4 scale_mat; - scale_mat.initScale(LLVector3(scale, scale, scale)); - - F32 max_scale = 0.f; - - BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug"); - BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); - - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) - { //for each transform in scene - LLMatrix4 mat = iter->first; - - // compute position - LLVector3 position = LLVector3(0, 0, 0) * mat; - - // compute scale - LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - - max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length); - - mat *= scale_mat; - - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) - { //for each instance with said transform applied - LLModelInstance instance = *model_iter++; - - LLModel* base_model = instance.mModel; - - if (base_model && !requested_name.empty()) - { - base_model->mRequestedLabel = requested_name; - } - - for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--) - { - LLModel* lod_model = NULL; - if (!legacyMatching) - { - // Fill LOD slots by finding matching meshes by label with name extensions - // in the appropriate scene for each LOD. This fixes all kinds of issues - // where the indexed method below fails in spectacular fashion. - // If you don't take the time to name your LOD and PHYS meshes - // with the name of their corresponding mesh in the HIGH LOD, - // then the indexed method will be attempted below. - - LLMatrix4 transform; - - std::string name_to_match = instance.mLabel; - llassert(!name_to_match.empty()); - - int extensionLOD; - if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty()) - { - extensionLOD = i; - } - else - { - //Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for - extensionLOD = mPhysicsSearchLOD; - } - - std::string toAdd; - switch (extensionLOD) - { - case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break; - case LLModel::LOD_LOW: toAdd = "_LOD1"; break; - case LLModel::LOD_MEDIUM: toAdd = "_LOD2"; break; - case LLModel::LOD_PHYSICS: toAdd = "_PHYS"; break; - case LLModel::LOD_HIGH: break; - } - - if (name_to_match.find(toAdd) == -1) - { - name_to_match += toAdd; - } - - FindModel(mScene[i], name_to_match, lod_model, transform); - - if (!lod_model && i != LLModel::LOD_PHYSICS) - { - if (importerDebug) - { - LL_INFOS() << "Search of" << name_to_match << " in LOD" << i << " list failed. Searching for alternative among LOD lists." << LL_ENDL; - } - - int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i; - while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model) - { - std::string name_to_match = instance.mLabel; - llassert(!name_to_match.empty()); - - std::string toAdd; - switch (searchLOD) - { - case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break; - case LLModel::LOD_LOW: toAdd = "_LOD1"; break; - case LLModel::LOD_MEDIUM: toAdd = "_LOD2"; break; - case LLModel::LOD_PHYSICS: toAdd = "_PHYS"; break; - case LLModel::LOD_HIGH: break; - } - - if (name_to_match.find(toAdd) == -1) - { - name_to_match += toAdd; - } - - // See if we can find an appropriately named model in LOD 'searchLOD' - // - FindModel(mScene[searchLOD], name_to_match, lod_model, transform); - searchLOD++; - } - } - } - else - { - // Use old method of index-based association - U32 idx = 0; - for (idx = 0; idx < mBaseModel.size(); ++idx) - { - // find reference instance for this model - if (mBaseModel[idx] == base_model) - { - if (importerDebug) - { - LL_INFOS() << "Attempting to use model index " << idx << " for LOD " << i << " of " << instance.mLabel << LL_ENDL; - } - break; - } - } - - // If the model list for the current LOD includes that index... - // - if (mModel[i].size() > idx) - { - // Assign that index from the model list for our LOD as the LOD model for this instance - // - lod_model = mModel[i][idx]; - if (importerDebug) - { - LL_INFOS() << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel << LL_ENDL; - } - } - else if (importerDebug) - { - LL_INFOS() << "List of models does not include index " << idx << LL_ENDL; - } - } - - if (lod_model) - { - if (importerDebug) - { - if (i == LLModel::LOD_PHYSICS) - { - LL_INFOS() << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel << LL_ENDL; - } - else - { - LL_INFOS() << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel << LL_ENDL; - } - } - instance.mLOD[i] = lod_model; - } - else - { - if (i < LLModel::LOD_HIGH && !lodsReady()) - { - // assign a placeholder from previous LOD until lod generation is complete. - // Note: we might need to assign it regardless of conditions like named search does, to prevent crashes. - instance.mLOD[i] = instance.mLOD[i + 1]; - } - if (importerDebug) - { - LL_INFOS() << "List of models does not include " << instance.mLabel << LL_ENDL; - } - } - } - - LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH]; - if (!high_lod_model) - { - setLoadState( LLModelLoader::ERROR_MATERIALS ); - mFMP->childDisable( "calculate_btn" ); - } - else - { - for (U32 i = 0; i < LLModel::NUM_LODS-1; i++) - { - int refFaceCnt = 0; - int modelFaceCnt = 0; - llassert(instance.mLOD[i]); - if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt ) ) - { - setLoadState( LLModelLoader::ERROR_MATERIALS ); - mFMP->childDisable( "calculate_btn" ); - } - } - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) mFMP; - bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean(); - if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0) - { - LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix); - LLQuaternion identity; - if (!bind_rot.isEqualEps(identity,0.01)) - { - LL_WARNS() << "non-identity bind shape rot. mat is " << high_lod_model->mSkinInfo.mBindShapeMatrix - << " bind_rot " << bind_rot << LL_ENDL; - setLoadState( LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION ); - } - } - } - instance.mTransform = mat; - mUploadData.push_back(instance); - } - } - - for (U32 lod = 0; lod < LLModel::NUM_LODS-1; lod++) - { - // Search for models that are not included into upload data - // If we found any, that means something we loaded is not a sub-model. - for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind) - { - bool found_model = false; - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - if (instance.mLOD[lod] == mModel[lod][model_ind]) - { - found_model = true; - break; - } - } - if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID) - { - if (importerDebug) - { - LL_INFOS() << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models." << LL_ENDL; - } - setLoadState( LLModelLoader::ERROR_MATERIALS ); - mFMP->childDisable( "calculate_btn" ); - } - } - } - - F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE-0.1f)/max_scale; - - F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]); - max_axis = llmax(max_axis, mPreviewScale.mV[2]); - max_axis *= 2.f; - - //clamp scale so that total imported model bounding box is smaller than 240m on a side - max_import_scale = llmin(max_import_scale, 240.f/max_axis); - - scale_spinner->setMaxValue(max_import_scale); - - if (max_import_scale < scale) - { - scale_spinner->setValue(max_import_scale); - } - -} - -void LLModelPreview::saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) +//static +void LLFloaterModelPreview::addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod) { - if (!mLODFile[LLModel::LOD_HIGH].empty()) - { - std::string filename = mLODFile[LLModel::LOD_HIGH]; - std::string slm_filename; - - if (LLModelLoader::getSLMFilename(filename, slm_filename)) + if (sInstance && sInstance->hasString(message)) + { + std::string str; + switch (lod) { - saveUploadData(slm_filename, save_skinweights, save_joint_positions, lock_scale_if_joint_position); - } - } -} - -void LLModelPreview::saveUploadData(const std::string& filename, - bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) -{ - - std::set<LLPointer<LLModel> > meshes; - std::map<LLModel*, std::string> mesh_binary; - - LLModel::hull empty_hull; - - LLSD data; - - data["version"] = SLM_SUPPORTED_VERSION; - if (!mBaseModel.empty()) - { - data["name"] = mBaseModel[0]->getName(); - } - - S32 mesh_id = 0; - - //build list of unique models and initialize local id - for (U32 i = 0; i < mUploadData.size(); ++i) - { - LLModelInstance& instance = mUploadData[i]; - - if (meshes.find(instance.mModel) == meshes.end()) - { - instance.mModel->mLocalID = mesh_id++; - meshes.insert(instance.mModel); - - std::stringstream str; - LLModel::Decomposition& decomp = - instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? - instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : - instance.mModel->mPhysics; - - LLModel::writeModel(str, - instance.mLOD[LLModel::LOD_PHYSICS], - instance.mLOD[LLModel::LOD_HIGH], - instance.mLOD[LLModel::LOD_MEDIUM], - instance.mLOD[LLModel::LOD_LOW], - instance.mLOD[LLModel::LOD_IMPOSTOR], - decomp, - save_skinweights, - save_joint_positions, - lock_scale_if_joint_position, - FALSE, TRUE, instance.mModel->mSubmodelID); - - data["mesh"][instance.mModel->mLocalID] = str.str(); - } - - data["instance"][i] = instance.asLLSD(); - } - - llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary); - LLSDSerialize::toBinary(data, out); - out.flush(); - out.close(); -} - -void LLModelPreview::clearModel(S32 lod) -{ - if (lod < 0 || lod > LLModel::LOD_PHYSICS) - { - return; - } - - mVertexBuffer[lod].clear(); - mModel[lod].clear(); - mScene[lod].clear(); + case LLModel::LOD_IMPOSTOR: str = "LOD0 "; break; + case LLModel::LOD_LOW: str = "LOD1 "; break; + case LLModel::LOD_MEDIUM: str = "LOD2 "; break; + case LLModel::LOD_PHYSICS: str = "PHYS "; break; + case LLModel::LOD_HIGH: str = "LOD3 "; break; + default: break; + } + + LLStringUtil::format_map_t args_msg; + LLSD::map_const_iterator iter = args.beginMap(); + LLSD::map_const_iterator end = args.endMap(); + for (; iter != end; ++iter) + { + args_msg[iter->first] = iter->second.asString(); + } + str += sInstance->getString(message, args_msg); + sInstance->addStringToLogTab(str, flash); + } } -void LLModelPreview::getJointAliases( JointMap& joint_map) +// static +void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash) { - // Get all standard skeleton joints from the preview avatar. - LLVOAvatar *av = getPreviewAvatar(); - - //Joint names and aliases come from avatar_skeleton.xml - - joint_map = av->getJointAliases(); - - std::vector<std::string> cv_names, attach_names; - av->getSortedJointNames(1, cv_names); - av->getSortedJointNames(2, attach_names); - for (std::vector<std::string>::iterator it = cv_names.begin(); it != cv_names.end(); ++it) - { - joint_map[*it] = *it; - } - for (std::vector<std::string>::iterator it = attach_names.begin(); it != attach_names.end(); ++it) + if (sInstance) { - joint_map[*it] = *it; + sInstance->addStringToLogTab(str, flash); } } -void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable_slm) -{ - assert_main_thread(); - - LLMutexLock lock(this); - - if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1) - { - LL_WARNS() << "Invalid level of detail: " << lod << LL_ENDL; - assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS); - return; - } - - // This triggers if you bring up the file picker and then hit CANCEL. - // Just use the previous model (if any) and ignore that you brought up - // the file picker. - - if (filename.empty()) - { - if (mBaseModel.empty()) - { - // this is the initial file picking. Close the whole floater - // if we don't have a base model to show for high LOD. - mFMP->closeFloater(false); - } - mLoading = false; - return; - } - - if (mModelLoader) - { - LL_WARNS() << "Incompleted model load operation pending." << LL_ENDL; - return; - } - - mLODFile[lod] = filename; - - if (lod == LLModel::LOD_HIGH) - { - clearGLODGroup(); - } - - std::map<std::string, std::string> joint_alias_map; - getJointAliases(joint_alias_map); - - mModelLoader = new LLDAELoader( - filename, - lod, - &LLModelPreview::loadedCallback, - &LLModelPreview::lookupJointByName, - &LLModelPreview::loadTextures, - &LLModelPreview::stateChangedCallback, - this, - mJointTransformMap, - mJointsFromNode, - joint_alias_map, - LLSkinningUtil::getMaxJointCount(), - gSavedSettings.getU32("ImporterModelLimit"), - gSavedSettings.getBOOL("ImporterPreprocessDAE")); - - if (force_disable_slm) - { - mModelLoader->mTrySLM = false; - } - else - { - // For MAINT-6647, we have set force_disable_slm to true, - // which means this code path will never be taken. Trying to - // re-use SLM files has never worked properly; in particular, - // it tends to force the UI into strange checkbox options - // which cannot be altered. - - //only try to load from slm if viewer is configured to do so and this is the - //initial model load (not an LoD or physics shape) - mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty(); - } - mModelLoader->start(); - - mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); - - setPreviewLOD(lod); - - if ( getLoadState() >= LLModelLoader::ERROR_PARSING ) - { - mFMP->childDisable("ok_btn"); - mFMP->childDisable( "calculate_btn" ); - } - - if (lod == mPreviewLOD) - { - mFMP->childSetValue("lod_file_" + lod_name[lod], mLODFile[lod]); - } - else if (lod == LLModel::LOD_PHYSICS) - { - mFMP->childSetValue("physics_file", mLODFile[lod]); - } - - mFMP->openFloater(); -} - -void LLModelPreview::setPhysicsFromLOD(S32 lod) -{ - assert_main_thread(); - - if (lod >= 0 && lod <= 3) - { - mPhysicsSearchLOD = lod; - mModel[LLModel::LOD_PHYSICS] = mModel[lod]; - mScene[LLModel::LOD_PHYSICS] = mScene[lod]; - mLODFile[LLModel::LOD_PHYSICS].clear(); - mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); - mVertexBuffer[LLModel::LOD_PHYSICS].clear(); - rebuildUploadData(); - refresh(); - updateStatusMessages(); - } -} - -void LLModelPreview::clearIncompatible(S32 lod) -{ - //Don't discard models if specified model is the physic rep - if ( lod == LLModel::LOD_PHYSICS ) - { - return; - } - - // at this point we don't care about sub-models, - // different amount of sub-models means face count mismatch, not incompatibility - U32 lod_size = countRootModels(mModel[lod]); - for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) - { //clear out any entries that aren't compatible with this model - if (i != lod) - { - if (countRootModels(mModel[i]) != lod_size) - { - mModel[i].clear(); - mScene[i].clear(); - mVertexBuffer[i].clear(); - - if (i == LLModel::LOD_HIGH) - { - mBaseModel = mModel[lod]; - clearGLODGroup(); - mBaseScene = mScene[lod]; - mVertexBuffer[5].clear(); - } - } - } - } -} - -void LLModelPreview::clearGLODGroup() -{ - if (mGroup) - { - for (std::map<LLPointer<LLModel>, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) - { - glodDeleteObject(iter->second); - stop_gloderror(); - } - mObject.clear(); - - glodDeleteGroup(mGroup); - stop_gloderror(); - mGroup = 0; - } -} - -void LLModelPreview::loadModelCallback(S32 loaded_lod) -{ - assert_main_thread(); - - LLMutexLock lock(this); - if (!mModelLoader) - { - mLoading = false ; - return; - } - if(getLoadState() >= LLModelLoader::ERROR_PARSING) - { - mLoading = false ; - mModelLoader = NULL; - mLodsWithParsingError.push_back(loaded_lod); - return ; - } - - mLodsWithParsingError.erase(std::remove(mLodsWithParsingError.begin(), mLodsWithParsingError.end(), loaded_lod), mLodsWithParsingError.end()); - if(mLodsWithParsingError.empty()) - { - mFMP->childEnable( "calculate_btn" ); - } - - // Copy determinations about rig so UI will reflect them - // - setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload()); - setLegacyRigValid(mModelLoader->isLegacyRigValid()); - - mModelLoader->loadTextures() ; - - if (loaded_lod == -1) - { //populate all LoDs from model loader scene - mBaseModel.clear(); - mBaseScene.clear(); - - bool skin_weights = false; - bool joint_positions = false; - bool lock_scale_if_joint_position = false; - - for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) - { //for each LoD - - //clear scene and model info - mScene[lod].clear(); - mModel[lod].clear(); - mVertexBuffer[lod].clear(); - - if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull()) - { //if this LoD exists in the loaded scene - - //copy scene to current LoD - mScene[lod] = mModelLoader->mScene; - - //touch up copied scene to look like current LoD - for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) - { - LLModelLoader::model_instance_list& list = iter->second; - - for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter) - { - //override displayed model with current LoD - list_iter->mModel = list_iter->mLOD[lod]; - - if (!list_iter->mModel) - { - continue; - } - - //add current model to current LoD's model list (LLModel::mLocalID makes a good vector index) - S32 idx = list_iter->mModel->mLocalID; - - if (mModel[lod].size() <= idx) - { //stretch model list to fit model at given index - mModel[lod].resize(idx+1); - } - - mModel[lod][idx] = list_iter->mModel; - if (!list_iter->mModel->mSkinWeights.empty()) - { - skin_weights = true; - - if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty()) - { - joint_positions = true; - } - if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition) - { - lock_scale_if_joint_position = true; - } - } - } - } - } - } - - if (mFMP) - { - LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) mFMP; - - if (skin_weights) - { //enable uploading/previewing of skin weights if present in .slm file - fmp->enableViewOption("show_skin_weight"); - mViewOption["show_skin_weight"] = true; - fmp->childSetValue("upload_skin", true); - } - - if (joint_positions) - { - fmp->enableViewOption("show_joint_positions"); - mViewOption["show_joint_positions"] = true; - fmp->childSetValue("upload_joints", true); - } - - if (lock_scale_if_joint_position) - { - fmp->enableViewOption("lock_scale_if_joint_position"); - mViewOption["lock_scale_if_joint_position"] = true; - fmp->childSetValue("lock_scale_if_joint_position", true); - } - } - - //copy high lod to base scene for LoD generation - mBaseScene = mScene[LLModel::LOD_HIGH]; - mBaseModel = mModel[LLModel::LOD_HIGH]; - - mDirty = true; - resetPreviewTarget(); - } - else - { //only replace given LoD - mModel[loaded_lod] = mModelLoader->mModelList; - mScene[loaded_lod] = mModelLoader->mScene; - mVertexBuffer[loaded_lod].clear(); - - setPreviewLOD(loaded_lod); - - if (loaded_lod == LLModel::LOD_HIGH) - { //save a copy of the highest LOD for automatic LOD manipulation - if (mBaseModel.empty()) - { //first time we've loaded a model, auto-gen LoD - mGenLOD = true; - } - - mBaseModel = mModel[loaded_lod]; - clearGLODGroup(); - - mBaseScene = mScene[loaded_lod]; - mVertexBuffer[5].clear(); - } - else - { - BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug"); - BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); - if (!legacyMatching) - { - if (!mBaseModel.empty()) - { - BOOL name_based = FALSE; - BOOL has_submodels = FALSE; - for (U32 idx = 0; idx < mBaseModel.size(); ++idx) - { - if (mBaseModel[idx]->mSubmodelID) - { // don't do index-based renaming when the base model has submodels - has_submodels = TRUE; - if (importerDebug) - { - LL_INFOS() << "High LOD has submodels" << LL_ENDL; - } - break; - } - } - - for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx) - { - std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); - - LLModel* found_model = NULL; - LLMatrix4 transform; - FindModel(mBaseScene, loaded_name, found_model, transform); - if (found_model) - { // don't rename correctly named models (even if they are placed in a wrong order) - name_based = TRUE; - } - - if (mModel[loaded_lod][idx]->mSubmodelID) - { // don't rename the models when loaded LOD model has submodels - has_submodels = TRUE; - } - } - - if (importerDebug) - { - LL_INFOS() << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found" << LL_ENDL; - } - - if (!name_based && !has_submodels) - { // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601) - // this actually works like "ImporterLegacyMatching" for this particular LOD - for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx) - { - std::string name = mBaseModel[idx]->mLabel; - std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); - - if (loaded_name != name) - { - switch (loaded_lod) - { - case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break; - case LLModel::LOD_LOW: name += "_LOD1"; break; - case LLModel::LOD_MEDIUM: name += "_LOD2"; break; - case LLModel::LOD_PHYSICS: name += "_PHYS"; break; - case LLModel::LOD_HIGH: break; - } - - if (importerDebug) - { - LL_WARNS() << "Loded model name " << mModel[loaded_lod][idx]->mLabel << " for LOD " << loaded_lod << " doesn't match the base model. Renaming to " << name << LL_ENDL; - } - - mModel[loaded_lod][idx]->mLabel = name; - } - } - } - } - } - } - - clearIncompatible(loaded_lod); - - mDirty = true; - - if (loaded_lod == LLModel::LOD_HIGH) - { - resetPreviewTarget(); - } - } - - mLoading = false; - if (mFMP) - { - mFMP->getChild<LLCheckBoxCtrl>("confirm_checkbox")->set(FALSE); - if (!mBaseModel.empty()) - { - const std::string& model_name = mBaseModel[0]->getName(); - LLLineEditor* description_form = mFMP->getChild<LLLineEditor>("description_form"); - if (description_form->getText().empty()) - { - description_form->setText(model_name); - } - } - } - refresh(); - - mModelLoadedSignal(); - - mModelLoader = NULL; -} - -void LLModelPreview::resetPreviewTarget() -{ - if ( mModelLoader ) - { - mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; - mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; - } - - setPreviewTarget(mPreviewScale.magVec()*10.f); -} - -void LLModelPreview::generateNormals() -{ - assert_main_thread(); - - S32 which_lod = mPreviewLOD; - - if (which_lod > 4 || which_lod < 0 || - mModel[which_lod].empty()) - { - return; - } - - F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); - - mRequestedCreaseAngle[which_lod] = angle_cutoff; - - angle_cutoff *= DEG_TO_RAD; - - if (which_lod == 3 && !mBaseModel.empty()) - { - if(mBaseModelFacesCopy.empty()) - { - mBaseModelFacesCopy.reserve(mBaseModel.size()); - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) - { - v_LLVolumeFace_t faces; - (*it)->copyFacesTo(faces); - mBaseModelFacesCopy.push_back(faces); - } - } - - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) - { - (*it)->generateNormals(angle_cutoff); - } - - mVertexBuffer[5].clear(); - } - - bool perform_copy = mModelFacesCopy[which_lod].empty(); - if(perform_copy) { - mModelFacesCopy[which_lod].reserve(mModel[which_lod].size()); - } - - for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it) - { - if(perform_copy) - { - v_LLVolumeFace_t faces; - (*it)->copyFacesTo(faces); - mModelFacesCopy[which_lod].push_back(faces); - } - - (*it)->generateNormals(angle_cutoff); - } - - mVertexBuffer[which_lod].clear(); - refresh(); - updateStatusMessages(); -} - -void LLModelPreview::restoreNormals() +// static +void LLFloaterModelPreview::addStringToLog(const std::ostringstream& strm, bool flash) { - S32 which_lod = mPreviewLOD; - - if (which_lod > 4 || which_lod < 0 || - mModel[which_lod].empty()) - { - return; - } - - if(!mBaseModelFacesCopy.empty()) - { - llassert(mBaseModelFacesCopy.size() == mBaseModel.size()); - - vv_LLVolumeFace_t::const_iterator itF = mBaseModelFacesCopy.begin(); - for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it, ++itF) - { - (*it)->copyFacesFrom((*itF)); - } - - mBaseModelFacesCopy.clear(); - } - - if(!mModelFacesCopy[which_lod].empty()) - { - vv_LLVolumeFace_t::const_iterator itF = mModelFacesCopy[which_lod].begin(); - for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it, ++itF) - { - (*it)->copyFacesFrom((*itF)); - } - - mModelFacesCopy[which_lod].clear(); - } - - mVertexBuffer[which_lod].clear(); - refresh(); - updateStatusMessages(); + if (sInstance) + { + sInstance->addStringToLogTab(strm.str(), flash); + } } -void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) +void LLFloaterModelPreview::clearAvatarTab() { - // Allow LoD from -1 to LLModel::LOD_PHYSICS - if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1) - { - LL_WARNS() << "Invalid level of detail: " << which_lod << LL_ENDL; - assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS); - return; - } - - if (mBaseModel.empty()) - { - return; - } - - LLVertexBuffer::unbind(); - - bool no_ff = LLGLSLShader::sNoFixedFunction; - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - LLGLSLShader::sNoFixedFunction = false; - - if (shader) - { - shader->unbind(); - } - - stop_gloderror(); - static U32 cur_name = 1; - - S32 limit = -1; - - U32 triangle_count = 0; - - U32 instanced_triangle_count = 0; - - //get the triangle count for the whole scene - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter) - { - for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) - { - LLModel* mdl = instance->mModel; - if (mdl) - { - instanced_triangle_count += mdl->getNumTriangles(); - } - } - } - - //get the triangle count for the non-instanced set of models - for (U32 i = 0; i < mBaseModel.size(); ++i) - { - triangle_count += mBaseModel[i]->getNumTriangles(); - } - - //get ratio of uninstanced triangles to instanced triangles - F32 triangle_ratio = (F32) triangle_count / (F32) instanced_triangle_count; - - U32 base_triangle_count = triangle_count; - - U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - U32 lod_mode = 0; - - F32 lod_error_threshold = 0; - - // The LoD should be in range from Lowest to High - if (which_lod > -1 && which_lod < NUM_LOD) - { - LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]); - if (iface) - { - lod_mode = iface->getFirstSelectedIndex(); - } - - lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal(); - } - - if (which_lod != -1) - { - mRequestedLoDMode[which_lod] = lod_mode; - } - - if (lod_mode == 0) - { - lod_mode = GLOD_TRIANGLE_BUDGET; - - // The LoD should be in range from Lowest to High - if (which_lod > -1 && which_lod < NUM_LOD) - { - limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger(); - //convert from "scene wide" to "non-instanced" triangle limit - limit = (S32) ( (F32) limit*triangle_ratio ); - } - } - else - { - lod_mode = GLOD_ERROR_THRESHOLD; - } - - bool object_dirty = false; - - if (mGroup == 0) - { - object_dirty = true; - mGroup = cur_name++; - glodNewGroup(mGroup); - } - - if (object_dirty) - { - for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) - { //build GLOD objects for each model in base model list - LLModel* mdl = *iter; - - if (mObject[mdl] != 0) - { - glodDeleteObject(mObject[mdl]); - } - - mObject[mdl] = cur_name++; - - glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); - stop_gloderror(); - - if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) - { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation - mVertexBuffer[5].clear(); - } - - if (mVertexBuffer[5].empty()) - { - genBuffers(5, false); - } - - U32 tri_count = 0; - for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) - { - LLVertexBuffer* buff = mVertexBuffer[5][mdl][i]; - buff->setBuffer(type_mask & buff->getTypeMask()); - - U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); - if (num_indices > 2) - { - glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*) mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); - } - tri_count += num_indices/3; - stop_gloderror(); - } - - glodBuildObject(mObject[mdl]); - stop_gloderror(); - } - } - - - S32 start = LLModel::LOD_HIGH; - S32 end = 0; - - if (which_lod != -1) - { - start = end = which_lod; - } - - mMaxTriangleLimit = base_triangle_count; - - for (S32 lod = start; lod >= end; --lod) - { - if (which_lod == -1) - { - if (lod < start) - { - triangle_count /= decimation; - } - } - else - { - if (enforce_tri_limit) - { - triangle_count = limit; - } - else - { - for (S32 j=LLModel::LOD_HIGH; j>which_lod; --j) - { - triangle_count /= decimation; - } - } - } - - mModel[lod].clear(); - mModel[lod].resize(mBaseModel.size()); - mVertexBuffer[lod].clear(); - - U32 actual_tris = 0; - U32 actual_verts = 0; - U32 submeshes = 0; - - mRequestedTriangleCount[lod] = (S32) ( (F32) triangle_count / triangle_ratio ); - mRequestedErrorThreshold[lod] = lod_error_threshold; - - glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); - stop_gloderror(); - - glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); - stop_gloderror(); - - glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); - stop_gloderror(); - - if (lod_mode != GLOD_TRIANGLE_BUDGET) - { - glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0); - } - else - { - //SH-632: always add 1 to desired amount to avoid decimating below desired amount - glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count+1); - } - - stop_gloderror(); - glodAdaptGroup(mGroup); - stop_gloderror(); - - for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) - { - LLModel* base = mBaseModel[mdl_idx]; - - GLint patch_count = 0; - glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); - stop_gloderror(); - - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); - - std::string name = base->mLabel; - - switch (lod) - { - case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break; - case LLModel::LOD_LOW: name += "_LOD1"; break; - case LLModel::LOD_MEDIUM: name += "_LOD2"; break; - case LLModel::LOD_PHYSICS: name += "_PHYS"; break; - case LLModel::LOD_HIGH: break; - } - - mModel[lod][mdl_idx]->mLabel = name; - mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; - - GLint* sizes = new GLint[patch_count*2]; - glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); - stop_gloderror(); - - GLint* names = new GLint[patch_count]; - glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); - stop_gloderror(); - - mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); - - LLModel* target_model = mModel[lod][mdl_idx]; - - for (GLint i = 0; i < patch_count; ++i) - { - type_mask = mVertexBuffer[5][base][i]->getTypeMask(); - - LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0); - - if (sizes[i*2+1] > 0 && sizes[i*2] > 0) - { - if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true)) - { - // Todo: find a way to stop preview in this case instead of crashing - LL_ERRS() << "Failed buffer allocation during preview LOD generation." - << " Vertices: " << sizes[i * 2 + 1] - << " Indices: " << sizes[i * 2] << LL_ENDL; - } - buff->setBuffer(type_mask); - glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*) buff->getIndicesPointer()); - stop_gloderror(); - } - else - { - // This face was eliminated or we failed to allocate buffer, - // attempt to create a dummy triangle (one vertex, 3 indices, all 0) - buff->allocateBuffer(1, 3, true); - memset((U8*) buff->getMappedData(), 0, buff->getSize()); - memset((U8*) buff->getIndicesPointer(), 0, buff->getIndicesSize()); - } - - buff->validateRange(0, buff->getNumVerts()-1, buff->getNumIndices(), 0); - - LLStrider<LLVector3> pos; - LLStrider<LLVector3> norm; - LLStrider<LLVector2> tc; - LLStrider<U16> index; - - buff->getVertexStrider(pos); - if (type_mask & LLVertexBuffer::MAP_NORMAL) - { - buff->getNormalStrider(norm); - } - if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) - { - buff->getTexCoord0Strider(tc); - } - - buff->getIndexStrider(index); - - target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); - actual_tris += buff->getNumIndices()/3; - actual_verts += buff->getNumVerts(); - ++submeshes; - - if (!validate_face(target_model->getVolumeFace(names[i]))) - { - LL_ERRS() << "Invalid face generated during LOD generation." << LL_ENDL; - } - } + LLPanel *panel = mTabContainer->getPanelByName("rigging_panel"); + LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list"); + joints_list->deleteAllItems(); + LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list"); + joints_pos->deleteAllItems(); mSelectedJointName.clear(); - //blind copy skin weights and just take closest skin weight to point on - //decimated mesh for now (auto-generating LODs with skin weights is still a bit - //of an open problem). - target_model->mPosition = base->mPosition; - target_model->mSkinWeights = base->mSkinWeights; - target_model->mSkinInfo = base->mSkinInfo; - //copy material list - target_model->mMaterialList = base->mMaterialList; - - if (!validate_model(target_model)) - { - LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL; - } + for (U32 i = 0; i < LLModel::NUM_LODS; ++i) + { + mJointOverrides[i].clear(); + } - delete [] sizes; - delete [] names; - } + LLTextBox *joint_total_descr = panel->getChild<LLTextBox>("conflicts_description"); + joint_total_descr->setTextArg("[CONFLICTS]", llformat("%d", 0)); + joint_total_descr->setTextArg("[JOINTS_COUNT]", llformat("%d", 0)); - //rebuild scene based on mBaseScene - mScene[lod].clear(); - mScene[lod] = mBaseScene; - for (U32 i = 0; i < mBaseModel.size(); ++i) - { - LLModel* mdl = mBaseModel[i]; - LLModel* target = mModel[lod][i]; - if (target) - { - for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) - { - for (U32 j = 0; j < iter->second.size(); ++j) - { - if (iter->second[j].mModel == mdl) - { - iter->second[j].mModel = target; - } - } - } - } - } - } - - mResourceCost = calcResourceCost(); - - LLVertexBuffer::unbind(); - LLGLSLShader::sNoFixedFunction = no_ff; - if (shader) - { - shader->bind(); - } + LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr"); + joint_pos_descr->setTextArg("[JOINT]", std::string("mPelvis")); // Might be better to hide it } -void LLModelPreview::updateStatusMessages() +void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides) { - assert_main_thread(); - - //triangle/vertex/submesh count for each mesh asset for each lod - std::vector<S32> tris[LLModel::NUM_LODS]; - std::vector<S32> verts[LLModel::NUM_LODS]; - std::vector<S32> submeshes[LLModel::NUM_LODS]; - - //total triangle/vertex/submesh count for each lod - S32 total_tris[LLModel::NUM_LODS]; - S32 total_verts[LLModel::NUM_LODS]; - S32 total_submeshes[LLModel::NUM_LODS]; - - for (U32 i = 0; i < LLModel::NUM_LODS-1; i++) + S32 display_lod = mModelPreview->mPreviewLOD; + if (mModelPreview->mModel[display_lod].empty()) { - total_tris[i] = 0; - total_verts[i] = 0; - total_submeshes[i] = 0; + mSelectedJointName.clear(); + return; } - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH]; - if (!model_high_lod) - { - setLoadState( LLModelLoader::ERROR_MATERIALS ); - mFMP->childDisable( "calculate_btn" ); - continue; - } - - for (U32 i = 0; i < LLModel::NUM_LODS-1; i++) - { - LLModel* lod_model = instance.mLOD[i]; - if (!lod_model) + // Joints will be listed as long as they are listed in mAlternateBindMatrix + // even if they are for some reason identical to defaults. + // Todo: Are overrides always identical for all lods? They normally are, but there might be situations where they aren't. + if (mJointOverrides[display_lod].empty()) + { + // populate map + for (LLModelLoader::scene::iterator iter = mModelPreview->mScene[display_lod].begin(); iter != mModelPreview->mScene[display_lod].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) { - setLoadState( LLModelLoader::ERROR_MATERIALS ); - mFMP->childDisable( "calculate_btn" ); - } - else - { - //for each model in the lod - S32 cur_tris = 0; - S32 cur_verts = 0; - S32 cur_submeshes = lod_model->getNumVolumeFaces(); - - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = lod_model->getVolumeFace(j); - cur_tris += face.mNumIndices/3; - cur_verts += face.mNumVertices; - } - - std::string instance_name = instance.mLabel; - - BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug"); - if (importerDebug) + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + const LLMeshSkinInfo *skin = &model->mSkinInfo; + U32 joint_count = LLSkinningUtil::getMeshJointCount(skin); + U32 bind_count = highlight_overrides ? skin->mAlternateBindMatrix.size() : 0; // simply do not include overrides if data is not needed + if (bind_count > 0 && bind_count != joint_count) + { + std::ostringstream out; + out << "Invalid joint overrides for model " << model->getName(); + out << ". Amount of joints " << joint_count; + out << ", is different from amount of overrides " << bind_count; + LL_INFOS() << out.str() << LL_ENDL; + addStringToLog(out.str(), true); + // Disable overrides for this model + bind_count = 0; + } + if (bind_count > 0) { - // Useful for debugging generalized complaints below about total submeshes which don't have enough - // context to address exactly what needs to be fixed to move towards compliance with the rules. - // - LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts << LL_ENDL; - LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Tris: " << cur_tris << LL_ENDL; - LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes << LL_ENDL; - - LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin(); - while (mat_iter != lod_model->mMaterialList.end()) + for (U32 j = 0; j < joint_count; ++j) { - LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter) << LL_ENDL; - mat_iter++; + const LLVector3& joint_pos = skin->mAlternateBindMatrix[j].getTranslation(); + LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]]; + + LLJoint* pJoint = LLModelPreview::lookupJointByName(skin->mJointNames[j], mModelPreview); + if (pJoint) + { + // see how voavatar uses aboveJointPosThreshold + if (pJoint->aboveJointPosThreshold(joint_pos)) + { + // valid override + if (data.mPosOverrides.size() > 0 + && (data.mPosOverrides.begin()->second - joint_pos).lengthSquared() > (LL_JOINT_TRESHOLD_POS_OFFSET * LL_JOINT_TRESHOLD_POS_OFFSET)) + { + // File contains multiple meshes with conflicting joint offsets + // preview may be incorrect, upload result might wary (depends onto + // mesh_id that hasn't been generated yet). + data.mHasConflicts = true; + } + data.mPosOverrides[model->getName()] = joint_pos; + } + else + { + // default value, it won't be accounted for by avatar + data.mModelsNoOverrides.insert(model->getName()); + } + } } } - - //add this model to the lod total - total_tris[i] += cur_tris; - total_verts[i] += cur_verts; - total_submeshes[i] += cur_submeshes; - - //store this model's counts to asset data - tris[i].push_back(cur_tris); - verts[i].push_back(cur_verts); - submeshes[i].push_back(cur_submeshes); - } - } + else + { + for (U32 j = 0; j < joint_count; ++j) + { + LLJointOverrideData &data = mJointOverrides[display_lod][skin->mJointNames[j]]; + data.mModelsNoOverrides.insert(model->getName()); + } + } + } + } } - if (mMaxTriangleLimit == 0) - { - mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; - } - - bool has_degenerate = false; - - {//check for degenerate triangles in physics mesh - U32 lod = LLModel::LOD_PHYSICS; - const LLVector4a scale(0.5f); - for (U32 i = 0; i < mModel[lod].size() && !has_degenerate; ++i) - { //for each model in the lod - if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty()) - { //no decomp exists - S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); - for (S32 j = 0; j < cur_submeshes && !has_degenerate; ++j) - { //for each submesh (face), add triangles and vertices to current total - LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); - for (S32 k = 0; (k < face.mNumIndices) && !has_degenerate; ) - { - U16 index_a = face.mIndices[k+0]; - U16 index_b = face.mIndices[k+1]; - U16 index_c = face.mIndices[k+2]; - - LLVector4a v1; v1.setMul(face.mPositions[index_a], scale); - LLVector4a v2; v2.setMul(face.mPositions[index_b], scale); - LLVector4a v3; v3.setMul(face.mPositions[index_c], scale); - - if (ll_is_degenerate(v1,v2,v3)) - { - has_degenerate = true; - } - else - { - k += 3; - } - } - } - } - } - } - - mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH])); - - std::string mesh_status_na = mFMP->getString("mesh_status_na"); - - S32 upload_status[LLModel::LOD_HIGH+1]; - - mModelNoErrors = true; - - const U32 lod_high = LLModel::LOD_HIGH; - U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]); - - for (S32 lod = 0; lod <= lod_high; ++lod) - { - upload_status[lod] = 0; - - std::string message = "mesh_status_good"; - - if (total_tris[lod] > 0) - { - mFMP->childSetValue(lod_triangles_name[lod], llformat("%d", total_tris[lod])); - mFMP->childSetValue(lod_vertices_name[lod], llformat("%d", total_verts[lod])); - } - else - { - if (lod == lod_high) - { - upload_status[lod] = 2; - message = "mesh_status_missing_lod"; - } - else - { - for (S32 i = lod-1; i >= 0; --i) - { - if (total_tris[i] > 0) - { - upload_status[lod] = 2; - message = "mesh_status_missing_lod"; - } - } - } - - mFMP->childSetValue(lod_triangles_name[lod], mesh_status_na); - mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na); - } - - if (lod != lod_high) - { - if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) - { //number of submeshes is different - message = "mesh_status_submesh_mismatch"; - upload_status[lod] = 2; - } - else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count) - {//number of submodels is different, not all faces are matched correctly. - message = "mesh_status_submesh_mismatch"; - upload_status[lod] = 2; - // Note: Submodels in instance were loaded from higher LOD and as result face count - // returns same value and total_submeshes[lod] is identical to high_lod one. - } - else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) - { //number of meshes is different - message = "mesh_status_mesh_mismatch"; - upload_status[lod] = 2; - } - else if (!verts[lod].empty()) - { - S32 sum_verts_higher_lod = 0; - S32 sum_verts_this_lod = 0; - for (U32 i = 0; i < verts[lod].size(); ++i) - { - sum_verts_higher_lod += ((i < verts[lod+1].size()) ? verts[lod+1][i] : 0); - sum_verts_this_lod += verts[lod][i]; - } - - if ((sum_verts_higher_lod > 0) && - (sum_verts_this_lod > sum_verts_higher_lod)) - { - //too many vertices in this lod - message = "mesh_status_too_many_vertices"; - upload_status[lod] = 1; - } - } - } - - LLIconCtrl* icon = mFMP->getChild<LLIconCtrl>(lod_icon_name[lod]); - LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]); - icon->setVisible(true); - icon->setImage(img); - - if (upload_status[lod] >= 2) - { - mModelNoErrors = false; - } - - if (lod == mPreviewLOD) - { - mFMP->childSetValue("lod_status_message_text", mFMP->getString(message)); - icon = mFMP->getChild<LLIconCtrl>("lod_status_message_icon"); - icon->setImage(img); - } - - updateLodControls(lod); - } - - - //warn if hulls have more than 256 points in them - BOOL physExceededVertexLimit = FALSE; - for (U32 i = 0; mModelNoErrors && i < mModel[LLModel::LOD_PHYSICS].size(); ++i) - { - LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i]; - - if (mdl) - { - for (U32 j = 0; j < mdl->mPhysics.mHull.size(); ++j) - { - if (mdl->mPhysics.mHull[j].size() > 256) - { - physExceededVertexLimit = TRUE; - LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL; - break; - } - } - } - } - mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit); - LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon"); - physStatusIcon->setVisible(physExceededVertexLimit); - if (physExceededVertexLimit) - { - mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); - LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); - physStatusIcon->setImage(img); - } - - if (getLoadState() >= LLModelLoader::ERROR_PARSING) - { - mModelNoErrors = false; - LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL; - } - - bool uploadingSkin = mFMP->childGetValue("upload_skin").asBoolean(); - bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); - - if ( uploadingSkin ) - { - if ( uploadingJointPositions && !isRigValidForJointPositionUpload() ) - { - mModelNoErrors = false; - LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL; - } - } + LLPanel *panel = mTabContainer->getPanelByName("rigging_panel"); + LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list"); - if(mModelNoErrors && mModelLoader) - { - if(!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) - { - // Some textures are still loading, prevent upload until they are done - mModelNoErrors = false; - } - } - - // Todo: investigate use of has_degenerate and include into mModelNoErrors upload blocking mechanics - // current use of has_degenerate won't block upload permanently - later checks will restore the button - if (!mModelNoErrors || has_degenerate) - { - mFMP->childDisable("ok_btn"); - } - - if (mModelNoErrors && mLodsWithParsingError.empty()) - { - mFMP->childEnable("calculate_btn"); - } - else + if (joints_list->isEmpty()) { - mFMP->childDisable("calculate_btn"); - } - - //add up physics triangles etc - S32 phys_tris = 0; - S32 phys_hulls = 0; - S32 phys_points = 0; - - //get the triangle count for the whole scene - for (LLModelLoader::scene::iterator iter = mScene[LLModel::LOD_PHYSICS].begin(), endIter = mScene[LLModel::LOD_PHYSICS].end(); iter != endIter; ++iter) - { - for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) - { - LLModel* model = instance->mModel; - if (model) - { - S32 cur_submeshes = model->getNumVolumeFaces(); - - LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull; - - if (!decomp.empty()) - { - phys_hulls += decomp.size(); - for (U32 i = 0; i < decomp.size(); ++i) - { - phys_points += decomp[i].size(); - } - } - else - { //choose physics shape OR decomposition, can't use both - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = model->getVolumeFace(j); - phys_tris += face.mNumIndices/3; - } - } - } - } - } - - if (phys_tris > 0) - { - mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", llformat("%d", phys_tris)); - } - else - { - mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", mesh_status_na); - } - - if (phys_hulls > 0) - { - mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls)); - mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points)); - } - else - { - mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na); - mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na); - } - - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp) - { - if (phys_tris > 0 || phys_hulls > 0) - { - if (!fmp->isViewOptionEnabled("show_physics")) - { - fmp->enableViewOption("show_physics"); - mViewOption["show_physics"] = true; - fmp->childSetValue("show_physics", true); - } - } - else - { - fmp->disableViewOption("show_physics"); - mViewOption["show_physics"] = false; - fmp->childSetValue("show_physics", false); - - } - - //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean(); - - //fmp->childSetEnabled("physics_optimize", !use_hull); - - bool enable = (phys_tris > 0 || phys_hulls > 0) && fmp->mCurRequest.empty(); - //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); - - //enable/disable "analysis" UI - LLPanel* panel = fmp->getChild<LLPanel>("physics analysis"); - LLView* child = panel->getFirstChild(); - while (child) - { - child->setEnabled(enable); - child = panel->findNextSibling(child); - } - - enable = phys_hulls > 0 && fmp->mCurRequest.empty(); - //enable/disable "simplification" UI - panel = fmp->getChild<LLPanel>("physics simplification"); - child = panel->getFirstChild(); - while (child) - { - child->setEnabled(enable); - child = panel->findNextSibling(child); - } - - if (fmp->mCurRequest.empty()) - { - fmp->childSetVisible("Simplify", true); - fmp->childSetVisible("simplify_cancel", false); - fmp->childSetVisible("Decompose", true); - fmp->childSetVisible("decompose_cancel", false); - - if (phys_hulls > 0) - { - fmp->childEnable("Simplify"); - } - - if (phys_tris || phys_hulls > 0) - { - fmp->childEnable("Decompose"); - } - } - else - { - fmp->childEnable("simplify_cancel"); - fmp->childEnable("decompose_cancel"); - } - } - - - LLCtrlSelectionInterface* iface = fmp->childGetSelectionInterface("physics_lod_combo"); - S32 which_mode = 0; - S32 file_mode = 1; - if (iface) - { - which_mode = iface->getFirstSelectedIndex(); - file_mode = iface->getItemCount() - 1; - } - - if (which_mode == file_mode) - { - mFMP->childEnable("physics_file"); - mFMP->childEnable("physics_browse"); - } - else - { - mFMP->childDisable("physics_file"); - mFMP->childDisable("physics_browse"); - } - - LLSpinCtrl* crease = mFMP->getChild<LLSpinCtrl>("crease_angle"); - - if (mRequestedCreaseAngle[mPreviewLOD] == -1.f) - { - mFMP->childSetColor("crease_label", LLColor4::grey); - crease->forceSetValue(75.f); - } - else - { - mFMP->childSetColor("crease_label", LLColor4::white); - crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]); - } - - mModelUpdatedSignal(true); - -} - -void LLModelPreview::updateLodControls(S32 lod) -{ - if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH) - { - LL_WARNS() << "Invalid level of detail: " << lod << LL_ENDL; - assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH); - return; - } - - const char* lod_controls[] = - { - "lod_mode_", - "lod_triangle_limit_", - "lod_error_threshold_" - }; - const U32 num_lod_controls = sizeof(lod_controls)/sizeof(char*); - - const char* file_controls[] = - { - "lod_browse_", - "lod_file_", - }; - const U32 num_file_controls = sizeof(file_controls)/sizeof(char*); - - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (!fmp) return; - - LLComboBox* lod_combo = mFMP->findChild<LLComboBox>("lod_source_" + lod_name[lod]); - if (!lod_combo) return; - - S32 lod_mode = lod_combo->getCurrentIndex(); - if (lod_mode == LOD_FROM_FILE) // LoD from file - { - fmp->mLODMode[lod] = 0; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childSetVisible(file_controls[i] + lod_name[lod], true); - } - - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); - } - } - else if (lod_mode == USE_LOD_ABOVE) // use LoD above - { - fmp->mLODMode[lod] = 2; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childSetVisible(file_controls[i] + lod_name[lod], false); - } - - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); - } - - if (lod < LLModel::LOD_HIGH) - { - mModel[lod] = mModel[lod + 1]; - mScene[lod] = mScene[lod + 1]; - mVertexBuffer[lod].clear(); - - // Also update lower LoD - if (lod > LLModel::LOD_IMPOSTOR) - { - updateLodControls(lod - 1); - } - } - } - else // auto generate, the default case for all LoDs except High - { - fmp->mLODMode[lod] = 1; - - //don't actually regenerate lod when refreshing UI - mLODFrozen = true; - - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->getChildView(file_controls[i] + lod_name[lod])->setVisible(false); - } - - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->getChildView(lod_controls[i] + lod_name[lod])->setVisible(true); - } - - - LLSpinCtrl* threshold = mFMP->getChild<LLSpinCtrl>("lod_error_threshold_" + lod_name[lod]); - LLSpinCtrl* limit = mFMP->getChild<LLSpinCtrl>("lod_triangle_limit_" + lod_name[lod]); - - limit->setMaxValue(mMaxTriangleLimit); - limit->forceSetValue(mRequestedTriangleCount[lod]); - - threshold->forceSetValue(mRequestedErrorThreshold[lod]); - - mFMP->getChild<LLComboBox>("lod_mode_" + lod_name[lod])->selectNthItem(mRequestedLoDMode[lod]); - - if (mRequestedLoDMode[lod] == 0) - { - limit->setVisible(true); - threshold->setVisible(false); - - limit->setMaxValue(mMaxTriangleLimit); - limit->setIncrement(mMaxTriangleLimit/32); - } - else - { - limit->setVisible(false); - threshold->setVisible(true); - } - - mLODFrozen = false; - } -} - -void LLModelPreview::setPreviewTarget(F32 distance) -{ - mCameraDistance = distance; - mCameraZoom = 1.f; - mCameraPitch = 0.f; - mCameraYaw = 0.f; - mCameraOffset.clearVec(); -} - -void LLModelPreview::clearBuffers() -{ - for (U32 i = 0; i < 6; i++) - { - mVertexBuffer[i].clear(); - } -} - -void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) -{ - U32 tri_count = 0; - U32 vertex_count = 0; - U32 mesh_count = 0; - - - LLModelLoader::model_list* model = NULL; - - if (lod < 0 || lod > 4) - { - model = &mBaseModel; - lod = 5; - } - else - { - model = &(mModel[lod]); - } - - if (!mVertexBuffer[lod].empty()) - { - mVertexBuffer[lod].clear(); - } - - mVertexBuffer[lod].clear(); - - LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); - - for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) - { - LLModel* mdl = *iter; - if (!mdl) - { - continue; - } - - LLModel* base_mdl = *base_iter; - base_iter++; - - S32 num_faces = mdl->getNumVolumeFaces(); - for (S32 i = 0; i < num_faces; ++i) - { - const LLVolumeFace &vf = mdl->getVolumeFace(i); - U32 num_vertices = vf.mNumVertices; - U32 num_indices = vf.mNumIndices; - - if (!num_vertices || ! num_indices) - { - continue; - } + // Populate table - LLVertexBuffer* vb = NULL; + std::map<std::string, std::string> joint_alias_map; + mModelPreview->getJointAliases(joint_alias_map); - bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); - - U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0 ; - - if (skinned) - { - mask |= LLVertexBuffer::MAP_WEIGHT4; - } - - vb = new LLVertexBuffer(mask, 0); - - if (!vb->allocateBuffer(num_vertices, num_indices, TRUE)) - { - // We are likely to crash due this failure, if this happens, find a way to gracefully stop preview - LL_WARNS() << "Failed to allocate Vertex Buffer for model preview " - << num_vertices << " vertices and " - << num_indices << " indices" << LL_ENDL; - } - - LLStrider<LLVector3> vertex_strider; - LLStrider<LLVector3> normal_strider; - LLStrider<LLVector2> tc_strider; - LLStrider<U16> index_strider; - LLStrider<LLVector4> weights_strider; - - vb->getVertexStrider(vertex_strider); - vb->getIndexStrider(index_strider); - - if (skinned) - { - vb->getWeight4Strider(weights_strider); - } - - LLVector4a::memcpyNonAliased16((F32*) vertex_strider.get(), (F32*) vf.mPositions, num_vertices*4*sizeof(F32)); - - if (vf.mTexCoords) - { - vb->getTexCoord0Strider(tc_strider); - S32 tex_size = (num_vertices*2*sizeof(F32)+0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, tex_size); - } - - if (vf.mNormals) - { - vb->getNormalStrider(normal_strider); - LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32)); - } - - if (skinned) - { - for (U32 i = 0; i < num_vertices; i++) - { - //find closest weight to vf.mVertices[i].mPosition - LLVector3 pos(vf.mPositions[i].getF32ptr()); - - const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); - llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this - - LLVector4 w(0,0,0,0); - - for (U32 i = 0; i < weight_list.size(); ++i) - { - F32 wght = llclamp(weight_list[i].mWeight, 0.001f, 0.999f); - F32 joint = (F32) weight_list[i].mJointIdx; - w.mV[i] = joint + wght; - llassert(w.mV[i]-(S32)w.mV[i]>0.0f); // because weights are non-zero, and range of wt values - //should not cause floating point precision issues. - } - - *(weights_strider++) = w; - } - } - - // build indices - for (U32 i = 0; i < num_indices; i++) - { - *(index_strider++) = vf.mIndices[i]; - } - - mVertexBuffer[lod][mdl].push_back(vb); + S32 conflicts = 0; + joint_override_data_map_t::iterator joint_iter = mJointOverrides[display_lod].begin(); + joint_override_data_map_t::iterator joint_end = mJointOverrides[display_lod].end(); + while (joint_iter != joint_end) + { + const std::string& listName = joint_iter->first; - vertex_count += num_vertices; - tri_count += num_indices/3; - ++mesh_count; + LLScrollListItem::Params item_params; + item_params.value(listName); - } - } -} + LLScrollListCell::Params cell_params; + cell_params.font = LLFontGL::getFontSansSerif(); + cell_params.value = listName; + if (joint_alias_map.find(listName) == joint_alias_map.end()) + { + // Missing names + cell_params.color = LLColor4::red; + } + if (joint_iter->second.mHasConflicts) + { + // Conflicts + cell_params.color = LLColor4::orange; + conflicts++; + } + if (highlight_overrides && joint_iter->second.mPosOverrides.size() > 0) + { + cell_params.font.style = "BOLD"; + } -void LLModelPreview::update() -{ - if (mGenLOD) - { - bool subscribe_for_generation = mLodsQuery.empty(); - mGenLOD = false; - mDirty = true; - mLodsQuery.clear(); + item_params.columns.add(cell_params); - for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod) - { - // adding all lods into query for generation - mLodsQuery.push_back(lod); + joints_list->addRow(item_params, ADD_BOTTOM); + joint_iter++; } - - if (subscribe_for_generation) + joints_list->selectFirstItem(); + LLScrollListItem *selected = joints_list->getFirstSelected(); + if (selected) { - doOnIdleRepeating(lodQueryCallback); + mSelectedJointName = selected->getValue().asString(); } - } - - if (mDirty && mLodsQuery.empty()) - { - mDirty = false; - mResourceCost = calcResourceCost(); - refresh(); - updateStatusMessages(); - } -} - -//----------------------------------------------------------------------------- -// createPreviewAvatar -//----------------------------------------------------------------------------- -void LLModelPreview::createPreviewAvatar( void ) -{ - mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer( LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR ); - if ( mPreviewAvatar ) - { - mPreviewAvatar->createDrawable( &gPipeline ); - mPreviewAvatar->mSpecialRenderMode = 1; - mPreviewAvatar->startMotion( ANIM_AGENT_STAND ); - mPreviewAvatar->hideSkirt(); - } - else - { - LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL; - } -} - -//static -U32 LLModelPreview::countRootModels(LLModelLoader::model_list models) -{ - U32 root_models = 0; - model_list::iterator model_iter = models.begin(); - while (model_iter != models.end()) - { - LLModel* mdl = *model_iter; - if (mdl && mdl->mSubmodelID == 0) - { - root_models++; - } - model_iter++; - } - return root_models; -} -void LLModelPreview::loadedCallback( - LLModelLoader::scene& scene, - LLModelLoader::model_list& model_list, - S32 lod, - void* opaque) -{ - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) - { - pPreview->loadModelCallback(lod); - } -} - -void LLModelPreview::stateChangedCallback(U32 state,void* opaque) -{ - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview) - { - pPreview->setLoadState(state); - } -} - -LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque) -{ - LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); - if (pPreview) - { - return pPreview->getPreviewAvatar()->getJoint(str); - } - return NULL; -} - -U32 LLModelPreview::loadTextures(LLImportMaterial& material,void* opaque) -{ - (void)opaque; - - if (material.mDiffuseMapFilename.size()) - { - material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >; - LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData)); - - tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + material.mDiffuseMapFilename, FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); - tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE); - tex->forceToSaveRawImage(0, F32_MAX); - material.setDiffuseMap(tex->getID()); // record tex ID - return 1; - } - - material.mOpaqueData = NULL; - return 0; + LLTextBox *joint_conf_descr = panel->getChild<LLTextBox>("conflicts_description"); + joint_conf_descr->setTextArg("[CONFLICTS]", llformat("%d", conflicts)); + joint_conf_descr->setTextArg("[JOINTS_COUNT]", llformat("%d", mJointOverrides[display_lod].size())); + } } -void LLModelPreview::addEmptyFace( LLModel* pTarget ) -{ - U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0); - - buff->allocateBuffer(1, 3, true); - memset( (U8*) buff->getMappedData(), 0, buff->getSize() ); - memset( (U8*) buff->getIndicesPointer(), 0, buff->getIndicesSize() ); - - buff->validateRange( 0, buff->getNumVerts()-1, buff->getNumIndices(), 0 ); - - LLStrider<LLVector3> pos; - LLStrider<LLVector3> norm; - LLStrider<LLVector2> tc; - LLStrider<U16> index; - - buff->getVertexStrider(pos); - - if ( type_mask & LLVertexBuffer::MAP_NORMAL ) - { - buff->getNormalStrider(norm); - } - if ( type_mask & LLVertexBuffer::MAP_TEXCOORD0 ) - { - buff->getTexCoord0Strider(tc); - } - - buff->getIndexStrider(index); - - //resize face array - int faceCnt = pTarget->getNumVolumeFaces(); - pTarget->setNumVolumeFaces( faceCnt+1 ); - pTarget->setVolumeFaceData( faceCnt+1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices() ); - -} - //----------------------------------------------------------------------------- -// render() +// addStringToLogTab() //----------------------------------------------------------------------------- -BOOL LLModelPreview::render() +void LLFloaterModelPreview::addStringToLogTab(const std::string& str, bool flash) { - assert_main_thread(); - - LLMutexLock lock(this); - mNeedsUpdate = FALSE; - - bool use_shaders = LLGLSLShader::sNoFixedFunction; - - bool edges = mViewOption["show_edges"]; - bool joint_positions = mViewOption["show_joint_positions"]; - bool skin_weight = mViewOption["show_skin_weight"]; - bool textures = mViewOption["show_textures"]; - bool physics = mViewOption["show_physics"]; - - S32 width = getWidth(); - S32 height = getHeight(); - - LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test - LLGLDisable no_blend(GL_BLEND); - LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color - LLGLDisable fog(GL_FOG); - - { - if (use_shaders) - { - gUIProgram.bind(); - } - //clear background to grey - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.ortho(0.0f, width, 0.0f, height, -1.0f, 1.0f); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.pushMatrix(); - gGL.loadIdentity(); - - gGL.color4f(0.169f, 0.169f, 0.169f, 1.f); - - gl_rect_2d_simple( width, height ); - - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.popMatrix(); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); - if (use_shaders) - { - gUIProgram.unbind(); - } - } - - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - - bool has_skin_weights = false; - bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); - bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); - - if ( upload_joints != mLastJointUpdate ) - { - mLastJointUpdate = upload_joints; - } - - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - model->mPelvisOffset = mPelvisZOffset; - if (!model->mSkinWeights.empty()) - { - has_skin_weights = true; - } - } - } - - if (has_skin_weights && lodsReady()) - { //model has skin weights, enable view options for skin weights and joint positions - if (fmp && isLegacyRigValid() ) - { - fmp->enableViewOption("show_skin_weight"); - fmp->setViewOptionEnabled("show_joint_positions", skin_weight); - mFMP->childEnable("upload_skin"); - mFMP->childSetValue("show_skin_weight", skin_weight); - } - } - else - { - mFMP->childDisable("upload_skin"); - if (fmp) - { - mViewOption["show_skin_weight"] = false; - fmp->disableViewOption("show_skin_weight"); - fmp->disableViewOption("show_joint_positions"); - - skin_weight = false; - mFMP->childSetValue("show_skin_weight", false); - fmp->setViewOptionEnabled("show_skin_weight", skin_weight); - } - } - - if (upload_skin && !has_skin_weights) - { //can't upload skin weights if model has no skin weights - mFMP->childSetValue("upload_skin", false); - upload_skin = false; - } + if (str.empty()) + { + return; + } - if (!upload_skin && upload_joints) - { //can't upload joints if not uploading skin weights - mFMP->childSetValue("upload_joints", false); - upload_joints = false; - } + LLWString text = utf8str_to_wstring(str); + S32 add_text_len = text.length() + 1; // newline + S32 editor_max_len = mUploadLogText->getMaxTextLength(); + if (add_text_len > editor_max_len) + { + return; + } - if (upload_skin && upload_joints) + // Make sure we have space for new string + S32 editor_text_len = mUploadLogText->getLength(); + if (editor_max_len < (editor_text_len + add_text_len) + && mUploadLogText->getLineCount() <= 0) { - mFMP->childEnable("lock_scale_if_joint_position"); + mUploadLogText->getTextBoundingRect();// forces a reflow() to fix line count } - else + while (editor_max_len < (editor_text_len + add_text_len)) { - mFMP->childDisable("lock_scale_if_joint_position"); - mFMP->childSetValue("lock_scale_if_joint_position", false); + S32 shift = mUploadLogText->removeFirstLine(); + if (shift > 0) + { + // removed a line + editor_text_len -= shift; + } + else + { + //nothing to remove? + LL_WARNS() << "Failed to clear log lines" << LL_ENDL; + break; + } } - - //Only enable joint offsets if it passed the earlier critiquing - if ( isRigValidForJointPositionUpload() ) - { - mFMP->childSetEnabled("upload_joints", upload_skin); - } - - F32 explode = mFMP->childGetValue("physics_explode").asReal(); - - LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview - - LLRect preview_rect; - - preview_rect = mFMP->getChildView("preview_panel")->getRect(); - - F32 aspect = (F32) preview_rect.getWidth()/preview_rect.getHeight(); - - LLViewerCamera::getInstance()->setAspect(aspect); - - LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); - - LLVector3 offset = mCameraOffset; - LLVector3 target_pos = mPreviewTarget+offset; - - F32 z_near = 0.001f; - F32 z_far = mCameraDistance*10.0f+mPreviewScale.magVec()+mCameraOffset.magVec(); - - if (skin_weight) - { - target_pos = getPreviewAvatar()->getPositionAgent(); - z_near = 0.01f; - z_far = 1024.f; - - //render avatar previews every frame - refresh(); - } - - if (use_shaders) - { - gObjectPreviewProgram.bind(); - } - - gGL.loadIdentity(); - gPipeline.enableLightsPreview(); - - LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * - LLQuaternion(mCameraYaw, LLVector3::z_axis); - - LLQuaternion av_rot = camera_rot; - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest - - - z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f); - - LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); - - stop_glerror(); - - gGL.pushMatrix(); - const F32 BRIGHTNESS = 0.9f; - gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); - - const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - LLGLEnable normalize(GL_NORMALIZE); - - if (!mBaseModel.empty() && mVertexBuffer[5].empty()) - { - genBuffers(-1, skin_weight); - //genBuffers(3); - //genLODs(); - } - - if (!mModel[mPreviewLOD].empty()) - { - mFMP->childEnable("reset_btn"); - - bool regen = mVertexBuffer[mPreviewLOD].empty(); - if (!regen) - { - const std::vector<LLPointer<LLVertexBuffer> >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second; - if (!vb_vec.empty()) - { - const LLVertexBuffer* buff = vb_vec[0]; - regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; - } - else - { - LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL; - regen = TRUE; - } - } - - if (regen) - { - genBuffers(mPreviewLOD, skin_weight); - } - - if (!skin_weight) - { - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[mPreviewLOD]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*) mat.mMatrix); - - - U32 num_models = mVertexBuffer[mPreviewLOD][model].size(); - for (U32 i = 0; i < num_models; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - - if (textures) - { - int materialCnt = instance.mModel->mMaterialList.size(); - if ( i < materialCnt ) - { - const std::string& binding = instance.mModel->mMaterialList[i]; - const LLImportMaterial& material = instance.mMaterial[binding]; - - gGL.diffuseColor4fv(material.mDiffuseColor.mV); - - // Find the tex for this material, bind it, and add it to our set - // - LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); - if (tex) - { - mTextureSet.insert(tex); - } - } - } - else - { - gGL.diffuseColor4f(1,1,1,1); - } - - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.diffuseColor3f(0.4f, 0.4f, 0.4f); - - if (edges) - { - glLineWidth(3.f); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); - } - } - gGL.popMatrix(); - } - - if (physics) - { - glClear(GL_DEPTH_BUFFER_BIT); - - for (U32 pass = 0; pass < 2; pass++) - { - if (pass == 0) - { //depth only pass - gGL.setColorMask(false, false); - } - else - { - gGL.setColorMask(true, true); - } - - //enable alpha blending on second pass but not first pass - LLGLState blend(GL_BLEND, pass); - - gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA); - - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*) mat.mMatrix); - - - bool render_mesh = true; - - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); - - LLModel::Decomposition& physics = model->mPhysics; - - if (!physics.mHull.empty()) - { - render_mesh = false; - - if (physics.mMesh.empty()) - { //build vertex buffer for physics mesh - gMeshRepo.buildPhysicsMesh(physics); - } - - if (!physics.mMesh.empty()) - { //render hull instead of mesh - for (U32 i = 0; i < physics.mMesh.size(); ++i) - { - if (explode > 0.f) - { - gGL.pushMatrix(); - - LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters; - offset *= explode; - - gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); - } - - static std::vector<LLColor4U> hull_colors; - - if (i+1 >= hull_colors.size()) - { - hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 128)); - } - - gGL.diffuseColor4ubv(hull_colors[i].mV); - LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); - - if (explode > 0.f) - { - gGL.popMatrix(); - } - } - } - } - } - - if (render_mesh) - { - if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) - { - genBuffers(LLModel::LOD_PHYSICS, false); - } - - U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); - for (U32 i = 0; i < num_models; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f); - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - - gGL.diffuseColor3f(1.f, 1.f, 0.f); - - glLineWidth(2.f); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); - } - } - - gGL.popMatrix(); - } - - glLineWidth(3.f); - glPointSize(8.f); - gPipeline.enableLightsFullbright(); - //show degenerate triangles - LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS); - LLGLDisable cull(GL_CULL_FACE); - gGL.diffuseColor4f(1.f,0.f,0.f,1.f); - const LLVector4a scale(0.5f); - - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - gGL.multMatrix((GLfloat*) mat.mMatrix); - - - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); - - LLModel::Decomposition& physics = model->mPhysics; - - if (physics.mHull.empty()) - { - if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) - { - genBuffers(LLModel::LOD_PHYSICS, false); - } - - for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - - LLStrider<LLVector3> pos_strider; - buffer->getVertexStrider(pos_strider, 0); - LLVector4a* pos = (LLVector4a*) pos_strider.get(); - - LLStrider<U16> idx; - buffer->getIndexStrider(idx, 0); - - for (U32 i = 0; i < buffer->getNumIndices(); i += 3) - { - LLVector4a v1; v1.setMul(pos[*idx++], scale); - LLVector4a v2; v2.setMul(pos[*idx++], scale); - LLVector4a v3; v3.setMul(pos[*idx++], scale); - - if (ll_is_degenerate(v1,v2,v3)) - { - buffer->draw(LLRender::LINE_LOOP, 3, i); - buffer->draw(LLRender::POINTS, 3, i); - } - } - } - } - } - - gGL.popMatrix(); - } - glLineWidth(1.f); - glPointSize(1.f); - gPipeline.enableLightsPreview(); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - } - } - else - { - target_pos = getPreviewAvatar()->getPositionAgent(); - - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest - - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - - if (!model->mSkinWeights.empty()) - { - for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - const LLVolumeFace& face = model->getVolumeFace(i); - - LLStrider<LLVector3> position; - buffer->getVertexStrider(position); - - LLStrider<LLVector4> weight; - buffer->getWeight4Strider(weight); - - //quick 'n dirty software vertex skinning - - //build matrix palette - - LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT]; - const LLMeshSkinInfo *skin = &model->mSkinInfo; - U32 count = LLSkinningUtil::getMeshJointCount(skin); - LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, count, - skin, getPreviewAvatar()); - LLMatrix4a bind_shape_matrix; - bind_shape_matrix.loadu(skin->mBindShapeMatrix); - U32 max_joints = LLSkinningUtil::getMaxJointCount(); - for (U32 j = 0; j < buffer->getNumVerts(); ++j) - { - LLMatrix4a final_mat; - F32 *wptr = weight[j].mV; - LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints); - - //VECTORIZE THIS - LLVector4a& v = face.mPositions[j]; - - LLVector4a t; - LLVector4a dst; - bind_shape_matrix.affineTransform(v, t); - final_mat.affineTransform(t, dst); - - position[j][0] = dst[0]; - position[j][1] = dst[1]; - position[j][2] = dst[2]; - } - - llassert(model->mMaterialList.size() > i); - const std::string& binding = instance.mModel->mMaterialList[i]; - const LLImportMaterial& material = instance.mMaterial[binding]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - gGL.diffuseColor4fv(material.mDiffuseColor.mV); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - // Find the tex for this material, bind it, and add it to our set - // - LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); - if (tex) - { - mTextureSet.insert(tex); - } - - buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - gGL.diffuseColor3f(0.4f, 0.4f, 0.4f); - - if (edges) - { - glLineWidth(3.f); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); - } - } - } - } - } - - if (joint_positions) - { - LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - if (shader) - { - gDebugProgram.bind(); - } - getPreviewAvatar()->renderCollisionVolumes(); - getPreviewAvatar()->renderBones(); - if (shader) - { - shader->bind(); - } - } - - } - } - - if (use_shaders) - { - gObjectPreviewProgram.unbind(); - } - gGL.popMatrix(); + mUploadLogText->appendText(str, true); - return TRUE; -} - -//----------------------------------------------------------------------------- -// refresh() -//----------------------------------------------------------------------------- -void LLModelPreview::refresh() -{ - mNeedsUpdate = TRUE; -} - -//----------------------------------------------------------------------------- -// rotate() -//----------------------------------------------------------------------------- -void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) -{ - mCameraYaw = mCameraYaw + yaw_radians; - - mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); -} - -//----------------------------------------------------------------------------- -// zoom() -//----------------------------------------------------------------------------- -void LLModelPreview::zoom(F32 zoom_amt) -{ - F32 new_zoom = mCameraZoom+zoom_amt; - - mCameraZoom = llclamp(new_zoom, 1.f, 10.f); + if (flash) + { + LLPanel* panel = mTabContainer->getPanelByName("logs_panel"); + if (mTabContainer->getCurrentPanel() != panel) + { + mTabContainer->setTabPanelFlashing(panel, true); + } + } } -void LLModelPreview::pan(F32 right, F32 up) +void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost) { - mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); - mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); + assert_main_thread(); + childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x)); + childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", y)); + childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", z)); } -void LLModelPreview::setPreviewLOD(S32 lod) +void LLFloaterModelPreview::setPreviewLOD(S32 lod) { - lod = llclamp(lod, 0, (S32) LLModel::LOD_HIGH); - - if (lod != mPreviewLOD) + if (mModelPreview) { - mPreviewLOD = lod; - - LLComboBox* combo_box = mFMP->getChild<LLComboBox>("preview_lod_combo"); - combo_box->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order - mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]); - - LLComboBox* combo_box2 = mFMP->getChild<LLComboBox>("preview_lod_combo2"); - combo_box2->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order - - LLComboBox* combo_box3 = mFMP->getChild<LLComboBox>("preview_lod_combo3"); - combo_box3->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order - - LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); - LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); - - for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i) - { - const LLColor4& color = (i == lod) ? highlight_color : normal_color; - - mFMP->childSetColor(lod_status_name[i], color); - mFMP->childSetColor(lod_label_name[i], color); - mFMP->childSetColor(lod_triangles_name[i], color); - mFMP->childSetColor(lod_vertices_name[i], color); - } + mModelPreview->setPreviewLOD(lod); } - refresh(); - updateStatusMessages(); } void LLFloaterModelPreview::onBrowseLOD(S32 lod) @@ -4317,12 +1573,16 @@ void LLFloaterModelPreview::onReset(void* user_data) { assert_main_thread(); + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data; fmp->childDisable("reset_btn"); + fmp->clearLogTab(); + fmp->clearAvatarTab(); LLModelPreview* mp = fmp->mModelPreview; std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; fmp->resetDisplayOptions(); + fmp->resetUploadOptions(); //reset model preview fmp->initModelPreview(); @@ -4336,6 +1596,7 @@ void LLFloaterModelPreview::onUpload(void* user_data) assert_main_thread(); LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; + mp->clearLogTab(); mp->mUploadBtn->setEnabled(false); @@ -4364,60 +1625,6 @@ void LLFloaterModelPreview::refresh() sInstance->mModelPreview->mDirty = true; } -//static -void LLModelPreview::textureLoadedCallback( - BOOL success, - LLViewerFetchedTexture *src_vi, - LLImageRaw* src, - LLImageRaw* src_aux, - S32 discard_level, - BOOL final, - void* userdata ) -{ - LLModelPreview* preview = (LLModelPreview*) userdata; - preview->refresh(); - - if(final && preview->mModelLoader) - { - if(preview->mModelLoader->mNumOfFetchingTextures > 0) - { - preview->mModelLoader->mNumOfFetchingTextures-- ; - } - } -} - -// static -bool LLModelPreview::lodQueryCallback() -{ - // not the best solution, but model preview belongs to floater - // so it is an easy way to check that preview still exists. - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp && fmp->mModelPreview) - { - LLModelPreview* preview = fmp->mModelPreview; - if (preview->mLodsQuery.size() > 0) - { - S32 lod = preview->mLodsQuery.back(); - preview->mLodsQuery.pop_back(); - preview->genLODs(lod); - - // return false to continue cycle - return false; - } - } - // nothing to process - return true; -} - -void LLModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) -{ - if (!mLODFrozen) - { - genLODs(lod, 3, enforce_tri_limit); - refresh(); - } -} - LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LLModel* mdl) { mStage = stage; @@ -4430,6 +1637,26 @@ LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LL assignData(mdl) ; } +void LLFloaterModelPreview::setCtrlLoadFromFile(S32 lod) +{ + if (lod == LLModel::LOD_PHYSICS) + { + LLComboBox* lod_combo = findChild<LLComboBox>("physics_lod_combo"); + if (lod_combo) + { + lod_combo->setCurrentByIndex(5); + } + } + else + { + LLComboBox* lod_combo = findChild<LLComboBox>("lod_source_" + lod_name[lod]); + if (lod_combo) + { + lod_combo->setCurrentByIndex(0); + } + } +} + void LLFloaterModelPreview::setStatusMessage(const std::string& msg) { LLMutexLock lock(mStatusLock); @@ -4476,11 +1703,15 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible) { childSetTextArg("upload_fee", "[FEE]", tbd); } - childSetTextArg("price_breakdown", "[STREAMING]", tbd); - childSetTextArg("price_breakdown", "[PHYSICS]", tbd); - childSetTextArg("price_breakdown", "[INSTANCES]", tbd); - childSetTextArg("price_breakdown", "[TEXTURES]", tbd); - childSetTextArg("price_breakdown", "[MODEL]", tbd); + std::string dashes = hasString("--") ? getString("--") : "--"; + childSetTextArg("price_breakdown", "[STREAMING]", dashes); + childSetTextArg("price_breakdown", "[PHYSICS]", dashes); + childSetTextArg("price_breakdown", "[INSTANCES]", dashes); + childSetTextArg("price_breakdown", "[TEXTURES]", dashes); + childSetTextArg("price_breakdown", "[MODEL]", dashes); + childSetTextArg("physics_breakdown", "[PCH]", dashes); + childSetTextArg("physics_breakdown", "[PM]", dashes); + childSetTextArg("physics_breakdown", "[PHU]", dashes); } } @@ -4507,6 +1738,44 @@ void LLFloaterModelPreview::resetDisplayOptions() } } +void LLFloaterModelPreview::resetUploadOptions() +{ + childSetValue("import_scale", 1); + childSetValue("pelvis_offset", 0); + childSetValue("physics_explode", 0); + childSetValue("physics_file", ""); + childSetVisible("Retain%", false); + childSetVisible("Retain%_label", false); + childSetVisible("Detail Scale", true); + childSetVisible("Detail Scale label", true); + + getChild<LLComboBox>("lod_source_" + lod_name[NUM_LOD - 1])->setCurrentByIndex(LLModelPreview::LOD_FROM_FILE); + for (S32 lod = 0; lod < NUM_LOD - 1; ++lod) + { + getChild<LLComboBox>("lod_source_" + lod_name[lod])->setCurrentByIndex(LLModelPreview::GENERATE); + childSetValue("lod_file_" + lod_name[lod], ""); + } + + for(auto& p : mDefaultDecompParams) + { + std::string ctrl_name(p.first); + LLUICtrl* ctrl = getChild<LLUICtrl>(ctrl_name); + if (ctrl) + { + ctrl->setValue(p.second); + } + } + getChild<LLComboBox>("physics_lod_combo")->setCurrentByIndex(0); + getChild<LLComboBox>("Cosine%")->setCurrentByIndex(0); +} + +void LLFloaterModelPreview::clearLogTab() +{ + mUploadLogText->clear(); + LLPanel* panel = mTabContainer->getPanelByName("logs_panel"); + mTabContainer->setTabPanelFlashing(panel, false); +} + void LLFloaterModelPreview::onModelPhysicsFeeReceived(const LLSD& result, std::string upload_url) { mModelPhysicsFee = result; @@ -4530,6 +1799,16 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived() childSetTextArg("price_breakdown", "[INSTANCES]", llformat("%d", result["upload_price_breakdown"]["mesh_instance"].asInteger())); childSetTextArg("price_breakdown", "[TEXTURES]", llformat("%d", result["upload_price_breakdown"]["texture"].asInteger())); childSetTextArg("price_breakdown", "[MODEL]", llformat("%d", result["upload_price_breakdown"]["model"].asInteger())); + + childSetTextArg("physics_breakdown", "[PCH]", llformat("%0.3f", result["model_physics_cost"]["hull"].asReal())); + childSetTextArg("physics_breakdown", "[PM]", llformat("%0.3f", result["model_physics_cost"]["mesh"].asReal())); + childSetTextArg("physics_breakdown", "[PHU]", llformat("%0.3f", result["model_physics_cost"]["decomposition"].asReal())); + childSetTextArg("streaming_breakdown", "[STR_TOTAL]", llformat("%d", result["streaming_cost"].asInteger())); + childSetTextArg("streaming_breakdown", "[STR_HIGH]", llformat("%d", result["streaming_params"]["high_lod"].asInteger())); + childSetTextArg("streaming_breakdown", "[STR_MED]", llformat("%d", result["streaming_params"]["medium_lod"].asInteger())); + childSetTextArg("streaming_breakdown", "[STR_LOW]", llformat("%d", result["streaming_params"]["low_lod"].asInteger())); + childSetTextArg("streaming_breakdown", "[STR_LOWEST]", llformat("%d", result["streaming_params"]["lowest_lod"].asInteger())); + childSetVisible("upload_fee", true); childSetVisible("price_breakdown", true); mUploadBtn->setEnabled(isModelUploadAllowed()); @@ -4537,7 +1816,11 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived() void LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(S32 status, const std::string& reason, const LLSD& result) { - LL_WARNS() << "LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(" << status << " : " << reason << ")" << LL_ENDL; + std::ostringstream out; + out << "LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(" << status; + out << " : " << reason << ")"; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); doOnIdleOneTime(boost::bind(&LLFloaterModelPreview::toggleCalculateButton, this, true)); if (result.has("upload_price")) diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 1c66570650..8a01b0c307 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -28,36 +28,26 @@ #define LL_LLFLOATERMODELPREVIEW_H #include "llfloaternamedesc.h" - -#include "lldynamictexture.h" -#include "llquaternion.h" -#include "llmeshrepository.h" -#include "llmodel.h" -#include "llthread.h" -#include "llviewermenufile.h" #include "llfloatermodeluploadbase.h" - -#include "lldaeloader.h" +#include "llmeshrepository.h" class LLComboBox; class LLJoint; -class LLViewerJointMesh; -class LLVOAvatar; -class LLTextBox; -class LLVertexBuffer; +class LLMeshFilePicker; class LLModelPreview; -class LLFloaterModelPreview; -class DAE; -class daeElement; -class domProfile_COMMON; -class domInstance_geometry; -class domNode; -class domTranslate; -class domController; -class domSkin; -class domMesh; -class LLMenuButton; -class LLToggleableMenu; +class LLTabContainer; +class LLViewerTextEditor; + + +class LLJointOverrideData +{ +public: + LLJointOverrideData() : mHasConflicts(false) {}; + std::map<std::string, LLVector3> mPosOverrides; // models with overrides + std::set<std::string> mModelsNoOverrides; // models without defined overrides + bool mHasConflicts; +}; +typedef std::map<std::string, LLJointOverrideData> joint_override_data_map_t; class LLFloaterModelPreview : public LLFloaterModelUploadBase { @@ -80,6 +70,7 @@ public: virtual ~LLFloaterModelPreview(); virtual BOOL postBuild(); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); void initModelPreview(); @@ -93,6 +84,11 @@ public: static void onMouseCaptureLostModelPreview(LLMouseHandler*); static void setUploadAmount(S32 amount) { sUploadAmount = amount; } + static void addStringToLog(const std::string& message, const LLSD& args, bool flash, S32 lod = -1); + static void addStringToLog(const std::string& str, bool flash); + static void addStringToLog(const std::ostringstream& strm, bool flash); + void clearAvatarTab(); // clears table + void updateAvatarTab(bool highlight_overrides); // populates table and data as nessesary void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost); void setPreviewLOD(S32 lod); @@ -107,13 +103,17 @@ public: void loadModel(S32 lod); void loadModel(S32 lod, const std::string& file_name, bool force_disable_slm = false); + + void loadHighLodModel(); void onViewOptionChecked(LLUICtrl* ctrl); + void onUploadOptionChecked(LLUICtrl* ctrl); bool isViewOptionChecked(const LLSD& userdata); bool isViewOptionEnabled(const LLSD& userdata); void setViewOptionEnabled(const std::string& option, bool enabled); void enableViewOption(const std::string& option); void disableViewOption(const std::string& option); + void onShowSkinWeightChecked(LLUICtrl* ctrl); bool isModelLoading(); @@ -142,8 +142,6 @@ protected: static void onImportScaleCommit(LLUICtrl*, void*); static void onPelvisOffsetCommit(LLUICtrl*, void*); - static void onUploadJointsCommit(LLUICtrl*,void*); - static void onUploadSkinCommit(LLUICtrl*,void*); static void onPreviewLODCommit(LLUICtrl*,void*); @@ -154,6 +152,7 @@ protected: static void onAutoFillCommit(LLUICtrl*,void*); void onLODParamCommit(S32 lod, bool enforce_tri_limit); + void draw3dPreview(); static void onExplodeCommit(LLUICtrl*, void*); @@ -175,11 +174,15 @@ protected: // FIXME - this function and mStatusMessage have no visible effect, and the // actual status messages are managed by directly manipulation of // the UI element. - void setStatusMessage(const std::string& msg); + void setStatusMessage(const std::string& msg); + void addStringToLogTab(const std::string& str, bool flash); + + void setCtrlLoadFromFile(S32 lod); LLModelPreview* mModelPreview; LLPhysicsDecomp::decomp_params mDecompParams; + LLPhysicsDecomp::decomp_params mDefaultDecompParams; S32 mLastMouseX; S32 mLastMouseY; @@ -203,223 +206,34 @@ protected: LLSD mModelPhysicsFee; private: - void onClickCalculateBtn(); - void toggleCalculateButton(); + void onClickCalculateBtn(); + void onJointListSelection(); void onLoDSourceCommit(S32 lod); void modelUpdated(bool calculate_visible); // Toggles between "Calculate weights & fee" and "Upload" buttons. + void toggleCalculateButton(); void toggleCalculateButton(bool visible); // resets display options of model preview to their defaults. void resetDisplayOptions(); + void resetUploadOptions(); + void clearLogTab(); + void createSmoothComboBox(LLComboBox* combo_box, float min, float max); LLButton* mUploadBtn; LLButton* mCalculateBtn; -}; - -class LLMeshFilePicker : public LLFilePickerThread -{ -public: - LLMeshFilePicker(LLModelPreview* mp, S32 lod); - virtual void notify(const std::vector<std::string>& filenames); - -private: - LLModelPreview* mMP; - S32 mLOD; -}; - - -class LLModelPreview : public LLViewerDynamicTexture, public LLMutex -{ - typedef boost::signals2::signal<void (F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)> details_signal_t; - typedef boost::signals2::signal<void (void)> model_loaded_signal_t; - typedef boost::signals2::signal<void (bool)> model_updated_signal_t; - -public: - - typedef enum - { - LOD_FROM_FILE = 0, - GENERATE, - USE_LOD_ABOVE, - } eLoDMode; - -public: - LLModelPreview(S32 width, S32 height, LLFloater* fmp); - virtual ~LLModelPreview(); - - void resetPreviewTarget(); - void setPreviewTarget(F32 distance); - void setTexture(U32 name) { mTextureName = name; } - - void setPhysicsFromLOD(S32 lod); - BOOL render(); - void update(); - void genBuffers(S32 lod, bool skinned); - void clearBuffers(); - void refresh(); - void rotate(F32 yaw_radians, F32 pitch_radians); - void zoom(F32 zoom_amt); - void pan(F32 right, F32 up); - virtual BOOL needsRender() { return mNeedsUpdate; } - void setPreviewLOD(S32 lod); - void clearModel(S32 lod); - void getJointAliases(JointMap& joint_map); - void loadModel(std::string filename, S32 lod, bool force_disable_slm = false); - void loadModelCallback(S32 lod); - bool lodsReady() { return !mGenLOD && mLodsQuery.empty(); } - void queryLODs() { mGenLOD = true; }; - void genLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); - void generateNormals(); - void restoreNormals(); - U32 calcResourceCost(); - void rebuildUploadData(); - void saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position); - void saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position); - void clearIncompatible(S32 lod); - void updateStatusMessages(); - void updateLodControls(S32 lod); - void clearGLODGroup(); - void onLODParamCommit(S32 lod, bool enforce_tri_limit); - void addEmptyFace( LLModel* pTarget ); - - const bool getModelPivot( void ) const { return mHasPivot; } - void setHasPivot( bool val ) { mHasPivot = val; } - void setModelPivot( const LLVector3& pivot ) { mModelPivot = pivot; } - - //Is a rig valid so that it can be used as a criteria for allowing for uploading of joint positions - //Accessors for joint position upload friendly rigs - const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; } - void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; } - - //Accessors for the legacy rigs - const bool isLegacyRigValid( void ) const { return mLegacyRigValid; } - void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; } - - static void textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); - static bool lodQueryCallback(); - - boost::signals2::connection setDetailsCallback( const details_signal_t::slot_type& cb ){ return mDetailsSignal.connect(cb); } - boost::signals2::connection setModelLoadedCallback( const model_loaded_signal_t::slot_type& cb ){ return mModelLoadedSignal.connect(cb); } - boost::signals2::connection setModelUpdatedCallback( const model_updated_signal_t::slot_type& cb ){ return mModelUpdatedSignal.connect(cb); } - - void setLoadState( U32 state ) { mLoadState = state; } - U32 getLoadState() { return mLoadState; } - - static bool sIgnoreLoadedCallback; - std::vector<S32> mLodsQuery; - std::vector<S32> mLodsWithParsingError; - -protected: - - static void loadedCallback(LLModelLoader::scene& scene,LLModelLoader::model_list& model_list, S32 lod, void* opaque); - static void stateChangedCallback(U32 state, void* opaque); - - static LLJoint* lookupJointByName(const std::string&, void* opaque); - static U32 loadTextures(LLImportMaterial& material, void* opaque); - -private: - //Utility function for controller vertex compare - bool verifyCount( int expected, int result ); - //Creates the dummy avatar for the preview window - void createPreviewAvatar( void ); - //Accessor for the dummy avatar - LLVOAvatar* getPreviewAvatar( void ) { return mPreviewAvatar; } - // Count amount of original models, excluding sub-models - static U32 countRootModels(LLModelLoader::model_list models); - - protected: - friend class LLModelLoader; - friend class LLFloaterModelPreview; - friend class LLFloaterModelPreview::DecompRequest; - friend class LLPhysicsDecomp; - - LLFloater* mFMP; - - BOOL mNeedsUpdate; - bool mDirty; - bool mGenLOD; - U32 mTextureName; - F32 mCameraDistance; - F32 mCameraYaw; - F32 mCameraPitch; - F32 mCameraZoom; - LLVector3 mCameraOffset; - LLVector3 mPreviewTarget; - LLVector3 mPreviewScale; - S32 mPreviewLOD; - S32 mPhysicsSearchLOD; - U32 mResourceCost; - std::string mLODFile[LLModel::NUM_LODS]; - bool mLoading; - U32 mLoadState; - bool mResetJoints; - bool mModelNoErrors; - - std::map<std::string, bool> mViewOption; - - //GLOD object parameters (must rebuild object if these change) - bool mLODFrozen; - F32 mBuildShareTolerance; - U32 mBuildQueueMode; - U32 mBuildOperator; - U32 mBuildBorderMode; - U32 mRequestedLoDMode[LLModel::NUM_LODS]; - S32 mRequestedTriangleCount[LLModel::NUM_LODS]; - F32 mRequestedErrorThreshold[LLModel::NUM_LODS]; - U32 mRequestedBuildOperator[LLModel::NUM_LODS]; - U32 mRequestedQueueMode[LLModel::NUM_LODS]; - U32 mRequestedBorderMode[LLModel::NUM_LODS]; - F32 mRequestedShareTolerance[LLModel::NUM_LODS]; - F32 mRequestedCreaseAngle[LLModel::NUM_LODS]; - - LLModelLoader* mModelLoader; - - LLModelLoader::scene mScene[LLModel::NUM_LODS]; - LLModelLoader::scene mBaseScene; - - LLModelLoader::model_list mModel[LLModel::NUM_LODS]; - LLModelLoader::model_list mBaseModel; - - typedef std::vector<LLVolumeFace> v_LLVolumeFace_t; - typedef std::vector<v_LLVolumeFace_t> vv_LLVolumeFace_t; - - vv_LLVolumeFace_t mModelFacesCopy[LLModel::NUM_LODS]; - vv_LLVolumeFace_t mBaseModelFacesCopy; - - U32 mGroup; - std::map<LLPointer<LLModel>, U32> mObject; - U32 mMaxTriangleLimit; - - LLMeshUploadThread::instance_list mUploadData; - std::set<LLViewerFetchedTexture * > mTextureSet; - - //map of vertex buffers to models (one vertex buffer in vector per face in model - std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mVertexBuffer[LLModel::NUM_LODS+1]; - - details_signal_t mDetailsSignal; - model_loaded_signal_t mModelLoadedSignal; - model_updated_signal_t mModelUpdatedSignal; - - LLVector3 mModelPivot; - bool mHasPivot; - - float mPelvisZOffset; - - bool mRigValidJointUpload; - bool mLegacyRigValid; - - bool mLastJointUpdate; + LLViewerTextEditor* mUploadLogText; + LLTabContainer* mTabContainer; - JointNameSet mJointsFromNode; - JointTransformMap mJointTransformMap; + S32 mAvatarTabIndex; // just to avoid any issues in case of xml changes + std::string mSelectedJointName; - LLPointer<LLVOAvatar> mPreviewAvatar; + joint_override_data_map_t mJointOverrides[LLModel::NUM_LODS]; }; #endif // LL_LLFLOATERMODELPREVIEW_H diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp index 3968f43485..649a107d74 100644 --- a/indra/newview/llfloaterperms.cpp +++ b/indra/newview/llfloaterperms.cpp @@ -178,7 +178,6 @@ void LLFloaterPermsDefault::sendInitialPerms() if(!mCapSent) { updateCap(); - setCapSent(true); } } @@ -240,7 +239,7 @@ void LLFloaterPermsDefault::updateCapCoro(std::string url) { const std::string& reason = status.toString(); // Do not display the same error more than once in a row - if (reason != previousReason) + if ((reason != previousReason) && mCapSent) { previousReason = reason; LLSD args; diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 81f4b2234c..6bf2136f60 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -71,8 +71,9 @@ #include "lltrans.h" #include "llviewercontrol.h" #include "llviewercamera.h" -#include "llviewerwindow.h" +#include "llviewereventrecorder.h" #include "llviewermessage.h" +#include "llviewerwindow.h" #include "llviewershadermgr.h" #include "llviewerthrottle.h" #include "llvoavatarself.h" @@ -159,87 +160,6 @@ struct LabelTable : public LLInitParam::Block<LabelTable> {} }; -class LLVoiceSetKeyDialog : public LLModalDialog -{ -public: - LLVoiceSetKeyDialog(const LLSD& key); - ~LLVoiceSetKeyDialog(); - - /*virtual*/ BOOL postBuild(); - - void setParent(LLFloaterPreference* parent) { mParent = parent; } - - BOOL handleKeyHere(KEY key, MASK mask); - BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); - static void onCancel(void* user_data); - -private: - LLFloaterPreference* mParent; -}; - -LLVoiceSetKeyDialog::LLVoiceSetKeyDialog(const LLSD& key) - : LLModalDialog(key), - mParent(NULL) -{ -} - -//virtual -BOOL LLVoiceSetKeyDialog::postBuild() -{ - childSetAction("Cancel", onCancel, this); - getChild<LLUICtrl>("Cancel")->setFocus(TRUE); - - gFocusMgr.setKeystrokesOnly(TRUE); - - return TRUE; -} - -LLVoiceSetKeyDialog::~LLVoiceSetKeyDialog() -{ -} - -BOOL LLVoiceSetKeyDialog::handleKeyHere(KEY key, MASK mask) -{ - BOOL result = TRUE; - - if (key == 'Q' && mask == MASK_CONTROL) - { - result = FALSE; - } - else if (mParent) - { - mParent->setKey(key); - } - closeFloater(); - return result; -} - -BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) -{ - BOOL result = FALSE; - if (down - && (clicktype == LLMouseHandler::CLICK_MIDDLE || clicktype == LLMouseHandler::CLICK_BUTTON4 || clicktype == LLMouseHandler::CLICK_BUTTON5) - && mask == 0) - { - mParent->setMouse(clicktype); - result = TRUE; - closeFloater(); - } - else - { - result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); - } - - return result; -} - -//static -void LLVoiceSetKeyDialog::onCancel(void* user_data) -{ - LLVoiceSetKeyDialog* self = (LLVoiceSetKeyDialog*)user_data; - self->closeFloater(); -} - // global functions @@ -319,37 +239,6 @@ void handleAppearanceCameraMovementChanged(const LLSD& newvalue) } } -/*bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option && floater ) - { - if ( floater ) - { - floater->setAllIgnored(); - // LLFirstUse::disableFirstUse(); - floater->buildPopupLists(); - } - } - return false; -} - -bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if ( 0 == option && floater ) - { - if ( floater ) - { - floater->resetAllIgnored(); - //LLFirstUse::resetFirstUse(); - floater->buildPopupLists(); - } - } - return false; -} -*/ - void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator) { numerator = 0; @@ -374,8 +263,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mGotPersonalInfo(false), mOriginalIMViaEmail(false), mLanguageChanged(false), - mAvatarDataInitialized(false), - mClickActionDirty(false) + mAvatarDataInitialized(false) { LLConversationLog::instance().addObserver(this); @@ -384,7 +272,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) static bool registered_dialog = false; if (!registered_dialog) { - LLFloaterReg::add("voice_set_key", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLVoiceSetKeyDialog>); + LLFloaterReg::add("keybind_dialog", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLSetKeyBindDialog>); registered_dialog = true; } @@ -397,8 +285,6 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.ResetCache", boost::bind(&LLFloaterPreference::onClickResetCache, this)); mCommitCallbackRegistrar.add("Pref.ClickSkin", boost::bind(&LLFloaterPreference::onClickSkin, this,_1, _2)); mCommitCallbackRegistrar.add("Pref.SelectSkin", boost::bind(&LLFloaterPreference::onSelectSkin, this)); - mCommitCallbackRegistrar.add("Pref.VoiceSetKey", boost::bind(&LLFloaterPreference::onClickSetKey, this)); - mCommitCallbackRegistrar.add("Pref.VoiceSetMiddleMouse", boost::bind(&LLFloaterPreference::onClickSetMiddleMouse, this)); mCommitCallbackRegistrar.add("Pref.SetSounds", boost::bind(&LLFloaterPreference::onClickSetSounds, this)); mCommitCallbackRegistrar.add("Pref.ClickEnablePopup", boost::bind(&LLFloaterPreference::onClickEnablePopup, this)); mCommitCallbackRegistrar.add("Pref.ClickDisablePopup", boost::bind(&LLFloaterPreference::onClickDisablePopup, this)); @@ -693,12 +579,6 @@ void LLFloaterPreference::apply() } saveAvatarProperties(); - - if (mClickActionDirty) - { - updateClickActionSettings(); - mClickActionDirty = false; - } } void LLFloaterPreference::cancel() @@ -731,11 +611,7 @@ void LLFloaterPreference::cancel() // reverts any changes to current skin gSavedSettings.setString("SkinCurrent", sSkin); - if (mClickActionDirty) - { - updateClickActionControls(); - mClickActionDirty = false; - } + updateClickActionViews(); LLFloaterPreferenceProxy * advanced_proxy_settings = LLFloaterReg::findTypedInstance<LLFloaterPreferenceProxy>("prefs_proxy"); if (advanced_proxy_settings) @@ -820,7 +696,7 @@ void LLFloaterPreference::onOpen(const LLSD& key) onChangeAnimationFolder(); // Load (double-)click to walk/teleport settings. - updateClickActionControls(); + updateClickActionViews(); // Enabled/disabled popups, might have been changed by user actions // while preferences floater was closed. @@ -1094,6 +970,7 @@ void LLFloaterPreference::onBtnCancel(const LLSD& userdata) if (userdata.asString() == "closeadvanced") { LLFloaterReg::hideInstance("prefs_graphics_advanced"); + updateMaxComplexity(); } else { @@ -1321,9 +1198,11 @@ void LLFloaterPreference::refreshEnabledState() //Deferred/SSAO/Shadows BOOL bumpshiny = gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps && LLFeatureManager::getInstance()->isFeatureAvailable("RenderObjectBump") && gSavedSettings.getBOOL("RenderObjectBump"); + BOOL transparent_water = LLFeatureManager::getInstance()->isFeatureAvailable("RenderTransparentWater") && gSavedSettings.getBOOL("RenderTransparentWater"); BOOL shaders = gSavedSettings.getBOOL("WindLightUseAtmosShaders"); BOOL enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && bumpshiny && + transparent_water && shaders && gGLManager.mHasFramebufferObject && gSavedSettings.getBOOL("RenderAvatarVP") && @@ -1346,7 +1225,10 @@ void LLFloaterPreferenceGraphicsAdvanced::refreshEnabledState() BOOL reflections = gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps; ctrl_reflections->setEnabled(reflections); reflections_text->setEnabled(reflections); - + + // Transparent Water + LLCheckBoxCtrl* transparent_water_ctrl = getChild<LLCheckBoxCtrl>("TransparentWater"); + // Bump & Shiny LLCheckBoxCtrl* bumpshiny_ctrl = getChild<LLCheckBoxCtrl>("BumpShiny"); bool bumpshiny = gGLManager.mHasCubeMap && LLCubeMap::sUseCubeMaps && LLFeatureManager::getInstance()->isFeatureAvailable("RenderObjectBump"); @@ -1397,6 +1279,7 @@ void LLFloaterPreferenceGraphicsAdvanced::refreshEnabledState() BOOL enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && ((bumpshiny_ctrl && bumpshiny_ctrl->get()) ? TRUE : FALSE) && + ((transparent_water_ctrl && transparent_water_ctrl->get()) ? TRUE : FALSE) && gGLManager.mHasFramebufferObject && gSavedSettings.getBOOL("RenderAvatarVP") && (ctrl_wind_light->get()) ? TRUE : FALSE; @@ -1472,7 +1355,7 @@ void LLAvatarComplexityControls::setIndirectMaxNonImpostors() { U32 max_non_impostors = gSavedSettings.getU32("RenderAvatarMaxNonImpostors"); // for this one, we just need to make zero, which means off, the max value of the slider - U32 indirect_max_non_impostors = (0 == max_non_impostors) ? LLVOAvatar::IMPOSTORS_OFF : max_non_impostors; + U32 indirect_max_non_impostors = (0 == max_non_impostors) ? LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER : max_non_impostors; gSavedSettings.setU32("IndirectMaxNonImpostors", indirect_max_non_impostors); } @@ -1617,6 +1500,7 @@ void LLFloaterPreference::refresh() { advanced->refresh(); } + updateClickActionViews(); } void LLFloaterPreferenceGraphicsAdvanced::refresh() @@ -1658,72 +1542,6 @@ void LLFloaterPreference::onChangeQuality(const LLSD& data) refresh(); } -void LLFloaterPreference::onClickSetKey() -{ - LLVoiceSetKeyDialog* dialog = LLFloaterReg::showTypedInstance<LLVoiceSetKeyDialog>("voice_set_key", LLSD(), TRUE); - if (dialog) - { - dialog->setParent(this); - } -} - -void LLFloaterPreference::setKey(KEY key) -{ - getChild<LLUICtrl>("modifier_combo")->setValue(LLKeyboard::stringFromKey(key)); - // update the control right away since we no longer wait for apply - getChild<LLUICtrl>("modifier_combo")->onCommit(); -} - -void LLFloaterPreference::setMouse(LLMouseHandler::EClickType click) -{ - std::string bt_name; - std::string ctrl_value; - switch (click) - { - case LLMouseHandler::CLICK_MIDDLE: - bt_name = "middle_mouse"; - ctrl_value = MIDDLE_MOUSE_CV; - break; - case LLMouseHandler::CLICK_BUTTON4: - bt_name = "button4_mouse"; - ctrl_value = MOUSE_BUTTON_4_CV; - break; - case LLMouseHandler::CLICK_BUTTON5: - bt_name = "button5_mouse"; - ctrl_value = MOUSE_BUTTON_5_CV; - break; - default: - break; - } - - if (!ctrl_value.empty()) - { - LLUICtrl* p2t_line_editor = getChild<LLUICtrl>("modifier_combo"); - // We are using text control names for readability and compatibility with voice - p2t_line_editor->setControlValue(ctrl_value); - LLPanel* advanced_preferences = dynamic_cast<LLPanel*>(p2t_line_editor->getParent()); - if (advanced_preferences) - { - p2t_line_editor->setValue(advanced_preferences->getString(bt_name)); - } - } -} - -void LLFloaterPreference::onClickSetMiddleMouse() -{ - LLUICtrl* p2t_line_editor = getChild<LLUICtrl>("modifier_combo"); - - // update the control right away since we no longer wait for apply - p2t_line_editor->setControlValue(MIDDLE_MOUSE_CV); - - //push2talk button "middle mouse" control value is in English, need to localize it for presentation - LLPanel* advanced_preferences = dynamic_cast<LLPanel*>(p2t_line_editor->getParent()); - if (advanced_preferences) - { - p2t_line_editor->setValue(advanced_preferences->getString("middle_mouse")); - } -} - void LLFloaterPreference::onClickSetSounds() { // Disable Enable gesture sounds checkbox if the master sound is disabled @@ -1731,18 +1549,6 @@ void LLFloaterPreference::onClickSetSounds() getChild<LLCheckBoxCtrl>("gesture_audio_play_btn")->setEnabled(!gSavedSettings.getBOOL("MuteSounds")); } -/* -void LLFloaterPreference::onClickSkipDialogs() -{ - LLNotificationsUtil::add("SkipShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_skip_dialogs, _1, _2, this)); -} - -void LLFloaterPreference::onClickResetDialogs() -{ - LLNotificationsUtil::add("ResetShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_reset_dialogs, _1, _2, this)); -} - */ - void LLFloaterPreference::onClickEnablePopup() { LLScrollListCtrl& disabled_popups = getChildRef<LLScrollListCtrl>("disabled_popups"); @@ -1926,6 +1732,8 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im getChildView("log_path_button")->setEnabled(TRUE); getChildView("chat_font_size")->setEnabled(TRUE); getChildView("conversation_log_combo")->setEnabled(TRUE); + getChild<LLUICtrl>("voice_call_friends_only_check")->setEnabled(TRUE); + getChild<LLUICtrl>("voice_call_friends_only_check")->setValue(gSavedPerAccountSettings.getBOOL("VoiceCallsFriendsOnly")); } @@ -1970,7 +1778,7 @@ void LLFloaterPreferenceGraphicsAdvanced::updateMaxNonImpostors() LLSliderCtrl* ctrl = getChild<LLSliderCtrl>("IndirectMaxNonImpostors",true); U32 value = ctrl->getValue().asInteger(); - if (0 == value || LLVOAvatar::IMPOSTORS_OFF <= value) + if (0 == value || LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER <= value) { value=0; } @@ -2034,6 +1842,14 @@ void LLFloaterPreference::updateMaxComplexity() LLAvatarComplexityControls::updateMax( getChild<LLSliderCtrl>("IndirectMaxComplexity"), getChild<LLTextBox>("IndirectMaxComplexityText")); + + LLFloaterPreferenceGraphicsAdvanced* floater_graphics_advanced = LLFloaterReg::findTypedInstance<LLFloaterPreferenceGraphicsAdvanced>("prefs_graphics_advanced"); + if (floater_graphics_advanced) + { + LLAvatarComplexityControls::updateMax( + floater_graphics_advanced->getChild<LLSliderCtrl>("IndirectMaxComplexity"), + floater_graphics_advanced->getChild<LLTextBox>("IndirectMaxComplexityText")); + } } bool LLFloaterPreference::loadFromFilename(const std::string& filename, std::map<std::string, std::string> &label_map) @@ -2081,6 +1897,14 @@ void LLFloaterPreferenceGraphicsAdvanced::updateMaxComplexity() LLAvatarComplexityControls::updateMax( getChild<LLSliderCtrl>("IndirectMaxComplexity"), getChild<LLTextBox>("IndirectMaxComplexityText")); + + LLFloaterPreference* floater_preferences = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences"); + if (floater_preferences) + { + LLAvatarComplexityControls::updateMax( + floater_preferences->getChild<LLSliderCtrl>("IndirectMaxComplexity"), + floater_preferences->getChild<LLTextBox>("IndirectMaxComplexityText")); + } } void LLFloaterPreference::onChangeMaturity() @@ -2206,7 +2030,7 @@ void LLFloaterPreference::onClickAdvanced() void LLFloaterPreference::onClickActionChange() { - mClickActionDirty = true; + updateClickActionControls(); } void LLFloaterPreference::onClickPermsDefault() @@ -2246,21 +2070,81 @@ void LLFloaterPreference::onLogChatHistorySaved() } } -void LLFloaterPreference::updateClickActionSettings() +void LLFloaterPreference::updateClickActionControls() { - const int single_clk_action = getChild<LLComboBox>("single_click_action_combo")->getValue().asInteger(); - const int double_clk_action = getChild<LLComboBox>("double_click_action_combo")->getValue().asInteger(); - - gSavedSettings.setBOOL("ClickToWalk", single_clk_action == 1); - gSavedSettings.setBOOL("DoubleClickAutoPilot", double_clk_action == 1); - gSavedSettings.setBOOL("DoubleClickTeleport", double_clk_action == 2); + const int single_clk_action = getChild<LLComboBox>("single_click_action_combo")->getValue().asInteger(); + const int double_clk_action = getChild<LLComboBox>("double_click_action_combo")->getValue().asInteger(); + + // Todo: This is a very ugly way to get access to keybindings. + // Reconsider possible options. + // Potential option: make constructor of LLKeyConflictHandler private + // but add a getter that will return shared pointer for specific + // mode, pointer should only exist so long as there are external users. + // In such case we won't need to do this 'dynamic_cast' nightmare. + // updateTable() can also be avoided + LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); + for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); + iter != tabcontainer->getChildList()->end(); ++iter) + { + LLView* view = *iter; + LLPanelPreferenceControls* panel = dynamic_cast<LLPanelPreferenceControls*>(view); + if (panel) + { + panel->setKeyBind("walk_to", + EMouseClickType::CLICK_LEFT, + KEY_NONE, + MASK_NONE, + single_clk_action == 1); + + panel->setKeyBind("walk_to", + EMouseClickType::CLICK_DOUBLELEFT, + KEY_NONE, + MASK_NONE, + double_clk_action == 1); + + panel->setKeyBind("teleport_to", + EMouseClickType::CLICK_DOUBLELEFT, + KEY_NONE, + MASK_NONE, + double_clk_action == 2); + + panel->updateAndApply(); + } + } } -void LLFloaterPreference::updateClickActionControls() +void LLFloaterPreference::updateClickActionViews() { - const bool click_to_walk = gSavedSettings.getBOOL("ClickToWalk"); - const bool dbl_click_to_walk = gSavedSettings.getBOOL("DoubleClickAutoPilot"); - const bool dbl_click_to_teleport = gSavedSettings.getBOOL("DoubleClickTeleport"); + bool click_to_walk = false; + bool dbl_click_to_walk = false; + bool dbl_click_to_teleport = false; + + // Todo: This is a very ugly way to get access to keybindings. + // Reconsider possible options. + LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); + for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); + iter != tabcontainer->getChildList()->end(); ++iter) + { + LLView* view = *iter; + LLPanelPreferenceControls* panel = dynamic_cast<LLPanelPreferenceControls*>(view); + if (panel) + { + click_to_walk = panel->canKeyBindHandle("walk_to", + EMouseClickType::CLICK_LEFT, + KEY_NONE, + MASK_NONE); + + dbl_click_to_walk = panel->canKeyBindHandle("walk_to", + EMouseClickType::CLICK_DOUBLELEFT, + KEY_NONE, + MASK_NONE); + + dbl_click_to_teleport = panel->canKeyBindHandle("teleport_to", + EMouseClickType::CLICK_DOUBLELEFT, + KEY_NONE, + MASK_NONE); + } + } getChild<LLComboBox>("single_click_action_combo")->setValue((int)click_to_walk); getChild<LLComboBox>("double_click_action_combo")->setValue(dbl_click_to_teleport ? 2 : (int)dbl_click_to_walk); @@ -2447,25 +2331,6 @@ BOOL LLPanelPreference::postBuild() getChild<LLTextBox>("mute_chb_label")->setClickedCallback(boost::bind(&toggleMuteWhenMinimized)); } - //////////////////////PanelAdvanced /////////////////// - if (hasChild("modifier_combo", TRUE)) - { - //localizing if push2talk button is set to middle mouse - std::string modifier_value = getChild<LLUICtrl>("modifier_combo")->getValue().asString(); - if (MIDDLE_MOUSE_CV == modifier_value) - { - getChild<LLUICtrl>("modifier_combo")->setValue(getString("middle_mouse")); - } - else if (MOUSE_BUTTON_4_CV == modifier_value) - { - getChild<LLUICtrl>("modifier_combo")->setValue(getString("button4_mouse")); - } - else if (MOUSE_BUTTON_5_CV == modifier_value) - { - getChild<LLUICtrl>("modifier_combo")->setValue(getString("button5_mouse")); - } - } - //////////////////////PanelSetup /////////////////// if (hasChild("max_bandwidth"), TRUE) { @@ -2554,9 +2419,13 @@ void LLPanelPreference::showMultipleViewersWarning(LLUICtrl* checkbox, const LLS void LLPanelPreference::showFriendsOnlyWarning(LLUICtrl* checkbox, const LLSD& value) { - if (checkbox && checkbox->getValue()) + if (checkbox) { - LLNotificationsUtil::add("FriendsAndGroupsOnly"); + gSavedPerAccountSettings.setBOOL("VoiceCallsFriendsOnly", checkbox->getValue().asBoolean()); + if (checkbox->getValue()) + { + LLNotificationsUtil::add("FriendsAndGroupsOnly"); + } } } @@ -2659,7 +2528,6 @@ class LLPanelPreferencePrivacy : public LLPanelPreference public: LLPanelPreferencePrivacy() { - mAccountIndependentSettings.push_back("VoiceCallsFriendsOnly"); mAccountIndependentSettings.push_back("AutoDisengageMic"); } @@ -2743,7 +2611,7 @@ void LLPanelPreferenceGraphics::setPresetText() } } - if (hasDirtyChilds() && !preset_graphic_active.empty()) + if (hasDirtyChilds() && !preset_graphic_active.empty()) { gSavedSettings.setString("PresetGraphicActive", ""); preset_graphic_active.clear(); @@ -2863,6 +2731,585 @@ void LLPanelPreferenceGraphics::setHardwareDefaults() resetDirtyChilds(); } +//------------------------LLPanelPreferenceControls-------------------------------- +static LLPanelInjector<LLPanelPreferenceControls> t_pref_contrls("panel_preference_controls"); + +LLPanelPreferenceControls::LLPanelPreferenceControls() + :LLPanelPreference(), + mEditingColumn(-1), + mEditingMode(0) +{ + // MODE_COUNT - 1 because there are currently no settings assigned to 'saved settings'. + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + mConflictHandler[i].setLoadMode((LLKeyConflictHandler::ESourceMode)i); + } +} + +LLPanelPreferenceControls::~LLPanelPreferenceControls() +{ +} + +BOOL LLPanelPreferenceControls::postBuild() +{ + // populate list of controls + pControlsTable = getChild<LLScrollListCtrl>("controls_list"); + pKeyModeBox = getChild<LLComboBox>("key_mode"); + + pControlsTable->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onListCommit, this)); + pKeyModeBox->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onModeCommit, this)); + getChild<LLButton>("restore_defaults")->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onRestoreDefaultsBtn, this)); + + return TRUE; +} + +void LLPanelPreferenceControls::regenerateControls() +{ + mEditingMode = pKeyModeBox->getValue().asInteger(); + mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::ESourceMode)mEditingMode); + populateControlTable(); +} + +bool LLPanelPreferenceControls::addControlTableColumns(const std::string &filename) +{ + LLXMLNodePtr xmlNode; + LLScrollListCtrl::Contents contents; + if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) + { + LL_WARNS() << "Failed to load " << filename << LL_ENDL; + return false; + } + LLXUIParser parser; + parser.readXUI(xmlNode, contents, filename); + + if (!contents.validateBlock()) + { + return false; + } + + for (LLInitParam::ParamIterator<LLScrollListColumn::Params>::const_iterator col_it = contents.columns.begin(); + col_it != contents.columns.end(); + ++col_it) + { + pControlsTable->addColumn(*col_it); + } + + return true; +} + +bool LLPanelPreferenceControls::addControlTableRows(const std::string &filename) +{ + LLXMLNodePtr xmlNode; + LLScrollListCtrl::Contents contents; + if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) + { + LL_WARNS() << "Failed to load " << filename << LL_ENDL; + return false; + } + LLXUIParser parser; + parser.readXUI(xmlNode, contents, filename); + + if (!contents.validateBlock()) + { + return false; + } + + LLScrollListCell::Params cell_params; + // init basic cell params + cell_params.font = LLFontGL::getFontSansSerif(); + cell_params.font_halign = LLFontGL::LEFT; + cell_params.column = ""; + cell_params.value = ""; + + + for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = contents.rows.begin(); + row_it != contents.rows.end(); + ++row_it) + { + std::string control = row_it->value.getValue().asString(); + if (!control.empty() && control != "menu_separator") + { + bool show = true; + bool enabled = mConflictHandler[mEditingMode].canAssignControl(control); + if (!enabled) + { + // If empty: this is a placeholder to make sure user won't assign + // value by accident, don't show it + // If not empty: predefined control combination user should see + // to know that combination is reserved + show = !mConflictHandler[mEditingMode].isControlEmpty(control); + // example: teleport_to and walk_to in first person view, and + // sitting related functions, see generatePlaceholders() + } + + if (show) + { + // At the moment viewer is hardcoded to assume that columns are named as lst_ctrl%d + LLScrollListItem::Params item_params(*row_it); + item_params.enabled.setValue(enabled); + + S32 num_columns = pControlsTable->getNumColumns(); + for (S32 col = 1; col < num_columns; col++) + { + cell_params.column = llformat("lst_ctrl%d", col); + cell_params.value = mConflictHandler[mEditingMode].getControlString(control, col - 1); + item_params.columns.add(cell_params); + } + pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM); + } + } + else + { + // Separator example: + // <rows + // enabled = "false"> + // <columns + // type = "icon" + // color = "0 0 0 0.7" + // halign = "center" + // value = "menu_separator" + // column = "lst_action" / > + //</rows> + pControlsTable->addRow(*row_it, EAddPosition::ADD_BOTTOM); + } + } + return true; +} + +void LLPanelPreferenceControls::addControlTableSeparator() +{ + LLScrollListItem::Params separator_params; + separator_params.enabled(false); + LLScrollListCell::Params column_params; + column_params.type = "icon"; + column_params.value = "menu_separator"; + column_params.column = "lst_action"; + column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f); + column_params.font_halign = LLFontGL::HCENTER; + separator_params.columns.add(column_params); + pControlsTable->addRow(separator_params, EAddPosition::ADD_BOTTOM); +} + +void LLPanelPreferenceControls::populateControlTable() +{ + pControlsTable->clearRows(); + pControlsTable->clearColumns(); + + // Add columns + std::string filename; + switch ((LLKeyConflictHandler::ESourceMode)mEditingMode) + { + case LLKeyConflictHandler::MODE_THIRD_PERSON: + case LLKeyConflictHandler::MODE_FIRST_PERSON: + case LLKeyConflictHandler::MODE_EDIT_AVATAR: + case LLKeyConflictHandler::MODE_SITTING: + filename = "control_table_contents_columns_basic.xml"; + break; + default: + // Either unknown mode or MODE_SAVED_SETTINGS + // It doesn't have UI or actual settings yet + LL_INFOS() << "Unimplemented mode" << LL_ENDL; + return; + } + addControlTableColumns(filename); + + // Add rows. + // Each file represents individual visual group (movement/camera/media...) + if (mEditingMode == LLKeyConflictHandler::MODE_FIRST_PERSON) + { + // Don't display whole camera and editing groups + addControlTableRows("control_table_contents_movement.xml"); + addControlTableSeparator(); + addControlTableRows("control_table_contents_media.xml"); + } + // MODE_THIRD_PERSON; MODE_EDIT_AVATAR; MODE_SITTING + else if (mEditingMode < LLKeyConflictHandler::MODE_SAVED_SETTINGS) + { + // In case of 'sitting' mode, movements still apply due to vehicles + // but walk_to is not supported and will be hidden by addControlTableRows + addControlTableRows("control_table_contents_movement.xml"); + addControlTableSeparator(); + + addControlTableRows("control_table_contents_camera.xml"); + addControlTableSeparator(); + + addControlTableRows("control_table_contents_editing.xml"); + addControlTableSeparator(); + + addControlTableRows("control_table_contents_media.xml"); + } + else + { + LL_INFOS() << "Unimplemented mode" << LL_ENDL; + return; + } +} + +void LLPanelPreferenceControls::updateTable() +{ + mEditingControl.clear(); + std::vector<LLScrollListItem*> list = pControlsTable->getAllData(); + for (S32 i = 0; i < list.size(); ++i) + { + std::string control = list[i]->getValue(); + if (!control.empty()) + { + LLScrollListCell* cell = NULL; + + S32 num_columns = pControlsTable->getNumColumns(); + for (S32 col = 1; col < num_columns; col++) + { + cell = list[i]->getColumn(col); + cell->setValue(mConflictHandler[mEditingMode].getControlString(control, col - 1)); + } + } + } + pControlsTable->deselectAllItems(); +} + +void LLPanelPreferenceControls::apply() +{ + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].saveToSettings(); + } + } +} + +void LLPanelPreferenceControls::cancel() +{ + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].clear(); + } + } + pControlsTable->clearRows(); + pControlsTable->clearColumns(); +} + +void LLPanelPreferenceControls::saveSettings() +{ + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].saveToSettings(); + mConflictHandler[i].clear(); + } + } + + S32 mode = pKeyModeBox->getValue().asInteger(); + if (mConflictHandler[mode].empty() || pControlsTable->isEmpty()) + { + regenerateControls(); + } +} + +void LLPanelPreferenceControls::resetDirtyChilds() +{ + regenerateControls(); +} + +void LLPanelPreferenceControls::onListCommit() +{ + LLScrollListItem* item = pControlsTable->getFirstSelected(); + if (item == NULL) + { + return; + } + + std::string control = item->getValue(); + + if (control.empty()) + { + pControlsTable->deselectAllItems(); + return; + } + + if (!mConflictHandler[mEditingMode].canAssignControl(control)) + { + pControlsTable->deselectAllItems(); + return; + } + + S32 cell_ind = item->getSelectedCell(); + if (cell_ind <= 0) + { + pControlsTable->deselectAllItems(); + return; + } + + // List does not tell us what cell was clicked, so we have to figure it out manually, but + // fresh mouse coordinates are not yet accessible during onCommit() and there are other issues, + // so we cheat: remember item user clicked at, trigger 'key dialog' on hover that comes next, + // use coordinates from hover to calculate cell + + LLScrollListCell* cell = item->getColumn(cell_ind); + if (cell) + { + LLSetKeyBindDialog* dialog = LLFloaterReg::getTypedInstance<LLSetKeyBindDialog>("keybind_dialog", LLSD()); + if (dialog) + { + mEditingControl = control; + mEditingColumn = cell_ind; + dialog->setParent(this, pControlsTable, DEFAULT_KEY_FILTER); + + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (root_floater) + root_floater->addDependentFloater(dialog); + dialog->openFloater(); + dialog->setFocus(TRUE); + } + } + else + { + pControlsTable->deselectAllItems(); + } +} + +void LLPanelPreferenceControls::onModeCommit() +{ + mEditingMode = pKeyModeBox->getValue().asInteger(); + if (mConflictHandler[mEditingMode].empty()) + { + // opening for first time + mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::ESourceMode)mEditingMode); + } + populateControlTable(); +} + +void LLPanelPreferenceControls::onRestoreDefaultsBtn() +{ + LLNotificationsUtil::add("PreferenceControlsDefaults", LLSD(), LLSD(), boost::bind(&LLPanelPreferenceControls::onRestoreDefaultsResponse, this, _1, _2)); +} + +void LLPanelPreferenceControls::onRestoreDefaultsResponse(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch(option) + { + case 0: // All + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + mConflictHandler[i].resetToDefaults(); + // Apply changes to viewer as 'temporary' + mConflictHandler[i].saveToSettings(true); + + // notify comboboxes in move&view about potential change + LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences"); + if (instance) + { + instance->updateClickActionViews(); + } + } + + updateTable(); + break; + case 1: // Current + mConflictHandler[mEditingMode].resetToDefaults(); + // Apply changes to viewer as 'temporary' + mConflictHandler[mEditingMode].saveToSettings(true); + + if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON) + { + // notify comboboxes in move&view about potential change + LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences"); + if (instance) + { + instance->updateClickActionViews(); + } + } + + updateTable(); + break; + case 2: // Cancel + default: + //exit; + break; + } +} + +// Bypass to let Move & view read values without need to create own key binding handler +// Assumes third person view +// Might be better idea to just move whole mConflictHandler into LLFloaterPreference +bool LLPanelPreferenceControls::canKeyBindHandle(const std::string &control, EMouseClickType click, KEY key, MASK mask) +{ + S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; + if (mConflictHandler[mode].empty()) + { + // opening for first time + mConflictHandler[mode].loadFromSettings(LLKeyConflictHandler::MODE_THIRD_PERSON); + } + + return mConflictHandler[mode].canHandleControl(control, click, key, mask); +} + +// Bypass to let Move & view modify values without need to create own key binding handler +// Assumes third person view +// Might be better idea to just move whole mConflictHandler into LLFloaterPreference +void LLPanelPreferenceControls::setKeyBind(const std::string &control, EMouseClickType click, KEY key, MASK mask, bool set) +{ + S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; + if (mConflictHandler[mode].empty()) + { + // opening for first time + mConflictHandler[mode].loadFromSettings(LLKeyConflictHandler::MODE_THIRD_PERSON); + } + + if (!mConflictHandler[mode].canAssignControl(mEditingControl)) + { + return; + } + + bool already_recorded = mConflictHandler[mode].canHandleControl(control, click, key, mask); + if (set) + { + if (already_recorded) + { + // nothing to do + return; + } + + // find free spot to add data, if no free spot, assign to first + S32 index = 0; + for (S32 i = 0; i < 3; i++) + { + if (mConflictHandler[mode].getControl(control, i).isEmpty()) + { + index = i; + break; + } + } + mConflictHandler[mode].registerControl(control, index, click, key, mask, true); + } + else if (!set) + { + if (!already_recorded) + { + // nothing to do + return; + } + + // find specific control and reset it + for (S32 i = 0; i < 3; i++) + { + LLKeyData data = mConflictHandler[mode].getControl(control, i); + if (data.mMouse == click && data.mKey == key && data.mMask == mask) + { + mConflictHandler[mode].clearControl(control, i); + } + } + } +} + +void LLPanelPreferenceControls::updateAndApply() +{ + S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; + mConflictHandler[mode].saveToSettings(true); + updateTable(); +} + +// from LLSetKeybindDialog's interface +bool LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) +{ + if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl)) + { + return true; + } + + if ( mEditingColumn > 0) + { + if (all_modes) + { + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].empty()) + { + mConflictHandler[i].loadFromSettings((LLKeyConflictHandler::ESourceMode)i); + } + mConflictHandler[i].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, true); + // Apply changes to viewer as 'temporary' + mConflictHandler[i].saveToSettings(true); + } + } + else + { + mConflictHandler[mEditingMode].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, true); + // Apply changes to viewer as 'temporary' + mConflictHandler[mEditingMode].saveToSettings(true); + } + } + + updateTable(); + + if ((mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) + && (mEditingControl == "walk_to" + || mEditingControl == "teleport_to" + || click == CLICK_LEFT + || click == CLICK_DOUBLELEFT)) + { + // notify comboboxes in move&view about potential change + LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences"); + if (instance) + { + instance->updateClickActionViews(); + } + } + + return true; +} + +void LLPanelPreferenceControls::onDefaultKeyBind(bool all_modes) +{ + if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl)) + { + return; + } + + if (mEditingColumn > 0) + { + if (all_modes) + { + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].empty()) + { + mConflictHandler[i].loadFromSettings((LLKeyConflictHandler::ESourceMode)i); + } + mConflictHandler[i].resetToDefault(mEditingControl, mEditingColumn - 1); + // Apply changes to viewer as 'temporary' + mConflictHandler[i].saveToSettings(true); + } + } + else + { + mConflictHandler[mEditingMode].resetToDefault(mEditingControl, mEditingColumn - 1); + // Apply changes to viewer as 'temporary' + mConflictHandler[mEditingMode].saveToSettings(true); + } + } + updateTable(); + + if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) + { + // notify comboboxes in move&view about potential change + LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences"); + if (instance) + { + instance->updateClickActionViews(); + } + } +} + +void LLPanelPreferenceControls::onCancelKeyBind() +{ + pControlsTable->deselectAllItems(); +} + LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const LLSD& key) : LLFloater(key) { @@ -2916,6 +3363,7 @@ void LLFloaterPreferenceGraphicsAdvanced::onClickCloseBtn(bool app_quitting) { instance->cancel(); } + updateMaxComplexity(); } LLFloaterPreferenceProxy::~LLFloaterPreferenceProxy() diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 526214a617..1268935712 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -37,13 +37,17 @@ #include "llavatarpropertiesprocessor.h" #include "llconversationlog.h" #include "llsearcheditor.h" +#include "llsetkeybinddialog.h" +#include "llkeyconflict.h" class LLConversationLogObserver; class LLPanelPreference; class LLPanelLCD; class LLPanelDebug; class LLMessageSystem; +class LLComboBox; class LLScrollListCtrl; +class LLScrollListCell; class LLSliderCtrl; class LLSD; class LLTextBox; @@ -102,6 +106,8 @@ public: void selectPrivacyPanel(); void selectChatPanel(); void getControlNames(std::vector<std::string>& names); + // updates click/double-click action controls depending on values from settings.xml + void updateClickActionViews(); protected: void onBtnOK(const LLSD& userdata); @@ -129,9 +135,7 @@ protected: // callback for commit in the "Single click on land" and "Double click on land" comboboxes. void onClickActionChange(); - // updates click/double-click action settings depending on controls values - void updateClickActionSettings(); - // updates click/double-click action controls depending on values from settings.xml + // updates click/double-click action keybindngs depending on view values void updateClickActionControls(); public: @@ -146,10 +150,6 @@ public: void onClickResetCache(); void onClickSkin(LLUICtrl* ctrl,const LLSD& userdata); void onSelectSkin(); - void onClickSetKey(); - void setKey(KEY key); - void setMouse(LLMouseHandler::EClickType click); - void onClickSetMiddleMouse(); void onClickSetSounds(); void onClickEnablePopup(); void onClickDisablePopup(); @@ -204,7 +204,6 @@ private: static std::string sSkin; notifications_map mNotificationOptions; - bool mClickActionDirty; ///< Set to true when the click/double-click options get changed by user. bool mGotPersonalInfo; bool mOriginalIMViaEmail; bool mLanguageChanged; @@ -294,6 +293,60 @@ private: LOG_CLASS(LLPanelPreferenceGraphics); }; +class LLPanelPreferenceControls : public LLPanelPreference, public LLKeyBindResponderInterface +{ + LOG_CLASS(LLPanelPreferenceControls); +public: + LLPanelPreferenceControls(); + virtual ~LLPanelPreferenceControls(); + + BOOL postBuild(); + + void apply(); + void cancel(); + void saveSettings(); + void resetDirtyChilds(); + + void onListCommit(); + void onModeCommit(); + void onRestoreDefaultsBtn(); + void onRestoreDefaultsResponse(const LLSD& notification, const LLSD& response); + + // Bypass to let Move & view read values without need to create own key binding handler + // Todo: consider a better way to share access to keybindings + bool canKeyBindHandle(const std::string &control, EMouseClickType click, KEY key, MASK mask); + // Bypasses to let Move & view modify values without need to create own key binding handler + void setKeyBind(const std::string &control, EMouseClickType click, KEY key, MASK mask, bool set /*set or reset*/ ); + void updateAndApply(); + + // from interface + /*virtual*/ bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes); + /*virtual*/ void onDefaultKeyBind(bool all_modes); + /*virtual*/ void onCancelKeyBind(); + +private: + // reloads settings, discards current changes, updates table + void regenerateControls(); + + // These fuctions do not clean previous content + bool addControlTableColumns(const std::string &filename); + bool addControlTableRows(const std::string &filename); + void addControlTableSeparator(); + + // Cleans content and then adds content from xml files according to current mEditingMode + void populateControlTable(); + + // Updates keybindings from storage to table + void updateTable(); + + LLScrollListCtrl* pControlsTable; + LLComboBox *pKeyModeBox; + LLKeyConflictHandler mConflictHandler[LLKeyConflictHandler::MODE_COUNT]; + std::string mEditingControl; + S32 mEditingColumn; + S32 mEditingMode; +}; + class LLFloaterPreferenceGraphicsAdvanced : public LLFloater { public: diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 702d612343..7bfba2a6d7 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -930,7 +930,7 @@ void LLFloaterReporter::takeNewSnapshot() // Take a screenshot, but don't draw this floater. setVisible(FALSE); - if (!gViewerWindow->rawSnapshot(mImageRaw, IMAGE_WIDTH, IMAGE_HEIGHT, TRUE, FALSE, gSavedSettings.getBOOL("RenderHUDInSnapshot"), TRUE, FALSE)) + if (!gViewerWindow->rawSnapshot(mImageRaw,IMAGE_WIDTH, IMAGE_HEIGHT, TRUE, FALSE, TRUE /*UI*/, TRUE, FALSE)) { LL_WARNS() << "Unable to take screenshot" << LL_ENDL; setVisible(TRUE); diff --git a/indra/newview/llfloatersaveprefpreset.cpp b/indra/newview/llfloatersaveprefpreset.cpp index 5f3cf9d95b..dd47d02bfa 100644 --- a/indra/newview/llfloatersaveprefpreset.cpp +++ b/indra/newview/llfloatersaveprefpreset.cpp @@ -86,7 +86,10 @@ void LLFloaterSavePrefPreset::onBtnSave() { std::string name = mPresetCombo->getSimple(); - if ((name == LLTrans::getString(PRESETS_DEFAULT)) || (name == PRESETS_DEFAULT)) + std::string upper_name(name); + LLStringUtil::toUpper(upper_name); + + if ((name == LLTrans::getString(PRESETS_DEFAULT)) || (upper_name == PRESETS_DEFAULT_UPPER)) { LLNotificationsUtil::add("DefaultPresetNotSaved"); } diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp index 64ddeff1b0..dd7ce40e97 100644 --- a/indra/newview/llfloatersidepanelcontainer.cpp +++ b/indra/newview/llfloatersidepanelcontainer.cpp @@ -60,7 +60,7 @@ void LLFloaterSidePanelContainer::onOpen(const LLSD& key) void LLFloaterSidePanelContainer::closeFloater(bool app_quitting) { LLPanelOutfitEdit* panel_outfit_edit = - dynamic_cast<LLPanelOutfitEdit*>(LLFloaterSidePanelContainer::getPanel("appearance", "panel_outfit_edit")); + dynamic_cast<LLPanelOutfitEdit*>(LLFloaterSidePanelContainer::findPanel("appearance", "panel_outfit_edit")); if (panel_outfit_edit) { LLFloater *parent = gFloaterView->getParentFloater(panel_outfit_edit); @@ -145,3 +145,15 @@ LLPanel* LLFloaterSidePanelContainer::getPanel(const std::string& floater_name, return NULL; } + +LLPanel* LLFloaterSidePanelContainer::findPanel(const std::string& floater_name, const std::string& panel_name) +{ + LLFloaterSidePanelContainer* floaterp = LLFloaterReg::findTypedInstance<LLFloaterSidePanelContainer>(floater_name); + + if (floaterp) + { + return floaterp->findChild<LLPanel>(panel_name, true); + } + + return NULL; +} diff --git a/indra/newview/llfloatersidepanelcontainer.h b/indra/newview/llfloatersidepanelcontainer.h index 635514e26c..20baf28184 100644 --- a/indra/newview/llfloatersidepanelcontainer.h +++ b/indra/newview/llfloatersidepanelcontainer.h @@ -62,6 +62,8 @@ public: static void showPanel(const std::string& floater_name, const std::string& panel_name, const LLSD& key); static LLPanel* getPanel(const std::string& floater_name, const std::string& panel_name = sMainPanelName); + + static LLPanel* findPanel(const std::string& floater_name, const std::string& panel_name = sMainPanelName); /** * Gets the panel of given type T (doesn't show it or do anything else with it). diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h index bcba14d63d..8221b0a637 100644 --- a/indra/newview/llfloatersnapshot.h +++ b/indra/newview/llfloatersnapshot.h @@ -34,6 +34,7 @@ class LLSpinCtrl; class LLSnapshotLivePreview; +class LLToolset; class LLFloaterSnapshotBase : public LLFloater { diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index fce945df6c..97e99297cf 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -33,7 +33,6 @@ #define LL_LLFLOATERWORLDMAP_H #include "llfloater.h" -#include "llhudtext.h" #include "llmapimagetype.h" #include "lltracker.h" #include "llslurl.h" diff --git a/indra/newview/llgiveinventory.cpp b/indra/newview/llgiveinventory.cpp index 3ab8fed2c6..127055459d 100644 --- a/indra/newview/llgiveinventory.cpp +++ b/indra/newview/llgiveinventory.cpp @@ -129,23 +129,14 @@ bool LLGiveInventory::isInventoryGiveAcceptable(const LLInventoryItem* item) switch(item->getType()) { case LLAssetType::AT_OBJECT: - if (get_is_item_worn(item->getUUID())) - { - acceptable = false; - } - break; case LLAssetType::AT_BODYPART: case LLAssetType::AT_CLOTHING: { - BOOL copyable = false; - if (item->getPermissions().allowCopyBy(gAgentID)) copyable = true; - - if (!copyable && get_is_item_worn(item->getUUID())) + if (get_is_item_worn(item->getUUID())) { - // worn no-copy items can't be transfered, - // but it is valid to transfer a copy of a worn item acceptable = false; } + break; } break; default: diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index d2bd716f55..12d82d101f 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -39,6 +39,7 @@ #include "llfloaterimcontainer.h" #include "llimview.h" // for gIMMgr #include "llnotificationsutil.h" +#include "llstartup.h" #include "llstatusbar.h" // can_afford_transaction() #include "groupchatlistener.h" @@ -55,6 +56,11 @@ public: bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { + if (LLStartUp::getStartupState() < STATE_STARTED) + { + return true; + } + if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableGroupInfo")) { LLNotificationsUtil::add("NoGroupInfo", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index dbf7639539..32f88b49ac 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -944,10 +944,14 @@ static void formatDateString(std::string &date_string) } } +static LLTrace::BlockTimerStatHandle FTM_PROCESS_GROUP_MEMBERS_REPLY("Process Group Members"); + // static void LLGroupMgr::processGroupMembersReply(LLMessageSystem* msg, void** data) { - LL_DEBUGS() << "LLGroupMgr::processGroupMembersReply" << LL_ENDL; + LL_RECORD_BLOCK_TIME(FTM_PROCESS_GROUP_MEMBERS_REPLY); + + LL_DEBUGS("GrpMgr") << "LLGroupMgr::processGroupMembersReply" << LL_ENDL; LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (gAgent.getID() != agent_id) @@ -1050,10 +1054,14 @@ void LLGroupMgr::processGroupMembersReply(LLMessageSystem* msg, void** data) LLGroupMgr::getInstance()->notifyObservers(GC_MEMBER_DATA); } +static LLTrace::BlockTimerStatHandle FTM_PROCESS_GROUP_PROPERTIES_REPLY("Process Group Properties"); + //static void LLGroupMgr::processGroupPropertiesReply(LLMessageSystem* msg, void** data) { - LL_DEBUGS() << "LLGroupMgr::processGroupPropertiesReply" << LL_ENDL; + LL_RECORD_BLOCK_TIME(FTM_PROCESS_GROUP_PROPERTIES_REPLY); + + LL_DEBUGS("GrpMgr") << "LLGroupMgr::processGroupPropertiesReply" << LL_ENDL; if (!msg) { LL_ERRS() << "Can't access the messaging system" << LL_ENDL; @@ -1119,13 +1127,25 @@ void LLGroupMgr::processGroupPropertiesReply(LLMessageSystem* msg, void** data) group_datap->mGroupPropertiesDataComplete = true; group_datap->mChanged = TRUE; + properties_request_map_t::iterator request = LLGroupMgr::getInstance()->mPropRequests.find(group_id); + if (request != LLGroupMgr::getInstance()->mPropRequests.end()) + { + LLGroupMgr::getInstance()->mPropRequests.erase(request); + } + else + { + LL_DEBUGS("GrpMgr") << "GroupPropertyResponse received with no pending request. Response was slow." << LL_ENDL; + } LLGroupMgr::getInstance()->notifyObservers(GC_PROPERTIES); } +static LLTrace::BlockTimerStatHandle FTM_PROCESS_GROUP_ROLE_DATA_REPLY("Process Group Role Data"); // static void LLGroupMgr::processGroupRoleDataReply(LLMessageSystem* msg, void** data) { - LL_DEBUGS() << "LLGroupMgr::processGroupRoleDataReply" << LL_ENDL; + LL_RECORD_BLOCK_TIME(FTM_PROCESS_GROUP_ROLE_DATA_REPLY); + + LL_DEBUGS("GrpMgr") << "LLGroupMgr::processGroupRoleDataReply" << LL_ENDL; LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (gAgent.getID() != agent_id) @@ -1186,7 +1206,7 @@ void LLGroupMgr::processGroupRoleDataReply(LLMessageSystem* msg, void** data) - LL_DEBUGS() << "Adding role data: " << name << " {" << role_id << "}" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "Adding role data: " << name << " {" << role_id << "}" << LL_ENDL; LLGroupRoleData* rd = new LLGroupRoleData(role_id,name,title,desc,powers,member_count); group_datap->mRoles[role_id] = rd; } @@ -1207,10 +1227,13 @@ void LLGroupMgr::processGroupRoleDataReply(LLMessageSystem* msg, void** data) LLGroupMgr::getInstance()->notifyObservers(GC_ROLE_DATA); } +static LLTrace::BlockTimerStatHandle FTM_PROCESS_GROUP_ROLE_MEMBERS_REPLY("Process Group Role Members"); // static void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data) { - LL_DEBUGS() << "LLGroupMgr::processGroupRoleMembersReply" << LL_ENDL; + LL_RECORD_BLOCK_TIME(FTM_PROCESS_GROUP_ROLE_MEMBERS_REPLY); + + LL_DEBUGS("GrpMgr") << "LLGroupMgr::processGroupRoleMembersReply" << LL_ENDL; LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (gAgent.getID() != agent_id) @@ -1271,7 +1294,7 @@ void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data) if (rd && md) { - LL_DEBUGS() << "Adding role-member pair: " << role_id << ", " << member_id << LL_ENDL; + LL_DEBUGS("GrpMgr") << "Adding role-member pair: " << role_id << ", " << member_id << LL_ENDL; rd->addMember(member_id); md->addRole(role_id,rd); } @@ -1323,7 +1346,7 @@ void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data) // static void LLGroupMgr::processGroupTitlesReply(LLMessageSystem* msg, void** data) { - LL_DEBUGS() << "LLGroupMgr::processGroupTitlesReply" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::processGroupTitlesReply" << LL_ENDL; LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id ); if (gAgent.getID() != agent_id) @@ -1356,7 +1379,7 @@ void LLGroupMgr::processGroupTitlesReply(LLMessageSystem* msg, void** data) if (!title.mTitle.empty()) { - LL_DEBUGS() << "LLGroupMgr adding title: " << title.mTitle << ", " << title.mRoleID << ", " << (title.mSelected ? 'Y' : 'N') << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr adding title: " << title.mTitle << ", " << title.mRoleID << ", " << (title.mSelected ? 'Y' : 'N') << LL_ENDL; group_datap->mTitles.push_back(title); } } @@ -1368,7 +1391,7 @@ void LLGroupMgr::processGroupTitlesReply(LLMessageSystem* msg, void** data) // static void LLGroupMgr::processEjectGroupMemberReply(LLMessageSystem* msg, void ** data) { - LL_DEBUGS() << "processEjectGroupMemberReply" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "processEjectGroupMemberReply" << LL_ENDL; LLUUID group_id; msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id); BOOL success; @@ -1384,7 +1407,7 @@ void LLGroupMgr::processEjectGroupMemberReply(LLMessageSystem* msg, void ** data // static void LLGroupMgr::processJoinGroupReply(LLMessageSystem* msg, void ** data) { - LL_DEBUGS() << "processJoinGroupReply" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "processJoinGroupReply" << LL_ENDL; LLUUID group_id; BOOL success; msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id); @@ -1404,7 +1427,7 @@ void LLGroupMgr::processJoinGroupReply(LLMessageSystem* msg, void ** data) // static void LLGroupMgr::processLeaveGroupReply(LLMessageSystem* msg, void ** data) { - LL_DEBUGS() << "processLeaveGroupReply" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "processLeaveGroupReply" << LL_ENDL; LLUUID group_id; BOOL success; msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id); @@ -1488,6 +1511,28 @@ LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) return group_datap; } +bool LLGroupMgr::hasPendingPropertyRequest(const LLUUID & id) +{ + properties_request_map_t::iterator existing_req = LLGroupMgr::getInstance()->mPropRequests.find(id); + if (existing_req != LLGroupMgr::getInstance()->mPropRequests.end()) + { + if (gFrameTime - existing_req->second < MIN_GROUP_PROPERTY_REQUEST_FREQ) + { + return true; + } + else + { + LLGroupMgr::getInstance()->mPropRequests.erase(existing_req); + } + } + return false; +} + +void LLGroupMgr::addPendingPropertyRequest(const LLUUID& id) +{ + LLGroupMgr::getInstance()->mPropRequests[id] = gFrameTime; +} + void LLGroupMgr::notifyObservers(LLGroupChange gc) { for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end(); ++gi) @@ -1566,10 +1611,17 @@ void LLGroupMgr::addGroup(LLGroupMgrGroupData* group_datap) void LLGroupMgr::sendGroupPropertiesRequest(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::sendGroupPropertiesRequest" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupPropertiesRequest" << LL_ENDL; // This will happen when we get the reply //LLGroupMgrGroupData* group_datap = createGroupData(group_id); + if (LLGroupMgr::getInstance()->hasPendingPropertyRequest(group_id)) + { + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupPropertiesRequest suppressed repeat for " << group_id << LL_ENDL; + return; + } + LLGroupMgr::getInstance()->addPendingPropertyRequest(group_id); + LLMessageSystem* msg = gMessageSystem; msg->newMessage("GroupProfileRequest"); msg->nextBlock("AgentData"); @@ -1582,7 +1634,7 @@ void LLGroupMgr::sendGroupPropertiesRequest(const LLUUID& group_id) void LLGroupMgr::sendGroupMembersRequest(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::sendGroupMembersRequest" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupMembersRequest" << LL_ENDL; LLGroupMgrGroupData* group_datap = createGroupData(group_id); if (group_datap->mMemberRequestID.isNull()) { @@ -1604,7 +1656,7 @@ void LLGroupMgr::sendGroupMembersRequest(const LLUUID& group_id) void LLGroupMgr::sendGroupRoleDataRequest(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::sendGroupRoleDataRequest" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupRoleDataRequest" << LL_ENDL; LLGroupMgrGroupData* group_datap = createGroupData(group_id); if (group_datap->mRoleDataRequestID.isNull()) { @@ -1625,7 +1677,7 @@ void LLGroupMgr::sendGroupRoleDataRequest(const LLUUID& group_id) void LLGroupMgr::sendGroupRoleMembersRequest(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::sendGroupRoleMembersRequest" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupRoleMembersRequest" << LL_ENDL; LLGroupMgrGroupData* group_datap = createGroupData(group_id); if (group_datap->mRoleMembersRequestID.isNull()) @@ -1635,7 +1687,7 @@ void LLGroupMgr::sendGroupRoleMembersRequest(const LLUUID& group_id) || !group_datap->isRoleDataComplete()) { // *TODO: KLW FIXME: Should we start a member or role data request? - LL_INFOS() << " Pending: " << (group_datap->mPendingRoleMemberRequest ? "Y" : "N") + LL_INFOS("GrpMgr") << " Pending: " << (group_datap->mPendingRoleMemberRequest ? "Y" : "N") << " MemberDataComplete: " << (group_datap->mMemberDataComplete ? "Y" : "N") << " RoleDataComplete: " << (group_datap->mRoleDataComplete ? "Y" : "N") << LL_ENDL; group_datap->mPendingRoleMemberRequest = TRUE; @@ -1659,7 +1711,7 @@ void LLGroupMgr::sendGroupRoleMembersRequest(const LLUUID& group_id) void LLGroupMgr::sendGroupTitlesRequest(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::sendGroupTitlesRequest" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupTitlesRequest" << LL_ENDL; LLGroupMgrGroupData* group_datap = createGroupData(group_id); group_datap->mTitles.clear(); @@ -1678,7 +1730,7 @@ void LLGroupMgr::sendGroupTitlesRequest(const LLUUID& group_id) void LLGroupMgr::sendGroupTitleUpdate(const LLUUID& group_id, const LLUUID& title_role_id) { - LL_DEBUGS() << "LLGroupMgr::sendGroupTitleUpdate" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupTitleUpdate" << LL_ENDL; LLMessageSystem* msg = gMessageSystem; msg->newMessage("GroupTitleUpdate"); @@ -1737,7 +1789,7 @@ void LLGroupMgr::sendCreateGroupRequest(const std::string& name, void LLGroupMgr::sendUpdateGroupInfo(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::sendUpdateGroupInfo" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendUpdateGroupInfo" << LL_ENDL; LLGroupMgrGroupData* group_datap = createGroupData(group_id); LLMessageSystem* msg = gMessageSystem; @@ -1766,7 +1818,7 @@ void LLGroupMgr::sendUpdateGroupInfo(const LLUUID& group_id) void LLGroupMgr::sendGroupRoleMemberChanges(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::sendGroupRoleMemberChanges" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupRoleMemberChanges" << LL_ENDL; LLGroupMgrGroupData* group_datap = createGroupData(group_id); if (group_datap->mRoleMemberChanges.empty()) return; @@ -2313,7 +2365,7 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) void LLGroupMgr::sendGroupRoleChanges(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::sendGroupRoleChanges" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupRoleChanges" << LL_ENDL; LLGroupMgrGroupData* group_datap = getGroupData(group_id); if (group_datap && group_datap->pendingRoleChanges()) @@ -2328,7 +2380,7 @@ void LLGroupMgr::sendGroupRoleChanges(const LLUUID& group_id) void LLGroupMgr::cancelGroupRoleChanges(const LLUUID& group_id) { - LL_DEBUGS() << "LLGroupMgr::cancelGroupRoleChanges" << LL_ENDL; + LL_DEBUGS("GrpMgr") << "LLGroupMgr::cancelGroupRoleChanges" << LL_ENDL; LLGroupMgrGroupData* group_datap = getGroupData(group_id); if (group_datap) group_datap->cancelRoleChanges(); @@ -2362,7 +2414,7 @@ bool LLGroupMgr::parseRoleActions(const std::string& xml_filename) std::string action_set_name; if (action_set->getAttributeString("name", action_set_name)) { - LL_DEBUGS() << "Loading action set " << action_set_name << LL_ENDL; + LL_DEBUGS("GrpMgr") << "Loading action set " << action_set_name << LL_ENDL; role_action_data->mName = action_set_name; } else @@ -2403,7 +2455,7 @@ bool LLGroupMgr::parseRoleActions(const std::string& xml_filename) std::string action_name; if (action->getAttributeString("name", action_name)) { - LL_DEBUGS() << "Loading action " << action_name << LL_ENDL; + LL_DEBUGS("GrpMgr") << "Loading action " << action_name << LL_ENDL; role_action->mName = action_name; } else diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h index cf9735e38a..0d25e8fb22 100644 --- a/indra/newview/llgroupmgr.h +++ b/indra/newview/llgroupmgr.h @@ -448,6 +448,8 @@ private: void notifyObserver(const LLUUID& group_id, LLGroupChange gc); void addGroup(LLGroupMgrGroupData* group_datap); LLGroupMgrGroupData* createGroupData(const LLUUID &id); + bool hasPendingPropertyRequest(const LLUUID& id); + void addPendingPropertyRequest(const LLUUID& id); typedef std::multimap<LLUUID,LLGroupMgrObserver*> observer_multimap_t; observer_multimap_t mObservers; @@ -455,6 +457,10 @@ private: typedef std::map<LLUUID, LLGroupMgrGroupData*> group_map_t; group_map_t mGroups; + const U64MicrosecondsImplicit MIN_GROUP_PROPERTY_REQUEST_FREQ = 100000;//100ms between requests should be enough to avoid spamming. + typedef std::map<LLUUID, U64MicrosecondsImplicit> properties_request_map_t; + properties_request_map_t mPropRequests; + typedef std::set<LLParticularGroupObserver*> observer_set_t; typedef std::map<LLUUID,observer_set_t> observer_map_t; observer_map_t mParticularObservers; diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp index 81d862a827..4ed802138d 100644 --- a/indra/newview/llhudnametag.cpp +++ b/indra/newview/llhudnametag.cpp @@ -415,7 +415,8 @@ void LLHUDNameTag::clearString() void LLHUDNameTag::addLine(const std::string &text_utf8, const LLColor4& color, const LLFontGL::StyleFlags style, - const LLFontGL* font) + const LLFontGL* font, + const bool use_ellipses) { LLWString wline = utf8str_to_wstring(text_utf8); if (!wline.empty()) @@ -432,18 +433,52 @@ void LLHUDNameTag::addLine(const std::string &text_utf8, tokenizer tokens(wline, sep); tokenizer::iterator iter = tokens.begin(); - while (iter != tokens.end()) - { - U32 line_length = 0; - do - { - F32 max_pixels = HUD_TEXT_MAX_WIDTH; - S32 segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); - LLHUDTextSegment segment(iter->substr(line_length, segment_length), style, color, font); - mTextSegments.push_back(segment); - line_length += segment_length; - } - while (line_length != iter->size()); + const F32 max_pixels = HUD_TEXT_MAX_WIDTH; + while (iter != tokens.end()) + { + U32 line_length = 0; + if (use_ellipses) + { + // "QualityAssuranceAssuresQuality1" will end up like "QualityAssuranceAssuresQual..." + // "QualityAssuranceAssuresQuality QualityAssuranceAssuresQuality" will end up like "QualityAssuranceAssuresQual..." + // "QualityAssurance AssuresQuality1" will end up as "QualityAssurance AssuresQua..." because we are enforcing single line + do + { + S32 segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels, wline.length(), LLFontGL::ANYWHERE); + if (segment_length + line_length < wline.length()) // since we only draw one string, line_length should be 0 + { + // token does does not fit into signle line, need to draw "...". + // Use four dots for ellipsis width to generate padding + const LLWString dots_pad(utf8str_to_wstring(std::string("...."))); + S32 elipses_width = font->getWidthF32(dots_pad.c_str()); + // truncated string length + segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels - elipses_width, wline.length(), LLFontGL::ANYWHERE); + const LLWString dots(utf8str_to_wstring(std::string("..."))); + LLHUDTextSegment segment(iter->substr(line_length, segment_length) + dots, style, color, font); + mTextSegments.push_back(segment); + break; // consider it to be complete + } + else + { + // token fits fully into string + LLHUDTextSegment segment(iter->substr(line_length, segment_length), style, color, font); + mTextSegments.push_back(segment); + line_length += segment_length; + } + } while (line_length != iter->size()); + } + else + { + // "QualityAssuranceAssuresQuality 1" will be split into two lines "QualityAssuranceAssuresQualit" and "y 1" + // "QualityAssurance AssuresQuality 1" will be split into two lines "QualityAssurance" and "AssuresQuality" + do + { + S32 segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); + LLHUDTextSegment segment(iter->substr(line_length, segment_length), style, color, font); + mTextSegments.push_back(segment); + line_length += segment_length; + } while (line_length != iter->size()); + } ++iter; } } diff --git a/indra/newview/llhudnametag.h b/indra/newview/llhudnametag.h index 38a4f18415..20272a8232 100644 --- a/indra/newview/llhudnametag.h +++ b/indra/newview/llhudnametag.h @@ -92,7 +92,7 @@ public: void clearString(); // Add text a line at a time, allowing custom formatting - void addLine(const std::string &text_utf8, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL, const LLFontGL* font = NULL); + void addLine(const std::string &text_utf8, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL, const LLFontGL* font = NULL, const bool use_ellipses = false); // For bubble chat, set the part above the chat text void setLabel(const std::string& label_utf8); diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h index 2f7a98c86c..ce128519ea 100644 --- a/indra/newview/llhudobject.h +++ b/indra/newview/llhudobject.h @@ -102,7 +102,7 @@ protected: static void sortObjects(); LLHUDObject(const U8 type); - ~LLHUDObject(); + virtual ~LLHUDObject(); virtual void render() = 0; virtual void renderForTimer() {}; diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index 82824861a9..72d28a3d44 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -565,7 +565,10 @@ S32 LLHUDText::getMaxLines() void LLHUDText::markDead() { - sTextObjects.erase(LLPointer<LLHUDText>(this)); + // make sure we have at least one pointer + // till the end of the function + LLPointer<LLHUDText> ptr(this); + sTextObjects.erase(ptr); LLHUDObject::markDead(); } diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index e1b58dde51..1e43e4ea3a 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -450,7 +450,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, || (dialog == IM_FROM_TASK && LLMuteList::getInstance()->isMuted(session_id)); BOOL is_owned_by_me = FALSE; BOOL is_friend = (LLAvatarTracker::instance().getBuddyInfo(from_id) == NULL) ? false : true; - BOOL accept_im_from_only_friend = gSavedSettings.getBOOL("VoiceCallsFriendsOnly"); + BOOL accept_im_from_only_friend = gSavedPerAccountSettings.getBOOL("VoiceCallsFriendsOnly"); BOOL is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && LLMuteList::getInstance()->isLinden(name); @@ -1164,7 +1164,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, { return; } - else if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(from_id) == NULL)) + else if (gSavedPerAccountSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(from_id) == NULL)) { return; } diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index d5142a4496..1059324a16 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -515,6 +515,7 @@ LLIMModel::LLIMModel() { addNewMsgCallback(boost::bind(&LLFloaterIMSession::newIMCallback, _1)); addNewMsgCallback(boost::bind(&on_new_message, _1)); + LLCallDialogManager::instance(); } LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) @@ -968,6 +969,9 @@ void LLIMModel::LLIMSession::buildHistoryFileName() // Incoming P2P sessions include a name that we can use to build a history file name mHistoryFileName = LLCacheName::buildUsername(mName); } + + // user's account name can change, but filenames and session names are account name based + LLConversationLog::getInstance()->verifyFilename(mSessionID, mHistoryFileName, av_name.getCompleteName()); } else if (isGroupChat()) { @@ -2253,6 +2257,19 @@ BOOL LLOutgoingCallDialog::postBuild() // Class LLIncomingCallDialog //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const std::array<std::string, 4> voice_call_types = +{ + "VoiceInviteP2P", + "VoiceInviteGroup", + "VoiceInviteAdHoc", + "InviteAdHoc" +}; + +bool is_voice_call_type(const std::string &value) +{ + return std::find(voice_call_types.begin(), voice_call_types.end(), value) != voice_call_types.end(); +} + LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) : LLCallDialog(payload), mAvatarNameCacheConnection() @@ -2282,9 +2299,28 @@ BOOL LLIncomingCallDialog::postBuild() { LLCallDialog::postBuild(); + if (!mPayload.isMap() || mPayload.size() == 0) + { + LL_INFOS("IMVIEW") << "IncomingCall: invalid argument" << LL_ENDL; + return TRUE; + } + LLUUID session_id = mPayload["session_id"].asUUID(); LLSD caller_id = mPayload["caller_id"]; std::string caller_name = mPayload["caller_name"].asString(); + + if (session_id.isNull() && caller_id.asUUID().isNull()) + { + LL_INFOS("IMVIEW") << "IncomingCall: invalid ids" << LL_ENDL; + return TRUE; + } + + std::string notify_box_type = mPayload["notify_box_type"].asString(); + if (!is_voice_call_type(notify_box_type)) + { + LL_INFOS("IMVIEW") << "IncomingCall: notify_box_type was not provided" << LL_ENDL; + return TRUE; + } // init notification's lifetime std::istringstream ss( getString("lifetime") ); @@ -2301,15 +2337,14 @@ BOOL LLIncomingCallDialog::postBuild() if (gAgent.getGroupData(session_id, data)) { args["[GROUP]"] = data.mName; - call_type = getString(mPayload["notify_box_type"], args); + call_type = getString(notify_box_type, args); } } else { - call_type = getString(mPayload["notify_box_type"]); + call_type = getString(notify_box_type); } - - + // check to see if this is an Avaline call bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); if (caller_name == "anonymous") @@ -2339,7 +2374,6 @@ BOOL LLIncomingCallDialog::postBuild() childSetAction("Start IM", onStartIM, this); setDefaultBtn("Accept"); - std::string notify_box_type = mPayload["notify_box_type"].asString(); if(notify_box_type != "VoiceInviteGroup" && notify_box_type != "VoiceInviteAdHoc") { // starting notification's timer for P2P and AVALINE invitations @@ -2490,10 +2524,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload correct_session_name.append(ADHOC_NAME_SUFFIX); } } - LL_INFOS() << "Corrected session name is " << correct_session_name << LL_ENDL; + LL_INFOS("IMVIEW") << "Corrected session name is " << correct_session_name << LL_ENDL; break; default: - LL_WARNS() << "Received an empty session name from a server and failed to generate a new proper session name" << LL_ENDL; + LL_WARNS("IMVIEW") << "Received an empty session name from a server and failed to generate a new proper session name" << LL_ENDL; break; } } @@ -2683,7 +2717,7 @@ void LLIMMgr::addMessage( } bool skip_message = false; bool from_linden = LLMuteList::getInstance()->isLinden(from); - if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && !from_linden) + if (gSavedPerAccountSettings.getBOOL("VoiceCallsFriendsOnly") && !from_linden) { // Evaluate if we need to skip this message when that setting is true (default is false) skip_message = (LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL); // Skip non friends... @@ -2693,6 +2727,14 @@ void LLIMMgr::addMessage( bool new_session = !hasSession(new_session_id); if (new_session) { + // Group chat session was initiated by muted resident, do not start this session viewerside + // do not send leave msg either, so we are able to get group messages from other participants + if ((IM_SESSION_INVITE == dialog) && gAgent.isInGroup(new_session_id) && + LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !from_linden) + { + return; + } + LLAvatarName av_name; if (LLAvatarNameCache::get(other_participant_id, &av_name) && !name_is_setted) { @@ -2736,13 +2778,13 @@ void LLIMMgr::addMessage( LL_WARNS() << "Leaving IM session from initiating muted resident " << from << LL_ENDL; if (!gIMMgr->leaveSession(new_session_id)) { - LL_INFOS() << "Session " << new_session_id << " does not exist." << LL_ENDL; + LL_INFOS("IMVIEW") << "Session " << new_session_id << " does not exist." << LL_ENDL; } return; } //Play sound for new conversations - if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation") == TRUE)) + if (!skip_message & !gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation") == TRUE)) { make_ui_sound("UISndNewIncomingIMSession"); } @@ -2947,7 +2989,7 @@ LLUUID LLIMMgr::addSession( //we don't need to show notes about online/offline, mute/unmute users' statuses for existing sessions if (!new_session) return session_id; - LL_INFOS() << "LLIMMgr::addSession, new session added, name = " << name << ", session id = " << session_id << LL_ENDL; + LL_INFOS("IMVIEW") << "LLIMMgr::addSession, new session added, name = " << name << ", session id = " << session_id << LL_ENDL; //Per Plan's suggestion commented "explicit offline status warning" out to make Dessie happier (see EXT-3609) //*TODO After February 2010 remove this commented out line if no one will be missing that warning @@ -2984,7 +3026,7 @@ void LLIMMgr::removeSession(const LLUUID& session_id) LLIMModel::getInstance()->clearSession(session_id); - LL_INFOS() << "LLIMMgr::removeSession, session removed, session id = " << session_id << LL_ENDL; + LL_INFOS("IMVIEW") << "LLIMMgr::removeSession, session removed, session id = " << session_id << LL_ENDL; notifyObserverSessionRemoved(session_id); } @@ -3050,13 +3092,13 @@ void LLIMMgr::inviteToSession( if (LLMuteList::getInstance()->isMuted(caller_id, LLMute::flagVoiceChat) && voice_invite && "VoiceInviteQuestionDefault" == question_type) { - LL_INFOS() << "Rejecting voice call from initiating muted resident " << caller_name << LL_ENDL; + LL_INFOS("IMVIEW") << "Rejecting voice call from initiating muted resident " << caller_name << LL_ENDL; LLIncomingCallDialog::processCallResponse(1, payload); return; } else if (LLMuteList::getInstance()->isMuted(caller_id, LLMute::flagAll & ~LLMute::flagVoiceChat) && !voice_invite) { - LL_INFOS() << "Rejecting session invite from initiating muted resident " << caller_name << LL_ENDL; + LL_INFOS("IMVIEW") << "Rejecting session invite from initiating muted resident " << caller_name << LL_ENDL; return; } } @@ -3072,7 +3114,7 @@ void LLIMMgr::inviteToSession( if (voice_invite) { bool isRejectGroupCall = (gSavedSettings.getBOOL("VoiceCallsRejectGroup") && (notify_box_type == "VoiceInviteGroup")); - bool isRejectNonFriendCall = (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)); + bool isRejectNonFriendCall = (gSavedPerAccountSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)); if (isRejectGroupCall || isRejectNonFriendCall || gAgent.isDoNotDisturb()) { if (gAgent.isDoNotDisturb() && !isRejectGroupCall && !isRejectNonFriendCall) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 657c65c68d..d35d8456be 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4101,12 +4101,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items items.push_back(std::string("New Body Parts")); items.push_back(std::string("New Settings")); items.push_back(std::string("upload_def")); - - if (!LLEnvironment::instance().isInventoryEnabled()) - { - disabled_items.push_back("New Settings"); - } - } } getClipboardEntries(false, items, disabled_items, flags); @@ -6385,6 +6379,14 @@ void LLObjectBridge::performAction(LLInventoryModel* model, std::string action) { LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding. } + else if ("touch" == action) + { + handle_attachment_touch(mUUID); + } + else if ("edit" == action) + { + handle_attachment_edit(mUUID); + } else if (isRemoveAction(action)) { LLAppearanceMgr::instance().removeItemFromAvatar(mUUID); @@ -6535,6 +6537,19 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if( get_is_item_worn( mUUID ) ) { items.push_back(std::string("Wearable And Object Separator")); + + items.push_back(std::string("Attachment Touch")); + if ( ((flags & FIRST_SELECTED_ITEM) == 0) || !enable_attachment_touch(mUUID) ) + { + disabled_items.push_back(std::string("Attachment Touch")); + } + + items.push_back(std::string("Wearable Edit")); + if ( ((flags & FIRST_SELECTED_ITEM) == 0) || !get_is_item_editable(mUUID) ) + { + disabled_items.push_back(std::string("Wearable Edit")); + } + items.push_back(std::string("Detach From Yourself")); } else if (!isItemInTrash() && !isLinkedObjectInTrash() && !isLinkedObjectMissing() && !isCOFFolder()) @@ -7650,13 +7665,12 @@ void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_dequ disable_context_entries_if_present(menu, disabled_items); } -bool LLFolderViewGroupedItemBridge::canWearSelected(uuid_vec_t item_ids) +bool LLFolderViewGroupedItemBridge::canWearSelected(const uuid_vec_t& item_ids) const { for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it) { - LLViewerInventoryItem* item = gInventory.getItem(*it); - LLAssetType::EType asset_type = item->getType(); - if (!item || (asset_type >= LLAssetType::AT_COUNT) || (asset_type <= LLAssetType::AT_NONE)) + const LLViewerInventoryItem* item = gInventory.getItem(*it); + if (!item || (item->getType() >= LLAssetType::AT_COUNT) || (item->getType() <= LLAssetType::AT_NONE)) { return false; } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index fce7ade661..9af8664388 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -760,7 +760,7 @@ class LLFolderViewGroupedItemBridge: public LLFolderViewGroupedItemModel public: LLFolderViewGroupedItemBridge(); virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu); - bool canWearSelected(uuid_vec_t item_ids); + bool canWearSelected(const uuid_vec_t& item_ids) const; }; #endif // LL_LLINVENTORYBRIDGE_H diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 745b953996..2a22eb1329 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -74,6 +74,7 @@ LLInventoryFilter::LLInventoryFilter(const Params& p) : mName(p.name), mFilterModified(FILTER_NONE), mEmptyLookupMessage("InventoryNoMatchingItems"), + mDefaultEmptyLookupMessage(""), mFilterOps(p.filter_ops), mBackupFilterOps(mFilterOps), mFilterSubString(p.substring), @@ -192,10 +193,15 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const // when applying a filter, matching folders get their contents downloaded first // but make sure we are not interfering with pre-download if (isNotDefault() - && !gInventory.isCategoryComplete(folder_id) && LLStartUp::getStartupState() > STATE_WEARABLES_WAIT) - { - LLInventoryModelBackgroundFetch::instance().start(folder_id); + { + LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); + if (!cat || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)) + { + // At the moment background fetch only cares about VERSION_UNKNOWN, + // so do not check isCategoryComplete that compares descendant count + LLInventoryModelBackgroundFetch::instance().start(folder_id); + } } // Marketplace folder filtering @@ -287,21 +293,34 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewModelItemInvent // Pass if this item's type is of the correct filter type if (filterTypes & FILTERTYPE_OBJECT) { - - // If it has no type, pass it, unless it's a link. - if (object_type == LLInventoryType::IT_NONE) - { - if (object && object->getIsLinkType()) - { - return FALSE; - } - } - else if ((1LL << object_type & mFilterOps.mFilterObjectTypes) == U64(0)) - { - return FALSE; - } + switch (object_type) + { + case LLInventoryType::IT_NONE: + // If it has no type, pass it, unless it's a link. + if (object && object->getIsLinkType()) + { + return FALSE; + } + break; + case LLInventoryType::IT_UNKNOWN: + { + // Unknows are only shown when we show every type. + // Unknows are 255 and won't fit in 64 bits. + if (mFilterOps.mFilterObjectTypes != 0xffffffffffffffffULL) + { + return FALSE; + } + break; + } + default: + if ((1LL << object_type & mFilterOps.mFilterObjectTypes) == U64(0)) + { + return FALSE; + } + break; + } } - + if(filterTypes & FILTERTYPE_WORN) { if (!get_is_item_worn(object_id)) @@ -426,18 +445,32 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLInventoryItem* item) cons // Pass if this item's type is of the correct filter type if (filterTypes & FILTERTYPE_OBJECT) { - // If it has no type, pass it, unless it's a link. - if (object_type == LLInventoryType::IT_NONE) - { - if (item && item->getIsLinkType()) - { - return false; - } - } - else if ((1LL << object_type & mFilterOps.mFilterObjectTypes) == U64(0)) - { - return false; - } + switch (object_type) + { + case LLInventoryType::IT_NONE: + // If it has no type, pass it, unless it's a link. + if (item && item->getIsLinkType()) + { + return FALSE; + } + break; + case LLInventoryType::IT_UNKNOWN: + { + // Unknows are only shown when we show every type. + // Unknows are 255 and won't fit in 64 bits. + if (mFilterOps.mFilterObjectTypes != 0xffffffffffffffffULL) + { + return FALSE; + } + break; + } + default: + if ((1LL << object_type & mFilterOps.mFilterObjectTypes) == U64(0)) + { + return FALSE; + } + break; + } } //////////////////////////////////////////////////////////////////////////////// @@ -1409,12 +1442,24 @@ void LLInventoryFilter::setEmptyLookupMessage(const std::string& message) mEmptyLookupMessage = message; } +void LLInventoryFilter::setDefaultEmptyLookupMessage(const std::string& message) +{ + mDefaultEmptyLookupMessage = message; +} + std::string LLInventoryFilter::getEmptyLookupMessage() const { - LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(getFilterSubStringOrig()); + if (isDefault() && !mDefaultEmptyLookupMessage.empty()) + { + return LLTrans::getString(mDefaultEmptyLookupMessage); + } + else + { + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(getFilterSubStringOrig()); - return LLTrans::getString(mEmptyLookupMessage, args); + return LLTrans::getString(mEmptyLookupMessage, args); + } } diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index be02ee3623..61cc5ae602 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -257,6 +257,7 @@ public: EFilterCreatorType getFilterCreatorType() const; void setEmptyLookupMessage(const std::string& message); + void setDefaultEmptyLookupMessage(const std::string& message); std::string getEmptyLookupMessage() const; // +-------------------------------------------------------------------+ @@ -332,6 +333,7 @@ private: std::string mFilterText; std::string mEmptyLookupMessage; + std::string mDefaultEmptyLookupMessage; ESearchType mSearchType; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 646d92b9e1..7a0ea8b668 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -78,6 +78,7 @@ #include "lltooldraganddrop.h" #include "lltrans.h" #include "lluictrlfactory.h" +#include "llviewermenu.h" #include "llviewermessage.h" #include "llviewerfoldertype.h" #include "llviewerobjectlist.h" @@ -655,6 +656,50 @@ BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) return TRUE; } +bool get_is_item_editable(const LLUUID& inv_item_id) +{ + if (const LLInventoryItem* inv_item = gInventory.getLinkedItem(inv_item_id)) + { + switch (inv_item->getType()) + { + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_CLOTHING: + return gAgentWearables.isWearableModifiable(inv_item_id); + case LLAssetType::AT_OBJECT: + return true; + default: + return false;; + } + } + return gAgentAvatarp->getWornAttachment(inv_item_id) != nullptr; +} + +void handle_item_edit(const LLUUID& inv_item_id) +{ + if (get_is_item_editable(inv_item_id)) + { + if (const LLInventoryItem* inv_item = gInventory.getLinkedItem(inv_item_id)) + { + switch (inv_item->getType()) + { + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_CLOTHING: + LLAgentWearables::editWearable(inv_item_id); + break; + case LLAssetType::AT_OBJECT: + handle_attachment_edit(inv_item_id); + break; + default: + break; + } + } + else + { + handle_attachment_edit(inv_item_id); + } + } +} + BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) { // NOTE: This function doesn't check the folder's children. @@ -2397,16 +2442,19 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { bool open_multi_preview = true; - for (std::set<LLFolderViewItem*>::iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + if ("open" == action) { - LLFolderViewItem* folder_item = *set_iter; - if (folder_item) + for (std::set<LLFolderViewItem*>::iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) { - LLInvFVBridge* bridge = dynamic_cast<LLInvFVBridge*>(folder_item->getViewModelItem()); - if (!bridge || !bridge->isMultiPreviewAllowed()) + LLFolderViewItem* folder_item = *set_iter; + if (folder_item) { - open_multi_preview = false; - break; + LLInvFVBridge* bridge = dynamic_cast<LLInvFVBridge*>(folder_item->getViewModelItem()); + if (!bridge || !bridge->isMultiPreviewAllowed()) + { + open_multi_preview = false; + break; + } } } } diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index fd106bc2d8..04eb962372 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -53,6 +53,10 @@ BOOL get_can_item_be_worn(const LLUUID& id); BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id); +// Performs the appropiate edit action (if one exists) for this item +bool get_is_item_editable(const LLUUID& inv_item_id); +void handle_item_edit(const LLUUID& inv_item_id); + BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id); BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id); diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 17e80dca89..28db6a5808 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -56,6 +56,7 @@ #include "llcallbacklist.h" #include "llvoavatarself.h" #include "llgesturemgr.h" +#include "llsdserialize.h" #include "llsdutil.h" #include "bufferarray.h" #include "bufferstream.h" @@ -76,8 +77,8 @@ BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE; ///---------------------------------------------------------------------------- //BOOL decompress_file(const char* src_filename, const char* dst_filename); -static const char PRODUCTION_CACHE_FORMAT_STRING[] = "%s.inv"; -static const char GRID_CACHE_FORMAT_STRING[] = "%s.%s.inv"; +static const char PRODUCTION_CACHE_FORMAT_STRING[] = "%s.inv.llsd"; +static const char GRID_CACHE_FORMAT_STRING[] = "%s.%s.inv.llsd"; static const char * const LOG_INV("Inventory"); struct InventoryIDPtrLess @@ -678,17 +679,59 @@ void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inv LLUUID categoryId = result["folder_id"].asUUID(); - // Add the category to the internal representation - LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(categoryId, - result["parent_id"].asUUID(), (LLFolderType::EType)result["type"].asInteger(), - result["name"].asString(), gAgent.getID()); + LLViewerInventoryCategory* folderp = gInventory.getCategory(categoryId); + if (!folderp) + { + // Add the category to the internal representation + LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(categoryId, + result["parent_id"].asUUID(), (LLFolderType::EType)result["type"].asInteger(), + result["name"].asString(), gAgent.getID()); - cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 - cat->setDescendentCount(0); - LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); - - accountForUpdate(update); - updateCategory(cat); + LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); + accountForUpdate(update); + + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 + cat->setDescendentCount(0); + updateCategory(cat); + } + else + { + // bulk processing was faster than coroutine (coro request->processBulkUpdateInventory->coro response) + // category already exists, but needs an update + if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_INITIAL + || folderp->getDescendentCount() != LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) + { + LL_WARNS() << "Inventory desync on folder creation. Newly created folder already has descendants or got a version.\n" + << "Name: " << folderp->getName() + << " Id: " << folderp->getUUID() + << " Version: " << folderp->getVersion() + << " Descendants: " << folderp->getDescendentCount() + << LL_ENDL; + } + // Recreate category with correct values + // Creating it anew just simplifies figuring out needed change-masks + // and making all needed updates, see updateCategory + LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(categoryId, + result["parent_id"].asUUID(), (LLFolderType::EType)result["type"].asInteger(), + result["name"].asString(), gAgent.getID()); + + if (folderp->getParentUUID() != cat->getParentUUID()) + { + LL_WARNS() << "Inventory desync on folder creation. Newly created folder has wrong parent.\n" + << "Name: " << folderp->getName() + << " Id: " << folderp->getUUID() + << " Expected parent: " << cat->getParentUUID() + << " Actual parent: " << folderp->getParentUUID() + << LL_ENDL; + LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); + accountForUpdate(update); + } + // else: Do not update parent, parent is already aware of the change. See processBulkUpdateInventory + + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 + cat->setDescendentCount(0); + updateCategory(cat); + } if (callback) { @@ -903,16 +946,29 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask) LLUUID new_parent_id = item->getParentUUID(); bool update_parent_on_server = false; - if (new_parent_id.isNull()) + if (new_parent_id.isNull() && !LLApp::isExiting()) { - // item with null parent will end in random location and then in Lost&Found, - // either move to default folder as if it is new item or don't move at all - LL_WARNS(LOG_INV) << "Update attempts to reparent item " << item->getUUID() - << " to null folder. Moving to Lost&Found. Old item name: " << old_item->getName() - << ". New name: " << item->getName() - << "." << LL_ENDL; - new_parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); - update_parent_on_server = true; + if (old_parent_id.isNull()) + { + // Item with null parent will end in random location and then in Lost&Found, + // either move to default folder as if it is new item or don't move at all + LL_WARNS(LOG_INV) << "Update attempts to reparent item " << item->getUUID() + << " to null folder. Moving to Lost&Found. Old item name: " << old_item->getName() + << ". New name: " << item->getName() + << "." << LL_ENDL; + new_parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + update_parent_on_server = true; + } + else + { + // Probably not the best way to handle this, we might encounter real case of 'lost&found' at some point + LL_WARNS(LOG_INV) << "Update attempts to reparent item " << item->getUUID() + << " to null folder. Old parent not null. Moving to old parent. Old item name: " << old_item->getName() + << ". New name: " << item->getName() + << "." << LL_ENDL; + new_parent_id = old_parent_id; + update_parent_on_server = true; + } } if(old_parent_id != new_parent_id) @@ -2648,29 +2704,37 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, { if(filename.empty()) { - LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL; + LL_ERRS(LOG_INV) << "filename is Null!" << LL_ENDL; return false; } - LL_INFOS(LOG_INV) << "LLInventoryModel::loadFromFile(" << filename << ")" << LL_ENDL; - LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ - if(!file) + LL_INFOS(LOG_INV) << "loading inventory from: (" << filename << ")" << LL_ENDL; + + llifstream file(filename.c_str()); + + if (!file.is_open()) { LL_INFOS(LOG_INV) << "unable to load inventory from: " << filename << LL_ENDL; return false; } - // *NOTE: This buffer size is hard coded into scanf() below. - char buffer[MAX_STRING]; /*Flawfinder: ignore*/ - char keyword[MAX_STRING]; /*Flawfinder: ignore*/ - char value[MAX_STRING]; /*Flawfinder: ignore*/ - is_cache_obsolete = true; // Obsolete until proven current - while(!feof(file) && fgets(buffer, MAX_STRING, file)) + + is_cache_obsolete = true; // Obsolete until proven current + + std::string line; + LLPointer<LLSDParser> parser = new LLSDNotationParser(); + while (std::getline(file, line)) { - sscanf(buffer, " %126s %126s", keyword, value); /* Flawfinder: ignore */ - if(0 == strcmp("inv_cache_version", keyword)) + LLSD s_item; + std::istringstream iss(line); + if (parser->parse(iss, s_item, line.length()) == LLSDParser::PARSE_FAILURE) + { + LL_WARNS(LOG_INV)<< "Parsing inventory cache failed" << LL_ENDL; + break; + } + + if (s_item.has("inv_cache_version")) { - S32 version; - int succ = sscanf(value,"%d",&version); - if ((1 == succ) && (version == sCurrentInvCacheVersion)) + S32 version = s_item["inv_cache_version"].asInteger(); + if (version == sCurrentInvCacheVersion) { // Cache is up to date is_cache_obsolete = false; @@ -2678,43 +2742,33 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } else { - // Cache is out of date + LL_WARNS(LOG_INV)<< "Inventory cache is out of date" << LL_ENDL; break; } } - else if(0 == strcmp("inv_category", keyword)) + else if (s_item.has("cat_id")) { if (is_cache_obsolete) break; - + LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null); - if(inv_cat->importFileLocal(file)) + if(inv_cat->importLLSD(s_item)) { categories.push_back(inv_cat); } - else - { - LL_WARNS(LOG_INV) << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << LL_ENDL; - //delete inv_cat; // automatic when inv_cat is reassigned or destroyed - } } - else if(0 == strcmp("inv_item", keyword)) + else if (s_item.has("item_id")) { if (is_cache_obsolete) break; LLPointer<LLViewerInventoryItem> inv_item = new LLViewerInventoryItem; - if( inv_item->importFileLocal(file) ) + if( inv_item->fromLLSD(s_item) ) { - // *FIX: Need a better solution, this prevents the - // application from freezing, but breaks inventory - // caching. if(inv_item->getUUID().isNull()) { - //delete inv_item; // automatic when inv_cat is reassigned or destroyed LL_WARNS(LOG_INV) << "Ignoring inventory with null item id: " - << inv_item->getName() << LL_ENDL; - + << inv_item->getName() << LL_ENDL; } else { @@ -2727,62 +2781,63 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, items.push_back(inv_item); } } - } - else - { - LL_WARNS(LOG_INV) << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << LL_ENDL; - //delete inv_item; // automatic when inv_cat is reassigned or destroyed - } - } - else - { - LL_WARNS(LOG_INV) << "Unknown token in inventory file '" << keyword << "'" - << LL_ENDL; + } } } - fclose(file); - if (is_cache_obsolete) - return false; - return true; + + file.close(); + + return !is_cache_obsolete; } // static bool LLInventoryModel::saveToFile(const std::string& filename, - const cat_array_t& categories, - const item_array_t& items) + const cat_array_t& categories, + const item_array_t& items) { - if(filename.empty()) + if (filename.empty()) { LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL; return false; } - LL_INFOS(LOG_INV) << "LLInventoryModel::saveToFile(" << filename << ")" << LL_ENDL; - LLFILE* file = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/ - if(!file) + + LL_INFOS(LOG_INV) << "saving inventory to: (" << filename << ")" << LL_ENDL; + + llofstream fileXML(filename.c_str()); + if (!fileXML.is_open()) { LL_WARNS(LOG_INV) << "unable to save inventory to: " << filename << LL_ENDL; return false; } - fprintf(file, "\tinv_cache_version\t%d\n",sCurrentInvCacheVersion); + LLSD cache_ver; + cache_ver["inv_cache_version"] = sCurrentInvCacheVersion; + + fileXML << LLSDOStreamer<LLSDNotationFormatter>(cache_ver) << std::endl; + S32 count = categories.size(); + S32 cat_count = 0; S32 i; for(i = 0; i < count; ++i) { LLViewerInventoryCategory* cat = categories[i]; if(cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) { - cat->exportFileLocal(file); + fileXML << LLSDOStreamer<LLSDNotationFormatter>(cat->exportLLSD()) << std::endl; + cat_count++; } } - count = items.size(); - for(i = 0; i < count; ++i) + S32 it_count = items.size(); + for(i = 0; i < it_count; ++i) { - items[i]->exportFile(file); + fileXML << LLSDOStreamer<LLSDNotationFormatter>(items[i]->asLLSD()) << std::endl; } - fclose(file); + fileXML.close(); + + LL_INFOS(LOG_INV) << "Inventory saved: " << cat_count << " categories, " << it_count << " items." << LL_ENDL; + return true; } diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index c6075b4066..74d9e895c2 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -146,7 +146,7 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mCompletionObserver(NULL), mScroller(NULL), mSortOrderSetting(p.sort_order_setting), - mInventory(p.inventory), + mInventory(p.inventory), //inventory("", &gInventory) mAcceptsDragAndDrop(p.accepts_drag_and_drop), mAllowMultiSelect(p.allow_multi_select), mAllowDrag(p.allow_drag), @@ -154,7 +154,8 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mShowEmptyMessage(p.show_empty_message), mSuppressFolderMenu(p.suppress_folder_menu), mSuppressOpenItemAction(false), - mViewsInitialized(false), + mBuildViewsOnInit(p.preinitialize_views), + mViewsInitialized(VIEWS_UNINITIALIZED), mInvFVBridgeBuilder(NULL), mInventoryViewModel(p.name), mGroupedItemBridge(new LLFolderViewGroupedItemBridge) @@ -281,14 +282,22 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); mInventory->addObserver(mCompletionObserver); - // Build view of inventory if we need default full hierarchy and inventory ready, otherwise do in onIdle. - // Initializing views takes a while so always do it onIdle if viewer already loaded. - if (mInventory->isInventoryUsable() && !mViewsInitialized && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT) - { - initializeViews(); - } - - gIdleCallbacks.addFunction(onIdle, (void*)this); + if (mBuildViewsOnInit) + { + // Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle. + // Initializing views takes a while so always do it onIdle if viewer already loaded. + if (mInventory->isInventoryUsable() + && mViewsInitialized == VIEWS_UNINITIALIZED + && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT) + { + initializeViews(); + } + else if (mViewsInitialized != VIEWS_INITIALIZING) + { + mViewsInitialized = VIEWS_INITIALIZING; + gIdleCallbacks.addFunction(onIdle, (void*)this); + } + } if (mSortOrderSetting != INHERIT_SORT_ORDER) { @@ -334,6 +343,17 @@ LLInventoryPanel::~LLInventoryPanel() clearFolderRoot(); } +/*virtual*/ +void LLInventoryPanel::onVisibilityChange(BOOL new_visibility) +{ + if (new_visibility && mViewsInitialized == VIEWS_UNINITIALIZED) + { + mViewsInitialized = VIEWS_INITIALIZING; + gIdleCallbacks.addFunction(onIdle, (void*)this); + } + LLPanel::onVisibilityChange(new_visibility); +} + void LLInventoryPanel::draw() { // Select the desired item (in case it wasn't loaded when the selection was requested) @@ -512,7 +532,18 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve view_item->destroyView(); removeItemID(idp); } - view_item = buildNewViews(item_id); + + LLInventoryObject const* objectp = mInventory->getObject(item_id); + if (objectp) + { + // providing NULL directly avoids unnessesary getItemByID calls + view_item = buildNewViews(item_id, objectp, NULL); + } + else + { + view_item = NULL; + } + viewmodel_item = static_cast<LLFolderViewModelItemInventory*>(view_item ? view_item->getViewModelItem() : NULL); view_folder = dynamic_cast<LLFolderViewFolder *>(view_item); @@ -555,7 +586,13 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve if (model_item && !view_item) { // Add the UI element for this item. - buildNewViews(item_id); + LLInventoryObject const* objectp = mInventory->getObject(item_id); + if (objectp) + { + // providing NULL directly avoids unnessesary getItemByID calls + buildNewViews(item_id, objectp, NULL); + } + // Select any newly created object that has the auto rename at top of folder root set. if(mFolderRoot.get()->getRoot()->needsAutoRename()) { @@ -640,7 +677,7 @@ void LLInventoryPanel::modelChanged(U32 mask) { LL_RECORD_BLOCK_TIME(FTM_REFRESH); - if (!mViewsInitialized) return; + if (mViewsInitialized != VIEWS_INITIALIZED) return; const LLInventoryModel* model = getModel(); if (!model) return; @@ -706,11 +743,11 @@ void LLInventoryPanel::onIdle(void *userdata) LLInventoryPanel *self = (LLInventoryPanel*)userdata; // Inventory just initialized, do complete build - if (!self->mViewsInitialized) + if (self->mViewsInitialized != VIEWS_INITIALIZED) { self->initializeViews(); } - if (self->mViewsInitialized) + if (self->mViewsInitialized == VIEWS_INITIALIZED) { gIdleCallbacks.deleteFunction(onIdle, (void*)self); } @@ -793,7 +830,7 @@ void LLInventoryPanel::initializeViews() gIdleCallbacks.addFunction(idle, this); - mViewsInitialized = true; + mViewsInitialized = VIEWS_INITIALIZED; openStartFolderOrMyInventory(); @@ -852,7 +889,7 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) { - LLInventoryObject const* objectp = gInventory.getObject(id); + LLInventoryObject const* objectp = mInventory->getObject(id); return buildNewViews(id, objectp); } @@ -862,11 +899,43 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryO { return NULL; } + if (!typedViewsFilter(id, objectp)) + { + // if certain types are not allowed permanently, no reason to create views + return NULL; + } + + const LLUUID &parent_id = objectp->getParentUUID(); LLFolderViewItem* folder_view_item = getItemByID(id); + LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); + + return buildViewsTree(id, parent_id, objectp, folder_view_item, parent_folder); +} + +LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryObject const* objectp, LLFolderViewItem *folder_view_item) +{ + if (!objectp) + { + return NULL; + } + if (!typedViewsFilter(id, objectp)) + { + // if certain types are not allowed permanently, no reason to create views + return NULL; + } const LLUUID &parent_id = objectp->getParentUUID(); - LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); - + LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); + + return buildViewsTree(id, parent_id, objectp, folder_view_item, parent_folder); +} + +LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, + const LLUUID& parent_id, + LLInventoryObject const* objectp, + LLFolderViewItem *folder_view_item, + LLFolderViewFolder *parent_folder) +{ // Force the creation of an extra root level folder item if required by the inventory panel (default is "false") bool allow_drop = true; bool create_root = false; @@ -887,7 +956,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryO { if (objectp->getType() <= LLAssetType::AT_NONE) { - LL_WARNS() << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " + LL_WARNS() << "LLInventoryPanel::buildViewsTree called with invalid objectp->mType : " << ((S32)objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() << LL_ENDL; return NULL; @@ -896,7 +965,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryO if (objectp->getType() >= LLAssetType::AT_COUNT) { // Example: Happens when we add assets of new, not yet supported type to library - LL_DEBUGS() << "LLInventoryPanel::buildNewViews called with unknown objectp->mType : " + LL_DEBUGS() << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : " << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() << LL_ENDL; @@ -973,26 +1042,52 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryO LLViewerInventoryCategory::cat_array_t* categories; LLViewerInventoryItem::item_array_t* items; mInventory->lockDirectDescendentArrays(id, categories, items); - + + LLFolderViewFolder *parentp = dynamic_cast<LLFolderViewFolder*>(folder_view_item); + if(categories) - { + { + bool has_folders = parentp->getFoldersCount() > 0; for (LLViewerInventoryCategory::cat_array_t::const_iterator cat_iter = categories->begin(); cat_iter != categories->end(); ++cat_iter) { const LLViewerInventoryCategory* cat = (*cat_iter); - buildNewViews(cat->getUUID()); + if (typedViewsFilter(cat->getUUID(), cat)) + { + if (has_folders) + { + // This can be optimized: we don't need to call getItemByID() + // each time, especially since content is growing, we can just + // iter over copy of mItemMap in some way + LLFolderViewItem* view_itemp = getItemByID(cat->getUUID()); + buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp); + } + else + { + buildViewsTree(cat->getUUID(), id, cat, NULL, parentp); + } + } } } if(items) - { + { for (LLViewerInventoryItem::item_array_t::const_iterator item_iter = items->begin(); item_iter != items->end(); ++item_iter) { const LLViewerInventoryItem* item = (*item_iter); - buildNewViews(item->getUUID()); + if (typedViewsFilter(item->getUUID(), item)) + { + + // This can be optimized: we don't need to call getItemByID() + // each time, especially since content is growing, we can just + // iter over copy of mItemMap in some way + LLFolderViewItem* view_itemp = getItemByID(item->getUUID()); + buildViewsTree(item->getUUID(), id, item, view_itemp, parentp); + } + } } mInventory->unlockDirectDescendentArrays(id); @@ -1755,41 +1850,7 @@ LLInventoryRecentItemsPanel::LLInventoryRecentItemsPanel( const Params& params) /************************************************************************/ /* Asset Pre-Filtered Inventory Panel related class */ -/* Exchanges filter's flexibility for speed of generation and */ -/* improved performance */ /************************************************************************/ -class LLAssetFilteredInventoryPanel : public LLInventoryPanel -{ -public: - struct Params - : public LLInitParam::Block<Params, LLInventoryPanel::Params> - { - Mandatory<std::string> filter_asset_type; - - Params() : filter_asset_type("filter_asset_type") {} - }; - - void initFromParams(const Params& p); -protected: - LLAssetFilteredInventoryPanel(const Params& p) : LLInventoryPanel(p) {} - friend class LLUICtrlFactory; -public: - ~LLAssetFilteredInventoryPanel() {} - - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) override; - -protected: - /*virtual*/ LLFolderViewItem* buildNewViews(const LLUUID& id) override; - /*virtual*/ void itemChanged(const LLUUID& item_id, U32 mask, const LLInventoryObject* model_item) override; - -private: - LLAssetType::EType mAssetType; -}; - void LLAssetFilteredInventoryPanel::initFromParams(const Params& p) { @@ -1823,21 +1884,20 @@ BOOL LLAssetFilteredInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, B return result; } -LLFolderViewItem* LLAssetFilteredInventoryPanel::buildNewViews(const LLUUID& id) +/*virtual*/ +bool LLAssetFilteredInventoryPanel::typedViewsFilter(const LLUUID& id, LLInventoryObject const* objectp) { - LLInventoryObject const* objectp = gInventory.getObject(id); - if (!objectp) { - return NULL; + return false; } if (objectp->getType() != mAssetType && objectp->getType() != LLAssetType::AT_CATEGORY) { - return NULL; + return false; } - return LLInventoryPanel::buildNewViews(id, objectp); + return true; } void LLAssetFilteredInventoryPanel::itemChanged(const LLUUID& id, U32 mask, const LLInventoryObject* model_item) diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index b55eb2b828..ad6010f09c 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -108,6 +108,10 @@ public: Optional<LLFolderViewFolder::Params> folder; Optional<LLFolderViewItem::Params> item; + // All item and folder views will be initialized on init if true (default) + // Will initialize on visibility change otherwise. + Optional<bool> preinitialize_views; + Params() : sort_order_setting("sort_order_setting"), inventory("", &gInventory), @@ -126,7 +130,8 @@ public: accepts_drag_and_drop("accepts_drag_and_drop"), folder_view("folder_view"), folder("folder"), - item("item") + item("item"), + preinitialize_views("preinitialize_views", true) {} }; @@ -154,6 +159,7 @@ public: LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; } // LLView methods + /*virtual*/ void onVisibilityChange(BOOL new_visibility); void draw(); /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); BOOL handleHover(S32 x, S32 y, MASK mask); @@ -313,7 +319,7 @@ public: void addHideFolderType(LLFolderType::EType folder_type); public: - BOOL getIsViewsInitialized() const { return mViewsInitialized; } + bool getViewsInitialized() const { return mViewsInitialized == VIEWS_INITIALIZED; } protected: // Builds the UI. Call this once the inventory is usable. void initializeViews(); @@ -325,8 +331,15 @@ protected: static LLUIColor sLibraryColor; static LLUIColor sLinkColor; - virtual LLFolderViewItem* buildNewViews(const LLUUID& id); - LLFolderViewItem* buildNewViews(const LLUUID& id, LLInventoryObject const* objectp); + LLFolderViewItem* buildNewViews(const LLUUID& id); + LLFolderViewItem* buildNewViews(const LLUUID& id, + LLInventoryObject const* objectp); + LLFolderViewItem* buildNewViews(const LLUUID& id, + LLInventoryObject const* objectp, + LLFolderViewItem *target_view); + // if certain types are not allowed, no reason to create views + virtual bool typedViewsFilter(const LLUUID& id, LLInventoryObject const* objectp) { return true; } + virtual void itemChanged(const LLUUID& item_id, U32 mask, const LLInventoryObject* model_item); BOOL getIsHiddenFolderType(LLFolderType::EType folder_type) const; @@ -334,8 +347,82 @@ protected: virtual LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop); virtual LLFolderViewItem* createFolderViewItem(LLInvFVBridge * bridge); private: - bool mBuildDefaultHierarchy; // default inventory hierarchy should be created in postBuild() - bool mViewsInitialized; // Views have been generated + // buildViewsTree does not include some checks and is meant + // for recursive use, use buildNewViews() for first call + LLFolderViewItem* buildViewsTree(const LLUUID& id, + const LLUUID& parent_id, + LLInventoryObject const* objectp, + LLFolderViewItem *target_view, + LLFolderViewFolder *parent_folder_view); + + typedef enum e_views_initialization_state + { + VIEWS_UNINITIALIZED = 0, + VIEWS_INITIALIZING, + VIEWS_INITIALIZED, + } EViewsInitializationState; + + bool mBuildViewsOnInit; + EViewsInitializationState mViewsInitialized; // Whether views have been generated +}; + + +class LLInventoryFavoriteItemsPanel : public LLInventoryPanel +{ +public: + struct Params : public LLInitParam::Block<Params, LLInventoryPanel::Params> + {}; + + void initFromParams(const Params& p); + bool isSelectionRemovable() { return false; } + void setSelectCallback(const boost::function<void(const std::deque<LLFolderViewItem*>& items, BOOL user_action)>& cb); + +protected: + LLInventoryFavoriteItemsPanel(const Params& params); + ~LLInventoryFavoriteItemsPanel() { mFolderChangedSignal.disconnect(); } + void updateFavoritesRootFolder(); + + boost::signals2::connection mFolderChangedSignal; + boost::function<void(const std::deque<LLFolderViewItem*>& items, BOOL user_action)> mSelectionCallback; + friend class LLUICtrlFactory; +}; + +/************************************************************************/ +/* Asset Pre-Filtered Inventory Panel related class */ +/* Exchanges filter's flexibility for speed of generation and */ +/* improved performance */ +/************************************************************************/ + +class LLAssetFilteredInventoryPanel : public LLInventoryPanel +{ +public: + struct Params + : public LLInitParam::Block<Params, LLInventoryPanel::Params> + { + Mandatory<std::string> filter_asset_type; + + Params() : filter_asset_type("filter_asset_type") {} + }; + + void initFromParams(const Params& p); +protected: + LLAssetFilteredInventoryPanel(const Params& p) : LLInventoryPanel(p) {} + friend class LLUICtrlFactory; +public: + ~LLAssetFilteredInventoryPanel() {} + + /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) override; + +protected: + /*virtual*/ bool typedViewsFilter(const LLUUID& id, LLInventoryObject const* objectp) override; + /*virtual*/ void itemChanged(const LLUUID& item_id, U32 mask, const LLInventoryObject* model_item) override; + +private: + LLAssetType::EType mAssetType; }; #endif // LL_LLINVENTORYPANEL_H diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp new file mode 100644 index 0000000000..b6107eeedf --- /dev/null +++ b/indra/newview/llkeyconflict.cpp @@ -0,0 +1,1015 @@ +/** + * @file llkeyconflict.cpp + * @brief + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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$ + */ + +/* + * App-wide preferences. Note that these are not per-user, + * because we need to load many preferences before we have + * a login name. + */ + +#include "llviewerprecompiledheaders.h" + +#include "llkeyconflict.h" + +#include "llinitparam.h" +#include "llkeyboard.h" +#include "lltrans.h" +#include "llviewercontrol.h" +#include "llviewerinput.h" +#include "llviewermenu.h" +#include "llxuiparser.h" + +static const std::string saved_settings_key_controls[] = { "placeholder" }; // add settings from gSavedSettings here + +static const std::string filename_default = "key_bindings.xml"; +static const std::string filename_temporary = "key_bindings_tmp.xml"; // used to apply uncommited changes on the go. + +// LLKeyboard::stringFromMask is meant for UI and is OS dependent, +// so this class uses it's own version +std::string string_from_mask(MASK mask) +{ + std::string res; + if ((mask & MASK_CONTROL) != 0) + { + res = "CTL"; + } + if ((mask & MASK_ALT) != 0) + { + if (!res.empty()) res += "_"; + res += "ALT"; + } + if ((mask & MASK_SHIFT) != 0) + { + if (!res.empty()) res += "_"; + res += "SHIFT"; + } + + if (mask == MASK_NONE) + { + res = "NONE"; + } + return res; +} + +std::string string_from_mouse(EMouseClickType click, bool translate) +{ + std::string res; + switch (click) + { + case CLICK_LEFT: + res = "LMB"; + break; + case CLICK_MIDDLE: + res = "MMB"; + break; + case CLICK_RIGHT: + res = "RMB"; + break; + case CLICK_BUTTON4: + res = "MB4"; + break; + case CLICK_BUTTON5: + res = "MB5"; + break; + case CLICK_DOUBLELEFT: + res = "Double LMB"; + break; + default: + break; + } + + if (translate && !res.empty()) + { + res = LLTrans::getString(res); + } + return res; +} + +// LLKeyConflictHandler + +S32 LLKeyConflictHandler::sTemporaryFileUseCount = 0; + +LLKeyConflictHandler::LLKeyConflictHandler() +: mHasUnsavedChanges(false), + mUsesTemporaryFile(false), + mLoadMode(MODE_COUNT) +{ +} + +LLKeyConflictHandler::LLKeyConflictHandler(ESourceMode mode) +: mHasUnsavedChanges(false), + mUsesTemporaryFile(false), + mLoadMode(mode) +{ + loadFromSettings(mode); +} + +LLKeyConflictHandler::~LLKeyConflictHandler() +{ + clearUnsavedChanges(); + // Note: does not reset bindings if temporary file was used +} + +bool LLKeyConflictHandler::canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask) +{ + return mControlsMap[control_name].canHandle(mouse_ind, key, mask); +} + +bool LLKeyConflictHandler::canHandleKey(const std::string &control_name, KEY key, MASK mask) +{ + return canHandleControl(control_name, CLICK_NONE, key, mask); +} + +bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask) +{ + return canHandleControl(control_name, mouse_ind, KEY_NONE, mask); +} + +bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask) +{ + return canHandleControl(control_name, (EMouseClickType)mouse_ind, KEY_NONE, mask); +} + +bool LLKeyConflictHandler::canAssignControl(const std::string &control_name) +{ + control_map_t::iterator iter = mControlsMap.find(control_name); + if (iter != mControlsMap.end()) + { + return iter->second.mAssignable; + } + // If we don't know this control, means it wasn't assigned by user yet and thus is editable + return true; +} + +// static +bool LLKeyConflictHandler::isReservedByMenu(const KEY &key, const MASK &mask) +{ + if (key == KEY_NONE) + { + return false; + } + return (gMenuBarView && gMenuBarView->hasAccelerator(key, mask)) + || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(key, mask)); +} + +// static +bool LLKeyConflictHandler::isReservedByMenu(const LLKeyData &data) +{ + if (data.mMouse != CLICK_NONE || data.mKey == KEY_NONE) + { + return false; + } + return (gMenuBarView && gMenuBarView->hasAccelerator(data.mKey, data.mMask)) + || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(data.mKey, data.mMask)); +} + +bool LLKeyConflictHandler::registerControl(const std::string &control_name, U32 index, EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask) +{ + if (control_name.empty()) + { + return false; + } + LLKeyConflict &type_data = mControlsMap[control_name]; + if (!type_data.mAssignable) + { + // Example: user tried to assign camera spin to all modes, but first person mode doesn't support it + return false; + } + LLKeyData data(mouse, key, mask, ignore_mask); + if (type_data.mKeyBind.getKeyData(index) == data) + { + return true; + } + if (isReservedByMenu(data)) + { + return false; + } + if (removeConflicts(data, type_data.mConflictMask)) + { + type_data.mKeyBind.replaceKeyData(data, index); + mHasUnsavedChanges = true; + return true; + } + // control already in use/blocked + return false; +} + +bool LLKeyConflictHandler::clearControl(const std::string &control_name, U32 data_index) +{ + if (control_name.empty()) + { + return false; + } + LLKeyConflict &type_data = mControlsMap[control_name]; + if (!type_data.mAssignable) + { + // Example: user tried to assign camera spin to all modes, but first person mode doesn't support it + return false; + } + type_data.mKeyBind.resetKeyData(data_index); + mHasUnsavedChanges = true; + return true; +} + +LLKeyData LLKeyConflictHandler::getControl(const std::string &control_name, U32 index) +{ + if (control_name.empty()) + { + return LLKeyData(); + } + return mControlsMap[control_name].getKeyData(index); +} + +bool LLKeyConflictHandler::isControlEmpty(const std::string &control_name) +{ + if (control_name.empty()) + { + return true; + } + return mControlsMap[control_name].mKeyBind.isEmpty(); +} + +// static +std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata) +{ + std::string result; + + if (keydata.mMask != MASK_NONE && keydata.mKey != KEY_NONE) + { + result = LLKeyboard::stringFromAccelerator(keydata.mMask, keydata.mKey); + } + else if (keydata.mKey != KEY_NONE) + { + result = LLKeyboard::stringFromKey(keydata.mKey); + } + else if (keydata.mMask != MASK_NONE) + { + result = LLKeyboard::stringFromAccelerator(keydata.mMask); + } + + result += string_from_mouse(keydata.mMouse, true); + + return result; +} + +std::string LLKeyConflictHandler::getControlString(const std::string &control_name, U32 index) +{ + if (control_name.empty()) + { + return ""; + } + return getStringFromKeyData(mControlsMap[control_name].getKeyData(index)); +} + +void LLKeyConflictHandler::loadFromControlSettings(const std::string &name) +{ + LLControlVariablePtr var = gSavedSettings.getControl(name); + if (var) + { + LLKeyBind bind(var->getValue()); + LLKeyConflict key(bind, true, 0); + mControlsMap[name] = key; + } +} + +void LLKeyConflictHandler::loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination) +{ + for (LLInitParam::ParamIterator<LLViewerInput::KeyBinding>::const_iterator it = keymode.bindings.begin(), + end_it = keymode.bindings.end(); + it != end_it; + ++it) + { + KEY key; + MASK mask; + EMouseClickType mouse = CLICK_NONE; + if (it->mouse.isProvided()) + { + LLViewerInput::mouseFromString(it->mouse.getValue(), &mouse); + } + if (it->key.getValue().empty()) + { + key = KEY_NONE; + } + else + { + LLKeyboard::keyFromString(it->key, &key); + } + LLKeyboard::maskFromString(it->mask, &mask); + // Note: it->command is also the name of UI element, howhever xml we are loading from + // might not know all the commands, so UI will have to know what to fill by its own + // Assumes U32_MAX conflict mask, and is assignable by default, + // but assignability might have been overriden by generatePlaceholders. + LLKeyConflict &type_data = (*destination)[it->command]; + type_data.mKeyBind.addKeyData(mouse, key, mask, true); + } +} + +bool LLKeyConflictHandler::loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination) +{ + if (filename.empty()) + { + return false; + } + + bool res = false; + + LLViewerInput::Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + switch (load_mode) + { + case MODE_FIRST_PERSON: + if (keys.first_person.isProvided()) + { + loadFromSettings(keys.first_person, destination); + res = true; + } + break; + case MODE_THIRD_PERSON: + if (keys.third_person.isProvided()) + { + loadFromSettings(keys.third_person, destination); + res = true; + } + break; + case MODE_EDIT_AVATAR: + if (keys.edit_avatar.isProvided()) + { + loadFromSettings(keys.edit_avatar, destination); + res = true; + } + break; + case MODE_SITTING: + if (keys.sitting.isProvided()) + { + loadFromSettings(keys.sitting, destination); + res = true; + } + break; + default: + LL_ERRS() << "Not implememted mode " << load_mode << LL_ENDL; + break; + } + } + return res; +} + +void LLKeyConflictHandler::loadFromSettings(ESourceMode load_mode) +{ + mControlsMap.clear(); + mDefaultsMap.clear(); + + // E.X. In case we need placeholder keys for conflict resolution. + generatePlaceholders(load_mode); + + if (load_mode == MODE_SAVED_SETTINGS) + { + // load settings clss knows about, but it also possible to load settings by name separately + const S32 size = std::extent<decltype(saved_settings_key_controls)>::value; + for (U32 i = 0; i < size; i++) + { + loadFromControlSettings(saved_settings_key_controls[i]); + } + } + else + { + // load defaults + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename_default); + if (!loadFromSettings(load_mode, filename, &mDefaultsMap)) + { + LL_WARNS() << "Failed to load default settings, aborting" << LL_ENDL; + return; + } + + // load user's + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); + if (!gDirUtilp->fileExists(filename) || !loadFromSettings(load_mode, filename, &mControlsMap)) + { + // mind placeholders + mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end()); + } + } + mLoadMode = load_mode; +} + +void LLKeyConflictHandler::saveToSettings(bool temporary) +{ + if (mControlsMap.empty()) + { + return; + } + + if (mLoadMode == MODE_SAVED_SETTINGS) + { + // Does not support 'temporary', preferences handle that themself + // so in case of saved settings we just do not clear mHasUnsavedChanges + control_map_t::iterator iter = mControlsMap.begin(); + control_map_t::iterator end = mControlsMap.end(); + + for (; iter != end; ++iter) + { + if (iter->first.empty()) + { + continue; + } + + LLKeyConflict &key = iter->second; + key.mKeyBind.trimEmpty(); + if (!key.mAssignable) + { + continue; + } + + if (gSavedSettings.controlExists(iter->first)) + { + gSavedSettings.setLLSD(iter->first, key.mKeyBind.asLLSD()); + } + else if (!key.mKeyBind.empty()) + { + // Note: this is currently not in use, might be better for load mechanics to ask for and retain control group + // otherwise settings loaded from other control groups will end in gSavedSettings + LL_INFOS() << "Creating new keybinding " << iter->first << LL_ENDL; + gSavedSettings.declareLLSD(iter->first, key.mKeyBind.asLLSD(), "comment", LLControlVariable::PERSIST_ALWAYS); + } + } + } + else + { + // Determine what file to load and load full copy of that file + std::string filename; + + if (temporary) + { + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); + if (!gDirUtilp->fileExists(filename)) + { + filename.clear(); + } + } + + if (filename.empty()) + { + filename = gDirUtilp->findFile(filename_default, + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + } + + LLViewerInput::Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + // replace category we edited + + // mode is a HACK to correctly reset bindings without reparsing whole file and avoid doing + // own param container (which will face issues with inasseesible members of LLInitParam) + LLViewerInput::KeyMode mode; + LLViewerInput::KeyBinding binding; + + control_map_t::iterator iter = mControlsMap.begin(); + control_map_t::iterator end = mControlsMap.end(); + for (; iter != end; ++iter) + { + // By default xml have (had) up to 6 elements per function + // eventually it will be cleaned up and UI will only shows 3 per function, + // so make sure to cleanup. + // Also this helps in keeping file small. + iter->second.mKeyBind.trimEmpty(); + U32 size = iter->second.mKeyBind.getDataCount(); + for (U32 i = 0; i < size; ++i) + { + if (iter->first.empty()) + { + continue; + } + + LLKeyConflict &key = iter->second; + key.mKeyBind.trimEmpty(); + if (key.mKeyBind.empty() || !key.mAssignable) + { + continue; + } + + LLKeyData data = key.mKeyBind.getKeyData(i); + // Still write empty LLKeyData to make sure we will maintain UI position + if (data.mKey == KEY_NONE) + { + // Might be better idea to be consistent and use NONE. LLViewerInput can work with both cases + binding.key = ""; + } + else + { + binding.key = LLKeyboard::stringFromKey(data.mKey, false /*Do not localize*/); + } + binding.mask = string_from_mask(data.mMask); + if (data.mMouse == CLICK_NONE) + { + binding.mouse.setProvided(false); + } + else + { + // set() because 'optional', for compatibility purposes + // just copy old keys.xml and rename to key_bindings.xml, it should work + binding.mouse.set(string_from_mouse(data.mMouse, false), true); + } + binding.command = iter->first; + mode.bindings.add(binding); + } + } + + switch (mLoadMode) + { + case MODE_FIRST_PERSON: + if (keys.first_person.isProvided()) + { + keys.first_person.bindings.set(mode.bindings, true); + } + break; + case MODE_THIRD_PERSON: + if (keys.third_person.isProvided()) + { + keys.third_person.bindings.set(mode.bindings, true); + } + break; + case MODE_EDIT_AVATAR: + if (keys.edit_avatar.isProvided()) + { + keys.edit_avatar.bindings.set(mode.bindings, true); + } + break; + case MODE_SITTING: + if (keys.sitting.isProvided()) + { + keys.sitting.bindings.set(mode.bindings, true); + } + break; + default: + LL_ERRS() << "Not implememted mode " << mLoadMode << LL_ENDL; + break; + } + + if (temporary) + { + // write to temporary xml and use it for gViewerInput + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); + if (!mUsesTemporaryFile) + { + mUsesTemporaryFile = true; + sTemporaryFileUseCount++; + } + } + else + { + // write back to user's xml and use it for gViewerInput + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); + // Don't reset mUsesTemporaryFile, it will be reset at cleanup stage + } + + LLXMLNodePtr output_node = new LLXMLNode("keys", false); + LLXUIParser parser; + parser.writeXUI(output_node, keys); + + // Write the resulting XML to file + if (!output_node->isNull()) + { + LLFILE *fp = LLFile::fopen(filename, "w"); + if (fp != NULL) + { + LLXMLNode::writeHeaderToFile(fp); + output_node->writeToFile(fp); + fclose(fp); + } + } + // Now force a rebind for keyboard + if (gDirUtilp->fileExists(filename)) + { + // Ideally instead of rebinding immediately we should shedule + // the rebind since single file can have multiple handlers, + // one per mode, saving simultaneously. + // Or whatever uses LLKeyConflictHandler should control the process. + gViewerInput.loadBindingsXML(filename); + } + } + } + +#if 1 + // Legacy support + // Remove #if-#endif section half a year after DRTVWR-501 releases. + // Update legacy settings in settings.xml + // We only care for third person view since legacy settings can't store + // more than one mode. + // We are saving this even if we are in temporary mode - preferences + // will restore values on cancel + if (mLoadMode == MODE_THIRD_PERSON && mHasUnsavedChanges) + { + bool value = canHandleMouse("walk_to", CLICK_DOUBLELEFT, MASK_NONE); + gSavedSettings.setBOOL("DoubleClickAutoPilot", value); + + value = canHandleMouse("walk_to", CLICK_LEFT, MASK_NONE); + gSavedSettings.setBOOL("ClickToWalk", value); + + // new method can save both toggle and push-to-talk values simultaneously, + // but legacy one can save only one. It also doesn't support mask. + LLKeyData data = getControl("toggle_voice", 0); + bool can_toggle = !data.isEmpty(); + if (!can_toggle) + { + data = getControl("voice_follow_key", 0); + } + + gSavedSettings.setBOOL("PushToTalkToggle", can_toggle); + if (data.isEmpty()) + { + // legacy viewer has a bug that might crash it if NONE value is assigned. + // just reset to default + gSavedSettings.getControl("PushToTalkButton")->resetToDefault(false); + } + else + { + if (data.mKey != KEY_NONE) + { + gSavedSettings.setString("PushToTalkButton", LLKeyboard::stringFromKey(data.mKey)); + } + else + { + std::string ctrl_value; + switch (data.mMouse) + { + case CLICK_MIDDLE: + ctrl_value = "MiddleMouse"; + break; + case CLICK_BUTTON4: + ctrl_value = "MouseButton4"; + break; + case CLICK_BUTTON5: + ctrl_value = "MouseButton5"; + break; + default: + ctrl_value = "MiddleMouse"; + break; + } + gSavedSettings.setString("PushToTalkButton", ctrl_value); + } + } + } +#endif + + if (mLoadMode == MODE_THIRD_PERSON && mHasUnsavedChanges) + { + // Map floater should react to doubleclick if doubleclick for teleport is set + // Todo: Seems conterintuitive for map floater to share inworld controls + // after these changes release, discuss with UI UX engineer if this should just + // be set to 1 by default (before release this also doubles as legacy support) + bool value = canHandleMouse("teleport_to", CLICK_DOUBLELEFT, MASK_NONE); + gSavedSettings.setBOOL("DoubleClickTeleport", value); + } + + if (!temporary) + { + // will remove any temporary file if there were any + clearUnsavedChanges(); + } +} + +LLKeyData LLKeyConflictHandler::getDefaultControl(const std::string &control_name, U32 index) +{ + if (control_name.empty()) + { + return LLKeyData(); + } + if (mLoadMode == MODE_SAVED_SETTINGS) + { + LLControlVariablePtr var = gSavedSettings.getControl(control_name); + if (var) + { + return LLKeyBind(var->getDefault()).getKeyData(index); + } + return LLKeyData(); + } + else + { + control_map_t::iterator iter = mDefaultsMap.find(control_name); + if (iter != mDefaultsMap.end()) + { + return iter->second.mKeyBind.getKeyData(index); + } + return LLKeyData(); + } +} + +void LLKeyConflictHandler::resetToDefault(const std::string &control_name, U32 index) +{ + if (control_name.empty()) + { + return; + } + LLKeyData data = getDefaultControl(control_name, index); + + if (data != mControlsMap[control_name].getKeyData(index)) + { + // reset controls that might have been switched to our current control + removeConflicts(data, mControlsMap[control_name].mConflictMask); + mControlsMap[control_name].setKeyData(data, index); + } +} + +void LLKeyConflictHandler::resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts) +{ + if (control_name.empty()) + { + return; + } + if (mLoadMode == MODE_SAVED_SETTINGS) + { + LLControlVariablePtr var = gSavedSettings.getControl(control_name); + if (var) + { + LLKeyBind bind(var->getDefault()); + if (!ignore_conflicts) + { + for (S32 i = 0; i < bind.getDataCount(); ++i) + { + removeConflicts(bind.getKeyData(i), mControlsMap[control_name].mConflictMask); + } + } + mControlsMap[control_name].mKeyBind = bind; + } + else + { + mControlsMap[control_name].mKeyBind.clear(); + } + } + else + { + control_map_t::iterator iter = mDefaultsMap.find(control_name); + if (iter != mDefaultsMap.end()) + { + if (!ignore_conflicts) + { + for (S32 i = 0; i < iter->second.mKeyBind.getDataCount(); ++i) + { + removeConflicts(iter->second.mKeyBind.getKeyData(i), mControlsMap[control_name].mConflictMask); + } + } + mControlsMap[control_name].mKeyBind = iter->second.mKeyBind; + } + else + { + mControlsMap[control_name].mKeyBind.clear(); + } + } +} + +void LLKeyConflictHandler::resetToDefault(const std::string &control_name) +{ + // reset specific binding without ignoring conflicts + resetToDefaultAndResolve(control_name, false); +} + +void LLKeyConflictHandler::resetToDefaults(ESourceMode mode) +{ + if (mode == MODE_SAVED_SETTINGS) + { + control_map_t::iterator iter = mControlsMap.begin(); + control_map_t::iterator end = mControlsMap.end(); + + for (; iter != end; ++iter) + { + resetToDefaultAndResolve(iter->first, true); + } + } + else + { + mControlsMap.clear(); + generatePlaceholders(mode); + mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end()); + } + + mHasUnsavedChanges = true; +} + +void LLKeyConflictHandler::resetToDefaults() +{ + if (!empty()) + { + resetToDefaults(mLoadMode); + } + else + { + // not optimal since: + // 1. We are not sure that mLoadMode was set + // 2. We are not sure if there are any changes in comparison to default + // 3. We are loading 'current' only to replace it + // but it is reliable and works Todo: consider optimizing. + loadFromSettings(mLoadMode); + resetToDefaults(mLoadMode); + } +} + +void LLKeyConflictHandler::clear() +{ + if (clearUnsavedChanges()) + { + // temporary file was removed, this means we were using it and need to reload keyboard's bindings + resetKeyboardBindings(); + } + mControlsMap.clear(); + mDefaultsMap.clear(); +} + +// static +void LLKeyConflictHandler::resetKeyboardBindings() +{ + // Try to load User's bindings first + std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); + if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) + { + // Failed to load custom bindings, try default ones + key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename_default); + if (!gViewerInput.loadBindingsXML(key_bindings_file)) + { + LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL; + } + } +} + +void LLKeyConflictHandler::generatePlaceholders(ESourceMode load_mode) +{ + // These controls are meant to cause conflicts when user tries to assign same control somewhere else + // also this can be used to pre-record controls that should not conflict or to assign conflict groups/masks + + if (load_mode == MODE_FIRST_PERSON) + { + // First person view doesn't support camera controls + // Note: might be better idea to just load these from control_table_contents_camera.xml + // or to pass from floaterpreferences when it loads said file + registerTemporaryControl("look_up"); + registerTemporaryControl("look_down"); + registerTemporaryControl("move_forward"); + registerTemporaryControl("move_backward"); + registerTemporaryControl("move_forward_fast"); + registerTemporaryControl("move_backward_fast"); + registerTemporaryControl("spin_over"); + registerTemporaryControl("spin_under"); + registerTemporaryControl("pan_up"); + registerTemporaryControl("pan_down"); + registerTemporaryControl("pan_left"); + registerTemporaryControl("pan_right"); + registerTemporaryControl("pan_in"); + registerTemporaryControl("pan_out"); + registerTemporaryControl("spin_around_ccw"); + registerTemporaryControl("spin_around_cw"); + + // control_table_contents_editing.xml + registerTemporaryControl("edit_avatar_spin_ccw"); + registerTemporaryControl("edit_avatar_spin_cw"); + registerTemporaryControl("edit_avatar_spin_over"); + registerTemporaryControl("edit_avatar_spin_under"); + registerTemporaryControl("edit_avatar_move_forward"); + registerTemporaryControl("edit_avatar_move_backward"); + + // no autopilot or teleport + registerTemporaryControl("walk_to"); + registerTemporaryControl("teleport_to"); + } + + if (load_mode == MODE_EDIT_AVATAR) + { + // no autopilot or teleport + registerTemporaryControl("walk_to"); + registerTemporaryControl("teleport_to"); + } + + if (load_mode == MODE_SITTING) + { + // no autopilot + registerTemporaryControl("walk_to"); + } + else + { + // sitting related functions should only be avaliable in sitting mode + registerTemporaryControl("move_forward_sitting"); + registerTemporaryControl("move_backward_sitting"); + registerTemporaryControl("spin_over_sitting"); + registerTemporaryControl("spin_under_sitting"); + registerTemporaryControl("spin_around_ccw_sitting"); + registerTemporaryControl("spin_around_cw_sitting"); + } +} + +bool LLKeyConflictHandler::removeConflicts(const LLKeyData &data, const U32 &conlict_mask) +{ + if (conlict_mask == CONFLICT_NOTHING) + { + // Can't conflict + return true; + } + std::map<std::string, S32> conflict_list; + control_map_t::iterator cntrl_iter = mControlsMap.begin(); + control_map_t::iterator cntrl_end = mControlsMap.end(); + for (; cntrl_iter != cntrl_end; ++cntrl_iter) + { + S32 index = cntrl_iter->second.mKeyBind.findKeyData(data); + if (index >= 0 + && cntrl_iter->second.mConflictMask != CONFLICT_NOTHING + && (cntrl_iter->second.mConflictMask & conlict_mask) != 0) + { + if (cntrl_iter->second.mAssignable) + { + // Potentially we can have multiple conflict flags conflicting + // including unassignable keys. + // So record the conflict and find all others before doing any changes. + // Assume that there is only one conflict per bind + conflict_list[cntrl_iter->first] = index; + } + else + { + return false; + } + } + } + + std::map<std::string, S32>::iterator cnflct_iter = conflict_list.begin(); + std::map<std::string, S32>::iterator cnflct_end = conflict_list.end(); + for (; cnflct_iter != cnflct_end; ++cnflct_iter) + { + mControlsMap[cnflct_iter->first].mKeyBind.resetKeyData(cnflct_iter->second); + } + return true; +} + +void LLKeyConflictHandler::registerTemporaryControl(const std::string &control_name, EMouseClickType mouse, KEY key, MASK mask, U32 conflict_mask) +{ + LLKeyConflict *type_data = &mControlsMap[control_name]; + type_data->mAssignable = false; + type_data->mConflictMask = conflict_mask; + type_data->mKeyBind.addKeyData(mouse, key, mask, false); +} + +void LLKeyConflictHandler::registerTemporaryControl(const std::string &control_name, U32 conflict_mask) +{ + LLKeyConflict *type_data = &mControlsMap[control_name]; + type_data->mAssignable = false; + type_data->mConflictMask = conflict_mask; +} + +bool LLKeyConflictHandler::clearUnsavedChanges() +{ + bool result = false; + mHasUnsavedChanges = false; + + if (mUsesTemporaryFile) + { + mUsesTemporaryFile = false; + sTemporaryFileUseCount--; + if (!sTemporaryFileUseCount) + { + result = clearTemporaryFile(); + } + // else: might be usefull to overwrite content of temp file with defaults + // but at the moment there is no such need + } + return result; +} + +//static +bool LLKeyConflictHandler::clearTemporaryFile() +{ + // At the moment single file needs five handlers (one per mode), so doing this + // will remove file for all hadlers + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); + if (gDirUtilp->fileExists(filename)) + { + LLFile::remove(filename); + return true; + } + return false; +} + diff --git a/indra/newview/llkeyconflict.h b/indra/newview/llkeyconflict.h new file mode 100644 index 0000000000..2926ca3aeb --- /dev/null +++ b/indra/newview/llkeyconflict.h @@ -0,0 +1,175 @@ +/** + * @file llkeyconflict.h + * @brief + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLKEYCONFLICT_H +#define LL_LLKEYCONFLICT_H + +#include "llkeybind.h" +#include "llviewerinput.h" + + +class LLKeyConflict +{ +public: + LLKeyConflict() : mAssignable(true), mConflictMask(U32_MAX) {} //temporary assignable, don't forget to change once all keys are recorded + LLKeyConflict(bool assignable, U32 conflict_mask) + : mAssignable(assignable), mConflictMask(conflict_mask) {} + LLKeyConflict(const LLKeyBind &bind, bool assignable, U32 conflict_mask) + : mAssignable(assignable), mConflictMask(conflict_mask), mKeyBind(bind) {} + + LLKeyData getPrimaryKeyData() { return mKeyBind.getKeyData(0); } + LLKeyData getKeyData(U32 index) { return mKeyBind.getKeyData(index); } + void setPrimaryKeyData(const LLKeyData& data) { mKeyBind.replaceKeyData(data, 0); } + void setKeyData(const LLKeyData& data, U32 index) { mKeyBind.replaceKeyData(data, index); } + bool canHandle(EMouseClickType mouse, KEY key, MASK mask) { return mKeyBind.canHandle(mouse, key, mask); } + + LLKeyBind mKeyBind; + bool mAssignable; // whether user can change key or key simply acts as placeholder + U32 mConflictMask; +}; + +class LLKeyConflictHandler +{ +public: + + enum ESourceMode // partially repeats e_keyboard_mode + { + MODE_FIRST_PERSON, + MODE_THIRD_PERSON, + MODE_EDIT_AVATAR, + MODE_SITTING, + MODE_SAVED_SETTINGS, // for settings from saved settings + MODE_COUNT + }; + + const U32 CONFLICT_NOTHING = 0; + // at the moment this just means that key will conflict with everything that is identical + const U32 CONFLICT_ANY = U32_MAX; + + // Note: missed selection and edition commands (would be really nice to go through selection via MB4/5 or wheel) + + LLKeyConflictHandler(); + LLKeyConflictHandler(ESourceMode mode); + ~LLKeyConflictHandler(); + + bool canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask); + bool canHandleKey(const std::string &control_name, KEY key, MASK mask); + bool canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask); + bool canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask); //Just for convinience + bool canAssignControl(const std::string &control_name); + static bool isReservedByMenu(const KEY &key, const MASK &mask); + static bool isReservedByMenu(const LLKeyData &data); + + // @control_name - see REGISTER_KEYBOARD_ACTION in llviewerinput for avaliable options, + // usually this is just name of the function + // @data_index - single control (function) can have multiple key combinations trigering + // it, this index indicates combination function will change/add; Note that preferences + // floater can only display up to 3 options, but data_index can be bigger than that + // @mouse_ind - mouse action (middle click, MB5 etc) + // @key - keyboard key action + // @mask - shift/ctrl/alt flags + // @ignore_mask - Either to expect exact match (ctrl+K will not trigger if ctrl+shift+K + // is active) or ignore not expected masks as long as expected mask is present + // (ctrl+K will be triggered if ctrl+shift+K is active) + bool registerControl(const std::string &control_name, U32 data_index, EMouseClickType mouse_ind, KEY key, MASK mask, bool ignore_mask); //todo: return conflicts? + bool clearControl(const std::string &control_name, U32 data_index); + + LLKeyData getControl(const std::string &control_name, U32 data_index); + bool isControlEmpty(const std::string &control_name); + + // localized string + static std::string getStringFromKeyData(const LLKeyData& keydata); + std::string getControlString(const std::string &control_name, U32 data_index); + + // Load single control, overrides existing one if names match + void loadFromControlSettings(const std::string &name); + // Drops any changes loads controls with ones from 'saved settings' or from xml + void loadFromSettings(ESourceMode load_mode); + + // Saves settings to 'saved settings' or to xml + // If 'temporary' is set, function will save settings to temporary + // file and reload input bindings from temporary file. + // 'temporary' does not support gSavedSettings, those are handled + // by preferences, so 'temporary' is such case will simply not + // reset mHasUnsavedChanges + // + // 'temporary' exists to support ability of live-editing settings in + // preferences: temporary for testing changes 'live' without saving them, + // then hitting ok/cancel and save/discard values permanently. + void saveToSettings(bool apply_temporary = false); + + LLKeyData getDefaultControl(const std::string &control_name, U32 data_index); + // Resets keybinding to default variant from 'saved settings' or xml + void resetToDefault(const std::string &control_name, U32 index); + void resetToDefault(const std::string &control_name); + // resets current mode to defaults + void resetToDefaults(); + + bool empty() { return mControlsMap.empty(); } + void clear(); + + // reloads bindings from last valid user's xml or from default xml + // to keyboard's handler + static void resetKeyboardBindings(); + + bool hasUnsavedChanges() { return mHasUnsavedChanges; } + void setLoadMode(ESourceMode mode) { mLoadMode = mode; } + ESourceMode getLoadMode() { return mLoadMode; } + +private: + void resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts); + void resetToDefaults(ESourceMode mode); + + // at the moment these kind of control is not savable, but takes part in conflict resolution + void registerTemporaryControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask, U32 conflict_mask); + void registerTemporaryControl(const std::string &control_name, U32 conflict_mask = 0); + + typedef std::map<std::string, LLKeyConflict> control_map_t; + void loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination); + bool loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination); + void generatePlaceholders(ESourceMode load_mode); //E.x. non-assignable values + // returns false in case user is trying to reuse control that can't be reassigned + bool removeConflicts(const LLKeyData &data, const U32 &conlict_mask); + + // removes flags and removes temporary file, returns 'true' if file was removed + bool clearUnsavedChanges(); + // return true if there was a file to remove + static bool clearTemporaryFile(); + + control_map_t mControlsMap; + control_map_t mDefaultsMap; + bool mHasUnsavedChanges; + ESourceMode mLoadMode; + + // To implement 'apply immediately'+revert on cancel, class applies changes to temporary file + // but this only works for settings from keybndings files (key_bindings.xml) + // saved setting rely onto external mechanism of preferences floater + bool mUsesTemporaryFile; + static S32 sTemporaryFileUseCount; +}; + + +#endif // LL_LLKEYCONFLICT_H diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp index c58540914e..543d2a087f 100644 --- a/indra/newview/lllandmarklist.cpp +++ b/indra/newview/lllandmarklist.cpp @@ -39,6 +39,11 @@ // Globals LLLandmarkList gLandmarkList; +// number is mostly arbitrary, but it should be below DEFAULT_QUEUE_SIZE pool size, +// which is 4096, to not overfill the pool if user has more than 4K of landmarks +// and it should leave some space for other potential simultaneous asset request +const S32 MAX_SIMULTANEOUS_REQUESTS = 512; + //////////////////////////////////////////////////////////////////////////// // LLLandmarkList @@ -69,6 +74,11 @@ LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t { return NULL; } + if ( mWaitList.find(asset_uuid) != mWaitList.end() ) + { + // Landmark is sheduled for download, but not requested yet + return NULL; + } landmark_requested_list_t::iterator iter = mRequestedList.find(asset_uuid); if (iter != mRequestedList.end()) @@ -86,11 +96,24 @@ LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t mLoadedCallbackMap.insert(vt); } + if (mRequestedList.size() > MAX_SIMULTANEOUS_REQUESTS) + { + // Workarounds for corutines pending list size limit: + // Postpone download till queue is emptier. + // Coroutines have own built in 'pending' list, but unfortunately + // it is too small compared to potential amount of landmarks + // or assets. + mWaitList.insert(asset_uuid); + return NULL; + } + + mRequestedList[asset_uuid] = gFrameTimeSeconds; + + // Note that getAssetData can callback immediately and cleans mRequestedList gAssetStorage->getAssetData(asset_uuid, LLAssetType::AT_LANDMARK, LLLandmarkList::processGetAssetReply, NULL); - mRequestedList[asset_uuid] = gFrameTimeSeconds; } return NULL; } @@ -155,8 +178,36 @@ void LLLandmarkList::processGetAssetReply( } gLandmarkList.mBadList.insert(uuid); + gLandmarkList.mRequestedList.erase(uuid); //mBadList effectively blocks any load, so no point keeping id in requests + // todo: this should clean mLoadedCallbackMap! } + // getAssetData can fire callback immediately, causing + // a recursion which is suboptimal for very large wait list. + // 'scheduling' indicates that we are inside request and + // shouldn't be launching more requests. + static bool scheduling = false; + if (!scheduling && !gLandmarkList.mWaitList.empty()) + { + scheduling = true; + while (!gLandmarkList.mWaitList.empty() && gLandmarkList.mRequestedList.size() < MAX_SIMULTANEOUS_REQUESTS) + { + // start new download from wait list + landmark_uuid_list_t::iterator iter = gLandmarkList.mWaitList.begin(); + LLUUID asset_uuid = *iter; + gLandmarkList.mWaitList.erase(iter); + + // add to mRequestedList before calling getAssetData() + gLandmarkList.mRequestedList[asset_uuid] = gFrameTimeSeconds; + + // Note that getAssetData can callback immediately and cleans mRequestedList + gAssetStorage->getAssetData(asset_uuid, + LLAssetType::AT_LANDMARK, + LLLandmarkList::processGetAssetReply, + NULL); + } + scheduling = false; + } } BOOL LLLandmarkList::isAssetInLoadedCallbackMap(const LLUUID& asset_uuid) diff --git a/indra/newview/lllandmarklist.h b/indra/newview/lllandmarklist.h index 3356f866ce..2e7bd25610 100644 --- a/indra/newview/lllandmarklist.h +++ b/indra/newview/lllandmarklist.h @@ -70,9 +70,10 @@ protected: typedef std::map<LLUUID, LLLandmark*> landmark_list_t; landmark_list_t mList; - typedef std::set<LLUUID> landmark_bad_list_t; - landmark_bad_list_t mBadList; - + typedef std::set<LLUUID> landmark_uuid_list_t; + landmark_uuid_list_t mBadList; + landmark_uuid_list_t mWaitList; + typedef std::map<LLUUID,F32> landmark_requested_list_t; landmark_requested_list_t mRequestedList; diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index fd418afd2c..83195e9e3f 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -296,7 +296,6 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p) LLButton::Params maturity_button = p.maturity_button; mMaturityButton = LLUICtrlFactory::create<LLButton>(maturity_button); addChild(mMaturityButton); - mMaturityButton->setClickedCallback(boost::bind(&LLLocationInputCtrl::onMaturityButtonClicked, this)); LLButton::Params for_sale_button = p.for_sale_button; for_sale_button.tool_tip = LLTrans::getString("LocationCtrlForSaleTooltip"); @@ -663,11 +662,6 @@ void LLLocationInputCtrl::onAgentParcelChange() refresh(); } -void LLLocationInputCtrl::onMaturityButtonClicked() -{ - LLUI::getInstance()->mHelpImpl->showTopic(mMaturityHelpTopic); -} - void LLLocationInputCtrl::onRegionBoundaryCrossed() { createNavMeshStatusListenerForCurrentRegion(); diff --git a/indra/newview/lllocationinputctrl.h b/indra/newview/lllocationinputctrl.h index da71bab6c1..79dbe17982 100644 --- a/indra/newview/lllocationinputctrl.h +++ b/indra/newview/lllocationinputctrl.h @@ -160,7 +160,6 @@ private: void onForSaleButtonClicked(); void onAddLandmarkButtonClicked(); void onAgentParcelChange(); - void onMaturityButtonClicked(); void onRegionBoundaryCrossed(); void onNavMeshStatusChange(const LLPathfindingNavMeshStatus &pNavMeshStatus); // callbacks diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 0c64531783..eebc2486a2 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -133,6 +133,16 @@ void append_to_last_message(std::list<LLSD>& messages, const std::string& line) messages.back()[LL_IM_TEXT] = im_text; } +std::string remove_utf8_bom(const char* buf) +{ + std::string res(buf); + if (res[0] == (char)0xEF && res[1] == (char)0xBB && res[2] == (char)0xBF) + { + res.erase(0, 3); + } + return res; +} + class LLLogChatTimeScanner: public LLSingleton<LLLogChatTimeScanner> { LLSINGLETON(LLLogChatTimeScanner); @@ -271,6 +281,28 @@ std::string LLLogChat::makeLogFileName(std::string filename) return filename; } +//static +void LLLogChat::renameLogFile(const std::string& old_filename, const std::string& new_filename) +{ + std::string new_name = cleanFileName(new_filename); + std::string old_name = cleanFileName(old_filename); + new_name = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS, new_name); + old_name = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS, old_name); + + if (new_name.empty() || old_name.empty()) + { + return; + } + + new_name += '.' + LL_TRANSCRIPT_FILE_EXTENSION; + old_name += '.' + LL_TRANSCRIPT_FILE_EXTENSION; + + if (!LLFile::isfile(new_name) && LLFile::isfile(old_name)) + { + LLFile::rename(old_name, new_name); + } +} + std::string LLLogChat::cleanFileName(std::string filename) { std::string invalidChars = "\"\'\\/?*:.<>|[]{}~"; // Cannot match glob or illegal filename chars @@ -417,7 +449,7 @@ void LLLogChat::loadChatHistory(const std::string& file_name, std::list<LLSD>& m continue; } - std::string line(buffer); + std::string line(remove_utf8_bom(buffer)); //updated 1.23 plain text log format requires a space added before subsequent lines in a multilined message if (' ' == line[0]) @@ -761,8 +793,8 @@ bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id, bool is_group) { std::string file_name; gCacheName->getGroupName(avatar_id, file_name); - file_name = makeLogFileName(file_name); - return isTranscriptFileFound(makeLogFileName(file_name)); + file_name = makeLogFileName(file_name + GROUP_CHAT_SUFFIX); + return isTranscriptFileFound(file_name); } return false; } @@ -805,7 +837,7 @@ bool LLLogChat::isTranscriptFileFound(std::string fullname) { //matching a timestamp boost::match_results<std::string::const_iterator> matches; - if (boost::regex_match(std::string(buffer), matches, TIMESTAMP)) + if (boost::regex_match(remove_utf8_bom(buffer), matches, TIMESTAMP)) { result = true; } @@ -1126,7 +1158,7 @@ void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list<LL firstline = FALSE; continue; } - std::string line(buffer); + std::string line(remove_utf8_bom(buffer)); //updated 1.23 plaint text log format requires a space added before subsequent lines in a multilined message if (' ' == line[0]) diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 8b7fe14e16..c4b61ee716 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -94,6 +94,7 @@ public: static std::string timestamp(bool withdate = false); static std::string makeLogFileName(std::string(filename)); + static void renameLogFile(const std::string& old_filename, const std::string& new_filename); /** *Add functions to get old and non date stamped file names when needed */ diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 0f846c152a..e81d2cc082 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -58,6 +58,7 @@ #include "llevents.h" #include "llappviewer.h" #include "llsdserialize.h" +#include "lltrans.h" #include <boost/scoped_ptr.hpp> #include <sstream> @@ -332,7 +333,7 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) { data["certificate"] = response["certificate"]; } - + if (gViewerWindow) gViewerWindow->setShowProgress(FALSE); @@ -349,13 +350,36 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) // login.cgi is insisting on a required update. We were called with an // event that bundles both the login.cgi 'response' and the // synchronization event from the 'updater'. - std::string required_version = response["message_args"]["VERSION"]; - LL_WARNS("LLLogin") << "Login failed because an update to version " << required_version << " is required." << LL_ENDL; + std::string login_version = response["message_args"]["VERSION"]; + std::string vvm_version = updater["VERSION"]; + std::string relnotes = updater["URL"]; + LL_WARNS("LLLogin") << "Login failed because an update to version " << login_version << " is required." << LL_ENDL; + // vvm_version might be empty because we might not have gotten + // SLVersionChecker's LoginSync handshake. But if it IS populated, it + // should (!) be the same as the version we got from login.cgi. + if ((! vvm_version.empty()) && vvm_version != login_version) + { + LL_WARNS("LLLogin") << "VVM update version " << vvm_version + << " differs from login version " << login_version + << "; presenting VVM version to match release notes URL" + << LL_ENDL; + login_version = vvm_version; + } + if (relnotes.empty() || relnotes.find("://") == std::string::npos) + { + relnotes = LLTrans::getString("RELEASE_NOTES_BASE_URL"); + if (!LLStringUtil::endsWith(relnotes, "/")) + relnotes += "/"; + relnotes += LLURI::escape(login_version) + ".html"; + } if (gViewerWindow) gViewerWindow->setShowProgress(FALSE); - LLSD args(LLSDMap("VERSION", required_version)); + LLSD args; + args["VERSION"] = login_version; + args["URL"] = relnotes; + if (updater.isUndefined()) { // If the updater failed to shake hands, better advise the user to @@ -427,6 +451,7 @@ void LLLoginInstance::handleLoginSuccess(const LLSD& event) LL_INFOS("LLLogin") << "LLLoginInstance::handleLoginSuccess" << LL_ENDL; attemptComplete(); + mRequestData.clear(); } void LLLoginInstance::handleDisconnect(const LLSD& event) diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp index f158aae3d2..c3e39429a2 100644 --- a/indra/newview/llmaniprotate.cpp +++ b/indra/newview/llmaniprotate.cpp @@ -447,6 +447,7 @@ BOOL LLManipRotate::handleMouseDownOnPart( S32 x, S32 y, MASK mask ) } mMouseCur = mMouseDown; + mAgentSelfAtAxis = gAgent.getAtAxis(); // no point checking if avatar was selected, just save the value // Route future Mouse messages here preemptively. (Release on mouse up.) setMouseCapture( TRUE ); @@ -610,6 +611,26 @@ void LLManipRotate::drag( S32 x, S32 y ) else { object->setRotation(new_rot, damped); + LLVOAvatar* avatar = object->asAvatar(); + if (avatar && avatar->isSelf() + && LLSelectMgr::getInstance()->mAllowSelectAvatar + && !object->getParent()) + { + // Normal avatars use object's orienttion, but self uses + // separate LLCoordFrame + // See LVOAvatar::updateOrientation() + if (gAgentCamera.getFocusOnAvatar()) + { + //Don't rotate camera with avatar + gAgentCamera.setFocusOnAvatar(false, false, false); + } + + LLVector3 at_axis = mAgentSelfAtAxis; + at_axis *= mRotation; + at_axis.mV[VZ] = 0.f; + at_axis.normalize(); + gAgent.resetAxes(at_axis); + } rebuild(object); } @@ -717,7 +738,7 @@ void LLManipRotate::drag( S32 x, S32 y ) LLViewerObject *root_object = (cur == NULL) ? NULL : cur->getRootEdit(); if( cur->permModify() && cur->permMove() && !cur->isPermanentEnforced() && ((root_object == NULL) || !root_object->isPermanentEnforced()) && - !cur->isAvatar()) + (!cur->isAvatar() || LLSelectMgr::getInstance()->mAllowSelectAvatar)) { selectNode->mLastRotation = cur->getRotation(); selectNode->mLastPositionLocal = cur->getPosition(); diff --git a/indra/newview/llmaniprotate.h b/indra/newview/llmaniprotate.h index e8f1c24c58..dc36ef796a 100644 --- a/indra/newview/llmaniprotate.h +++ b/indra/newview/llmaniprotate.h @@ -95,6 +95,7 @@ private: LLVector3 mMouseDown; LLVector3 mMouseCur; + LLVector3 mAgentSelfAtAxis; // Own agent uses separate rotation method F32 mRadiusMeters; LLVector3 mCenterToCam; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index c5ced425f6..3e8731dfe6 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -99,7 +99,7 @@ // locking actions. In particular, the following operations // on LLMeshRepository are very averse to any stalls: // * loadMesh -// * getMeshHeader (For structural details, see: +// * search in mMeshHeader (For structural details, see: // http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format) // * notifyLoadedMeshes // * getSkinInfo @@ -1553,7 +1553,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (!zero) { //attempt to parse - if (physicsShapeReceived(mesh_id, buffer, size)) + if (physicsShapeReceived(mesh_id, buffer, size) == MESH_OK) { delete[] buffer; return true; @@ -1647,7 +1647,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c LLMeshRepository::sCacheBytesRead += bytes; ++LLMeshRepository::sCacheReads; file.read(buffer, bytes); - if (headerReceived(mesh_params, buffer, bytes)) + if (headerReceived(mesh_params, buffer, bytes) == MESH_OK) { // Found mesh in VFS cache return true; @@ -1794,7 +1794,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, return retval; } -bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) +EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) { const LLUUID mesh_id = mesh_params.getSculptID(); LLSD header; @@ -1802,30 +1802,39 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat U32 header_size = 0; if (data_size > 0) { - std::string res_str((char*) data, data_size); + std::istringstream stream; + try + { + std::string res_str((char*)data, data_size); - std::string deprecated_header("<? LLSD/Binary ?>"); + std::string deprecated_header("<? LLSD/Binary ?>"); - if (res_str.substr(0, deprecated_header.size()) == deprecated_header) - { - res_str = res_str.substr(deprecated_header.size()+1, data_size); - header_size = deprecated_header.size()+1; - } - data_size = res_str.size(); + if (res_str.substr(0, deprecated_header.size()) == deprecated_header) + { + res_str = res_str.substr(deprecated_header.size() + 1, data_size); + header_size = deprecated_header.size() + 1; + } + data_size = res_str.size(); - std::istringstream stream(res_str); + stream.str(res_str); + } + catch (std::bad_alloc&) + { + // out of memory, we won't be able to process this mesh + return MESH_OUT_OF_MEMORY; + } if (!LLSDSerialize::fromBinary(header, stream, data_size)) { LL_WARNS(LOG_MESH) << "Mesh header parse error. Not a valid mesh asset! ID: " << mesh_id << LL_ENDL; - return false; + return MESH_PARSE_FAILURE; } if (!header.isMap()) { LL_WARNS(LOG_MESH) << "Mesh header is invalid for ID: " << mesh_id << LL_ENDL; - return false; + return MESH_INVALID; } if (header.has("version") && header["version"].asInteger() > MAX_MESH_VERSION) @@ -1871,7 +1880,7 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat } } - return true; + return MESH_OK; } EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) @@ -1902,6 +1911,12 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p { LLMutexLock lock(mMutex); mLoadedQ.push(mesh); + // LLPointer is not thread safe, since we added this pointer into + // threaded list, make sure counter gets decreased inside mutex lock + // and won't affect mLoadedQ processing + volume = NULL; + // might be good idea to turn mesh into pointer to avoid making a copy + mesh.mVolume = NULL; } return MESH_OK; } @@ -1916,18 +1931,25 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat if (data_size > 0) { - std::string res_str((char*) data, data_size); - - std::istringstream stream(res_str); + try + { + std::string res_str((char*)data, data_size); + std::istringstream stream(res_str); - U32 uzip_result = LLUZipHelper::unzip_llsd(skin, stream, data_size); - if (uzip_result != LLUZipHelper::ZR_OK) - { - LL_WARNS(LOG_MESH) << "Mesh skin info parse error. Not a valid mesh asset! ID: " << mesh_id - << " uzip result" << uzip_result - << LL_ENDL; - return false; - } + U32 uzip_result = LLUZipHelper::unzip_llsd(skin, stream, data_size); + if (uzip_result != LLUZipHelper::ZR_OK) + { + LL_WARNS(LOG_MESH) << "Mesh skin info parse error. Not a valid mesh asset! ID: " << mesh_id + << " uzip result" << uzip_result + << LL_ENDL; + return false; + } + } + catch (std::bad_alloc&) + { + LL_WARNS(LOG_MESH) << "Out of memory for mesh ID " << mesh_id << " of size: " << data_size << LL_ENDL; + return false; + } } { @@ -1949,19 +1971,26 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 LLSD decomp; if (data_size > 0) - { - std::string res_str((char*) data, data_size); - - std::istringstream stream(res_str); + { + try + { + std::string res_str((char*)data, data_size); + std::istringstream stream(res_str); - U32 uzip_result = LLUZipHelper::unzip_llsd(decomp, stream, data_size); - if (uzip_result != LLUZipHelper::ZR_OK) - { - LL_WARNS(LOG_MESH) << "Mesh decomposition parse error. Not a valid mesh asset! ID: " << mesh_id - << " uzip result: " << uzip_result - << LL_ENDL; - return false; - } + U32 uzip_result = LLUZipHelper::unzip_llsd(decomp, stream, data_size); + if (uzip_result != LLUZipHelper::ZR_OK) + { + LL_WARNS(LOG_MESH) << "Mesh decomposition parse error. Not a valid mesh asset! ID: " << mesh_id + << " uzip result: " << uzip_result + << LL_ENDL; + return false; + } + } + catch (std::bad_alloc&) + { + LL_WARNS(LOG_MESH) << "Out of memory for mesh ID " << mesh_id << " of size: " << data_size << LL_ENDL; + return false; + } } { @@ -1976,7 +2005,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 return true; } -bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size) +EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { LLSD physics_shape; @@ -1993,8 +2022,19 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH); LLPointer<LLVolume> volume = new LLVolume(volume_params,0); - std::string mesh_string((char*) data, data_size); - std::istringstream stream(mesh_string); + + std::istringstream stream; + try + { + std::string mesh_string((char*)data, data_size); + stream.str(mesh_string); + } + catch (std::bad_alloc&) + { + // out of memory, we won't be able to process this mesh + delete d; + return MESH_OUT_OF_MEMORY; + } if (volume->unpackVolumeFaces(stream, data_size)) { @@ -2033,7 +2073,7 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 LLMutexLock lock(mMutex); mDecompositionQ.push_back(d); } - return true; + return MESH_OK; } LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, @@ -2829,12 +2869,12 @@ void LLMeshRepoThread::notifyLoadedMeshes() mMutex->unlock(); break; } - LoadedMesh mesh = mLoadedQ.front(); + LoadedMesh mesh = mLoadedQ.front(); // make sure nothing else owns volume pointer by this point mLoadedQ.pop(); mMutex->unlock(); update_metrics = true; - if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0) + if (mesh.mVolume->getNumVolumeFaces() > 0) { gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); } @@ -3142,15 +3182,21 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b U8 * data, S32 data_size) { LLUUID mesh_id = mMeshParams.getSculptID(); - bool success = (! MESH_HEADER_PROCESS_FAILED) - && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong - && gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); + bool success = (!MESH_HEADER_PROCESS_FAILED) + && ((data != NULL) == (data_size > 0)); // if we have data but no size or have size but no data, something is wrong; llassert(success); + EMeshProcessingResult res = MESH_UNKNOWN; + if (success) + { + res = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); + success = (res == MESH_OK); + } if (! success) { // *TODO: Get real reason for parse failure here. Might we want to retry? LL_WARNS(LOG_MESH) << "Unable to parse mesh header. ID: " << mesh_id - << ", Unknown reason. Not retrying." + << ", Size: " << data_size + << ", Reason: " << res << " Not retrying." << LL_ENDL; // Can't get the header so none of the LODs will be available @@ -3173,7 +3219,6 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b header_bytes = (S32)gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; header = iter->second; } - gMeshRepo.mThread->mHeaderMutex->unlock(); if (header_bytes > 0 && !header.has("404") @@ -3194,7 +3239,10 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger()); - S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; + // Do not unlock mutex untill we are done with LLSD. + // LLSD is smart and can work like smart pointer, is not thread safe. + gMeshRepo.mThread->mHeaderMutex->unlock(); + S32 bytes = lod_bytes + header_bytes; @@ -3230,6 +3278,8 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b { LL_WARNS(LOG_MESH) << "Trying to cache nonexistent mesh, mesh id: " << mesh_id << LL_ENDL; + gMeshRepo.mThread->mHeaderMutex->unlock(); + // headerReceived() parsed header, but header's data is invalid so none of the LODs will be available LLMutexLock lock(gMeshRepo.mThread->mMutex); for (int i(0); i < 4; ++i) @@ -3430,7 +3480,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 { if ((!MESH_PHYS_SHAPE_PROCESS_FAILED) && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong - && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size)) + && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size) == MESH_OK) { // good fetch from sim, write to VFS for caching LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); @@ -4099,42 +4149,42 @@ void LLMeshRepository::buildHull(const LLVolumeParams& params, S32 detail) bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id) { - LLSD mesh = mThread->getMeshHeader(mesh_id); - if (mesh.has("physics_mesh") && mesh["physics_mesh"].has("size") && (mesh["physics_mesh"]["size"].asInteger() > 0)) - { - return true; - } - - LLModel::Decomposition* decomp = getDecomposition(mesh_id); - if (decomp && !decomp->mHull.empty()) - { - return true; - } + if (mesh_id.isNull()) + { + return false; + } - return false; -} + if (mThread->hasPhysicsShapeInHeader(mesh_id)) + { + return true; + } -LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id) -{ - LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); + LLModel::Decomposition* decomp = getDecomposition(mesh_id); + if (decomp && !decomp->mHull.empty()) + { + return true; + } - return mThread->getMeshHeader(mesh_id); + return false; } -LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) { - static LLSD dummy_ret; - if (mesh_id.notNull()) - { - LLMutexLock lock(mHeaderMutex); - mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); - if (iter != mMeshHeader.end() && mMeshHeaderSize[mesh_id] > 0) - { - return iter->second; - } - } + LLMutexLock lock(mHeaderMutex); + if (mMeshHeaderSize[mesh_id] > 0) + { + mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); + if (iter != mMeshHeader.end()) + { + LLSD &mesh = iter->second; + if (mesh.has("physics_mesh") && mesh["physics_mesh"].has("size") && (mesh["physics_mesh"]["size"].asInteger() > 0)) + { + return true; + } + } + } - return dummy_ret; + return false; } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index bba0c9f2cb..81e49cb1d8 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -57,6 +57,8 @@ typedef enum e_mesh_processing_result_enum MESH_NO_DATA = 1, MESH_OUT_OF_MEMORY, MESH_HTTP_REQUEST_FAILED, + MESH_PARSE_FAILURE, + MESH_INVALID, MESH_UNKNOWN } EMeshProcessingResult; @@ -336,12 +338,12 @@ public: bool fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry = true); bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true); - bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); + EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); EMeshProcessingResult lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size); - bool physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size); - LLSD& getMeshHeader(const LLUUID& mesh_id); + EMeshProcessingResult physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size); + bool hasPhysicsShapeInHeader(const LLUUID& mesh_id); void notifyLoadedMeshes(); S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); @@ -593,9 +595,6 @@ public: bool meshUploadEnabled(); bool meshRezEnabled(); - - - LLSD& getMeshHeader(const LLUUID& mesh_id); void uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures, bool upload_skin, bool upload_joints, bool lock_scale_if_joint_position, diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp new file mode 100644 index 0000000000..a9e80ab5da --- /dev/null +++ b/indra/newview/llmodelpreview.cpp @@ -0,0 +1,3570 @@ +/** + * @file llmodelpreview.cpp + * @brief LLModelPreview class implementation + * + * $LicenseInfo:firstyear=2020&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2020, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llmodelpreview.h" + +#include "llmodelloader.h" +#include "lldaeloader.h" +#include "llfloatermodelpreview.h" + +#include "llagent.h" +#include "llanimationstates.h" +#include "llcallbacklist.h" +#include "lldatapacker.h" +#include "lldrawable.h" +#include "llface.h" +#include "lliconctrl.h" +#include "llmatrix4a.h" +#include "llmeshrepository.h" +#include "llrender.h" +#include "llsdutil_math.h" +#include "llskinningutil.h" +#include "llstring.h" +#include "llsdserialize.h" +#include "lltoolmgr.h" +#include "llui.h" +#include "llvector4a.h" +#include "llviewercamera.h" +#include "llviewercontrol.h" +#include "llviewerobjectlist.h" +#include "llviewernetwork.h" +#include "llviewershadermgr.h" +#include "llviewertexteditor.h" +#include "llviewertexturelist.h" +#include "llvoavatar.h" +#include "pipeline.h" + +// ui controls (from floater) +#include "llbutton.h" +#include "llcombobox.h" +#include "llspinctrl.h" +#include "lltabcontainer.h" +#include "lltextbox.h" + +#include "glod/glod.h" +#include <boost/algorithm/string.hpp> + +bool LLModelPreview::sIgnoreLoadedCallback = false; + +// Extra configurability, to be exposed later in xml (LLModelPreview probably +// should become UI control at some point or get split into preview control) +static const LLColor4 PREVIEW_CANVAS_COL(0.169f, 0.169f, 0.169f, 1.f); +static const LLColor4 PREVIEW_EDGE_COL(0.4f, 0.4f, 0.4f, 1.0); +static const LLColor4 PREVIEW_BASE_COL(1.f, 1.f, 1.f, 1.f); +static const LLColor3 PREVIEW_BRIGHTNESS(0.9f, 0.9f, 0.9f); +static const F32 PREVIEW_EDGE_WIDTH(1.f); +static const LLColor4 PREVIEW_PSYH_EDGE_COL(0.f, 0.25f, 0.5f, 0.25f); +static const LLColor4 PREVIEW_PSYH_FILL_COL(0.f, 0.5f, 1.0f, 0.5f); +static const F32 PREVIEW_PSYH_EDGE_WIDTH(1.f); +static const LLColor4 PREVIEW_DEG_EDGE_COL(1.f, 0.f, 0.f, 1.f); +static const LLColor4 PREVIEW_DEG_FILL_COL(1.f, 0.f, 0.f, 0.5f); +static const F32 PREVIEW_DEG_EDGE_WIDTH(3.f); +static const F32 PREVIEW_DEG_POINT_SIZE(8.f); +static const F32 PREVIEW_ZOOM_LIMIT(10.f); + +const F32 SKIN_WEIGHT_CAMERA_DISTANCE = 16.f; + +BOOL stop_gloderror() +{ + GLuint error = glodGetError(); + + if (error != GLOD_NO_ERROR) + { + LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; + return TRUE; + } + + return FALSE; +} + +LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material) +{ + LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); + + if (texture) + { + if (texture->getDiscardLevel() > -1) + { + gGL.getTexUnit(0)->bind(texture, true); + return texture; + } + } + + return NULL; +} + +std::string stripSuffix(std::string name) +{ + if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1)) + { + return name.substr(0, name.rfind('_')); + } + return name; +} + +std::string getLodSuffix(S32 lod) +{ + std::string suffix; + switch (lod) + { + case LLModel::LOD_IMPOSTOR: suffix = "_LOD0"; break; + case LLModel::LOD_LOW: suffix = "_LOD1"; break; + case LLModel::LOD_MEDIUM: suffix = "_LOD2"; break; + case LLModel::LOD_PHYSICS: suffix = "_PHYS"; break; + case LLModel::LOD_HIGH: break; + } + return suffix; +} + +void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut) +{ + LLModelLoader::scene::iterator base_iter = scene.begin(); + bool found = false; + while (!found && (base_iter != scene.end())) + { + matOut = base_iter->first; + + LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); + while (!found && (base_instance_iter != base_iter->second.end())) + { + LLModelInstance& base_instance = *base_instance_iter++; + LLModel* base_model = base_instance.mModel; + + if (base_model && (base_model->mLabel == name_to_match)) + { + baseModelOut = base_model; + return; + } + } + base_iter++; + } +} + +//----------------------------------------------------------------------------- +// LLModelPreview +//----------------------------------------------------------------------------- + +LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) + : LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex() + , mLodsQuery() + , mLodsWithParsingError() + , mPelvisZOffset(0.0f) + , mLegacyRigFlags(U32_MAX) + , mRigValidJointUpload(false) + , mPhysicsSearchLOD(LLModel::LOD_PHYSICS) + , mResetJoints(false) + , mModelNoErrors(true) + , mLastJointUpdate(false) + , mFirstSkinUpdate(true) + , mHasDegenerate(false) + , mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false)) +{ + mNeedsUpdate = TRUE; + mCameraDistance = 0.f; + mCameraYaw = 0.f; + mCameraPitch = 0.f; + mCameraZoom = 1.f; + mTextureName = 0; + mPreviewLOD = 0; + mModelLoader = NULL; + mMaxTriangleLimit = 0; + mDirty = false; + mGenLOD = false; + mLoading = false; + mLookUpLodFiles = false; + mLoadState = LLModelLoader::STARTING; + mGroup = 0; + mLODFrozen = false; + mBuildShareTolerance = 0.f; + mBuildQueueMode = GLOD_QUEUE_GREEDY; + mBuildBorderMode = GLOD_BORDER_UNLOCK; + mBuildOperator = GLOD_OPERATOR_EDGE_COLLAPSE; + + for (U32 i = 0; i < LLModel::NUM_LODS; ++i) + { + mRequestedTriangleCount[i] = 0; + mRequestedCreaseAngle[i] = -1.f; + mRequestedLoDMode[i] = 0; + mRequestedErrorThreshold[i] = 0.f; + mRequestedBuildOperator[i] = 0; + mRequestedQueueMode[i] = 0; + mRequestedBorderMode[i] = 0; + mRequestedShareTolerance[i] = 0.f; + } + + mViewOption["show_textures"] = false; + + mFMP = fmp; + + mHasPivot = false; + mModelPivot = LLVector3(0.0f, 0.0f, 0.0f); + + glodInit(); + + createPreviewAvatar(); +} + +LLModelPreview::~LLModelPreview() +{ + // glod apparently has internal mem alignment issues that are angering + // the heap-check code in windows, these should be hunted down in that + // TP code, if possible + // + // kernel32.dll!HeapFree() + 0x14 bytes + // msvcr100.dll!free(void * pBlock) Line 51 C + // glod.dll!glodGetGroupParameteriv() + 0x119 bytes + // glod.dll!glodShutdown() + 0x77 bytes + // + //glodShutdown(); + if (mModelLoader) + { + mModelLoader->shutdown(); + } + + if (mPreviewAvatar) + { + mPreviewAvatar->markDead(); + mPreviewAvatar = NULL; + } +} + +U32 LLModelPreview::calcResourceCost() +{ + assert_main_thread(); + + rebuildUploadData(); + + //Upload skin is selected BUT check to see if the joints coming in from the asset were malformed. + if (mFMP && mFMP->childGetValue("upload_skin").asBoolean()) + { + bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); + if (uploadingJointPositions && !isRigValidForJointPositionUpload()) + { + mFMP->childDisable("ok_btn"); + } + } + + std::set<LLModel*> accounted; + U32 num_points = 0; + U32 num_hulls = 0; + + F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f; + mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f; + + if (mFMP && mFMP->childGetValue("upload_joints").asBoolean()) + { + // FIXME if preview avatar ever gets reused, this fake mesh ID stuff will fail. + // see also call to addAttachmentPosOverride. + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); + } + + F32 streaming_cost = 0.f; + F32 physics_cost = 0.f; + for (U32 i = 0; i < mUploadData.size(); ++i) + { + LLModelInstance& instance = mUploadData[i]; + + if (accounted.find(instance.mModel) == accounted.end()) + { + accounted.insert(instance.mModel); + + LLModel::Decomposition& decomp = + instance.mLOD[LLModel::LOD_PHYSICS] ? + instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : + instance.mModel->mPhysics; + + //update instance skin info for each lods pelvisZoffset + for (int j = 0; j<LLModel::NUM_LODS; ++j) + { + if (instance.mLOD[j]) + { + instance.mLOD[j]->mSkinInfo.mPelvisOffset = mPelvisZOffset; + } + } + + std::stringstream ostr; + LLSD ret = LLModel::writeModel(ostr, + instance.mLOD[4], + instance.mLOD[3], + instance.mLOD[2], + instance.mLOD[1], + instance.mLOD[0], + decomp, + mFMP->childGetValue("upload_skin").asBoolean(), + mFMP->childGetValue("upload_joints").asBoolean(), + mFMP->childGetValue("lock_scale_if_joint_position").asBoolean(), + TRUE, + FALSE, + instance.mModel->mSubmodelID); + + num_hulls += decomp.mHull.size(); + for (U32 i = 0; i < decomp.mHull.size(); ++i) + { + num_points += decomp.mHull[i].size(); + } + + //calculate streaming cost + LLMatrix4 transformation = instance.mTransform; + + LLVector3 position = LLVector3(0, 0, 0) * transformation; + + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); + + F32 radius = scale.length()*0.5f*debug_scale; + + LLMeshCostData costs; + if (gMeshRepo.getCostData(ret, costs)) + { + streaming_cost += costs.getRadiusBasedStreamingCost(radius); + } + } + } + + F32 scale = mFMP ? mFMP->childGetValue("import_scale").asReal()*2.f : 2.f; + + mDetailsSignal(mPreviewScale[0] * scale, mPreviewScale[1] * scale, mPreviewScale[2] * scale, streaming_cost, physics_cost); + + updateStatusMessages(); + + return (U32)streaming_cost; +} + +void LLModelPreview::rebuildUploadData() +{ + assert_main_thread(); + + mUploadData.clear(); + mTextureSet.clear(); + + //fill uploaddata instance vectors from scene data + + std::string requested_name = mFMP->getChild<LLUICtrl>("description_form")->getValue().asString(); + + LLSpinCtrl* scale_spinner = mFMP->getChild<LLSpinCtrl>("import_scale"); + + F32 scale = scale_spinner->getValue().asReal(); + + LLMatrix4 scale_mat; + scale_mat.initScale(LLVector3(scale, scale, scale)); + + F32 max_scale = 0.f; + + BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); + U32 load_state = 0; + + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) + { //for each transform in scene + LLMatrix4 mat = iter->first; + + // compute position + LLVector3 position = LLVector3(0, 0, 0) * mat; + + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + + max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length); + + mat *= scale_mat; + + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) + { //for each instance with said transform applied + LLModelInstance instance = *model_iter++; + + LLModel* base_model = instance.mModel; + + if (base_model && !requested_name.empty()) + { + base_model->mRequestedLabel = requested_name; + } + + for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--) + { + LLModel* lod_model = NULL; + if (!legacyMatching) + { + // Fill LOD slots by finding matching meshes by label with name extensions + // in the appropriate scene for each LOD. This fixes all kinds of issues + // where the indexed method below fails in spectacular fashion. + // If you don't take the time to name your LOD and PHYS meshes + // with the name of their corresponding mesh in the HIGH LOD, + // then the indexed method will be attempted below. + + LLMatrix4 transform; + + std::string name_to_match = instance.mLabel; + llassert(!name_to_match.empty()); + + int extensionLOD; + if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty()) + { + extensionLOD = i; + } + else + { + //Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for + extensionLOD = mPhysicsSearchLOD; + } + + std::string toAdd = getLodSuffix(extensionLOD); + + if (name_to_match.find(toAdd) == -1) + { + name_to_match += toAdd; + } + + FindModel(mScene[i], name_to_match, lod_model, transform); + + if (!lod_model && i != LLModel::LOD_PHYSICS) + { + if (mImporterDebug) + { + std::ostringstream out; + out << "Search of" << name_to_match; + out << " in LOD" << i; + out << " list failed. Searching for alternative among LOD lists."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + + int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i; + while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model) + { + std::string name_to_match = instance.mLabel; + llassert(!name_to_match.empty()); + + std::string toAdd = getLodSuffix(searchLOD); + + if (name_to_match.find(toAdd) == -1) + { + name_to_match += toAdd; + } + + // See if we can find an appropriately named model in LOD 'searchLOD' + // + FindModel(mScene[searchLOD], name_to_match, lod_model, transform); + searchLOD++; + } + } + } + else + { + // Use old method of index-based association + U32 idx = 0; + for (idx = 0; idx < mBaseModel.size(); ++idx) + { + // find reference instance for this model + if (mBaseModel[idx] == base_model) + { + if (mImporterDebug) + { + std::ostringstream out; + out << "Attempting to use model index " << idx; + out << " for LOD" << i; + out << " of " << instance.mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + break; + } + } + + // If the model list for the current LOD includes that index... + // + if (mModel[i].size() > idx) + { + // Assign that index from the model list for our LOD as the LOD model for this instance + // + lod_model = mModel[i][idx]; + if (mImporterDebug) + { + std::ostringstream out; + out << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } + else if (mImporterDebug) + { + std::ostringstream out; + out << "List of models does not include index " << idx; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } + + if (lod_model) + { + if (mImporterDebug) + { + std::ostringstream out; + if (i == LLModel::LOD_PHYSICS) + { + out << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel; + } + else + { + out << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel; + } + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + instance.mLOD[i] = lod_model; + } + else + { + if (i < LLModel::LOD_HIGH && !lodsReady()) + { + // assign a placeholder from previous LOD until lod generation is complete. + // Note: we might need to assign it regardless of conditions like named search does, to prevent crashes. + instance.mLOD[i] = instance.mLOD[i + 1]; + } + if (mImporterDebug) + { + std::ostringstream out; + out << "List of models does not include " << instance.mLabel; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + } + } + + LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH]; + if (!high_lod_model) + { + LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has no High Lod (LOD3).", true); + load_state = LLModelLoader::ERROR_MATERIALS; + mFMP->childDisable("calculate_btn"); + } + else + { + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + int refFaceCnt = 0; + int modelFaceCnt = 0; + llassert(instance.mLOD[i]); + if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt)) + { + LLFloaterModelPreview::addStringToLog("Model " + instance.mLabel + " has mismatching materials between lods." , true); + load_state = LLModelLoader::ERROR_MATERIALS; + mFMP->childDisable("calculate_btn"); + } + } + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean(); + if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0) + { + LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix); + LLQuaternion identity; + if (!bind_rot.isEqualEps(identity, 0.01)) + { + // Bind shape matrix is not in standard X-forward orientation. + // Might be good idea to only show this once. It can be spammy. + std::ostringstream out; + out << "non-identity bind shape rot. mat is "; + out << high_lod_model->mSkinInfo.mBindShapeMatrix; + out << " bind_rot "; + out << bind_rot; + LL_WARNS() << out.str() << LL_ENDL; + + LLFloaterModelPreview::addStringToLog(out, getLoadState() != LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION); + load_state = LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION; + } + } + } + instance.mTransform = mat; + mUploadData.push_back(instance); + } + } + + for (U32 lod = 0; lod < LLModel::NUM_LODS - 1; lod++) + { + // Search for models that are not included into upload data + // If we found any, that means something we loaded is not a sub-model. + for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind) + { + bool found_model = false; + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + if (instance.mLOD[lod] == mModel[lod][model_ind]) + { + found_model = true; + break; + } + } + if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID) + { + if (mImporterDebug) + { + std::ostringstream out; + out << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models."; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + } + load_state = LLModelLoader::ERROR_MATERIALS; + mFMP->childDisable("calculate_btn"); + } + } + } + + // Update state for notifications + if (load_state > 0) + { + // encountered issues + setLoadState(load_state); + } + else if (getLoadState() == LLModelLoader::ERROR_MATERIALS + || getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION) + { + // This is only valid for these two error types because they are + // only used inside rebuildUploadData() and updateStatusMessages() + // updateStatusMessages() is called after rebuildUploadData() + setLoadState(LLModelLoader::DONE); + } + + F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE - 0.1f) / max_scale; + + F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]); + max_axis = llmax(max_axis, mPreviewScale.mV[2]); + max_axis *= 2.f; + + //clamp scale so that total imported model bounding box is smaller than 240m on a side + max_import_scale = llmin(max_import_scale, 240.f / max_axis); + + scale_spinner->setMaxValue(max_import_scale); + + if (max_import_scale < scale) + { + scale_spinner->setValue(max_import_scale); + } + +} + +void LLModelPreview::saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) +{ + if (!mLODFile[LLModel::LOD_HIGH].empty()) + { + std::string filename = mLODFile[LLModel::LOD_HIGH]; + std::string slm_filename; + + if (LLModelLoader::getSLMFilename(filename, slm_filename)) + { + saveUploadData(slm_filename, save_skinweights, save_joint_positions, lock_scale_if_joint_position); + } + } +} + +void LLModelPreview::saveUploadData(const std::string& filename, + bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position) +{ + + std::set<LLPointer<LLModel> > meshes; + std::map<LLModel*, std::string> mesh_binary; + + LLModel::hull empty_hull; + + LLSD data; + + data["version"] = SLM_SUPPORTED_VERSION; + if (!mBaseModel.empty()) + { + data["name"] = mBaseModel[0]->getName(); + } + + S32 mesh_id = 0; + + //build list of unique models and initialize local id + for (U32 i = 0; i < mUploadData.size(); ++i) + { + LLModelInstance& instance = mUploadData[i]; + + if (meshes.find(instance.mModel) == meshes.end()) + { + instance.mModel->mLocalID = mesh_id++; + meshes.insert(instance.mModel); + + std::stringstream str; + LLModel::Decomposition& decomp = + instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? + instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : + instance.mModel->mPhysics; + + LLModel::writeModel(str, + instance.mLOD[LLModel::LOD_PHYSICS], + instance.mLOD[LLModel::LOD_HIGH], + instance.mLOD[LLModel::LOD_MEDIUM], + instance.mLOD[LLModel::LOD_LOW], + instance.mLOD[LLModel::LOD_IMPOSTOR], + decomp, + save_skinweights, + save_joint_positions, + lock_scale_if_joint_position, + FALSE, TRUE, instance.mModel->mSubmodelID); + + data["mesh"][instance.mModel->mLocalID] = str.str(); + } + + data["instance"][i] = instance.asLLSD(); + } + + llofstream out(filename.c_str(), std::ios_base::out | std::ios_base::binary); + LLSDSerialize::toBinary(data, out); + out.flush(); + out.close(); +} + +void LLModelPreview::clearModel(S32 lod) +{ + if (lod < 0 || lod > LLModel::LOD_PHYSICS) + { + return; + } + + mVertexBuffer[lod].clear(); + mModel[lod].clear(); + mScene[lod].clear(); +} + +void LLModelPreview::getJointAliases(JointMap& joint_map) +{ + // Get all standard skeleton joints from the preview avatar. + LLVOAvatar *av = getPreviewAvatar(); + + //Joint names and aliases come from avatar_skeleton.xml + + joint_map = av->getJointAliases(); + + std::vector<std::string> cv_names, attach_names; + av->getSortedJointNames(1, cv_names); + av->getSortedJointNames(2, attach_names); + for (std::vector<std::string>::iterator it = cv_names.begin(); it != cv_names.end(); ++it) + { + joint_map[*it] = *it; + } + for (std::vector<std::string>::iterator it = attach_names.begin(); it != attach_names.end(); ++it) + { + joint_map[*it] = *it; + } +} + +void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable_slm) +{ + assert_main_thread(); + + LLMutexLock lock(this); + + if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::NUM_LODS - 1) + { + std::ostringstream out; + out << "Invalid level of detail: "; + out << lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS); + return; + } + + // This triggers if you bring up the file picker and then hit CANCEL. + // Just use the previous model (if any) and ignore that you brought up + // the file picker. + + if (filename.empty()) + { + if (mBaseModel.empty()) + { + // this is the initial file picking. Close the whole floater + // if we don't have a base model to show for high LOD. + mFMP->closeFloater(false); + } + mLoading = false; + return; + } + + if (mModelLoader) + { + LL_WARNS() << "Incompleted model load operation pending." << LL_ENDL; + return; + } + + mLODFile[lod] = filename; + + if (lod == LLModel::LOD_HIGH) + { + clearGLODGroup(); + } + + std::map<std::string, std::string> joint_alias_map; + getJointAliases(joint_alias_map); + + mModelLoader = new LLDAELoader( + filename, + lod, + &LLModelPreview::loadedCallback, + &LLModelPreview::lookupJointByName, + &LLModelPreview::loadTextures, + &LLModelPreview::stateChangedCallback, + this, + mJointTransformMap, + mJointsFromNode, + joint_alias_map, + LLSkinningUtil::getMaxJointCount(), + gSavedSettings.getU32("ImporterModelLimit"), + gSavedSettings.getBOOL("ImporterPreprocessDAE")); + + if (force_disable_slm) + { + mModelLoader->mTrySLM = false; + } + else + { + // For MAINT-6647, we have set force_disable_slm to true, + // which means this code path will never be taken. Trying to + // re-use SLM files has never worked properly; in particular, + // it tends to force the UI into strange checkbox options + // which cannot be altered. + + //only try to load from slm if viewer is configured to do so and this is the + //initial model load (not an LoD or physics shape) + mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty(); + } + mModelLoader->start(); + + mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); + + setPreviewLOD(lod); + + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mFMP->childDisable("ok_btn"); + mFMP->childDisable("calculate_btn"); + } + + if (lod == mPreviewLOD) + { + mFMP->childSetValue("lod_file_" + lod_name[lod], mLODFile[lod]); + } + else if (lod == LLModel::LOD_PHYSICS) + { + mFMP->childSetValue("physics_file", mLODFile[lod]); + } + + mFMP->openFloater(); +} + +void LLModelPreview::setPhysicsFromLOD(S32 lod) +{ + assert_main_thread(); + + if (lod >= 0 && lod <= 3) + { + mPhysicsSearchLOD = lod; + mModel[LLModel::LOD_PHYSICS] = mModel[lod]; + mScene[LLModel::LOD_PHYSICS] = mScene[lod]; + mLODFile[LLModel::LOD_PHYSICS].clear(); + mFMP->childSetValue("physics_file", mLODFile[LLModel::LOD_PHYSICS]); + mVertexBuffer[LLModel::LOD_PHYSICS].clear(); + rebuildUploadData(); + refresh(); + updateStatusMessages(); + } +} + +void LLModelPreview::clearIncompatible(S32 lod) +{ + //Don't discard models if specified model is the physic rep + if (lod == LLModel::LOD_PHYSICS) + { + return; + } + + // at this point we don't care about sub-models, + // different amount of sub-models means face count mismatch, not incompatibility + U32 lod_size = countRootModels(mModel[lod]); + for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) + { //clear out any entries that aren't compatible with this model + if (i != lod) + { + if (countRootModels(mModel[i]) != lod_size) + { + mModel[i].clear(); + mScene[i].clear(); + mVertexBuffer[i].clear(); + + if (i == LLModel::LOD_HIGH) + { + mBaseModel = mModel[lod]; + clearGLODGroup(); + mBaseScene = mScene[lod]; + mVertexBuffer[5].clear(); + } + } + } + } +} + +void LLModelPreview::clearGLODGroup() +{ + if (mGroup) + { + for (std::map<LLPointer<LLModel>, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) + { + glodDeleteObject(iter->second); + stop_gloderror(); + } + mObject.clear(); + + glodDeleteGroup(mGroup); + stop_gloderror(); + mGroup = 0; + } +} + +void LLModelPreview::loadModelCallback(S32 loaded_lod) +{ + assert_main_thread(); + + LLMutexLock lock(this); + if (!mModelLoader) + { + mLoading = false; + return; + } + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mLoading = false; + mModelLoader = NULL; + mLodsWithParsingError.push_back(loaded_lod); + return; + } + + mLodsWithParsingError.erase(std::remove(mLodsWithParsingError.begin(), mLodsWithParsingError.end(), loaded_lod), mLodsWithParsingError.end()); + if (mLodsWithParsingError.empty()) + { + mFMP->childEnable("calculate_btn"); + } + + // Copy determinations about rig so UI will reflect them + // + setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload()); + setLegacyRigFlags(mModelLoader->getLegacyRigFlags()); + + mModelLoader->loadTextures(); + + if (loaded_lod == -1) + { //populate all LoDs from model loader scene + mBaseModel.clear(); + mBaseScene.clear(); + + bool skin_weights = false; + bool joint_overrides = false; + bool lock_scale_if_joint_position = false; + + for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) + { //for each LoD + + //clear scene and model info + mScene[lod].clear(); + mModel[lod].clear(); + mVertexBuffer[lod].clear(); + + if (mModelLoader->mScene.begin()->second[0].mLOD[lod].notNull()) + { //if this LoD exists in the loaded scene + + //copy scene to current LoD + mScene[lod] = mModelLoader->mScene; + + //touch up copied scene to look like current LoD + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + LLModelLoader::model_instance_list& list = iter->second; + + for (LLModelLoader::model_instance_list::iterator list_iter = list.begin(); list_iter != list.end(); ++list_iter) + { + //override displayed model with current LoD + list_iter->mModel = list_iter->mLOD[lod]; + + if (!list_iter->mModel) + { + continue; + } + + //add current model to current LoD's model list (LLModel::mLocalID makes a good vector index) + S32 idx = list_iter->mModel->mLocalID; + + if (mModel[lod].size() <= idx) + { //stretch model list to fit model at given index + mModel[lod].resize(idx + 1); + } + + mModel[lod][idx] = list_iter->mModel; + if (!list_iter->mModel->mSkinWeights.empty()) + { + skin_weights = true; + + if (!list_iter->mModel->mSkinInfo.mAlternateBindMatrix.empty()) + { + joint_overrides = true; + } + if (list_iter->mModel->mSkinInfo.mLockScaleIfJointPosition) + { + lock_scale_if_joint_position = true; + } + } + } + } + } + } + + if (mFMP) + { + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + + if (skin_weights) + { //enable uploading/previewing of skin weights if present in .slm file + fmp->enableViewOption("show_skin_weight"); + mViewOption["show_skin_weight"] = true; + fmp->childSetValue("upload_skin", true); + } + + if (joint_overrides) + { + fmp->enableViewOption("show_joint_overrides"); + mViewOption["show_joint_overrides"] = true; + fmp->enableViewOption("show_joint_positions"); + mViewOption["show_joint_positions"] = true; + fmp->childSetValue("upload_joints", true); + } + else + { + fmp->clearAvatarTab(); + } + + if (lock_scale_if_joint_position) + { + fmp->enableViewOption("lock_scale_if_joint_position"); + mViewOption["lock_scale_if_joint_position"] = true; + fmp->childSetValue("lock_scale_if_joint_position", true); + } + } + + //copy high lod to base scene for LoD generation + mBaseScene = mScene[LLModel::LOD_HIGH]; + mBaseModel = mModel[LLModel::LOD_HIGH]; + + mDirty = true; + resetPreviewTarget(); + } + else + { //only replace given LoD + mModel[loaded_lod] = mModelLoader->mModelList; + mScene[loaded_lod] = mModelLoader->mScene; + mVertexBuffer[loaded_lod].clear(); + + setPreviewLOD(loaded_lod); + + if (loaded_lod == LLModel::LOD_HIGH) + { //save a copy of the highest LOD for automatic LOD manipulation + if (mBaseModel.empty()) + { //first time we've loaded a model, auto-gen LoD + mGenLOD = true; + } + + mBaseModel = mModel[loaded_lod]; + clearGLODGroup(); + + mBaseScene = mScene[loaded_lod]; + mVertexBuffer[5].clear(); + } + else + { + BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); + if (!legacyMatching) + { + if (!mBaseModel.empty()) + { + BOOL name_based = FALSE; + BOOL has_submodels = FALSE; + for (U32 idx = 0; idx < mBaseModel.size(); ++idx) + { + if (mBaseModel[idx]->mSubmodelID) + { // don't do index-based renaming when the base model has submodels + has_submodels = TRUE; + if (mImporterDebug) + { + std::ostringstream out; + out << "High LOD has submodels"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + break; + } + } + + for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx) + { + std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); + + LLModel* found_model = NULL; + LLMatrix4 transform; + FindModel(mBaseScene, loaded_name, found_model, transform); + if (found_model) + { // don't rename correctly named models (even if they are placed in a wrong order) + name_based = TRUE; + } + + if (mModel[loaded_lod][idx]->mSubmodelID) + { // don't rename the models when loaded LOD model has submodels + has_submodels = TRUE; + } + } + + if (mImporterDebug) + { + std::ostringstream out; + out << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found"; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + + if (!name_based && !has_submodels) + { // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601) + // this actually works like "ImporterLegacyMatching" for this particular LOD + for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx) + { + std::string name = mBaseModel[idx]->mLabel; + std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); + + if (loaded_name != name) + { + name += getLodSuffix(loaded_lod); + + if (mImporterDebug) + { + std::ostringstream out; + out << "Loded model name " << mModel[loaded_lod][idx]->mLabel; + out << " for LOD " << loaded_lod; + out << " doesn't match the base model. Renaming to " << name; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + } + + mModel[loaded_lod][idx]->mLabel = name; + } + } + } + } + } + } + + clearIncompatible(loaded_lod); + + mDirty = true; + + if (loaded_lod == LLModel::LOD_HIGH) + { + resetPreviewTarget(); + } + } + + mLoading = false; + if (mFMP) + { + if (!mBaseModel.empty()) + { + const std::string& model_name = mBaseModel[0]->getName(); + LLLineEditor* description_form = mFMP->getChild<LLLineEditor>("description_form"); + if (description_form->getText().empty()) + { + description_form->setText(model_name); + } + // Add info to log that loading is complete (purpose: separator between loading and other logs) + LLSD args; + args["MODEL_NAME"] = model_name; // Teoretically shouldn't be empty, but might be better idea to add filename here + LLFloaterModelPreview::addStringToLog("ModelLoaded", args, false, loaded_lod); + } + } + refresh(); + + mModelLoadedSignal(); + + mModelLoader = NULL; +} + +void LLModelPreview::resetPreviewTarget() +{ + if (mModelLoader) + { + mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; + mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; + } + + setPreviewTarget(mPreviewScale.magVec()*10.f); +} + +void LLModelPreview::generateNormals() +{ + assert_main_thread(); + + S32 which_lod = mPreviewLOD; + + if (which_lod > 4 || which_lod < 0 || + mModel[which_lod].empty()) + { + return; + } + + F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); + + mRequestedCreaseAngle[which_lod] = angle_cutoff; + + angle_cutoff *= DEG_TO_RAD; + + if (which_lod == 3 && !mBaseModel.empty()) + { + if (mBaseModelFacesCopy.empty()) + { + mBaseModelFacesCopy.reserve(mBaseModel.size()); + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) + { + v_LLVolumeFace_t faces; + (*it)->copyFacesTo(faces); + mBaseModelFacesCopy.push_back(faces); + } + } + + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it) + { + (*it)->generateNormals(angle_cutoff); + } + + mVertexBuffer[5].clear(); + } + + bool perform_copy = mModelFacesCopy[which_lod].empty(); + if (perform_copy) { + mModelFacesCopy[which_lod].reserve(mModel[which_lod].size()); + } + + for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it) + { + if (perform_copy) + { + v_LLVolumeFace_t faces; + (*it)->copyFacesTo(faces); + mModelFacesCopy[which_lod].push_back(faces); + } + + (*it)->generateNormals(angle_cutoff); + } + + mVertexBuffer[which_lod].clear(); + refresh(); + updateStatusMessages(); +} + +void LLModelPreview::restoreNormals() +{ + S32 which_lod = mPreviewLOD; + + if (which_lod > 4 || which_lod < 0 || + mModel[which_lod].empty()) + { + return; + } + + if (!mBaseModelFacesCopy.empty()) + { + llassert(mBaseModelFacesCopy.size() == mBaseModel.size()); + + vv_LLVolumeFace_t::const_iterator itF = mBaseModelFacesCopy.begin(); + for (LLModelLoader::model_list::iterator it = mBaseModel.begin(), itE = mBaseModel.end(); it != itE; ++it, ++itF) + { + (*it)->copyFacesFrom((*itF)); + } + + mBaseModelFacesCopy.clear(); + } + + if (!mModelFacesCopy[which_lod].empty()) + { + vv_LLVolumeFace_t::const_iterator itF = mModelFacesCopy[which_lod].begin(); + for (LLModelLoader::model_list::iterator it = mModel[which_lod].begin(), itE = mModel[which_lod].end(); it != itE; ++it, ++itF) + { + (*it)->copyFacesFrom((*itF)); + } + + mModelFacesCopy[which_lod].clear(); + } + + mVertexBuffer[which_lod].clear(); + refresh(); + updateStatusMessages(); +} + +void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_limit) +{ + // Allow LoD from -1 to LLModel::LOD_PHYSICS + if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1) + { + std::ostringstream out; + out << "Invalid level of detail: " << which_lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + assert(which_lod >= -1 && which_lod < LLModel::NUM_LODS); + return; + } + + if (mBaseModel.empty()) + { + return; + } + + LLVertexBuffer::unbind(); + + bool no_ff = LLGLSLShader::sNoFixedFunction; + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + LLGLSLShader::sNoFixedFunction = false; + + if (shader) + { + shader->unbind(); + } + + stop_gloderror(); + static U32 cur_name = 1; + + S32 limit = -1; + + U32 triangle_count = 0; + + U32 instanced_triangle_count = 0; + + //get the triangle count for the whole scene + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(), endIter = mBaseScene.end(); iter != endIter; ++iter) + { + for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) + { + LLModel* mdl = instance->mModel; + if (mdl) + { + instanced_triangle_count += mdl->getNumTriangles(); + } + } + } + + //get the triangle count for the non-instanced set of models + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + triangle_count += mBaseModel[i]->getNumTriangles(); + } + + //get ratio of uninstanced triangles to instanced triangles + F32 triangle_ratio = (F32)triangle_count / (F32)instanced_triangle_count; + + U32 base_triangle_count = triangle_count; + + U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + U32 lod_mode = 0; + + F32 lod_error_threshold = 0; + + // The LoD should be in range from Lowest to High + if (which_lod > -1 && which_lod < NUM_LOD) + { + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode_" + lod_name[which_lod]); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } + + lod_error_threshold = mFMP->childGetValue("lod_error_threshold_" + lod_name[which_lod]).asReal(); + } + + if (which_lod != -1) + { + mRequestedLoDMode[which_lod] = lod_mode; + } + + if (lod_mode == 0) + { + lod_mode = GLOD_TRIANGLE_BUDGET; + + // The LoD should be in range from Lowest to High + if (which_lod > -1 && which_lod < NUM_LOD) + { + limit = mFMP->childGetValue("lod_triangle_limit_" + lod_name[which_lod]).asInteger(); + //convert from "scene wide" to "non-instanced" triangle limit + limit = (S32)((F32)limit*triangle_ratio); + } + } + else + { + lod_mode = GLOD_ERROR_THRESHOLD; + } + + bool object_dirty = false; + + if (mGroup == 0) + { + object_dirty = true; + mGroup = cur_name++; + glodNewGroup(mGroup); + } + + if (object_dirty) + { + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { //build GLOD objects for each model in base model list + LLModel* mdl = *iter; + + if (mObject[mdl] != 0) + { + glodDeleteObject(mObject[mdl]); + } + + mObject[mdl] = cur_name++; + + glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); + stop_gloderror(); + + if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) + { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation + mVertexBuffer[5].clear(); + } + + if (mVertexBuffer[5].empty()) + { + genBuffers(5, false); + } + + U32 tri_count = 0; + for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) + { + LLVertexBuffer* buff = mVertexBuffer[5][mdl][i]; + buff->setBuffer(type_mask & buff->getTypeMask()); + + U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); + if (num_indices > 2) + { + glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, (U8*)mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); + } + tri_count += num_indices / 3; + stop_gloderror(); + } + + glodBuildObject(mObject[mdl]); + stop_gloderror(); + } + } + + + S32 start = LLModel::LOD_HIGH; + S32 end = 0; + + if (which_lod != -1) + { + start = end = which_lod; + } + + mMaxTriangleLimit = base_triangle_count; + + for (S32 lod = start; lod >= end; --lod) + { + if (which_lod == -1) + { + if (lod < start) + { + triangle_count /= decimation; + } + } + else + { + if (enforce_tri_limit) + { + triangle_count = limit; + } + else + { + for (S32 j = LLModel::LOD_HIGH; j>which_lod; --j) + { + triangle_count /= decimation; + } + } + } + + mModel[lod].clear(); + mModel[lod].resize(mBaseModel.size()); + mVertexBuffer[lod].clear(); + + U32 actual_tris = 0; + U32 actual_verts = 0; + U32 submeshes = 0; + + mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio); + mRequestedErrorThreshold[lod] = lod_error_threshold; + + glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); + stop_gloderror(); + + glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); + stop_gloderror(); + + glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); + stop_gloderror(); + + if (lod_mode != GLOD_TRIANGLE_BUDGET) + { + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, 0); + } + else + { + //SH-632: always add 1 to desired amount to avoid decimating below desired amount + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count + 1); + } + + stop_gloderror(); + glodAdaptGroup(mGroup); + stop_gloderror(); + + for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) + { + LLModel* base = mBaseModel[mdl_idx]; + + GLint patch_count = 0; + glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); + stop_gloderror(); + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); + + std::string name = base->mLabel + getLodSuffix(lod); + + mModel[lod][mdl_idx]->mLabel = name; + mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; + + GLint* sizes = new GLint[patch_count * 2]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); + stop_gloderror(); + + GLint* names = new GLint[patch_count]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); + stop_gloderror(); + + mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); + + LLModel* target_model = mModel[lod][mdl_idx]; + + for (GLint i = 0; i < patch_count; ++i) + { + type_mask = mVertexBuffer[5][base][i]->getTypeMask(); + + LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0); + + if (sizes[i * 2 + 1] > 0 && sizes[i * 2] > 0) + { + if (!buff->allocateBuffer(sizes[i * 2 + 1], sizes[i * 2], true)) + { + // Todo: find a way to stop preview in this case instead of crashing + LL_ERRS() << "Failed buffer allocation during preview LOD generation." + << " Vertices: " << sizes[i * 2 + 1] + << " Indices: " << sizes[i * 2] << LL_ENDL; + } + buff->setBuffer(type_mask); + glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, (U8*)buff->getIndicesPointer()); + stop_gloderror(); + } + else + { + // This face was eliminated or we failed to allocate buffer, + // attempt to create a dummy triangle (one vertex, 3 indices, all 0) + buff->allocateBuffer(1, 3, true); + memset((U8*)buff->getMappedData(), 0, buff->getSize()); + memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); + } + + buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); + + LLStrider<LLVector3> pos; + LLStrider<LLVector3> norm; + LLStrider<LLVector2> tc; + LLStrider<U16> index; + + buff->getVertexStrider(pos); + if (type_mask & LLVertexBuffer::MAP_NORMAL) + { + buff->getNormalStrider(norm); + } + if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) + { + buff->getTexCoord0Strider(tc); + } + + buff->getIndexStrider(index); + + target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + actual_tris += buff->getNumIndices() / 3; + actual_verts += buff->getNumVerts(); + ++submeshes; + + if (!validate_face(target_model->getVolumeFace(names[i]))) + { + LL_ERRS() << "Invalid face generated during LOD generation." << LL_ENDL; + } + } + + //blind copy skin weights and just take closest skin weight to point on + //decimated mesh for now (auto-generating LODs with skin weights is still a bit + //of an open problem). + target_model->mPosition = base->mPosition; + target_model->mSkinWeights = base->mSkinWeights; + target_model->mSkinInfo = base->mSkinInfo; + //copy material list + target_model->mMaterialList = base->mMaterialList; + + if (!validate_model(target_model)) + { + LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL; + } + + delete[] sizes; + delete[] names; + } + + //rebuild scene based on mBaseScene + mScene[lod].clear(); + mScene[lod] = mBaseScene; + + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + LLModel* mdl = mBaseModel[i]; + LLModel* target = mModel[lod][i]; + if (target) + { + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + for (U32 j = 0; j < iter->second.size(); ++j) + { + if (iter->second[j].mModel == mdl) + { + iter->second[j].mModel = target; + } + } + } + } + } + } + + mResourceCost = calcResourceCost(); + + LLVertexBuffer::unbind(); + LLGLSLShader::sNoFixedFunction = no_ff; + if (shader) + { + shader->bind(); + } +} + +void LLModelPreview::updateStatusMessages() +{ + // bit mask values for physics errors. used to prevent overwrite of single line status + // TODO: use this to provied multiline status + enum PhysicsError + { + NONE = 0, + NOHAVOK = 1, + DEGENERATE = 2, + TOOMANYHULLS = 4, + TOOMANYVERTSINHULL = 8 + }; + + assert_main_thread(); + + U32 has_physics_error{ PhysicsError::NONE }; // physics error bitmap + //triangle/vertex/submesh count for each mesh asset for each lod + std::vector<S32> tris[LLModel::NUM_LODS]; + std::vector<S32> verts[LLModel::NUM_LODS]; + std::vector<S32> submeshes[LLModel::NUM_LODS]; + + //total triangle/vertex/submesh count for each lod + S32 total_tris[LLModel::NUM_LODS]; + S32 total_verts[LLModel::NUM_LODS]; + S32 total_submeshes[LLModel::NUM_LODS]; + + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + total_tris[i] = 0; + total_verts[i] = 0; + total_submeshes[i] = 0; + } + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH]; + if (!model_high_lod) + { + setLoadState(LLModelLoader::ERROR_MATERIALS); + mFMP->childDisable("calculate_btn"); + continue; + } + + for (U32 i = 0; i < LLModel::NUM_LODS - 1; i++) + { + LLModel* lod_model = instance.mLOD[i]; + if (!lod_model) + { + setLoadState(LLModelLoader::ERROR_MATERIALS); + mFMP->childDisable("calculate_btn"); + } + else + { + //for each model in the lod + S32 cur_tris = 0; + S32 cur_verts = 0; + S32 cur_submeshes = lod_model->getNumVolumeFaces(); + + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = lod_model->getVolumeFace(j); + cur_tris += face.mNumIndices / 3; + cur_verts += face.mNumVertices; + } + + std::string instance_name = instance.mLabel; + + if (mImporterDebug) + { + // Useful for debugging generalized complaints below about total submeshes which don't have enough + // context to address exactly what needs to be fixed to move towards compliance with the rules. + // + std::ostringstream out; + out << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + + out.str(""); + out << "Instance " << lod_model->mLabel << " LOD " << i << " Tris: " << cur_tris; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + + out.str(""); + out << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes; + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + + out.str(""); + LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin(); + while (mat_iter != lod_model->mMaterialList.end()) + { + out << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter); + LL_INFOS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + out.str(""); + mat_iter++; + } + } + + //add this model to the lod total + total_tris[i] += cur_tris; + total_verts[i] += cur_verts; + total_submeshes[i] += cur_submeshes; + + //store this model's counts to asset data + tris[i].push_back(cur_tris); + verts[i].push_back(cur_verts); + submeshes[i].push_back(cur_submeshes); + } + } + } + + if (mMaxTriangleLimit == 0) + { + mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; + } + + mHasDegenerate = false; + {//check for degenerate triangles in physics mesh + U32 lod = LLModel::LOD_PHYSICS; + const LLVector4a scale(0.5f); + for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i) + { //for each model in the lod + if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty()) + { //no decomp exists + S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); + for (S32 j = 0; j < cur_submeshes && !mHasDegenerate; ++j) + { //for each submesh (face), add triangles and vertices to current total + LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); + for (S32 k = 0; (k < face.mNumIndices) && !mHasDegenerate;) + { + U16 index_a = face.mIndices[k + 0]; + U16 index_b = face.mIndices[k + 1]; + U16 index_c = face.mIndices[k + 2]; + + if (index_c == 0 && index_b == 0 && index_a == 0) // test in reverse as 3rd index is less likely to be 0 in a normal case + { + LL_DEBUGS("MeshValidation") << "Empty placeholder triangle (3 identical index 0 verts) ignored" << LL_ENDL; + } + else + { + LLVector4a v1; v1.setMul(face.mPositions[index_a], scale); + LLVector4a v2; v2.setMul(face.mPositions[index_b], scale); + LLVector4a v3; v3.setMul(face.mPositions[index_c], scale); + if (ll_is_degenerate(v1, v2, v3)) + { + mHasDegenerate = true; + } + } + k += 3; + } + } + } + } + } + + // flag degenerates here rather than deferring to a MAV error later + mFMP->childSetVisible("physics_status_message_text", mHasDegenerate); //display or clear + auto degenerateIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon"); + degenerateIcon->setVisible(mHasDegenerate); + if (mHasDegenerate) + { + has_physics_error |= PhysicsError::DEGENERATE; + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_degenerate_triangles")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Error"); + degenerateIcon->setImage(img); + } + + mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH])); + + std::string mesh_status_na = mFMP->getString("mesh_status_na"); + + S32 upload_status[LLModel::LOD_HIGH + 1]; + + mModelNoErrors = true; + + const U32 lod_high = LLModel::LOD_HIGH; + U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]); + + for (S32 lod = 0; lod <= lod_high; ++lod) + { + upload_status[lod] = 0; + + std::string message = "mesh_status_good"; + + if (total_tris[lod] > 0) + { + mFMP->childSetValue(lod_triangles_name[lod], llformat("%d", total_tris[lod])); + mFMP->childSetValue(lod_vertices_name[lod], llformat("%d", total_verts[lod])); + } + else + { + if (lod == lod_high) + { + upload_status[lod] = 2; + message = "mesh_status_missing_lod"; + } + else + { + for (S32 i = lod - 1; i >= 0; --i) + { + if (total_tris[i] > 0) + { + upload_status[lod] = 2; + message = "mesh_status_missing_lod"; + } + } + } + + mFMP->childSetValue(lod_triangles_name[lod], mesh_status_na); + mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na); + } + + if (lod != lod_high) + { + if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) + { //number of submeshes is different + message = "mesh_status_submesh_mismatch"; + upload_status[lod] = 2; + } + else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count) + {//number of submodels is different, not all faces are matched correctly. + message = "mesh_status_submesh_mismatch"; + upload_status[lod] = 2; + // Note: Submodels in instance were loaded from higher LOD and as result face count + // returns same value and total_submeshes[lod] is identical to high_lod one. + } + else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) + { //number of meshes is different + message = "mesh_status_mesh_mismatch"; + upload_status[lod] = 2; + } + else if (!verts[lod].empty()) + { + S32 sum_verts_higher_lod = 0; + S32 sum_verts_this_lod = 0; + for (U32 i = 0; i < verts[lod].size(); ++i) + { + sum_verts_higher_lod += ((i < verts[lod + 1].size()) ? verts[lod + 1][i] : 0); + sum_verts_this_lod += verts[lod][i]; + } + + if ((sum_verts_higher_lod > 0) && + (sum_verts_this_lod > sum_verts_higher_lod)) + { + //too many vertices in this lod + message = "mesh_status_too_many_vertices"; + upload_status[lod] = 1; + } + } + } + + LLIconCtrl* icon = mFMP->getChild<LLIconCtrl>(lod_icon_name[lod]); + LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]); + icon->setVisible(true); + icon->setImage(img); + + if (upload_status[lod] >= 2) + { + mModelNoErrors = false; + } + + if (lod == mPreviewLOD) + { + mFMP->childSetValue("lod_status_message_text", mFMP->getString(message)); + icon = mFMP->getChild<LLIconCtrl>("lod_status_message_icon"); + icon->setImage(img); + } + + updateLodControls(lod); + } + + + //warn if hulls have more than 256 points in them + BOOL physExceededVertexLimit = FALSE; + for (U32 i = 0; mModelNoErrors && i < mModel[LLModel::LOD_PHYSICS].size(); ++i) + { + LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i]; + + if (mdl) + { + for (U32 j = 0; j < mdl->mPhysics.mHull.size(); ++j) + { + if (mdl->mPhysics.mHull[j].size() > 256) + { + physExceededVertexLimit = TRUE; + LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL; + break; + } + } + } + } + + if (physExceededVertexLimit) + { + has_physics_error |= PhysicsError::TOOMANYVERTSINHULL; + } + + if (!(has_physics_error & PhysicsError::DEGENERATE)){ // only update this field (incluides clearing it) if it is not already in use. + mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit); + LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon"); + physStatusIcon->setVisible(physExceededVertexLimit); + if (physExceededVertexLimit) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); + physStatusIcon->setImage(img); + } + } + + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mModelNoErrors = false; + LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL; + } + + bool uploadingSkin = mFMP->childGetValue("upload_skin").asBoolean(); + bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); + + if (uploadingSkin) + { + if (uploadingJointPositions && !isRigValidForJointPositionUpload()) + { + mModelNoErrors = false; + LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL; + } + } + + if (mModelNoErrors && mModelLoader) + { + if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) + { + // Some textures are still loading, prevent upload until they are done + mModelNoErrors = false; + } + } + + if (!mModelNoErrors || mHasDegenerate) + { + mFMP->childDisable("ok_btn"); + mFMP->childDisable("calculate_btn"); + } + else + { + mFMP->childEnable("ok_btn"); + mFMP->childEnable("calculate_btn"); + } + + if (mModelNoErrors && mLodsWithParsingError.empty()) + { + mFMP->childEnable("calculate_btn"); + } + else + { + mFMP->childDisable("calculate_btn"); + } + + //add up physics triangles etc + S32 phys_tris = 0; + S32 phys_hulls = 0; + S32 phys_points = 0; + + //get the triangle count for the whole scene + for (LLModelLoader::scene::iterator iter = mScene[LLModel::LOD_PHYSICS].begin(), endIter = mScene[LLModel::LOD_PHYSICS].end(); iter != endIter; ++iter) + { + for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) + { + LLModel* model = instance->mModel; + if (model) + { + S32 cur_submeshes = model->getNumVolumeFaces(); + + LLModel::convex_hull_decomposition& decomp = model->mPhysics.mHull; + + if (!decomp.empty()) + { + phys_hulls += decomp.size(); + for (U32 i = 0; i < decomp.size(); ++i) + { + phys_points += decomp[i].size(); + } + } + else + { //choose physics shape OR decomposition, can't use both + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = model->getVolumeFace(j); + phys_tris += face.mNumIndices / 3; + } + } + } + } + } + + if (phys_tris > 0) + { + mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", llformat("%d", phys_tris)); + } + else + { + mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", mesh_status_na); + } + + if (phys_hulls > 0) + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls)); + mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points)); + } + else + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na); + mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na); + } + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + if (phys_tris > 0 || phys_hulls > 0) + { + if (!fmp->isViewOptionEnabled("show_physics")) + { + fmp->enableViewOption("show_physics"); + mViewOption["show_physics"] = true; + fmp->childSetValue("show_physics", true); + } + } + else + { + fmp->disableViewOption("show_physics"); + mViewOption["show_physics"] = false; + fmp->childSetValue("show_physics", false); + + } + + //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean(); + + //fmp->childSetEnabled("physics_optimize", !use_hull); + + bool enable = (phys_tris > 0 || phys_hulls > 0) && fmp->mCurRequest.empty(); + //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); + + //enable/disable "analysis" UI + LLPanel* panel = fmp->getChild<LLPanel>("physics analysis"); + LLView* child = panel->getFirstChild(); + while (child) + { + child->setEnabled(enable); + child = panel->findNextSibling(child); + } + + enable = phys_hulls > 0 && fmp->mCurRequest.empty(); + //enable/disable "simplification" UI + panel = fmp->getChild<LLPanel>("physics simplification"); + child = panel->getFirstChild(); + while (child) + { + child->setEnabled(enable); + child = panel->findNextSibling(child); + } + + if (fmp->mCurRequest.empty()) + { + fmp->childSetVisible("Simplify", true); + fmp->childSetVisible("simplify_cancel", false); + fmp->childSetVisible("Decompose", true); + fmp->childSetVisible("decompose_cancel", false); + + if (phys_hulls > 0) + { + fmp->childEnable("Simplify"); + } + + if (phys_tris || phys_hulls > 0) + { + fmp->childEnable("Decompose"); + } + } + else + { + fmp->childEnable("simplify_cancel"); + fmp->childEnable("decompose_cancel"); + } + } + + + LLCtrlSelectionInterface* iface = fmp->childGetSelectionInterface("physics_lod_combo"); + S32 which_mode = 0; + S32 file_mode = 1; + if (iface) + { + which_mode = iface->getFirstSelectedIndex(); + file_mode = iface->getItemCount() - 1; + } + + if (which_mode == file_mode) + { + mFMP->childEnable("physics_file"); + mFMP->childEnable("physics_browse"); + } + else + { + mFMP->childDisable("physics_file"); + mFMP->childDisable("physics_browse"); + } + + LLSpinCtrl* crease = mFMP->getChild<LLSpinCtrl>("crease_angle"); + + if (mRequestedCreaseAngle[mPreviewLOD] == -1.f) + { + mFMP->childSetColor("crease_label", LLColor4::grey); + crease->forceSetValue(75.f); + } + else + { + mFMP->childSetColor("crease_label", LLColor4::white); + crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]); + } + + mModelUpdatedSignal(true); + +} + +void LLModelPreview::updateLodControls(S32 lod) +{ + if (lod < LLModel::LOD_IMPOSTOR || lod > LLModel::LOD_HIGH) + { + std::ostringstream out; + out << "Invalid level of detail: " << lod; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, false); + assert(lod >= LLModel::LOD_IMPOSTOR && lod <= LLModel::LOD_HIGH); + return; + } + + const char* lod_controls[] = + { + "lod_mode_", + "lod_triangle_limit_", + "lod_error_threshold_" + }; + const U32 num_lod_controls = sizeof(lod_controls) / sizeof(char*); + + const char* file_controls[] = + { + "lod_browse_", + "lod_file_", + }; + const U32 num_file_controls = sizeof(file_controls) / sizeof(char*); + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (!fmp) return; + + LLComboBox* lod_combo = mFMP->findChild<LLComboBox>("lod_source_" + lod_name[lod]); + if (!lod_combo) return; + + S32 lod_mode = lod_combo->getCurrentIndex(); + if (lod_mode == LOD_FROM_FILE) // LoD from file + { + fmp->mLODMode[lod] = 0; + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childSetVisible(file_controls[i] + lod_name[lod], true); + } + + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); + } + } + else if (lod_mode == USE_LOD_ABOVE) // use LoD above + { + fmp->mLODMode[lod] = 2; + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childSetVisible(file_controls[i] + lod_name[lod], false); + } + + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childSetVisible(lod_controls[i] + lod_name[lod], false); + } + + if (lod < LLModel::LOD_HIGH) + { + mModel[lod] = mModel[lod + 1]; + mScene[lod] = mScene[lod + 1]; + mVertexBuffer[lod].clear(); + + // Also update lower LoD + if (lod > LLModel::LOD_IMPOSTOR) + { + updateLodControls(lod - 1); + } + } + } + else // auto generate, the default case for all LoDs except High + { + fmp->mLODMode[lod] = 1; + + //don't actually regenerate lod when refreshing UI + mLODFrozen = true; + + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->getChildView(file_controls[i] + lod_name[lod])->setVisible(false); + } + + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->getChildView(lod_controls[i] + lod_name[lod])->setVisible(true); + } + + + LLSpinCtrl* threshold = mFMP->getChild<LLSpinCtrl>("lod_error_threshold_" + lod_name[lod]); + LLSpinCtrl* limit = mFMP->getChild<LLSpinCtrl>("lod_triangle_limit_" + lod_name[lod]); + + limit->setMaxValue(mMaxTriangleLimit); + limit->forceSetValue(mRequestedTriangleCount[lod]); + + threshold->forceSetValue(mRequestedErrorThreshold[lod]); + + mFMP->getChild<LLComboBox>("lod_mode_" + lod_name[lod])->selectNthItem(mRequestedLoDMode[lod]); + + if (mRequestedLoDMode[lod] == 0) + { + limit->setVisible(true); + threshold->setVisible(false); + + limit->setMaxValue(mMaxTriangleLimit); + limit->setIncrement(mMaxTriangleLimit / 32); + } + else + { + limit->setVisible(false); + threshold->setVisible(true); + } + + mLODFrozen = false; + } +} + +void LLModelPreview::setPreviewTarget(F32 distance) +{ + mCameraDistance = distance; + mCameraZoom = 1.f; + mCameraPitch = 0.f; + mCameraYaw = 0.f; + mCameraOffset.clearVec(); +} + +void LLModelPreview::clearBuffers() +{ + for (U32 i = 0; i < 6; i++) + { + mVertexBuffer[i].clear(); + } +} + +void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) +{ + U32 tri_count = 0; + U32 vertex_count = 0; + U32 mesh_count = 0; + + + LLModelLoader::model_list* model = NULL; + + if (lod < 0 || lod > 4) + { + model = &mBaseModel; + lod = 5; + } + else + { + model = &(mModel[lod]); + } + + if (!mVertexBuffer[lod].empty()) + { + mVertexBuffer[lod].clear(); + } + + mVertexBuffer[lod].clear(); + + LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); + + for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) + { + LLModel* mdl = *iter; + if (!mdl) + { + continue; + } + + LLModel* base_mdl = *base_iter; + base_iter++; + + S32 num_faces = mdl->getNumVolumeFaces(); + for (S32 i = 0; i < num_faces; ++i) + { + const LLVolumeFace &vf = mdl->getVolumeFace(i); + U32 num_vertices = vf.mNumVertices; + U32 num_indices = vf.mNumIndices; + + if (!num_vertices || !num_indices) + { + continue; + } + + LLVertexBuffer* vb = NULL; + + bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); + + U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + if (skinned) + { + mask |= LLVertexBuffer::MAP_WEIGHT4; + } + + vb = new LLVertexBuffer(mask, 0); + + if (!vb->allocateBuffer(num_vertices, num_indices, TRUE)) + { + // We are likely to crash due this failure, if this happens, find a way to gracefully stop preview + std::ostringstream out; + out << "Failed to allocate Vertex Buffer for model preview "; + out << num_vertices << " vertices and "; + out << num_indices << " indices"; + LL_WARNS() << out.str() << LL_ENDL; + LLFloaterModelPreview::addStringToLog(out, true); + } + + LLStrider<LLVector3> vertex_strider; + LLStrider<LLVector3> normal_strider; + LLStrider<LLVector2> tc_strider; + LLStrider<U16> index_strider; + LLStrider<LLVector4> weights_strider; + + vb->getVertexStrider(vertex_strider); + vb->getIndexStrider(index_strider); + + if (skinned) + { + vb->getWeight4Strider(weights_strider); + } + + LLVector4a::memcpyNonAliased16((F32*)vertex_strider.get(), (F32*)vf.mPositions, num_vertices * 4 * sizeof(F32)); + + if (vf.mTexCoords) + { + vb->getTexCoord0Strider(tc_strider); + S32 tex_size = (num_vertices * 2 * sizeof(F32) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)tc_strider.get(), (F32*)vf.mTexCoords, tex_size); + } + + if (vf.mNormals) + { + vb->getNormalStrider(normal_strider); + LLVector4a::memcpyNonAliased16((F32*)normal_strider.get(), (F32*)vf.mNormals, num_vertices * 4 * sizeof(F32)); + } + + if (skinned) + { + for (U32 i = 0; i < num_vertices; i++) + { + //find closest weight to vf.mVertices[i].mPosition + LLVector3 pos(vf.mPositions[i].getF32ptr()); + + const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); + llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this + + LLVector4 w(0, 0, 0, 0); + + for (U32 i = 0; i < weight_list.size(); ++i) + { + F32 wght = llclamp(weight_list[i].mWeight, 0.001f, 0.999f); + F32 joint = (F32)weight_list[i].mJointIdx; + w.mV[i] = joint + wght; + llassert(w.mV[i] - (S32)w.mV[i]>0.0f); // because weights are non-zero, and range of wt values + //should not cause floating point precision issues. + } + + *(weights_strider++) = w; + } + } + + // build indices + for (U32 i = 0; i < num_indices; i++) + { + *(index_strider++) = vf.mIndices[i]; + } + + mVertexBuffer[lod][mdl].push_back(vb); + + vertex_count += num_vertices; + tri_count += num_indices / 3; + ++mesh_count; + + } + } +} + +void LLModelPreview::update() +{ + if (mGenLOD) + { + bool subscribe_for_generation = mLodsQuery.empty(); + mGenLOD = false; + mDirty = true; + mLodsQuery.clear(); + + for (S32 lod = LLModel::LOD_HIGH; lod >= 0; --lod) + { + // adding all lods into query for generation + mLodsQuery.push_back(lod); + } + + if (subscribe_for_generation) + { + doOnIdleRepeating(lodQueryCallback); + } + } + + if (mDirty && mLodsQuery.empty()) + { + mDirty = false; + mResourceCost = calcResourceCost(); + refresh(); + updateStatusMessages(); + } +} + +//----------------------------------------------------------------------------- +// createPreviewAvatar +//----------------------------------------------------------------------------- +void LLModelPreview::createPreviewAvatar(void) +{ + mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR); + if (mPreviewAvatar) + { + mPreviewAvatar->createDrawable(&gPipeline); + mPreviewAvatar->mSpecialRenderMode = 1; + mPreviewAvatar->startMotion(ANIM_AGENT_STAND); + mPreviewAvatar->hideSkirt(); + } + else + { + LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL; + } +} + +//static +U32 LLModelPreview::countRootModels(LLModelLoader::model_list models) +{ + U32 root_models = 0; + model_list::iterator model_iter = models.begin(); + while (model_iter != models.end()) + { + LLModel* mdl = *model_iter; + if (mdl && mdl->mSubmodelID == 0) + { + root_models++; + } + model_iter++; + } + return root_models; +} + +void LLModelPreview::loadedCallback( + LLModelLoader::scene& scene, + LLModelLoader::model_list& model_list, + S32 lod, + void* opaque) +{ + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) + { + // Load loader's warnings into floater's log tab + const LLSD out = pPreview->mModelLoader->logOut(); + LLSD::array_const_iterator iter_out = out.beginArray(); + LLSD::array_const_iterator end_out = out.endArray(); + for (; iter_out != end_out; ++iter_out) + { + if (iter_out->has("Message")) + { + LLFloaterModelPreview::addStringToLog(iter_out->get("Message"), *iter_out, true, pPreview->mModelLoader->mLod); + } + } + pPreview->mModelLoader->clearLog(); + pPreview->loadModelCallback(lod); // removes mModelLoader in some cases + if (pPreview->mLookUpLodFiles && (lod != LLModel::LOD_HIGH)) + { + pPreview->lookupLODModelFiles(lod); + } + } + +} + +void LLModelPreview::lookupLODModelFiles(S32 lod) +{ + if (lod == LLModel::LOD_PHYSICS) + { + mLookUpLodFiles = false; + return; + } + S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS; + + std::string lod_filename = mLODFile[LLModel::LOD_HIGH]; + std::string ext = ".dae"; + std::string::size_type i = lod_filename.rfind(ext); + if (i != std::string::npos) + { + lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext); + } + if (gDirUtilp->fileExists(lod_filename)) + { + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + fmp->setCtrlLoadFromFile(next_lod); + } + loadModel(lod_filename, next_lod); + } + else + { + lookupLODModelFiles(next_lod); + } +} + +void LLModelPreview::stateChangedCallback(U32 state, void* opaque) +{ + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview) + { + pPreview->setLoadState(state); + } +} + +LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque) +{ + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview) + { + return pPreview->getPreviewAvatar()->getJoint(str); + } + return NULL; +} + +U32 LLModelPreview::loadTextures(LLImportMaterial& material, void* opaque) +{ + (void)opaque; + + if (material.mDiffuseMapFilename.size()) + { + material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >; + LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData)); + + tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + LLURI::unescape(material.mDiffuseMapFilename), FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); + tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE); + tex->forceToSaveRawImage(0, F32_MAX); + material.setDiffuseMap(tex->getID()); // record tex ID + return 1; + } + + material.mOpaqueData = NULL; + return 0; +} + +void LLModelPreview::addEmptyFace(LLModel* pTarget) +{ + U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0); + + buff->allocateBuffer(1, 3, true); + memset((U8*)buff->getMappedData(), 0, buff->getSize()); + memset((U8*)buff->getIndicesPointer(), 0, buff->getIndicesSize()); + + buff->validateRange(0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); + + LLStrider<LLVector3> pos; + LLStrider<LLVector3> norm; + LLStrider<LLVector2> tc; + LLStrider<U16> index; + + buff->getVertexStrider(pos); + + if (type_mask & LLVertexBuffer::MAP_NORMAL) + { + buff->getNormalStrider(norm); + } + if (type_mask & LLVertexBuffer::MAP_TEXCOORD0) + { + buff->getTexCoord0Strider(tc); + } + + buff->getIndexStrider(index); + + //resize face array + int faceCnt = pTarget->getNumVolumeFaces(); + pTarget->setNumVolumeFaces(faceCnt + 1); + pTarget->setVolumeFaceData(faceCnt + 1, pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + +} + +//----------------------------------------------------------------------------- +// render() +//----------------------------------------------------------------------------- +// Todo: we shouldn't be setting all those UI elements on render. +// Note: Render happens each frame with skinned avatars +BOOL LLModelPreview::render() +{ + assert_main_thread(); + + LLMutexLock lock(this); + mNeedsUpdate = FALSE; + + bool use_shaders = LLGLSLShader::sNoFixedFunction; + + bool edges = mViewOption["show_edges"]; + bool joint_overrides = mViewOption["show_joint_overrides"]; + bool joint_positions = mViewOption["show_joint_positions"]; + bool skin_weight = mViewOption["show_skin_weight"]; + bool textures = mViewOption["show_textures"]; + bool physics = mViewOption["show_physics"]; + + S32 width = getWidth(); + S32 height = getHeight(); + + LLGLSUIDefault def; // GL_BLEND, GL_ALPHA_TEST, GL_CULL_FACE, depth test + LLGLDisable no_blend(GL_BLEND); + LLGLEnable cull(GL_CULL_FACE); + LLGLDepthTest depth(GL_FALSE); // SL-12781 disable z-buffer to render background color + LLGLDisable fog(GL_FOG); + + { + if (use_shaders) + { + gUIProgram.bind(); + } + //clear background to grey + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(0.0f, width, 0.0f, height, -1.0f, 1.0f); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.color4fv(PREVIEW_CANVAS_COL.mV); + gl_rect_2d_simple(width, height); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + if (use_shaders) + { + gUIProgram.unbind(); + } + } + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + + bool has_skin_weights = false; + bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); + bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); + + if (upload_joints != mLastJointUpdate) + { + mLastJointUpdate = upload_joints; + if (fmp) + { + fmp->clearAvatarTab(); + } + } + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + model->mPelvisOffset = mPelvisZOffset; + if (!model->mSkinWeights.empty()) + { + has_skin_weights = true; + } + } + } + + if (has_skin_weights && lodsReady()) + { //model has skin weights, enable view options for skin weights and joint positions + U32 flags = getLegacyRigFlags(); + if (fmp) + { + if (flags == LEGACY_RIG_OK) + { + if (mFirstSkinUpdate) + { + // auto enable weight upload if weights are present + // (note: all these UI updates need to be somewhere that is not render) + mViewOption["show_skin_weight"] = true; + skin_weight = true; + fmp->childSetValue("upload_skin", true); + mFirstSkinUpdate = false; + } + + fmp->enableViewOption("show_skin_weight"); + fmp->setViewOptionEnabled("show_joint_overrides", skin_weight); + fmp->setViewOptionEnabled("show_joint_positions", skin_weight); + mFMP->childEnable("upload_skin"); + mFMP->childSetValue("show_skin_weight", skin_weight); + + } + else if ((flags & LEGACY_RIG_FLAG_TOO_MANY_JOINTS) > 0) + { + mFMP->childSetVisible("skin_too_many_joints", true); + } + else if ((flags & LEGACY_RIG_FLAG_UNKNOWN_JOINT) > 0) + { + mFMP->childSetVisible("skin_unknown_joint", true); + } + } + } + else + { + mFMP->childDisable("upload_skin"); + if (fmp) + { + mViewOption["show_skin_weight"] = false; + fmp->disableViewOption("show_skin_weight"); + fmp->disableViewOption("show_joint_overrides"); + fmp->disableViewOption("show_joint_positions"); + + skin_weight = false; + mFMP->childSetValue("show_skin_weight", false); + fmp->setViewOptionEnabled("show_skin_weight", skin_weight); + } + } + + if (upload_skin && !has_skin_weights) + { //can't upload skin weights if model has no skin weights + mFMP->childSetValue("upload_skin", false); + upload_skin = false; + } + + if (!upload_skin && upload_joints) + { //can't upload joints if not uploading skin weights + mFMP->childSetValue("upload_joints", false); + upload_joints = false; + } + + if (fmp) + { + if (upload_skin) + { + // will populate list of joints + fmp->updateAvatarTab(upload_joints); + } + else + { + fmp->clearAvatarTab(); + } + } + + if (upload_skin && upload_joints) + { + mFMP->childEnable("lock_scale_if_joint_position"); + } + else + { + mFMP->childDisable("lock_scale_if_joint_position"); + mFMP->childSetValue("lock_scale_if_joint_position", false); + } + + //Only enable joint offsets if it passed the earlier critiquing + if (isRigValidForJointPositionUpload()) + { + mFMP->childSetEnabled("upload_joints", upload_skin); + } + + F32 explode = mFMP->childGetValue("physics_explode").asReal(); + + LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview + + LLRect preview_rect; + + preview_rect = mFMP->getChildView("preview_panel")->getRect(); + + F32 aspect = (F32)preview_rect.getWidth() / preview_rect.getHeight(); + + LLViewerCamera::getInstance()->setAspect(aspect); + + LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); + + LLVector3 offset = mCameraOffset; + LLVector3 target_pos = mPreviewTarget + offset; + + F32 z_near = 0.001f; + F32 z_far = mCameraDistance*10.0f + mPreviewScale.magVec() + mCameraOffset.magVec(); + + if (skin_weight) + { + target_pos = getPreviewAvatar()->getPositionAgent() + offset; + z_near = 0.01f; + z_far = 1024.f; + + //render avatar previews every frame + refresh(); + } + + if (use_shaders) + { + gObjectPreviewProgram.bind(); + } + + gGL.loadIdentity(); + gPipeline.enableLightsPreview(); + + LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * + LLQuaternion(mCameraYaw, LLVector3::z_axis); + + LLQuaternion av_rot = camera_rot; + F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + + z_near = llclamp(z_far * 0.001f, 0.001f, 0.1f); + + LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); + + stop_glerror(); + + gGL.pushMatrix(); + gGL.color4fv(PREVIEW_EDGE_COL.mV); + + const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + LLGLEnable normalize(GL_NORMALIZE); + + if (!mBaseModel.empty() && mVertexBuffer[5].empty()) + { + genBuffers(-1, skin_weight); + //genBuffers(3); + //genLODs(); + } + + if (!mModel[mPreviewLOD].empty()) + { + mFMP->childEnable("reset_btn"); + + bool regen = mVertexBuffer[mPreviewLOD].empty(); + if (!regen) + { + const std::vector<LLPointer<LLVertexBuffer> >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second; + if (!vb_vec.empty()) + { + const LLVertexBuffer* buff = vb_vec[0]; + regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; + } + else + { + LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL; + regen = TRUE; + } + } + + if (regen) + { + genBuffers(mPreviewLOD, skin_weight); + } + + if (!skin_weight) + { + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[mPreviewLOD]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + U32 num_models = mVertexBuffer[mPreviewLOD][model].size(); + for (U32 i = 0; i < num_models; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + + if (textures) + { + int materialCnt = instance.mModel->mMaterialList.size(); + if (i < materialCnt) + { + const std::string& binding = instance.mModel->mMaterialList[i]; + const LLImportMaterial& material = instance.mMaterial[binding]; + + gGL.diffuseColor4fv(material.mDiffuseColor.mV); + + // Find the tex for this material, bind it, and add it to our set + // + LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); + if (tex) + { + mTextureSet.insert(tex); + } + } + } + else + { + gGL.diffuseColor4fv(PREVIEW_BASE_COL.mV); + } + + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); + if (edges) + { + glLineWidth(PREVIEW_EDGE_WIDTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + gGL.popMatrix(); + } + + if (physics) + { + glClear(GL_DEPTH_BUFFER_BIT); + + for (U32 pass = 0; pass < 2; pass++) + { + if (pass == 0) + { //depth only pass + gGL.setColorMask(false, false); + } + else + { + gGL.setColorMask(true, true); + } + + //enable alpha blending on second pass but not first pass + LLGLState blend(GL_BLEND, pass); + + gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA); + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + bool render_mesh = true; + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); + + LLModel::Decomposition& physics = model->mPhysics; + + if (!physics.mHull.empty()) + { + render_mesh = false; + + if (physics.mMesh.empty()) + { //build vertex buffer for physics mesh + gMeshRepo.buildPhysicsMesh(physics); + } + + if (!physics.mMesh.empty()) + { //render hull instead of mesh + for (U32 i = 0; i < physics.mMesh.size(); ++i) + { + if (explode > 0.f) + { + gGL.pushMatrix(); + + LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; + offset *= explode; + + gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); + } + + static std::vector<LLColor4U> hull_colors; + + if (i + 1 >= hull_colors.size()) + { + hull_colors.push_back(LLColor4U(rand() % 128 + 127, rand() % 128 + 127, rand() % 128 + 127, 128)); + } + + gGL.diffuseColor4ubv(hull_colors[i].mV); + LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); + + if (explode > 0.f) + { + gGL.popMatrix(); + } + } + } + } + } + + if (render_mesh) + { + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + + U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); + if (pass > 0){ + for (U32 i = 0; i < num_models; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.diffuseColor4fv(PREVIEW_PSYH_FILL_COL.mV); + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + + gGL.diffuseColor4fv(PREVIEW_PSYH_EDGE_COL.mV); + glLineWidth(PREVIEW_PSYH_EDGE_WIDTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts() - 1, buffer->getNumIndices(), 0); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + } + gGL.popMatrix(); + } + + // only do this if mDegenerate was set in the preceding mesh checks [Check this if the ordering ever breaks] + if (mHasDegenerate) + { + glLineWidth(PREVIEW_DEG_EDGE_WIDTH); + glPointSize(PREVIEW_DEG_POINT_SIZE); + gPipeline.enableLightsFullbright(); + //show degenerate triangles + LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS); + LLGLDisable cull(GL_CULL_FACE); + gGL.diffuseColor4f(1.f, 0.f, 0.f, 1.f); + const LLVector4a scale(0.5f); + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + gGL.multMatrix((GLfloat*)mat.mMatrix); + + + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); + + LLModel::Decomposition& physics = model->mPhysics; + + if (physics.mHull.empty()) + { + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + + U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); + for (U32 v = 0; v < num_models; ++v) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][v]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + + LLStrider<LLVector3> pos_strider; + buffer->getVertexStrider(pos_strider, 0); + LLVector4a* pos = (LLVector4a*)pos_strider.get(); + + LLStrider<U16> idx; + buffer->getIndexStrider(idx, 0); + + for (U32 i = 0; i < buffer->getNumIndices(); i += 3) + { + LLVector4a v1; v1.setMul(pos[*idx++], scale); + LLVector4a v2; v2.setMul(pos[*idx++], scale); + LLVector4a v3; v3.setMul(pos[*idx++], scale); + + if (ll_is_degenerate(v1, v2, v3)) + { + buffer->draw(LLRender::LINE_LOOP, 3, i); + buffer->draw(LLRender::POINTS, 3, i); + } + } + } + } + } + + gGL.popMatrix(); + } + glLineWidth(1.f); + glPointSize(1.f); + gPipeline.enableLightsPreview(); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + } + } + } + else + { + target_pos = getPreviewAvatar()->getPositionAgent(); + getPreviewAvatar()->clearAttachmentOverrides(); // removes pelvis fixup + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + getPreviewAvatar()->addPelvisFixup(mPelvisZOffset, fake_mesh_id); + bool pelvis_recalc = false; + + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(camera_distance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + + if (!model->mSkinWeights.empty()) + { + const LLMeshSkinInfo *skin = &model->mSkinInfo; + LLSkinningUtil::initJointNums(&model->mSkinInfo, getPreviewAvatar());// inits skin->mJointNums if nessesary + U32 joint_count = LLSkinningUtil::getMeshJointCount(skin); + U32 bind_count = skin->mAlternateBindMatrix.size(); + + if (joint_overrides + && bind_count > 0 + && joint_count == bind_count) + { + // mesh_id is used to determine which mesh gets to + // set the joint offset, in the event of a conflict. Since + // we don't know the mesh id yet, we can't guarantee that + // joint offsets will be applied with the same priority as + // in the uploaded model. If the file contains multiple + // meshes with conflicting joint offsets, preview may be + // incorrect. + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + for (U32 j = 0; j < joint_count; ++j) + { + LLJoint *joint = getPreviewAvatar()->getJoint(skin->mJointNums[j]); + if (joint) + { + const LLVector3& jointPos = skin->mAlternateBindMatrix[j].getTranslation(); + if (joint->aboveJointPosThreshold(jointPos)) + { + bool override_changed; + joint->addAttachmentPosOverride(jointPos, fake_mesh_id, "model", override_changed); + + if (override_changed) + { + //If joint is a pelvis then handle old/new pelvis to foot values + if (joint->getName() == "mPelvis")// or skin->mJointNames[j] + { + pelvis_recalc = true; + } + } + if (skin->mLockScaleIfJointPosition) + { + // Note that unlike positions, there's no threshold check here, + // just a lock at the default value. + joint->addAttachmentScaleOverride(joint->getDefaultScale(), fake_mesh_id, "model"); + } + } + } + } + } + + for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + const LLVolumeFace& face = model->getVolumeFace(i); + + LLStrider<LLVector3> position; + buffer->getVertexStrider(position); + + LLStrider<LLVector4> weight; + buffer->getWeight4Strider(weight); + + //quick 'n dirty software vertex skinning + + //build matrix palette + + LLMatrix4a mat[LL_MAX_JOINTS_PER_MESH_OBJECT]; + LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, joint_count, + skin, getPreviewAvatar()); + + LLMatrix4a bind_shape_matrix; + bind_shape_matrix.loadu(skin->mBindShapeMatrix); + U32 max_joints = LLSkinningUtil::getMaxJointCount(); + for (U32 j = 0; j < buffer->getNumVerts(); ++j) + { + LLMatrix4a final_mat; + F32 *wptr = weight[j].mV; + LLSkinningUtil::getPerVertexSkinMatrix(wptr, mat, true, final_mat, max_joints); + + //VECTORIZE THIS + LLVector4a& v = face.mPositions[j]; + + LLVector4a t; + LLVector4a dst; + bind_shape_matrix.affineTransform(v, t); + final_mat.affineTransform(t, dst); + + position[j][0] = dst[0]; + position[j][1] = dst[1]; + position[j][2] = dst[2]; + } + + llassert(model->mMaterialList.size() > i); + const std::string& binding = instance.mModel->mMaterialList[i]; + const LLImportMaterial& material = instance.mMaterial[binding]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + gGL.diffuseColor4fv(material.mDiffuseColor.mV); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // Find the tex for this material, bind it, and add it to our set + // + LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); + if (tex) + { + mTextureSet.insert(tex); + } + + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + + if (edges) + { + gGL.diffuseColor4fv(PREVIEW_EDGE_COL.mV); + glLineWidth(PREVIEW_EDGE_WIDTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + } + } + } + + if (joint_positions) + { + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + if (shader) + { + gDebugProgram.bind(); + } + getPreviewAvatar()->renderCollisionVolumes(); + if (fmp->mTabContainer->getCurrentPanelIndex() == fmp->mAvatarTabIndex) + { + getPreviewAvatar()->renderBones(fmp->mSelectedJointName); + } + else + { + getPreviewAvatar()->renderBones(); + } + if (shader) + { + shader->bind(); + } + } + + if (pelvis_recalc) + { + // size/scale recalculation + getPreviewAvatar()->postPelvisSetRecalc(); + } + } + } + + if (use_shaders) + { + gObjectPreviewProgram.unbind(); + } + + gGL.popMatrix(); + + return TRUE; +} + +//----------------------------------------------------------------------------- +// refresh() +//----------------------------------------------------------------------------- +void LLModelPreview::refresh() +{ + mNeedsUpdate = TRUE; +} + +//----------------------------------------------------------------------------- +// rotate() +//----------------------------------------------------------------------------- +void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) +{ + mCameraYaw = mCameraYaw + yaw_radians; + + mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); +} + +//----------------------------------------------------------------------------- +// zoom() +//----------------------------------------------------------------------------- +void LLModelPreview::zoom(F32 zoom_amt) +{ + F32 new_zoom = mCameraZoom + zoom_amt; + // TODO: stop clamping in render + mCameraZoom = llclamp(new_zoom, 1.f, PREVIEW_ZOOM_LIMIT); +} + +void LLModelPreview::pan(F32 right, F32 up) +{ + bool skin_weight = mViewOption["show_skin_weight"]; + F32 camera_distance = skin_weight ? SKIN_WEIGHT_CAMERA_DISTANCE : mCameraDistance; + mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * camera_distance / mCameraZoom, -1.f, 1.f); + mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * camera_distance / mCameraZoom, -1.f, 1.f); +} + +void LLModelPreview::setPreviewLOD(S32 lod) +{ + lod = llclamp(lod, 0, (S32)LLModel::LOD_HIGH); + + if (lod != mPreviewLOD) + { + mPreviewLOD = lod; + + LLComboBox* combo_box = mFMP->getChild<LLComboBox>("preview_lod_combo"); + combo_box->setCurrentByIndex((NUM_LOD - 1) - mPreviewLOD); // combo box list of lods is in reverse order + mFMP->childSetValue("lod_file_" + lod_name[mPreviewLOD], mLODFile[mPreviewLOD]); + + LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); + LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); + + for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i) + { + const LLColor4& color = (i == lod) ? highlight_color : normal_color; + + mFMP->childSetColor(lod_status_name[i], color); + mFMP->childSetColor(lod_label_name[i], color); + mFMP->childSetColor(lod_triangles_name[i], color); + mFMP->childSetColor(lod_vertices_name[i], color); + } + + LLFloaterModelPreview* fmp = (LLFloaterModelPreview*)mFMP; + if (fmp) + { + // make preview repopulate tab + fmp->clearAvatarTab(); + } + } + refresh(); + updateStatusMessages(); +} + +//static +void LLModelPreview::textureLoadedCallback( + BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* src_aux, + S32 discard_level, + BOOL final, + void* userdata) +{ + LLModelPreview* preview = (LLModelPreview*)userdata; + preview->refresh(); + + if (final && preview->mModelLoader) + { + if (preview->mModelLoader->mNumOfFetchingTextures > 0) + { + preview->mModelLoader->mNumOfFetchingTextures--; + } + } +} + +// static +bool LLModelPreview::lodQueryCallback() +{ + // not the best solution, but model preview belongs to floater + // so it is an easy way to check that preview still exists. + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp && fmp->mModelPreview) + { + LLModelPreview* preview = fmp->mModelPreview; + if (preview->mLodsQuery.size() > 0) + { + S32 lod = preview->mLodsQuery.back(); + preview->mLodsQuery.pop_back(); + preview->genLODs(lod); + + if (preview->mLookUpLodFiles && (lod == LLModel::LOD_HIGH)) + { + preview->lookupLODModelFiles(LLModel::LOD_HIGH); + } + + // return false to continue cycle + return false; + } + } + // nothing to process + return true; +} + +void LLModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) +{ + if (!mLODFrozen) + { + genLODs(lod, 3, enforce_tri_limit); + refresh(); + } +} + diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h new file mode 100644 index 0000000000..3664a27a72 --- /dev/null +++ b/indra/newview/llmodelpreview.h @@ -0,0 +1,313 @@ +/** + * @file llmodelpreview.h + * @brief LLModelPreview class definition, class + * responsible for model preview inside LLFloaterModelPreview + * + * $LicenseInfo:firstyear=2020&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2020, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLMODELPREVIEW_H +#define LL_LLMODELPREVIEW_H + +#include "lldynamictexture.h" +#include "llfloatermodelpreview.h" +#include "llmeshrepository.h" +#include "llmodelloader.h" //NUM_LOD +#include "llmodel.h" + +class LLJoint; +class LLVOAvatar; +class LLTextBox; +class LLVertexBuffer; +class DAE; +class daeElement; +class domProfile_COMMON; +class domInstance_geometry; +class domNode; +class domTranslate; +class domController; +class domSkin; +class domMesh; + +// const strings needed by classes that use model preivew +static const std::string lod_name[NUM_LOD + 1] = +{ + "lowest", + "low", + "medium", + "high", + "I went off the end of the lod_name array. Me so smart." +}; + +static const std::string lod_triangles_name[NUM_LOD + 1] = +{ + "lowest_triangles", + "low_triangles", + "medium_triangles", + "high_triangles", + "I went off the end of the lod_triangles_name array. Me so smart." +}; + +static const std::string lod_vertices_name[NUM_LOD + 1] = +{ + "lowest_vertices", + "low_vertices", + "medium_vertices", + "high_vertices", + "I went off the end of the lod_vertices_name array. Me so smart." +}; + +static const std::string lod_status_name[NUM_LOD + 1] = +{ + "lowest_status", + "low_status", + "medium_status", + "high_status", + "I went off the end of the lod_status_name array. Me so smart." +}; + +static const std::string lod_icon_name[NUM_LOD + 1] = +{ + "status_icon_lowest", + "status_icon_low", + "status_icon_medium", + "status_icon_high", + "I went off the end of the lod_status_name array. Me so smart." +}; + +static const std::string lod_status_image[NUM_LOD + 1] = +{ + "ModelImport_Status_Good", + "ModelImport_Status_Warning", + "ModelImport_Status_Error", + "I went off the end of the lod_status_image array. Me so smart." +}; + +static const std::string lod_label_name[NUM_LOD + 1] = +{ + "lowest_label", + "low_label", + "medium_label", + "high_label", + "I went off the end of the lod_label_name array. Me so smart." +}; + +class LLModelPreview : public LLViewerDynamicTexture, public LLMutex +{ + LOG_CLASS(LLModelPreview); + + typedef boost::signals2::signal<void(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost)> details_signal_t; + typedef boost::signals2::signal<void(void)> model_loaded_signal_t; + typedef boost::signals2::signal<void(bool)> model_updated_signal_t; + +public: + + typedef enum + { + LOD_FROM_FILE = 0, + GENERATE, + USE_LOD_ABOVE, + } eLoDMode; + +public: + // Todo: model preview shouldn't need floater dependency, it + // should just expose data to floater, not control flaoter like it does + LLModelPreview(S32 width, S32 height, LLFloater* fmp); + virtual ~LLModelPreview(); + + void resetPreviewTarget(); + void setPreviewTarget(F32 distance); + void setTexture(U32 name) { mTextureName = name; } + + void setPhysicsFromLOD(S32 lod); + BOOL render(); + void update(); + void genBuffers(S32 lod, bool skinned); + void clearBuffers(); + void refresh(); + void rotate(F32 yaw_radians, F32 pitch_radians); + void zoom(F32 zoom_amt); + void pan(F32 right, F32 up); + virtual BOOL needsRender() { return mNeedsUpdate; } + void setPreviewLOD(S32 lod); + void clearModel(S32 lod); + void getJointAliases(JointMap& joint_map); + void loadModel(std::string filename, S32 lod, bool force_disable_slm = false); + void loadModelCallback(S32 lod); + bool lodsReady() { return !mGenLOD && mLodsQuery.empty(); } + void queryLODs() { mGenLOD = true; }; + void genLODs(S32 which_lod = -1, U32 decimation = 3, bool enforce_tri_limit = false); + void generateNormals(); + void restoreNormals(); + U32 calcResourceCost(); + void rebuildUploadData(); + void saveUploadData(bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position); + void saveUploadData(const std::string& filename, bool save_skinweights, bool save_joint_positions, bool lock_scale_if_joint_position); + void clearIncompatible(S32 lod); + void updateStatusMessages(); + void updateLodControls(S32 lod); + void clearGLODGroup(); + void onLODParamCommit(S32 lod, bool enforce_tri_limit); + void addEmptyFace(LLModel* pTarget); + + const bool getModelPivot(void) const { return mHasPivot; } + void setHasPivot(bool val) { mHasPivot = val; } + void setModelPivot(const LLVector3& pivot) { mModelPivot = pivot; } + + //Is a rig valid so that it can be used as a criteria for allowing for uploading of joint positions + //Accessors for joint position upload friendly rigs + const bool isRigValidForJointPositionUpload(void) const { return mRigValidJointUpload; } + void setRigValidForJointPositionUpload(bool rigValid) { mRigValidJointUpload = rigValid; } + + //Accessors for the legacy rigs + const bool isLegacyRigValid(void) const { return mLegacyRigFlags == 0; } + U32 getLegacyRigFlags() const { return mLegacyRigFlags; } + void setLegacyRigFlags(U32 rigFlags) { mLegacyRigFlags = rigFlags; } + + static void textureLoadedCallback(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata); + static bool lodQueryCallback(); + + boost::signals2::connection setDetailsCallback(const details_signal_t::slot_type& cb){ return mDetailsSignal.connect(cb); } + boost::signals2::connection setModelLoadedCallback(const model_loaded_signal_t::slot_type& cb){ return mModelLoadedSignal.connect(cb); } + boost::signals2::connection setModelUpdatedCallback(const model_updated_signal_t::slot_type& cb){ return mModelUpdatedSignal.connect(cb); } + + void setLoadState(U32 state) { mLoadState = state; } + U32 getLoadState() { return mLoadState; } + + static bool sIgnoreLoadedCallback; + std::vector<S32> mLodsQuery; + std::vector<S32> mLodsWithParsingError; + bool mHasDegenerate; + +protected: + + static void loadedCallback(LLModelLoader::scene& scene, LLModelLoader::model_list& model_list, S32 lod, void* opaque); + static void stateChangedCallback(U32 state, void* opaque); + + static LLJoint* lookupJointByName(const std::string&, void* opaque); + static U32 loadTextures(LLImportMaterial& material, void* opaque); + + void lookupLODModelFiles(S32 lod); + +private: + //Utility function for controller vertex compare + bool verifyCount(int expected, int result); + //Creates the dummy avatar for the preview window + void createPreviewAvatar(void); + //Accessor for the dummy avatar + LLVOAvatar* getPreviewAvatar(void) { return mPreviewAvatar; } + // Count amount of original models, excluding sub-models + static U32 countRootModels(LLModelLoader::model_list models); + +protected: + friend class LLModelLoader; + friend class LLFloaterModelPreview; + friend class LLFloaterModelPreview::DecompRequest; + friend class LLPhysicsDecomp; + + LLFloater* mFMP; + + BOOL mNeedsUpdate; + bool mDirty; + bool mGenLOD; + U32 mTextureName; + F32 mCameraDistance; + F32 mCameraYaw; + F32 mCameraPitch; + F32 mCameraZoom; + LLVector3 mCameraOffset; + LLVector3 mPreviewTarget; + LLVector3 mPreviewScale; + S32 mPreviewLOD; + S32 mPhysicsSearchLOD; + U32 mResourceCost; + std::string mLODFile[LLModel::NUM_LODS]; + bool mLoading; + U32 mLoadState; + bool mResetJoints; + bool mModelNoErrors; + bool mLookUpLodFiles; + + std::map<std::string, bool> mViewOption; + + //GLOD object parameters (must rebuild object if these change) + bool mLODFrozen; + F32 mBuildShareTolerance; + U32 mBuildQueueMode; + U32 mBuildOperator; + U32 mBuildBorderMode; + U32 mRequestedLoDMode[LLModel::NUM_LODS]; + S32 mRequestedTriangleCount[LLModel::NUM_LODS]; + F32 mRequestedErrorThreshold[LLModel::NUM_LODS]; + U32 mRequestedBuildOperator[LLModel::NUM_LODS]; + U32 mRequestedQueueMode[LLModel::NUM_LODS]; + U32 mRequestedBorderMode[LLModel::NUM_LODS]; + F32 mRequestedShareTolerance[LLModel::NUM_LODS]; + F32 mRequestedCreaseAngle[LLModel::NUM_LODS]; + + LLModelLoader* mModelLoader; + + LLModelLoader::scene mScene[LLModel::NUM_LODS]; + LLModelLoader::scene mBaseScene; + + LLModelLoader::model_list mModel[LLModel::NUM_LODS]; + LLModelLoader::model_list mBaseModel; + + typedef std::vector<LLVolumeFace> v_LLVolumeFace_t; + typedef std::vector<v_LLVolumeFace_t> vv_LLVolumeFace_t; + + vv_LLVolumeFace_t mModelFacesCopy[LLModel::NUM_LODS]; + vv_LLVolumeFace_t mBaseModelFacesCopy; + + U32 mGroup; + std::map<LLPointer<LLModel>, U32> mObject; + U32 mMaxTriangleLimit; + + LLMeshUploadThread::instance_list mUploadData; + std::set<LLViewerFetchedTexture * > mTextureSet; + + //map of vertex buffers to models (one vertex buffer in vector per face in model + std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mVertexBuffer[LLModel::NUM_LODS + 1]; + + details_signal_t mDetailsSignal; + model_loaded_signal_t mModelLoadedSignal; + model_updated_signal_t mModelUpdatedSignal; + + LLVector3 mModelPivot; + bool mHasPivot; + + float mPelvisZOffset; + + bool mRigValidJointUpload; + U32 mLegacyRigFlags; + + bool mLastJointUpdate; + bool mFirstSkinUpdate; + + JointNameSet mJointsFromNode; + JointTransformMap mJointTransformMap; + + LLPointer<LLVOAvatar> mPreviewAvatar; + LLCachedControl<bool> mImporterDebug; +}; + +#endif // LL_LLMODELPREVIEW_H diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 64df449c26..663a6071f7 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -169,6 +169,14 @@ LLMuteList::LLMuteList() : gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1, _PREHASH_UseCachedMuteList, processUseCachedMuteList, static_cast<void**>(NULL))); + + // make sure mute list's instance gets initialized before we start any name requests + LLAvatarNameCache::getInstance()->setAccountNameChangedCallback([this](const LLUUID& id, const LLAvatarName& av_name) + { + // it would be better to just pass LLAvatarName instead of doing unnesssesary copies + // but this way is just more convinient + onAccountNameChanged(id, av_name.getUserName()); + }); } //----------------------------------------------------------------------------- @@ -179,6 +187,11 @@ LLMuteList::~LLMuteList() } +void LLMuteList::cleanupSingleton() +{ + LLAvatarNameCache::getInstance()->setAccountNameChangedCallback(NULL); +} + BOOL LLMuteList::isLinden(const std::string& name) const { std::string username = boost::replace_all_copy(name, ".", " "); @@ -232,8 +245,8 @@ BOOL LLMuteList::add(const LLMute& mute, U32 flags) LL_WARNS() << "Trying to self; ignored" << LL_ENDL; return FALSE; } - - S32 mute_list_limit = gSavedSettings.getS32("MuteListLimit"); + + static LLCachedControl<S32> mute_list_limit(gSavedSettings, "MuteListLimit", 1000); if (getMutes().size() >= mute_list_limit) { LL_WARNS() << "Mute limit is reached; ignored" << LL_ENDL; @@ -358,6 +371,10 @@ void LLMuteList::updateAdd(const LLMute& mute) msg->addU32("MuteFlags", mute.mFlags); gAgent.sendReliableMessage(); + if (!mIsLoaded) + { + LL_WARNS() << "Added elements to non-initialized block list" << LL_ENDL; + } mIsLoaded = TRUE; // why is this here? -MG } @@ -391,6 +408,8 @@ BOOL LLMuteList::remove(const LLMute& mute, U32 flags) else { // The caller didn't pass any flags -- just remove the mute entry entirely. + // set flags to notify observers with (flag being present means that something is allowed) + localmute.mFlags = LLMute::flagAll; } // Always remove the entry from the set -- it will be re-added with new flags if necessary. @@ -411,8 +430,8 @@ BOOL LLMuteList::remove(const LLMute& mute, U32 flags) } // Must be after erase. + notifyObservers(); notifyObserversDetailed(localmute); - setLoaded(); // why is this here? -MG } else { @@ -425,8 +444,8 @@ BOOL LLMuteList::remove(const LLMute& mute, U32 flags) updateRemove(mute); mLegacyMutes.erase(legacy_it); // Must be after erase. + notifyObservers(); notifyObserversDetailed(mute); - setLoaded(); // why is this here? -MG } } @@ -584,6 +603,19 @@ BOOL LLMuteList::loadFromFile(const std::string& filename) } fclose(fp); setLoaded(); + + // server does not maintain up-to date account names (not display names!) + // in this list, so it falls to viewer. + pending_names_t::iterator iter = mPendingAgentNameUpdates.begin(); + pending_names_t::iterator end = mPendingAgentNameUpdates.end(); + while (iter != end) + { + // this will send updates to server, make sure mIsLoaded is set + onAccountNameChanged(iter->first, iter->second); + iter++; + } + mPendingAgentNameUpdates.clear(); + return TRUE; } @@ -767,6 +799,48 @@ void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_ delete local_filename_and_path; } +void LLMuteList::onAccountNameChanged(const LLUUID& id, const std::string& username) +{ + if (mIsLoaded) + { + LLMute mute(id, username, LLMute::AGENT); + mute_set_t::iterator mute_it = mMutes.find(mute); + if (mute_it != mMutes.end() + && mute_it->mName != mute.mName + && mute_it->mType == LLMute::AGENT) // just in case, it is std::set, not map + { + // existing mute, but name changed, copy data + mute.mFlags = mute_it->mFlags; + + // erase old variant + mMutes.erase(mute_it); + + // (re)add the mute entry. + { + std::pair<mute_set_t::iterator, bool> result = mMutes.insert(mute); + if (result.second) + { + LL_INFOS() << "Muting " << mute.mName << " id " << mute.mID << " flags " << mute.mFlags << LL_ENDL; + updateAdd(mute); + // Do not notify observers here, observers do not know or need to handle name changes + // Example: block list considers notifyObserversDetailed as a selection update; + // Various chat/voice statuses care only about id and flags + // Since apropriate update time for account names is considered to be in 'hours' it is + // fine not to update UI (will be fine after restart or couple other changes) + + } + } + } + } + else + { + // Delay update until we load file + // Ex: Buddies list can arrive too early since we prerequest + // names from Buddies list before we load blocklist + mPendingAgentNameUpdates[id] = username; + } +} + void LLMuteList::addObserver(LLMuteListObserver* observer) { mObservers.insert(observer); diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index f2fcf3dbb3..0d426fbd48 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -73,6 +73,7 @@ class LLMuteList : public LLSingleton<LLMuteList> { LLSINGLETON(LLMuteList); ~LLMuteList(); + /*virtual*/ void cleanupSingleton(); public: // reasons for auto-unmuting a resident enum EAutoReason @@ -131,6 +132,7 @@ private: static void processUseCachedMuteList(LLMessageSystem* msg, void**); static void onFileMuteList(void** user_data, S32 code, LLExtStat ext_status); + void onAccountNameChanged(const LLUUID& id, const std::string& username); private: struct compare_by_name @@ -155,7 +157,9 @@ private: }; typedef std::set<LLMute, compare_by_id> mute_set_t; mute_set_t mMutes; - + typedef std::map<LLUUID, std::string> pending_names_t; + pending_names_t mPendingAgentNameUpdates; + typedef std::set<std::string> string_set_t; string_set_t mLegacyMutes; diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 52c5234137..ef4aced2c7 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -297,6 +297,7 @@ public: * Writes notification message to IM p2p session. */ static void logToIMP2P(const LLNotificationPtr& notification, bool to_file_only = false); + static void logToIMP2P(const LLUUID& from_id, const std::string& message, bool to_file_only = false); /** * Writes group notice notification message to IM group session. diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 9a3f1a853a..39a0b9b50e 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -123,15 +123,13 @@ void log_name_callback(const LLAvatarName& av_name, const std::string& from_name } // static -void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_file_only) +void LLHandlerUtil::logToIMP2P(const LLUUID& from_id, const std::string& message, bool to_file_only) { if (!gCacheName) { return; } - LLUUID from_id = notification->getPayload()["from_id"]; - if (from_id.isNull()) { // Normal behavior for system generated messages, don't spam. @@ -141,15 +139,22 @@ void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_fi if(to_file_only) { - LLAvatarNameCache::get(from_id, boost::bind(&log_name_callback, _2, "", notification->getMessage(), LLUUID())); + LLAvatarNameCache::get(from_id, boost::bind(&log_name_callback, _2, "", message, LLUUID())); } else { - LLAvatarNameCache::get(from_id, boost::bind(&log_name_callback, _2, INTERACTIVE_SYSTEM_FROM, notification->getMessage(), from_id)); + LLAvatarNameCache::get(from_id, boost::bind(&log_name_callback, _2, INTERACTIVE_SYSTEM_FROM, message, from_id)); } } // static +void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_file_only) +{ + LLUUID from_id = notification->getPayload()["from_id"]; + logToIMP2P(from_id, notification->getMessage(), to_file_only); +} + +// static void LLHandlerUtil::logGroupNoticeToIMGroup( const LLNotificationPtr& notification) { diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index 14d25d8158..a9678b1e93 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -37,6 +37,8 @@ #include "llimview.h" #include "llnotificationsutil.h" +#include <boost/regex.hpp> + using namespace LLNotificationsUI; //-------------------------------------------------------------------------- @@ -143,7 +145,19 @@ bool LLOfferHandler::processNotification(const LLNotificationPtr& notification) { // log only to file if notif panel can be embedded to IM and IM is opened bool file_only = add_notif_to_im && LLHandlerUtil::isIMFloaterOpened(notification); - LLHandlerUtil::logToIMP2P(notification, file_only); + if ((notification->getName() == "TeleportOffered" + || notification->getName() == "TeleportOffered_MaturityExceeded" + || notification->getName() == "TeleportOffered_MaturityBlocked")) + { + boost::regex r("<icon\\s*>\\s*([^<]*)?\\s*</icon\\s*>( - )?", + boost::regex::perl|boost::regex::icase); + std::string stripped_msg = boost::regex_replace(notification->getMessage(), r, ""); + LLHandlerUtil::logToIMP2P(notification->getPayload()["from_id"], stripped_msg,file_only); + } + else + { + LLHandlerUtil::logToIMP2P(notification, file_only); + } } } diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 852ba846ff..272e7ae351 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -1047,6 +1047,7 @@ void LLOutfitGallery::updateSnapshotFolderObserver() void LLOutfitGallery::refreshOutfit(const LLUUID& category_id) { LLViewerInventoryCategory* category = gInventory.getCategory(category_id); + if (category) { bool photo_loaded = false; LLInventoryModel::cat_array_t sub_cat_array; diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index e9fe493d7e..7129641c20 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -312,10 +312,14 @@ void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& s } } -void LLOutputMonitorCtrl::onChange() +void LLOutputMonitorCtrl::onChangeDetailed(const LLMute& mute) { - // check only blocking on voice. EXT-3542 - mIsMuted = LLMuteList::getInstance()->isMuted(mSpeakerId, LLMute::flagVoiceChat); + if (mute.mID == mSpeakerId) + { + // Check only blocking on voice. + // Logic goes in reverse, if flag is set, action is allowed + mIsMuted = !(LLMute::flagVoiceChat & mute.mFlags); + } } // virtual diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 98966d39ee..e80745e1df 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -105,7 +105,8 @@ public: void setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id = LLUUID::null, bool show_other_participants_speaking = false); //called by mute list - virtual void onChange(); + virtual void onChange() {}; + virtual void onChangeDetailed(const LLMute& mute); /** * Implementation of LLSpeakingIndicator interface. diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 5d1b582d1f..37ed4bc74c 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -44,6 +44,7 @@ #include "llavatariconctrl.h" #include "llfloaterreg.h" #include "llnotificationsutil.h" +#include "llviewermenu.h" // is_agent_mappable #include "llvoiceclient.h" #include "lltextbox.h" #include "lltrans.h" diff --git a/indra/newview/llpaneleditsky.h b/indra/newview/llpaneleditsky.h index c02c9c95a0..801fb8b9b2 100644 --- a/indra/newview/llpaneleditsky.h +++ b/indra/newview/llpaneleditsky.h @@ -29,7 +29,7 @@ #include "llpanel.h" #include "llsettingssky.h" -#include "llfloaterfixedenvironment.h" +#include "llfloatereditenvironmentbase.h" //========================================================================= class LLSlider; diff --git a/indra/newview/llpaneleditwater.h b/indra/newview/llpaneleditwater.h index ab2dc47bcc..4b7ec903c9 100644 --- a/indra/newview/llpaneleditwater.h +++ b/indra/newview/llpaneleditwater.h @@ -30,7 +30,7 @@ #include "llpanel.h" #include "llsettingswater.h" -#include "llfloaterfixedenvironment.h" +#include "llfloatereditenvironmentbase.h" //========================================================================= class LLSlider; diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp index 37981b36a9..91d3b523fb 100644 --- a/indra/newview/llpanelexperiences.cpp +++ b/indra/newview/llpanelexperiences.cpp @@ -93,9 +93,20 @@ void LLPanelExperiences::setExperienceList( const LLSD& experiences ) item->init(public_key); mExperiencesList->addItem(item, public_key); + + const LLSD& experience_details = LLExperienceCache::instance().get(public_key); + if (experience_details.isUndefined()) + { + LLExperienceCache::instance().get(public_key, boost::bind(&LLPanelExperiences::sortExperiencesList, this)); + } } - mExperiencesList->sort(); + sortExperiencesList(); +} + +void LLPanelExperiences::sortExperiencesList() +{ + mExperiencesList->sort(); } void LLPanelExperiences::getExperienceIdsList(std::vector<LLUUID>& result) diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h index f29fdfdecb..9d5afd1a6a 100644 --- a/indra/newview/llpanelexperiences.h +++ b/indra/newview/llpanelexperiences.h @@ -60,6 +60,8 @@ public: void setExperienceList(const LLSD& experiences); void getExperienceIdsList(std::vector<LLUUID>& result); + void sortExperiencesList(); + LLExperienceItem* getSelectedExperienceItem(); void removeExperiences( const LLSD& ids ); void removeExperience( const LLUUID& id); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 5742b5ad1a..23394b26f2 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -1027,21 +1027,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChildView("maskcutoff")->setEnabled(editable && mIsAlpha); getChildView("label maskcutoff")->setEnabled(editable && mIsAlpha); - bool allAttachments = true; - for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin(); - iter != LLSelectMgr::getInstance()->getSelection()->end();iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if (!object->isAttachment()) - { - allAttachments = false; - break; - } - } - - texture_ctrl->setBakeTextureEnabled(allAttachments); - + texture_ctrl->setBakeTextureEnabled(TRUE); } else if (id.isNull()) { @@ -1066,21 +1052,8 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChildView("label alphamode")->setEnabled(editable && mIsAlpha); getChildView("maskcutoff")->setEnabled(editable && mIsAlpha); getChildView("label maskcutoff")->setEnabled(editable && mIsAlpha); - - bool allAttachments = true; - for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin(); - iter != LLSelectMgr::getInstance()->getSelection()->end();iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if (!object->isAttachment()) - { - allAttachments = false; - break; - } - } - - texture_ctrl->setBakeTextureEnabled(allAttachments); + + texture_ctrl->setBakeTextureEnabled(TRUE); } } @@ -1109,6 +1082,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) bool enabled = (editable && isIdenticalPlanarTexgen()); childSetValue("checkbox planar align", align_planar && enabled); + childSetVisible("checkbox planar align", enabled); childSetEnabled("checkbox planar align", enabled); childSetEnabled("button align textures", enabled && LLSelectMgr::getInstance()->getSelection()->getObjectCount() > 1); diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index 7373c7412c..c63d04cd55 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -201,9 +201,12 @@ std::string build_notice_date(const U32& the_time) time(&t); } - std::string dateStr = "["+LLTrans::getString("LTimeMthNum")+"]/[" - +LLTrans::getString("LTimeDay")+"]/[" - +LLTrans::getString("LTimeYear")+"]"; + std::string dateStr = "["+ LLTrans::getString("LTimeYear") + "]/[" + + LLTrans::getString("LTimeMthNum") + "]/[" + + LLTrans::getString("LTimeDay") + "] [" + + LLTrans::getString("LTimeHour") + "]:[" + + LLTrans::getString("LTimeMin") + "]:[" + + LLTrans::getString("LTimeSec") + "]"; LLSD substitution; substitution["datetime"] = (S32) t; LLStringUtil::format (dateStr, substitution); diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 06bb886ae8..6751c25fb9 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -39,6 +39,7 @@ #include "llagent.h" #include "llagentui.h" #include "lllandmarkactions.h" +#include "llparcel.h" #include "llslurl.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" @@ -77,7 +78,7 @@ BOOL LLPanelLandmarkInfo::postBuild() mCreator = getChild<LLTextBox>("creator"); mCreated = getChild<LLTextBox>("created"); - mLandmarkTitle = getChild<LLTextBox>("title_value"); + mLandmarkTitle = getChild<LLLineEditor>("title_value"); mLandmarkTitleEditor = getChild<LLLineEditor>("title_editor"); mNotesEditor = getChild<LLTextEditor>("notes_editor"); mFolderCombo = getChild<LLComboBox>("folder_combo"); @@ -113,6 +114,7 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type) landmark_info_panel->setVisible(type == LANDMARK); getChild<LLTextBox>("folder_label")->setVisible(is_info_type_create_landmark); + getChild<LLButton>("edit_btn")->setVisible(!is_info_type_create_landmark); mFolderCombo->setVisible(is_info_type_create_landmark); switch(type) @@ -126,13 +128,10 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type) mNotesEditor->setEnabled(TRUE); LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); - std::string name = parcel_mgr->getAgentParcelName(); + LLParcel* parcel = parcel_mgr->getAgentParcel(); + std::string name = parcel->getName(); LLVector3 agent_pos = gAgent.getPositionAgent(); - std::string desc; - LLAgentUI::buildLocationString(desc, LLAgentUI::LOCATION_FORMAT_FULL, agent_pos); - mNotesEditor->setText(desc); - if (name.empty()) { S32 region_x = ll_round(agent_pos.mV[VX]); @@ -147,6 +146,7 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type) } else { + std::string desc; LLAgentUI::buildLocationString(desc, LLAgentUI::LOCATION_FORMAT_NORMAL, agent_pos); region_name = desc; } @@ -159,6 +159,25 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type) mLandmarkTitleEditor->setText(name); } + LLUUID owner_id = parcel->getOwnerID(); + if (owner_id.notNull()) + { + if (parcel->getIsGroupOwned()) + { + std::string owner_name = LLSLURL("group", parcel->getGroupID(), "inspect").getSLURLString(); + mParcelOwner->setText(owner_name); + } + else + { + std::string owner_name = LLSLURL("agent", owner_id, "inspect").getSLURLString(); + mParcelOwner->setText(owner_name); + } + } + else + { + mParcelOwner->setText(getString("public")); + } + // Moved landmark creation here from LLPanelLandmarkInfo::processParcelInfo() // because we use only agent's current coordinates instead of waiting for // remote parcel request to complete. @@ -210,6 +229,24 @@ void LLPanelLandmarkInfo::processParcelInfo(const LLParcelData& parcel_data) mMaturityRatingText->setText(LLViewerRegion::accessToString(SIM_ACCESS_PG)); } + if (parcel_data.owner_id.notNull()) + { + if (parcel_data.flags & 0x4) // depends onto DRTSIM-453 + { + std::string owner_name = LLSLURL("group", parcel_data.owner_id, "inspect").getSLURLString(); + mParcelOwner->setText(owner_name); + } + else + { + std::string owner_name = LLSLURL("agent", parcel_data.owner_id, "inspect").getSLURLString(); + mParcelOwner->setText(owner_name); + } + } + else + { + mParcelOwner->setText(getString("public")); + } + LLSD info; info["update_verbs"] = true; info["global_x"] = parcel_data.global_x; @@ -264,7 +301,8 @@ void LLPanelLandmarkInfo::displayItemInfo(const LLInventoryItem* pItem) } else { - mOwner->setText(getString("public")); + std::string public_str = getString("public"); + mOwner->setText(public_str); } ////////////////// @@ -311,6 +349,7 @@ void LLPanelLandmarkInfo::toggleLandmarkEditMode(BOOL enabled) mNotesEditor->setReadOnly(!enabled); mFolderCombo->setVisible(enabled); getChild<LLTextBox>("folder_label")->setVisible(enabled); + getChild<LLButton>("edit_btn")->setVisible(!enabled); // HACK: To change the text color in a text editor // when it was enabled/disabled we set the text once again. @@ -357,7 +396,7 @@ void LLPanelLandmarkInfo::createLandmark(const LLUUID& folder_id) // If no parcel exists use the region name instead. if (name.empty()) { - name = mRegionName->getText(); + name = mRegionTitle; } } diff --git a/indra/newview/llpanellandmarkinfo.h b/indra/newview/llpanellandmarkinfo.h index 01a6fd6b3d..9712736182 100644 --- a/indra/newview/llpanellandmarkinfo.h +++ b/indra/newview/llpanellandmarkinfo.h @@ -71,7 +71,7 @@ private: LLTextBox* mOwner; LLTextBox* mCreator; LLTextBox* mCreated; - LLTextBox* mLandmarkTitle; + LLLineEditor* mLandmarkTitle; LLLineEditor* mLandmarkTitleEditor; LLTextEditor* mNotesEditor; LLComboBox* mFolderCombo; diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index cd1dc0f070..ccd8497484 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -227,6 +227,12 @@ BOOL LLLandmarksPanel::postBuild() initMyInventoryPanel(); initLibraryInventoryPanel(); + LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("landmarks_accordion"); + if (accordion) + { + accordion->setSkipScrollToChild(true); + } + return TRUE; } diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index da21d5e69a..3964dc075c 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -545,6 +545,16 @@ void LLPanelLogin::show(const LLRect &rect, } //static +void LLPanelLogin::reshapePanel() +{ + if (sInstance) + { + LLRect rect = sInstance->getRect(); + sInstance->reshape(rect.getWidth(), rect.getHeight()); + } +} + +//static void LLPanelLogin::populateFields(LLPointer<LLCredential> credential, bool remember_user, bool remember_psswrd) { if (!sInstance) @@ -563,7 +573,7 @@ void LLPanelLogin::populateFields(LLPointer<LLCredential> credential, bool remem { sInstance->getChild<LLUICtrl>("remember_name")->setValue(remember_user); LLUICtrl* remember_password = sInstance->getChild<LLUICtrl>("remember_password"); - remember_password->setValue(remember_psswrd); + remember_password->setValue(remember_user && remember_psswrd); remember_password->setEnabled(remember_user); sInstance->populateUserList(credential); } @@ -687,7 +697,6 @@ void LLPanelLogin::getFields(LLPointer<LLCredential>& credential, if (LLPanelLogin::sInstance->mPasswordModified) { - authenticator = LLSD::emptyMap(); // password is plaintext authenticator["type"] = CRED_AUTHENTICATOR_TYPE_CLEAR; authenticator["secret"] = password; @@ -698,6 +707,15 @@ void LLPanelLogin::getFields(LLPointer<LLCredential>& credential, if (credential.notNull()) { authenticator = credential->getAuthenticator(); + if (authenticator.emptyMap()) + { + // Likely caused by user trying to log in to non-system grid + // with unsupported name format, just retry + LL_WARNS() << "Authenticator failed to load for: " << username << LL_ENDL; + // password is plaintext + authenticator["type"] = CRED_AUTHENTICATOR_TYPE_CLEAR; + authenticator["secret"] = password; + } } } } @@ -1137,7 +1155,11 @@ void LLPanelLogin::onRememberUserCheck(void*) remember_name->setValue(true); LLNotificationsUtil::add("LoginCantRemoveUsername"); } - remember_psswrd->setEnabled(remember); + if (!remember) + { + remember_psswrd->setValue(false); + } + remember_psswrd->setEnabled(remember); } } @@ -1325,8 +1347,10 @@ void LLPanelLogin::onSelectServer() switch (index) { case 0: // last location + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); + break; case 1: // home location - // do nothing - these are grid-agnostic locations + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); break; default: diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index c9b8e1b6fc..788c269ffd 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -54,6 +54,7 @@ public: static void show(const LLRect &rect, void (*callback)(S32 option, void* user_data), void* callback_data); + static void reshapePanel(); static void populateFields(LLPointer<LLCredential> credential, bool remember_user, bool remember_psswrd); static void resetFields(); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 02cd22c307..fbc1b80857 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -165,6 +165,7 @@ BOOL LLPanelMainInventory::postBuild() recent_items_panel->setSinceLogoff(TRUE); recent_items_panel->setSortOrder(LLInventoryFilter::SO_DATE); recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); + recent_items_panel->setFilterLinks(LLInventoryFilter::FILTERLINK_EXCLUDE_LINKS); LLInventoryFilter& recent_filter = recent_items_panel->getFilter(); recent_filter.setFilterObjectTypes(recent_filter.getFilterObjectTypes() & ~(0x1 << LLInventoryType::IT_CATEGORY)); recent_filter.setEmptyLookupMessage("InventoryNoMatchingRecentItems"); diff --git a/indra/newview/llpanelmarketplaceinboxinventory.cpp b/indra/newview/llpanelmarketplaceinboxinventory.cpp index cea7054d6a..7a6631448b 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.cpp +++ b/indra/newview/llpanelmarketplaceinboxinventory.cpp @@ -36,12 +36,15 @@ #include "llpanellandmarks.h" #include "llplacesinventorybridge.h" #include "llviewerfoldertype.h" +#include "llsdserialize.h" #define DEBUGGING_FRESHNESS 0 const LLColor4U DEFAULT_WHITE(255, 255, 255); +const std::string NEW_INBOX_FILENAME("inbox_new_items.xml"); + // // statics // @@ -57,7 +60,9 @@ static LLDefaultChildRegistry::Register<LLInboxFolderViewItem> r3("inbox_folder_ LLInboxInventoryPanel::LLInboxInventoryPanel(const LLInboxInventoryPanel::Params& p) : LLInventoryPanel(p) -{} +{ + LLInboxNewItemsStorage::getInstance()->load(); +} LLInboxInventoryPanel::~LLInboxInventoryPanel() {} @@ -127,7 +132,7 @@ void LLInboxFolderViewFolder::addItem(LLFolderViewItem* item) } // Compute freshness if our parent is the root folder for the inbox - if (mParentFolder == mRoot) + if ((mParentFolder == mRoot) && !mFresh) { computeFreshness(); } @@ -145,6 +150,12 @@ void LLInboxFolderViewFolder::draw() setBadgeVisibility(mFresh); LLFolderViewFolder::draw(); + + if (mFresh) + { + reshapeBadge(getRect()); + } + } BOOL LLInboxFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) @@ -167,11 +178,12 @@ void LLInboxFolderViewFolder::selectItem() void LLInboxFolderViewFolder::computeFreshness() { + LLFolderViewModelItemInventory* view_model = static_cast<LLFolderViewModelItemInventory*>(getViewModelItem()); const U32 last_expansion_utc = gSavedPerAccountSettings.getU32("LastInventoryInboxActivity"); if (last_expansion_utc > 0) { - mFresh = (static_cast<LLFolderViewModelItemInventory*>(getViewModelItem())->getCreationDate() > last_expansion_utc); + mFresh = (view_model->getCreationDate() > last_expansion_utc) || LLInboxNewItemsStorage::getInstance()->isItemFresh(view_model->getUUID()); #if DEBUGGING_FRESHNESS if (mFresh) @@ -184,6 +196,11 @@ void LLInboxFolderViewFolder::computeFreshness() { mFresh = true; } + + if (mFresh) + { + LLInboxNewItemsStorage::getInstance()->addFreshItem(view_model->getUUID()); + } } void LLInboxFolderViewFolder::deFreshify() @@ -191,6 +208,7 @@ void LLInboxFolderViewFolder::deFreshify() mFresh = false; gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); + LLInboxNewItemsStorage::getInstance()->removeItem(static_cast<LLFolderViewModelItemInventory*>(getViewModelItem())->getUUID()); } // @@ -271,5 +289,55 @@ void LLInboxFolderViewItem::deFreshify() gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); } +LLInboxNewItemsStorage::LLInboxNewItemsStorage() +{ +} + +// static +void LLInboxNewItemsStorage::destroyClass() +{ + LLInboxNewItemsStorage::getInstance()->saveNewItemsIds(); +} + +void LLInboxNewItemsStorage::saveNewItemsIds() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, NEW_INBOX_FILENAME); + if (!filename.empty()) + { + LLSD uuids_data; + for (std::set<LLUUID>::const_iterator it = mNewItemsIDs.begin(); it != mNewItemsIDs.end(); it++) + { + uuids_data.append((*it)); + } + llofstream file; + file.open(filename.c_str()); + if ( file.is_open() ) + { + LLSDSerialize::toPrettyXML(uuids_data, file); + file.close(); + } + } +} + +void LLInboxNewItemsStorage::load() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, NEW_INBOX_FILENAME); + if (!filename.empty()) + { + llifstream in_file; + in_file.open(filename.c_str()); + + LLSD uuids_data; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(uuids_data, in_file); + in_file.close(); + for (LLSD::array_iterator i = uuids_data.beginArray(); i != uuids_data.endArray(); ++i) + { + mNewItemsIDs.insert((*i).asUUID()); + } + } + } +} // eof diff --git a/indra/newview/llpanelmarketplaceinboxinventory.h b/indra/newview/llpanelmarketplaceinboxinventory.h index 0b27818c95..3e508e801b 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.h +++ b/indra/newview/llpanelmarketplaceinboxinventory.h @@ -113,4 +113,23 @@ protected: bool mFresh; }; +class LLInboxNewItemsStorage : public LLSingleton<LLInboxNewItemsStorage> + , public LLDestroyClass<LLInboxNewItemsStorage> +{ + LLSINGLETON(LLInboxNewItemsStorage); + LOG_CLASS(LLInboxNewItemsStorage); +public: + static void destroyClass(); + void saveNewItemsIds(); + + void load(); + + void addFreshItem(const LLUUID& id) { mNewItemsIDs.insert(id); } + void removeItem(const LLUUID& id) { mNewItemsIDs.erase(id); } + bool isItemFresh(const LLUUID& id) { return (mNewItemsIDs.find(id) != mNewItemsIDs.end()); } + +private: + std::set<LLUUID> mNewItemsIDs; +}; + #endif //LL_INBOXINVENTORYPANEL_H diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 1d87aa6f5d..28a020870f 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -563,6 +563,8 @@ BOOL LLPanelOutfitEdit::postBuild() mGearMenuBtn->setMenu(mGearMenu); mSaveComboBtn.reset(new LLSaveOutfitComboBtn(this)); + + onOutfitChanging(gAgentWearables.isCOFChangeInProgress()); return TRUE; } @@ -703,6 +705,10 @@ void LLPanelOutfitEdit::onListViewFilterCommitted(LLUICtrl* ctrl) S32 curr_filter_type = mListViewFilterCmbBox->getCurrentIndex(); if (curr_filter_type < 0) return; + if (curr_filter_type >= LVIT_SHAPE) + { + mWearableItemsList->setMenuWearableType(LLWearableType::EType(curr_filter_type - LVIT_SHAPE)); + } mWearableListManager->setFilterCollector(mListViewItemTypes[curr_filter_type]->collector); } @@ -1282,6 +1288,7 @@ void LLPanelOutfitEdit::showFilteredWearablesListView(LLWearableType::EType type //e_list_view_item_type implicitly contains LLWearableType::EType starting from LVIT_SHAPE applyListViewFilter(static_cast<EListViewItemType>(LVIT_SHAPE + type)); + mWearableItemsList->setMenuWearableType(type); } static void update_status_widget_rect(LLView * widget, S32 right_border) diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 8019335f97..8fff52ca4e 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -55,6 +55,7 @@ LLPanelOutfitsInventory::LLPanelOutfitsInventory() : mMyOutfitsPanel(NULL), mCurrentOutfitPanel(NULL), mActivePanel(NULL), + mAppearanceTabs(NULL), mInitialized(false) { gAgentWearables.addLoadedCallback(boost::bind(&LLPanelOutfitsInventory::onWearablesLoaded, this)); @@ -312,6 +313,7 @@ void LLPanelOutfitsInventory::initTabPanels() void LLPanelOutfitsInventory::onTabChange() { + if (!mAppearanceTabs) return; mActivePanel = dynamic_cast<LLPanelAppearanceTab*>(mAppearanceTabs->getCurrentPanel()); if (!mActivePanel) return; diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index ef16427713..3e770958da 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -972,13 +972,45 @@ void LLPanelPermissions::refresh() getChildView("clickaction")->setEnabled(is_perm_modify && is_nonpermanent_enforced && all_volume); } +// Shorten name if it doesn't fit into max_pixels of two lines +void shorten_name(std::string &name, const LLStyle::Params& style_params, S32 max_pixels) +{ + const LLFontGL* font = style_params.font(); + + LLWString wline = utf8str_to_wstring(name); + // panel supports two lines long names + S32 segment_length = font->maxDrawableChars(wline.c_str(), max_pixels, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); + if (segment_length == wline.length()) + { + // no work needed + return; + } + + S32 first_line_length = segment_length; + segment_length = font->maxDrawableChars(wline.substr(first_line_length).c_str(), max_pixels, wline.length(), LLFontGL::ANYWHERE); + if (segment_length + first_line_length == wline.length()) + { + // no work needed + return; + } + + // name does not fit, cut it, add ... + const LLWString dots_pad(utf8str_to_wstring(std::string("...."))); + S32 elipses_width = font->getWidthF32(dots_pad.c_str()); + segment_length = font->maxDrawableChars(wline.substr(first_line_length).c_str(), max_pixels - elipses_width, wline.length(), LLFontGL::ANYWHERE); + + name = name.substr(0, segment_length + first_line_length) + std::string("..."); +} + void LLPanelPermissions::updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params) { if (mOwnerCacheConnection.connected()) { mOwnerCacheConnection.disconnect(); } - mLabelOwnerName->setText(owner_name.getCompleteName(), style_params); + std::string name = owner_name.getCompleteName(); + shorten_name(name, style_params, mLabelOwnerName->getLocalRect().getWidth()); + mLabelOwnerName->setText(name, style_params); } void LLPanelPermissions::updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params) @@ -987,7 +1019,9 @@ void LLPanelPermissions::updateCreatorName(const LLUUID& creator_id, const LLAva { mCreatorCacheConnection.disconnect(); } - mLabelCreatorName->setText(creator_name.getCompleteName(), style_params); + std::string name = creator_name.getCompleteName(); + shorten_name(name, style_params, mLabelCreatorName->getLocalRect().getWidth()); + mLabelCreatorName->setText(name, style_params); } // static diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index c39df3fe8b..4762e15d8f 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -37,6 +37,7 @@ #include "llfloatersidepanelcontainer.h" #include "llfloaterworldmap.h" #include "llnotificationsutil.h" +#include "llstartup.h" #include "lltexturectrl.h" #include "lltoggleablemenu.h" #include "lltrans.h" @@ -84,6 +85,11 @@ public: bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { + if (LLStartUp::getStartupState() < STATE_STARTED) + { + return true; + } + if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnablePicks")) { LLNotificationsUtil::add("NoPicks", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); @@ -198,6 +204,11 @@ public: bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { + if (LLStartUp::getStartupState() < STATE_STARTED) + { + return true; + } + if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableClassifieds")) { LLNotificationsUtil::add("NoClassifieds", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index 0c70aa87c2..9157df789f 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -43,6 +43,7 @@ #include "llagent.h" #include "llexpandabletextbox.h" #include "llpanelpick.h" +#include "llslurl.h" #include "lltexturectrl.h" #include "llviewerregion.h" #include "llhttpconstants.h" @@ -78,6 +79,7 @@ BOOL LLPanelPlaceInfo::postBuild() mSnapshotCtrl = getChild<LLTextureCtrl>("logo"); mRegionName = getChild<LLTextBox>("region_title"); mParcelName = getChild<LLTextBox>("parcel_title"); + mParcelOwner = getChild<LLTextBox>("parcel_owner"); mDescEditor = getChild<LLExpandableTextBox>("description"); mMaturityRatingIcon = getChild<LLIconCtrl>("maturity_icon"); @@ -98,11 +100,13 @@ void LLPanelPlaceInfo::resetLocation() mParcelID.setNull(); mRequestedID.setNull(); mPosRegion.clearVec(); + mRegionTitle.clear(); std::string loading = LLTrans::getString("LoadingData"); mMaturityRatingText->setValue(loading); - mRegionName->setText(loading); + mRegionName->setTextArg("[REGIONAMEPOS]", loading); mParcelName->setText(loading); + mParcelOwner->setText(loading); mDescEditor->setText(loading); mMaturityRatingIcon->setValue(LLUUID::null); @@ -182,9 +186,11 @@ void LLPanelPlaceInfo::setErrorStatus(S32 status, const std::string& reason) std::string not_available = getString("not_available"); mMaturityRatingText->setValue(not_available); - mRegionName->setText(not_available); + mRegionName->setTextArg("[REGIONAMEPOS]", not_available); mParcelName->setText(not_available); + mParcelOwner->setText(not_available); mMaturityRatingIcon->setValue(LLUUID::null); + mRegionTitle.clear(); // Enable "Back" button that was disabled when parcel request was sent. getChild<LLButton>("back_btn")->setEnabled(TRUE); @@ -198,12 +204,34 @@ void LLPanelPlaceInfo::processParcelInfo(const LLParcelData& parcel_data) mSnapshotCtrl->setImageAssetID(parcel_data.snapshot_id); } - if(!parcel_data.sim_name.empty()) - { - mRegionName->setText(parcel_data.sim_name); + S32 region_x; + S32 region_y; + S32 region_z; + + // If the region position is zero, grab position from the global + if (mPosRegion.isExactlyZero()) + { + region_x = ll_round(parcel_data.global_x) % REGION_WIDTH_UNITS; + region_y = ll_round(parcel_data.global_y) % REGION_WIDTH_UNITS; + region_z = ll_round(parcel_data.global_z); + } + else + { + region_x = ll_round(mPosRegion.mV[VX]); + region_y = ll_round(mPosRegion.mV[VY]); + region_z = ll_round(mPosRegion.mV[VZ]); + } + + if (!parcel_data.sim_name.empty()) + { + mRegionTitle = parcel_data.sim_name; + std::string name_and_pos = llformat("%s (%d, %d, %d)", + mRegionTitle.c_str(), region_x, region_y, region_z); + mRegionName->setTextArg("[REGIONAMEPOS]", name_and_pos); } else { + mRegionTitle.clear(); mRegionName->setText(LLStringUtil::null); } @@ -216,30 +244,11 @@ void LLPanelPlaceInfo::processParcelInfo(const LLParcelData& parcel_data) mDescEditor->setText(getString("not_available")); } - S32 region_x; - S32 region_y; - S32 region_z; - - // If the region position is zero, grab position from the global - if(mPosRegion.isExactlyZero()) - { - region_x = ll_round(parcel_data.global_x) % REGION_WIDTH_UNITS; - region_y = ll_round(parcel_data.global_y) % REGION_WIDTH_UNITS; - region_z = ll_round(parcel_data.global_z); - } - else - { - region_x = ll_round(mPosRegion.mV[VX]); - region_y = ll_round(mPosRegion.mV[VY]); - region_z = ll_round(mPosRegion.mV[VZ]); - } - if (!parcel_data.name.empty()) { mParcelTitle = parcel_data.name; - mParcelName->setText(llformat("%s (%d, %d, %d)", - mParcelTitle.c_str(), region_x, region_y, region_z)); + mParcelName->setText(mParcelTitle); } else { @@ -280,12 +289,10 @@ void LLPanelPlaceInfo::reshape(S32 width, S32 height, BOOL called_from_parent) void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global, LLPanelPickEdit* pick_panel) { - std::string region_name = mRegionName->getText(); - LLPickData data; data.pos_global = pos_global; - data.name = mParcelTitle.empty() ? region_name : mParcelTitle; - data.sim_name = region_name; + data.name = mParcelTitle.empty() ? mRegionTitle : mParcelTitle; + data.sim_name = mRegionTitle; data.desc = mDescEditor->getText(); data.snapshot_id = mSnapshotCtrl->getImageAssetID(); data.parcel_id = mParcelID; diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index 30327378ef..8bf67cfe7d 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -109,6 +109,7 @@ protected: LLUUID mRequestedID; LLVector3 mPosRegion; std::string mParcelTitle; // used for pick title without coordinates + std::string mRegionTitle; std::string mCurrentTitle; S32 mScrollingPanelMinHeight; S32 mScrollingPanelWidth; @@ -120,6 +121,7 @@ protected: LLTextureCtrl* mSnapshotCtrl; LLTextBox* mRegionName; LLTextBox* mParcelName; + LLTextBox* mParcelOwner; LLExpandableTextBox* mDescEditor; LLIconCtrl* mMaturityRatingIcon; LLTextBox* mMaturityRatingText; diff --git a/indra/newview/llpanelplaceprofile.cpp b/indra/newview/llpanelplaceprofile.cpp index 104316e253..9283dfa218 100644 --- a/indra/newview/llpanelplaceprofile.cpp +++ b/indra/newview/llpanelplaceprofile.cpp @@ -104,8 +104,6 @@ BOOL LLPanelPlaceProfile::postBuild() mForSalePanel->getChild<LLIconCtrl>("icon_for_sale")-> setMouseDownCallback(boost::bind(&LLPanelPlaceProfile::onForSaleBannerClick, this)); - mParcelOwner = getChild<LLTextBox>("owner_value"); - mParcelRatingIcon = getChild<LLIconCtrl>("rating_icon"); mParcelRatingText = getChild<LLTextBox>("rating_value"); mVoiceIcon = getChild<LLIconCtrl>("voice_icon"); @@ -183,7 +181,6 @@ void LLPanelPlaceProfile::resetLocation() mYouAreHerePanel->setVisible(FALSE); std::string loading = LLTrans::getString("LoadingData"); - mParcelOwner->setValue(loading); mParcelRatingIcon->setValue(loading); mParcelRatingText->setText(loading); @@ -248,14 +245,14 @@ void LLPanelPlaceProfile::setInfoType(EInfoType type) const S32 SEARCH_DESC_HEIGHT = 150; // Remember original geometry (once). - static const S32 sOrigDescVPad = getChildView("parcel_title")->getRect().mBottom - mDescEditor->getRect().mTop; + static const S32 sOrigDescVPad = getChildView("owner_label")->getRect().mBottom - mDescEditor->getRect().mTop; static const S32 sOrigDescHeight = mDescEditor->getRect().getHeight(); static const S32 sOrigMRIconVPad = mDescEditor->getRect().mBottom - mMaturityRatingIcon->getRect().mTop; static const S32 sOrigMRTextVPad = mDescEditor->getRect().mBottom - mMaturityRatingText->getRect().mTop; // Resize the description. const S32 desc_height = is_info_type_agent ? sOrigDescHeight : SEARCH_DESC_HEIGHT; - const S32 desc_top = getChildView("parcel_title")->getRect().mBottom - sOrigDescVPad; + const S32 desc_top = getChildView("owner_label")->getRect().mBottom - sOrigDescVPad; LLRect desc_rect = mDescEditor->getRect(); desc_rect.setOriginAndSize(desc_rect.mLeft, desc_top - desc_height, desc_rect.getWidth(), desc_height); mDescEditor->reshape(desc_rect.getWidth(), desc_rect.getHeight()); @@ -401,6 +398,7 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel, parcel_data.global_x = pos_global.mdV[VX]; parcel_data.global_y = pos_global.mdV[VY]; parcel_data.global_z = pos_global.mdV[VZ]; + parcel_data.owner_id = parcel->getOwnerID(); std::string on = getString("on"); std::string off = getString("off"); diff --git a/indra/newview/llpanelplaceprofile.h b/indra/newview/llpanelplaceprofile.h index 3d2654fc12..16478bc179 100644 --- a/indra/newview/llpanelplaceprofile.h +++ b/indra/newview/llpanelplaceprofile.h @@ -76,8 +76,6 @@ private: LLPanel* mForSalePanel; LLPanel* mYouAreHerePanel; - LLTextBox* mParcelOwner; - LLIconCtrl* mParcelRatingIcon; LLTextBox* mParcelRatingText; LLIconCtrl* mVoiceIcon; diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 2ef82d0cf9..53870fb5c7 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -59,6 +59,7 @@ #include "llinventorymodel.h" #include "lllandmarkactions.h" #include "lllandmarklist.h" +#include "lllayoutstack.h" #include "llpanellandmarkinfo.h" #include "llpanellandmarks.h" #include "llpanelpick.h" @@ -280,9 +281,6 @@ BOOL LLPanelPlaces::postBuild() mShowOnMapBtn = getChild<LLButton>("map_btn"); mShowOnMapBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onShowOnMapButtonClicked, this)); - mEditBtn = getChild<LLButton>("edit_btn"); - mEditBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this)); - mSaveBtn = getChild<LLButton>("save_btn"); mSaveBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onSaveButtonClicked, this)); @@ -355,6 +353,9 @@ BOOL LLPanelPlaces::postBuild() LLComboBox* folder_combo = mLandmarkInfo->getChild<LLComboBox>("folder_combo"); folder_combo->setCommitCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this)); + LLButton* edit_btn = mLandmarkInfo->getChild<LLButton>("edit_btn"); + edit_btn->setCommitCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this)); + createTabs(); updateVerbs(); @@ -532,7 +533,6 @@ void LLPanelPlaces::setItem(LLInventoryItem* item) BOOL is_landmark_editable = gInventory.isObjectDescendentOf(mItem->getUUID(), gInventory.getRootFolderID()) && mItem->getPermissions().allowModifyBy(gAgent.getID()); - mEditBtn->setEnabled(is_landmark_editable); mSaveBtn->setEnabled(is_landmark_editable); if (is_landmark_editable) @@ -1216,13 +1216,16 @@ void LLPanelPlaces::updateVerbs() mTeleportBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn && !is_pick_panel_visible); mShowOnMapBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn && !is_pick_panel_visible); - mOverflowBtn->setVisible(is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn); - mEditBtn->setVisible(mPlaceInfoType == LANDMARK_INFO_TYPE && !isLandmarkEditModeOn); mSaveBtn->setVisible(isLandmarkEditModeOn); mCancelBtn->setVisible(isLandmarkEditModeOn); mCloseBtn->setVisible(is_create_landmark_visible && !isLandmarkEditModeOn); mPlaceInfoBtn->setVisible(!is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn && !is_pick_panel_visible); + bool show_options_btn = is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn; + mOverflowBtn->setVisible(show_options_btn); + getChild<LLLayoutPanel>("lp_options")->setVisible(show_options_btn); + getChild<LLLayoutPanel>("lp2")->setVisible(!show_options_btn); + mPlaceInfoBtn->setEnabled(!is_create_landmark_visible && !isLandmarkEditModeOn && have_3d_pos); if (is_place_info_visible) diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 27f991c202..978b030b2e 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -121,7 +121,6 @@ private: LLButton* mPlaceProfileBackBtn; LLButton* mTeleportBtn; LLButton* mShowOnMapBtn; - LLButton* mEditBtn; LLButton* mSaveBtn; LLButton* mCancelBtn; LLButton* mCloseBtn; diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 55c84815aa..c42cd6c6ba 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -73,6 +73,9 @@ bool get_hud_matrices(glh::matrix4f &proj, glh::matrix4f &model); const LLPanelPrimMediaControls::EZoomLevel LLPanelPrimMediaControls::kZoomLevels[] = { ZOOM_NONE, ZOOM_MEDIUM }; const int LLPanelPrimMediaControls::kNumZoomLevels = 2; +const F32 EXCEEDING_ZOOM_DISTANCE = 0.5f; +const S32 ADDR_LEFT_PAD = 3; + // // LLPanelPrimMediaControls // @@ -93,6 +96,7 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mZoomObjectID(LLUUID::null), mZoomObjectFace(0), mVolumeSliderVisible(0), + mZoomedCameraPos(), mWindowShade(NULL), mHideImmediately(false), mSecureURL(false), @@ -154,7 +158,7 @@ BOOL LLPanelPrimMediaControls::postBuild() mMediaProgressPanel = getChild<LLPanel>("media_progress_indicator"); mMediaProgressBar = getChild<LLProgressBar>("media_progress_bar"); mMediaAddressCtrl = getChild<LLUICtrl>("media_address"); - mMediaAddress = getChild<LLUICtrl>("media_address_url"); + mMediaAddress = getChild<LLLineEditor>("media_address_url"); mMediaPlaySliderPanel = getChild<LLUICtrl>("media_play_position"); mMediaPlaySliderCtrl = getChild<LLUICtrl>("media_play_slider"); mSkipFwdCtrl = getChild<LLUICtrl>("skip_forward"); @@ -256,7 +260,7 @@ void LLPanelPrimMediaControls::focusOnTarget() LLViewerMediaImpl* media_impl = getTargetMediaImpl(); if(media_impl) { - if(!media_impl->hasFocus()) + if (!media_impl->hasFocus()) { // The current target doesn't have media focus -- focus on it. LLViewerObject* objectp = getTargetObject(); @@ -307,7 +311,8 @@ void LLPanelPrimMediaControls::updateShape() bool can_navigate = parcel->getMediaAllowNavigate(); bool enabled = false; - bool is_zoomed = (mCurrentZoom != ZOOM_NONE) && (mTargetObjectID == mZoomObjectID) && (mTargetObjectFace == mZoomObjectFace); + bool is_zoomed = (mCurrentZoom != ZOOM_NONE) && (mTargetObjectID == mZoomObjectID) && (mTargetObjectFace == mZoomObjectFace) && !isZoomDistExceeding(); + // There is no such thing as "has_focus" being different from normal controls set // anymore (as of user feedback from bri 10/09). So we cheat here and force 'has_focus' // to 'true' (or, actually, we use a setting) @@ -498,8 +503,10 @@ void LLPanelPrimMediaControls::updateShape() std::string test_prefix = mCurrentURL.substr(0, prefix.length()); LLStringUtil::toLower(test_prefix); mSecureURL = has_focus && (test_prefix == prefix); - mCurrentURL = (mSecureURL ? " " + mCurrentURL : mCurrentURL); - + + S32 left_pad = mSecureURL ? mSecureLockIcon->getRect().getWidth() : ADDR_LEFT_PAD; + mMediaAddress->setTextPadding(left_pad, 0); + if(mCurrentURL!=mPreviousURL) { setCurrentURL(); @@ -1141,7 +1148,7 @@ void LLPanelPrimMediaControls::updateZoom() if (zoom_padding > 0.0f) { // since we only zoom into medium for now, always set zoom_in constraint to true - LLViewerMediaFocus::setCameraZoom(getTargetObject(), mTargetObjectNormal, zoom_padding, true); + mZoomedCameraPos = LLViewerMediaFocus::setCameraZoom(getTargetObject(), mTargetObjectNormal, zoom_padding, true); } // Remember the object ID/face we zoomed into, so we can update the zoom icon appropriately @@ -1401,6 +1408,10 @@ bool LLPanelPrimMediaControls::shouldVolumeSliderBeVisible() return mVolumeSliderVisible > 0; } +bool LLPanelPrimMediaControls::isZoomDistExceeding() +{ + return (gAgentCamera.getCameraPositionGlobal() - mZoomedCameraPos).normalize() >= EXCEEDING_ZOOM_DISTANCE; +} void LLPanelPrimMediaControls::clearFaceOnFade() { diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h index d4301aaf7c..dd0e4ff095 100644 --- a/indra/newview/llpanelprimmediacontrols.h +++ b/indra/newview/llpanelprimmediacontrols.h @@ -39,6 +39,7 @@ class LLProgressBar; class LLSliderCtrl; class LLViewerMediaImpl; class LLWindowShade; +class LLLineEditor; class LLPanelPrimMediaControls : public LLPanel { @@ -119,6 +120,8 @@ private: void showVolumeSlider(); void hideVolumeSlider(); bool shouldVolumeSliderBeVisible(); + + bool isZoomDistExceeding(); static void onScrollUp(void* user_data); static void onScrollUpHeld(void* user_data); @@ -162,7 +165,7 @@ private: LLPanel *mMediaProgressPanel; LLProgressBar *mMediaProgressBar; LLUICtrl *mMediaAddressCtrl; - LLUICtrl *mMediaAddress; + LLLineEditor *mMediaAddress; LLUICtrl *mMediaPlaySliderPanel; LLUICtrl *mMediaPlaySliderCtrl; LLUICtrl *mVolumeCtrl; @@ -183,6 +186,8 @@ private: F32 mZoomMediumPadding; F32 mZoomFarPadding; S32 mTopWorldViewAvoidZone; + + LLVector3d mZoomedCameraPos; LLUICtrl *mMediaPanelScroll; LLButton *mScrollUpCtrl; diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index 89cb495db9..3347c40687 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -64,7 +64,9 @@ public: LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - registrar.add("Gear.Edit", boost::bind(&edit_outfit)); + registrar.add("Gear.TouchAttach", boost::bind(&LLWearingGearMenu::handleMultiple, this, handle_attachment_touch)); + registrar.add("Gear.EditItem", boost::bind(&LLWearingGearMenu::handleMultiple, this, handle_item_edit)); + registrar.add("Gear.EditOutfit", boost::bind(&edit_outfit)); registrar.add("Gear.TakeOff", boost::bind(&LLPanelWearing::onRemoveItem, mPanelWearing)); registrar.add("Gear.Copy", boost::bind(&LLPanelWearing::copyToClipboard, mPanelWearing)); @@ -78,6 +80,16 @@ public: LLToggleableMenu* getMenu() { return mMenu; } private: + void handleMultiple(std::function<void(const LLUUID& id)> functor) + { + uuid_vec_t selected_item_ids; + mPanelWearing->getSelectedItemsUUIDs(selected_item_ids); + + for (const LLUUID& item_id : selected_item_ids) + { + functor(item_id); + } + } LLToggleableMenu* mMenu; LLPanelWearing* mPanelWearing; @@ -92,7 +104,9 @@ protected: { LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - registrar.add("Wearing.Edit", boost::bind(&edit_outfit)); + registrar.add("Wearing.TouchAttach", boost::bind(handleMultiple, handle_attachment_touch, mUUIDs)); + registrar.add("Wearing.EditItem", boost::bind(handleMultiple, handle_item_edit, mUUIDs)); + registrar.add("Wearing.EditOutfit", boost::bind(&edit_outfit)); registrar.add("Wearing.ShowOriginal", boost::bind(show_item_original, mUUIDs.front())); registrar.add("Wearing.TakeOff", boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), mUUIDs)); @@ -138,14 +152,19 @@ protected: } // Enable/disable some menu items depending on the selection. + bool show_touch = !bp_selected && !clothes_selected && attachments_selected; + bool show_edit = bp_selected || clothes_selected || attachments_selected; bool allow_detach = !bp_selected && !clothes_selected && attachments_selected; bool allow_take_off = !bp_selected && clothes_selected && !attachments_selected; + menu->setItemVisible("touch_attach", show_touch); + menu->setItemEnabled("touch_attach", 1 == mUUIDs.size() && enable_attachment_touch(mUUIDs.front())); + menu->setItemVisible("edit_item", show_edit); + menu->setItemEnabled("edit_item", 1 == mUUIDs.size() && get_is_item_editable(mUUIDs.front())); menu->setItemVisible("take_off", allow_take_off); menu->setItemVisible("detach", allow_detach); - menu->setItemVisible("edit_outfit_separator", allow_take_off || allow_detach); + menu->setItemVisible("edit_outfit_separator", show_touch | show_edit | allow_take_off || allow_detach); menu->setItemVisible("show_original", mUUIDs.size() == 1); - menu->setItemVisible("edit_item", FALSE); } }; @@ -173,12 +192,15 @@ protected: void updateMenuItemsVisibility(LLContextMenu* menu) { + menu->setItemVisible("touch_attach", TRUE); + menu->setItemEnabled("touch_attach", 1 == mUUIDs.size()); + menu->setItemVisible("edit_item", TRUE); + menu->setItemEnabled("edit_item", 1 == mUUIDs.size()); menu->setItemVisible("take_off", FALSE); menu->setItemVisible("detach", TRUE); - menu->setItemVisible("edit_outfit_separator", TRUE); + menu->setItemVisible("edit_outfit_separator", FALSE); menu->setItemVisible("show_original", FALSE); - menu->setItemVisible("edit_item", TRUE); - menu->setItemVisible("edit", FALSE); + menu->setItemVisible("edit_outfit", FALSE); } LLPanelWearing* mPanelWearing; @@ -350,6 +372,18 @@ bool LLPanelWearing::isActionEnabled(const LLSD& userdata) } } + uuid_vec_t selected_uuids; + getSelectedItemsUUIDs(selected_uuids); + + if (command_name == "touch_attach") + { + return (1 == selected_uuids.size()) && (enable_attachment_touch(selected_uuids.front())); + } + else if (command_name == "edit_item") + { + return (1 == selected_uuids.size()) && (get_is_item_editable(selected_uuids.front())); + } + return false; } diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index ee6893907e..94d20828ec 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -259,11 +259,7 @@ LLParticipantList::~LLParticipantList() */ void LLParticipantList::onAvalineCallerFound(const LLUUID& participant_id) { - LLConversationItemParticipant* participant = findParticipant(participant_id); - if (participant) - { - removeParticipant(participant); - } + removeParticipant(participant_id); // re-add avaline caller with a correct class instance. addAvatarIDExceptAgent(participant_id); } @@ -397,6 +393,7 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) // Hack for this: LLAvatarTracker::instance().isBuddyOnline(avatar_id)) // Add the participant model to the session's children list + // This will post "add_participant" event addParticipant(participant); adjustParticipant(avatar_id); diff --git a/indra/newview/llplacesinventorypanel.cpp b/indra/newview/llplacesinventorypanel.cpp index a23830e8e1..1c14acd843 100644 --- a/indra/newview/llplacesinventorypanel.cpp +++ b/indra/newview/llplacesinventorypanel.cpp @@ -43,9 +43,8 @@ static LLDefaultChildRegistry::Register<LLPlacesInventoryPanel> r("places_invent static const LLPlacesInventoryBridgeBuilder PLACES_INVENTORY_BUILDER; LLPlacesInventoryPanel::LLPlacesInventoryPanel(const Params& p) : - LLInventoryPanel(p), + LLAssetFilteredInventoryPanel(p), mSavedFolderState(NULL) - { mInvFVBridgeBuilder = &PLACES_INVENTORY_BUILDER; mSavedFolderState = new LLSaveFolderState(); diff --git a/indra/newview/llplacesinventorypanel.h b/indra/newview/llplacesinventorypanel.h index 27d9b83bd1..5629438415 100644 --- a/indra/newview/llplacesinventorypanel.h +++ b/indra/newview/llplacesinventorypanel.h @@ -32,14 +32,16 @@ class LLLandmarksPanel; class LLFolderView; -class LLPlacesInventoryPanel : public LLInventoryPanel +class LLPlacesInventoryPanel : public LLAssetFilteredInventoryPanel { public: struct Params - : public LLInitParam::Block<Params, LLInventoryPanel::Params> + : public LLInitParam::Block<Params, LLAssetFilteredInventoryPanel::Params> { Params() - {} + { + filter_asset_type = "landmark"; + } }; LLPlacesInventoryPanel(const Params& p); diff --git a/indra/newview/llpresetsmanager.h b/indra/newview/llpresetsmanager.h index d5b384ceb9..0de30e9e01 100644 --- a/indra/newview/llpresetsmanager.h +++ b/indra/newview/llpresetsmanager.h @@ -33,6 +33,7 @@ #include <map> static const std::string PRESETS_DEFAULT = "Default"; +static const std::string PRESETS_DEFAULT_UPPER = "DEFAULT"; static const std::string PRESETS_DIR = "presets"; static const std::string PRESETS_GRAPHIC = "graphic"; static const std::string PRESETS_CAMERA = "camera"; diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 70ce275734..18c2fb5452 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -373,11 +373,11 @@ BOOL LLPreviewGesture::postBuild() mReplaceEditor = edit; combo = getChild<LLComboBox>( "modifier_combo"); - combo->setCommitCallback(onCommitSetDirty, this); + combo->setCommitCallback(boost::bind(&LLPreviewGesture::onCommitKeyorModifier, this)); mModifierCombo = combo; combo = getChild<LLComboBox>( "key_combo"); - combo->setCommitCallback(onCommitSetDirty, this); + combo->setCommitCallback(boost::bind(&LLPreviewGesture::onCommitKeyorModifier, this)); mKeyCombo = combo; list = getChild<LLScrollListCtrl>("library_list"); @@ -937,12 +937,16 @@ void LLPreviewGesture::loadUIFromGesture(LLMultiGesture* gesture) break; } + mModifierCombo->setEnabledByValue(CTRL_LABEL, gesture->mKey != KEY_F10); + mKeyCombo->setCurrentByIndex(0); if (gesture->mKey != KEY_NONE) { mKeyCombo->setSimple(LLKeyboard::stringFromKey(gesture->mKey)); } + mKeyCombo->setEnabledByValue(LLKeyboard::stringFromKey(KEY_F10), gesture->mMask != MASK_CONTROL); + // Make UI steps for each gesture step S32 i; S32 count = gesture->mSteps.size(); @@ -1338,6 +1342,17 @@ LLMultiGesture* LLPreviewGesture::createGesture() } +void LLPreviewGesture::onCommitKeyorModifier() +{ + // SL-14139: ctrl-F10 is currently used to access top menu, + // so don't allow to bound gestures to this combination. + + mKeyCombo->setEnabledByValue(LLKeyboard::stringFromKey(KEY_F10), mModifierCombo->getSimple() != CTRL_LABEL); + mModifierCombo->setEnabledByValue(CTRL_LABEL, mKeyCombo->getSimple() != LLKeyboard::stringFromKey(KEY_F10)); + mDirty = TRUE; + refresh(); +} + // static void LLPreviewGesture::updateLabel(LLScrollListItem* item) { diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h index 3ba4f56295..ffcada4cc2 100644 --- a/indra/newview/llpreviewgesture.h +++ b/indra/newview/llpreviewgesture.h @@ -103,6 +103,8 @@ protected: LLScrollListItem* addStep(const enum EStepType step_type); void onVisibilityChanged ( const LLSD& new_visibility ); + + void onCommitKeyorModifier(); static std::string getLabel(std::vector<std::string> labels); static void updateLabel(LLScrollListItem* item); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 7ef0ef0e8b..1b60610668 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -307,6 +307,7 @@ void LLPreviewNotecard::loadAsset() { editor->setEnabled(FALSE); getChildView("lock")->setVisible( TRUE); + getChildView("Edit")->setEnabled(FALSE); } if((allow_modify || is_owner) && !source_library) diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 7a68d1e270..4c539ded38 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -544,9 +544,6 @@ void LLScriptEdCore::initMenu() menuItem = getChild<LLMenuItemCallGL>("Go to line..."); menuItem->setClickCallback(boost::bind(&LLFloaterGotoLine::show, this)); - menuItem = getChild<LLMenuItemCallGL>("Help..."); - menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnHelp, this)); - menuItem = getChild<LLMenuItemCallGL>("Keyword Help..."); menuItem->setClickCallback(boost::bind(&LLScriptEdCore::onBtnDynamicHelp, this)); @@ -736,7 +733,10 @@ void LLScriptEdCore::updateDynamicHelp(BOOL immediate) } if (immediate || (mLiveHelpTimer.getStarted() && mLiveHelpTimer.getElapsedTimeF32() > LIVE_HELP_REFRESH_TIME)) { - std::string help_string = mEditor->getText().substr(segment->getStart(), segment->getEnd() - segment->getStart()); + // Use Wtext since segment's start/end are made for wstring and will + // result in a shift for case of multi-byte symbols inside std::string. + LLWString segment_text = mEditor->getWText().substr(segment->getStart(), segment->getEnd() - segment->getStart()); + std::string help_string = wstring_to_utf8str(segment_text); setHelpPage(help_string); mLiveHelpTimer.stop(); } @@ -882,11 +882,6 @@ bool LLScriptEdCore::handleSaveChangesDialog(const LLSD& notification, const LLS return false; } -void LLScriptEdCore::onBtnHelp() -{ - LLUI::getInstance()->mHelpImpl->showTopic(HELP_LSL_PORTAL_TOPIC); -} - void LLScriptEdCore::onBtnDynamicHelp() { LLFloater* live_help_floater = mLiveHelpHandle.get(); diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 74e4c00d43..3cf22a0e6e 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -144,7 +144,6 @@ public: void setItemRemoved(bool script_removed){mScriptRemoved = script_removed;}; private: - void onBtnHelp(); void onBtnDynamicHelp(); void onBtnUndoChanges(); diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 9d8be4b2fe..1e91da529c 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -401,7 +401,7 @@ void LLPreviewTexture::onFileLoadedForSave(BOOL success, { const U32 ext_length = 3; std::string extension = self->mSaveFileName.substr( self->mSaveFileName.length() - ext_length); - + LLStringUtil::toLower(extension); // We only support saving in PNG or TGA format LLPointer<LLImageFormatted> image; if(extension == "png") diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp index e9feae3457..416848f9af 100644 --- a/indra/newview/llprogressview.cpp +++ b/indra/newview/llprogressview.cpp @@ -404,34 +404,37 @@ void LLProgressView::initLogos() // with no internal paddings so it gets additional padding icon_width = 77; icon_height = 21; - S32 pad_y = 4; + S32 pad_fmod_y = 4; texture_start_x++; loadLogo(temp_str + "fmod_logo.png", image_codec, - LLRect(texture_start_x, texture_start_y + pad_y + icon_height, texture_start_x + icon_width, texture_start_y + pad_y), + LLRect(texture_start_x, texture_start_y + pad_fmod_y + icon_height, texture_start_x + icon_width, texture_start_y + pad_fmod_y), default_clip, default_clip); texture_start_x += icon_width + default_pad + 1; -#endif +#endif //LL_FMODSTUDIO +#ifdef LL_HAVOK // original image size is 342x113, central element is on a larger side // plus internal padding, so it gets slightly more height than desired 32 icon_width = 88; icon_height = 29; - pad_y = -1; + S32 pad_havok_y = -1; loadLogo(temp_str + "havok_logo.png", image_codec, - LLRect(texture_start_x, texture_start_y + pad_y + icon_height, texture_start_x + icon_width, texture_start_y + pad_y), + LLRect(texture_start_x, texture_start_y + pad_havok_y + icon_height, texture_start_x + icon_width, texture_start_y + pad_havok_y), default_clip, default_clip); texture_start_x += icon_width + default_pad; +#endif //LL_HAVOK // 108x41 icon_width = 74; + icon_height = default_height; loadLogo(temp_str + "vivox_logo.png", image_codec, - LLRect(texture_start_x, texture_start_y + default_height, texture_start_x + icon_width, texture_start_y), + LLRect(texture_start_x, texture_start_y + icon_height, texture_start_x + icon_width, texture_start_y), default_clip, default_clip); } @@ -545,6 +548,7 @@ void LLProgressView::onCancelButtonClicked(void*) // cancel is pressed while teleporting inside region (EXT-4911) if (LLStartUp::getStartupState() < STATE_STARTED) { + LL_INFOS() << "User requesting quit during login" << LL_ENDL; LLAppViewer::instance()->requestQuit(); } else diff --git a/indra/newview/llremoteparcelrequest.h b/indra/newview/llremoteparcelrequest.h index 5b0d189137..70c117be44 100644 --- a/indra/newview/llremoteparcelrequest.h +++ b/indra/newview/llremoteparcelrequest.h @@ -45,7 +45,7 @@ struct LLParcelData std::string desc; S32 actual_area; S32 billable_area; - U8 flags; + U8 flags; // group owned, maturity F32 global_x; F32 global_y; F32 global_z; diff --git a/indra/newview/llscenemonitor.cpp b/indra/newview/llscenemonitor.cpp index 2c0c38dc75..f9baf5fbd3 100644 --- a/indra/newview/llscenemonitor.cpp +++ b/indra/newview/llscenemonitor.cpp @@ -526,162 +526,167 @@ void LLSceneMonitor::fetchQueryResult() //dump results to a file _scene_xmonitor_results.csv void LLSceneMonitor::dumpToFile(std::string file_name) -{ using namespace LLTrace; - +{ if (!hasResults()) return; LL_INFOS("SceneMonitor") << "Saving scene load stats to " << file_name << LL_ENDL; - - llofstream os(file_name.c_str()); - - os << std::setprecision(10); - - PeriodicRecording& scene_load_recording = mSceneLoadRecording.getResults(); - const U32 frame_count = scene_load_recording.getNumRecordedPeriods(); - - F64Seconds frame_time; - - os << "Stat"; - for (S32 frame = 1; frame <= frame_count; frame++) - { - frame_time += scene_load_recording.getPrevRecording(frame_count - frame).getDuration(); - os << ", " << frame_time.value(); - } - os << '\n'; - - os << "Sample period(s)"; - for (S32 frame = 1; frame <= frame_count; frame++) + try { - frame_time = scene_load_recording.getPrevRecording(frame_count - frame).getDuration(); - os << ", " << frame_time.value(); - } - os << '\n'; + llofstream os(file_name.c_str()); + os << std::setprecision(10); - typedef StatType<CountAccumulator> trace_count; - for (auto& it : trace_count::instance_snapshot()) - { - std::ostringstream row; - row << std::setprecision(10); + LLTrace::PeriodicRecording& scene_load_recording = mSceneLoadRecording.getResults(); + const U32 frame_count = scene_load_recording.getNumRecordedPeriods(); - row << it.getName(); + F64Seconds frame_time; - const char* unit_label = it.getUnitLabel(); - if(unit_label[0]) + os << "Stat"; + for (S32 frame = 1; frame <= frame_count; frame++) { - row << "(" << unit_label << ")"; + frame_time += scene_load_recording.getPrevRecording(frame_count - frame).getDuration(); + os << ", " << frame_time.value(); } + os << '\n'; - S32 samples = 0; - + os << "Sample period(s)"; for (S32 frame = 1; frame <= frame_count; frame++) { - Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(it); - row << ", " << recording.getSum(it); + frame_time = scene_load_recording.getPrevRecording(frame_count - frame).getDuration(); + os << ", " << frame_time.value(); } + os << '\n'; - row << '\n'; - if (samples > 0) + typedef LLTrace::StatType<LLTrace::CountAccumulator> trace_count; + for (auto& it : trace_count::instance_snapshot()) { - os << row.str(); - } - } + std::ostringstream row; + row << std::setprecision(10); - typedef StatType<EventAccumulator> trace_event; + row << it.getName(); - for (auto& it : trace_event::instance_snapshot()) - { - std::ostringstream row; - row << std::setprecision(10); - row << it.getName(); - - const char* unit_label = it.getUnitLabel(); - if(unit_label[0]) - { - row << "(" << unit_label << ")"; - } + const char* unit_label = it.getUnitLabel(); + if (unit_label[0]) + { + row << "(" << unit_label << ")"; + } - S32 samples = 0; + S32 samples = 0; - for (S32 frame = 1; frame <= frame_count; frame++) - { - Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(it); - F64 mean = recording.getMean(it); - if (llisnan(mean)) + for (S32 frame = 1; frame <= frame_count; frame++) { - row << ", n/a"; + LLTrace::Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); + samples += recording.getSampleCount(it); + row << ", " << recording.getSum(it); } - else + + row << '\n'; + + if (samples > 0) { - row << ", " << mean; + os << row.str(); } } - row << '\n'; + typedef LLTrace::StatType<LLTrace::EventAccumulator> trace_event; - if (samples > 0) + for (auto& it : trace_event::instance_snapshot()) { - os << row.str(); - } - } + std::ostringstream row; + row << std::setprecision(10); + row << it.getName(); - typedef StatType<SampleAccumulator> trace_sample; + const char* unit_label = it.getUnitLabel(); + if (unit_label[0]) + { + row << "(" << unit_label << ")"; + } - for (auto& it : trace_sample::instance_snapshot()) - { - std::ostringstream row; - row << std::setprecision(10); - row << it.getName(); + S32 samples = 0; - const char* unit_label = it.getUnitLabel(); - if(unit_label[0]) - { - row << "(" << unit_label << ")"; + for (S32 frame = 1; frame <= frame_count; frame++) + { + LLTrace::Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); + samples += recording.getSampleCount(it); + F64 mean = recording.getMean(it); + if (llisnan(mean)) + { + row << ", n/a"; + } + else + { + row << ", " << mean; + } + } + + row << '\n'; + + if (samples > 0) + { + os << row.str(); + } } - S32 samples = 0; + typedef LLTrace::StatType<LLTrace::SampleAccumulator> trace_sample; - for (S32 frame = 1; frame <= frame_count; frame++) + for (auto& it : trace_sample::instance_snapshot()) { - Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(it); - F64 mean = recording.getMean(it); - if (llisnan(mean)) + std::ostringstream row; + row << std::setprecision(10); + row << it.getName(); + + const char* unit_label = it.getUnitLabel(); + if (unit_label[0]) { - row << ", n/a"; + row << "(" << unit_label << ")"; } - else + + S32 samples = 0; + + for (S32 frame = 1; frame <= frame_count; frame++) { - row << ", " << mean; + LLTrace::Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); + samples += recording.getSampleCount(it); + F64 mean = recording.getMean(it); + if (llisnan(mean)) + { + row << ", n/a"; + } + else + { + row << ", " << mean; + } } - } - row << '\n'; + row << '\n'; - if (samples > 0) - { - os << row.str(); + if (samples > 0) + { + os << row.str(); + } } - } - - typedef StatType<MemAccumulator> trace_mem; - for (auto& it : trace_mem::instance_snapshot()) - { - os << it.getName() << "(KiB)"; - for (S32 frame = 1; frame <= frame_count; frame++) + typedef LLTrace::StatType<LLTrace::MemAccumulator> trace_mem; + for (auto& it : trace_mem::instance_snapshot()) { - os << ", " << scene_load_recording.getPrevRecording(frame_count - frame).getMax(it).valueInUnits<LLUnits::Kilobytes>(); + os << it.getName() << "(KiB)"; + + for (S32 frame = 1; frame <= frame_count; frame++) + { + os << ", " << scene_load_recording.getPrevRecording(frame_count - frame).getMax(it).valueInUnits<LLUnits::Kilobytes>(); + } + + os << '\n'; } - os << '\n'; + os.flush(); + os.close(); + } + catch (const std::ios_base::failure &e) + { + LL_WARNS() << "Unable to dump scene monitor results: " << e.what() << LL_ENDL; } - - os.flush(); - os.close(); } //------------------------------------------------------------------------------------------------------------- diff --git a/indra/newview/llsearchableui.cpp b/indra/newview/llsearchableui.cpp index 93143eb33f..1119e80005 100644 --- a/indra/newview/llsearchableui.cpp +++ b/indra/newview/llsearchableui.cpp @@ -68,7 +68,10 @@ ll::prefs::PanelData::~PanelData() bool ll::prefs::PanelData::hightlightAndHide( LLWString const &aFilter ) { for( tSearchableItemList::iterator itr = mChildren.begin(); itr != mChildren.end(); ++itr ) - (*itr)->setNotHighlighted( ); + (*itr)->setNotHighlighted(); + + for (tPanelDataList::iterator itr = mChildPanel.begin(); itr != mChildPanel.end(); ++itr) + (*itr)->setNotHighlighted(); if (aFilter.empty()) { @@ -85,6 +88,15 @@ bool ll::prefs::PanelData::hightlightAndHide( LLWString const &aFilter ) return bVisible; } +void ll::prefs::PanelData::setNotHighlighted() +{ + for (tSearchableItemList::iterator itr = mChildren.begin(); itr != mChildren.end(); ++itr) + (*itr)->setNotHighlighted(); + + for (tPanelDataList::iterator itr = mChildPanel.begin(); itr != mChildPanel.end(); ++itr) + (*itr)->setNotHighlighted(); +} + bool ll::prefs::TabContainerData::hightlightAndHide( LLWString const &aFilter ) { for( tSearchableItemList::iterator itr = mChildren.begin(); itr != mChildren.end(); ++itr ) diff --git a/indra/newview/llsearchableui.h b/indra/newview/llsearchableui.h index 9741557e49..e033cae3ab 100644 --- a/indra/newview/llsearchableui.h +++ b/indra/newview/llsearchableui.h @@ -73,6 +73,7 @@ namespace ll virtual ~PanelData(); + void setNotHighlighted(); virtual bool hightlightAndHide( LLWString const &aFilter ); }; diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp index 10e510b842..b9259cb18d 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -117,7 +117,7 @@ LLSD LLCredential::getLoginParams() else if (mIdentifier["type"].asString() == "account") { result["username"] = mIdentifier["account_name"]; - result["passwd"] = mAuthenticator["secret"]; + result["passwd"] = mAuthenticator["secret"].asString(); username = result["username"].asString(); } } @@ -154,3 +154,10 @@ void LLCredential::authenticatorType(std::string &idType) } } + +LLCertException::LLCertException(const LLSD& cert_data, const std::string& msg) + : LLException(msg), + mCertData(cert_data) +{ + LL_WARNS("SECAPI") << "Certificate Error: " << msg << LL_ENDL; +} diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index 69b6b32923..14059f828a 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -75,6 +75,7 @@ #define CERT_EXTENDED_KEY_USAGE "extendedKeyUsage" #define CERT_EKU_SERVER_AUTH SN_server_auth +#define CERT_EKU_TLS_SERVER_AUTH LN_server_auth #define CERT_SUBJECT_KEY_IDENTFIER "subjectKeyIdentifier" #define CERT_AUTHORITY_KEY_IDENTIFIER "authorityKeyIdentifier" @@ -334,17 +335,23 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred); class LLCertException: public LLException { public: - LLCertException(const LLSD& cert_data, const std::string& msg): LLException(msg), - mCertData(cert_data) - { - LL_WARNS("SECAPI") << "Certificate Error: " << msg << LL_ENDL; - } + LLCertException(const LLSD& cert_data, const std::string& msg); virtual ~LLCertException() throw() {} LLSD getCertData() const { return mCertData; } protected: LLSD mCertData; }; +class LLAllocationCertException : public LLCertException +{ +public: + LLAllocationCertException(const LLSD& cert_data) : LLCertException(cert_data, "CertAllocationFailure") + { + } + virtual ~LLAllocationCertException() throw() {} +protected: +}; + class LLInvalidCertificate : public LLCertException { public: diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 55e49100c3..737ef30ada 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -78,16 +78,16 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert, BIO * pem_bio = BIO_new_mem_buf((void*)pem_cert.c_str(), pem_cert.length()); if(pem_bio == NULL) { - LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; - LLTHROW(LLInvalidCertificate(LLSD::emptyMap())); + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + LLTHROW(LLAllocationCertException(LLSD::emptyMap())); } mCert = NULL; PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); BIO_free(pem_bio); if (!mCert) { - LL_WARNS("SECAPI") << "Could not decode certificate to x509." << LL_ENDL; - LLTHROW(LLInvalidCertificate(LLSD::emptyMap())); + LL_WARNS("SECAPI") << "Could not decode certificate to x509." << LL_ENDL; + LLTHROW(LLInvalidCertificate(LLSD::emptyMap())); } } @@ -924,9 +924,13 @@ void _validateCert(int validation_policy, LLTHROW(LLCertKeyUsageValidationException(current_cert_info)); } // only validate EKU if the cert has it - if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() && - (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE], - LLSD((std::string)CERT_EKU_SERVER_AUTH)))) + if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) + && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() + && (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE], + LLSD((std::string)CERT_EKU_TLS_SERVER_AUTH))) + && (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE], + LLSD((std::string)CERT_EKU_SERVER_AUTH))) + ) { LLTHROW(LLCertKeyUsageValidationException(current_cert_info)); } diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 56068b3bbb..50884762a8 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -303,6 +303,27 @@ void LLSelectMgr::updateEffects() } } +void LLSelectMgr::resetObjectOverrides() +{ + resetObjectOverrides(getSelection()); +} + +void LLSelectMgr::resetObjectOverrides(LLObjectSelectionHandle selected_handle) +{ + struct f : public LLSelectedNodeFunctor + { + virtual bool apply(LLSelectNode* node) + { + node->mLastPositionLocal.setVec(0, 0, 0); + node->mLastRotation = LLQuaternion(); + node->mLastScale.setVec(0, 0, 0); + return true; + } + } func; + + selected_handle->applyToNodes(&func); +} + void LLSelectMgr::overrideObjectUpdates() { //override any position updates from simulator on objects being edited @@ -3910,11 +3931,11 @@ BOOL LLSelectMgr::selectGetAggregateTexturePermissions(LLAggregatePermissions& r return TRUE; } -BOOL LLSelectMgr::isSelfAvatarSelected() +BOOL LLSelectMgr::isMovableAvatarSelected() { if (mAllowSelectAvatar) { - return (getSelection()->getObjectCount() == 1) && (getSelection()->getFirstRootObject() == gAgentAvatarp); + return (getSelection()->getObjectCount() == 1) && (getSelection()->getFirstRootObject()->isAvatar()) && getSelection()->getFirstMoveableNode(TRUE); } return FALSE; } @@ -5130,18 +5151,27 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle, bool link_operation = message_name == "ObjectLink"; - //clear update override data (allow next update through) - struct f : public LLSelectedNodeFunctor - { - virtual bool apply(LLSelectNode* node) - { - node->mLastPositionLocal.setVec(0,0,0); - node->mLastRotation = LLQuaternion(); - node->mLastScale.setVec(0,0,0); - return true; - } - } func; - selected_handle->applyToNodes(&func); + if (mAllowSelectAvatar) + { + if (selected_handle->getObjectCount() == 1 + && selected_handle->getFirstObject() != NULL + && selected_handle->getFirstObject()->isAvatar()) + { + // Server doesn't move avatars at the moment, it is a local debug feature, + // but server does update position regularly, so do not drop mLastPositionLocal + // Position override for avatar gets reset in LLAgentCamera::resetView(). + } + else + { + // drop mLastPositionLocal (allow next update through) + resetObjectOverrides(selected_handle); + } + } + else + { + //clear update override data (allow next update through) + resetObjectOverrides(selected_handle); + } std::queue<LLSelectNode*> nodes_to_send; @@ -6851,51 +6881,26 @@ void LLSelectMgr::pauseAssociatedAvatars() mSelectedObjects->mSelectType = getSelectTypeForObject(object); - bool is_attached = false; - if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT && - isAgentAvatarValid()) + LLVOAvatar* parent_av = NULL; + if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT) { // Selection can be obsolete, confirm that this is an attachment - LLViewerObject* parent = (LLViewerObject*)object->getParent(); - while (parent != NULL) - { - if (parent->isAvatar()) - { - is_attached = true; - break; - } - else - { - parent = (LLViewerObject*)parent->getParent(); - } - } + // and find parent avatar + parent_av = object->getAvatarAncestor(); } - - if (is_attached) + // Can be both an attachment and animated object + if (parent_av) { - if (object->isAnimatedObject()) - { - // Is an animated object attachment. - // Pause both the control avatar and the avatar it's attached to. - if (object->getControlAvatar()) - { - mPauseRequests.push_back(object->getControlAvatar()->requestPause()); - } - mPauseRequests.push_back(gAgentAvatarp->requestPause()); - } - else - { - // Is a regular attachment. Pause the avatar it's attached to. - mPauseRequests.push_back(gAgentAvatarp->requestPause()); - } + // It's an attachment. Pause the avatar it's attached to. + mPauseRequests.push_back(parent_av->requestPause()); } - else if (object && object->isAnimatedObject() && object->getControlAvatar()) + + if (object->isAnimatedObject() && object->getControlAvatar()) { - // Is a non-attached animated object. Pause the control avatar. + // It's an animated object. Pause the control avatar. mPauseRequests.push_back(object->getControlAvatar()->requestPause()); } - } } diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 3bed484b58..57fdfce152 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -458,6 +458,13 @@ public: void clearSelections(); void update(); void updateEffects(); // Update HUD effects + + // When we edit object's position/rotation/scale we set local + // overrides and ignore any updates (override received valeus). + // When we send data to server, we send local values and reset + // overrides + void resetObjectOverrides(); + void resetObjectOverrides(LLObjectSelectionHandle selected_handle); void overrideObjectUpdates(); // Returns the previous value of mForceSelection @@ -725,7 +732,7 @@ public: LLPermissions* findObjectPermissions(const LLViewerObject* object); - BOOL isSelfAvatarSelected(); + BOOL isMovableAvatarSelected(); void selectDelete(); // Delete on simulator void selectForceDelete(); // just delete, no into trash diff --git a/indra/newview/llsetkeybinddialog.cpp b/indra/newview/llsetkeybinddialog.cpp new file mode 100644 index 0000000000..4b36822e9a --- /dev/null +++ b/indra/newview/llsetkeybinddialog.cpp @@ -0,0 +1,380 @@ +/** + * @file llsetkeybinddialog.cpp + * @brief LLSetKeyBindDialog class implementation. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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 "llsetkeybinddialog.h" + +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "lleventtimer.h" +#include "llfloaterreg.h" +#include "llfocusmgr.h" +#include "llkeyconflict.h" +#include "llviewercontrol.h" + +class LLSetKeyBindDialog::Updater : public LLEventTimer +{ +public: + + typedef boost::function<void(MASK)> callback_t; + + Updater(callback_t cb, F32 period, MASK mask) + :LLEventTimer(period), + mMask(mask), + mCallback(cb) + { + mEventTimer.start(); + } + + virtual ~Updater(){} + +protected: + BOOL tick() + { + mCallback(mMask); + // Deletes itseft after execution + return TRUE; + } + +private: + MASK mMask; + callback_t mCallback; +}; + +bool LLSetKeyBindDialog::sRecordKeys = false; + +LLSetKeyBindDialog::LLSetKeyBindDialog(const LLSD& key) + : LLModalDialog(key), + pParent(NULL), + mKeyFilterMask(DEFAULT_KEY_FILTER), + pUpdater(NULL), + mLastMaskKey(0), + mContextConeOpacity(0.f), + mContextConeInAlpha(0.f), + mContextConeOutAlpha(0.f), + mContextConeFadeTime(0.f) +{ + mContextConeInAlpha = gSavedSettings.getF32("ContextConeInAlpha"); + mContextConeOutAlpha = gSavedSettings.getF32("ContextConeOutAlpha"); + mContextConeFadeTime = gSavedSettings.getF32("ContextConeFadeTime"); +} + +LLSetKeyBindDialog::~LLSetKeyBindDialog() +{ +} + +//virtual +BOOL LLSetKeyBindDialog::postBuild() +{ + childSetAction("SetEmpty", onBlank, this); + childSetAction("Default", onDefault, this); + childSetAction("Cancel", onCancel, this); + getChild<LLUICtrl>("Cancel")->setFocus(TRUE); + + pCheckBox = getChild<LLCheckBoxCtrl>("apply_all"); + pDesription = getChild<LLTextBase>("descritption"); + + gFocusMgr.setKeystrokesOnly(TRUE); + + return TRUE; +} + +//virtual +void LLSetKeyBindDialog::onOpen(const LLSD& data) +{ + sRecordKeys = true; + LLModalDialog::onOpen(data); +} + +//virtual +void LLSetKeyBindDialog::onClose(bool app_quiting) +{ + sRecordKeys = false; + if (pParent) + { + pParent->onCancelKeyBind(); + pParent = NULL; + } + if (pUpdater) + { + // Doubleclick timer has't fired, delete it + delete pUpdater; + pUpdater = NULL; + } + LLModalDialog::onClose(app_quiting); +} + +void LLSetKeyBindDialog::drawFrustum() +{ + static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); + drawConeToOwner(mContextConeOpacity, max_opacity, mFrustumOrigin.get(), mContextConeFadeTime, mContextConeInAlpha, mContextConeOutAlpha); +} + +//virtual +void LLSetKeyBindDialog::draw() +{ + drawFrustum(); + LLModalDialog::draw(); +} + +void LLSetKeyBindDialog::setParent(LLKeyBindResponderInterface* parent, LLView* frustum_origin, U32 key_mask) +{ + pParent = parent; + mFrustumOrigin = frustum_origin->getHandle(); + mKeyFilterMask = key_mask; + + std::string input; + if ((key_mask & ALLOW_MOUSE) != 0) + { + input = getString("mouse"); + } + if ((key_mask & ALLOW_KEYS) != 0) + { + if (!input.empty()) + { + input += ", "; + } + input += getString("keyboard"); + } + pDesription->setText(getString("basic_description")); + pDesription->setTextArg("[INPUT]", input); +} + +// static +bool LLSetKeyBindDialog::recordKey(KEY key, MASK mask, BOOL down) +{ + if (sRecordKeys) + { + LLSetKeyBindDialog* dialog = LLFloaterReg::getTypedInstance<LLSetKeyBindDialog>("keybind_dialog", LLSD()); + if (dialog && dialog->getVisible()) + { + return dialog->recordAndHandleKey(key, mask, down); + } + else + { + LL_WARNS() << "Key recording was set despite no open dialog" << LL_ENDL; + sRecordKeys = false; + } + } + return false; +} + +bool LLSetKeyBindDialog::recordAndHandleKey(KEY key, MASK mask, BOOL down) +{ + if ((key == 'Q' && mask == MASK_CONTROL) + || key == KEY_ESCAPE) + { + sRecordKeys = false; + closeFloater(); + return true; + } + + if (key == KEY_DELETE) + { + setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false); + sRecordKeys = false; + closeFloater(); + return false; + } + + // forbidden keys + if (key == KEY_NONE + || key == KEY_RETURN + || key == KEY_BACKSPACE) + { + return false; + } + + if (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT) + { + // Mask keys get special treatment + if ((mKeyFilterMask & ALLOW_MASKS) == 0) + { + // Masks by themself are not allowed + return false; + } + if (down == TRUE) + { + // Most keys are handled on 'down' event because menu is handled on 'down' + // masks are exceptions to let other keys be handled + mLastMaskKey = key; + return false; + } + if (mLastMaskKey != key) + { + // This was mask+key combination that got rejected, don't handle mask's key + // Or user did something like: press shift, press ctrl, release shift + return false; + } + // Mask up event often generates things like 'shift key + shift mask', filter it out. + if (key == KEY_CONTROL) + { + mask &= ~MASK_CONTROL; + } + if (key == KEY_SHIFT) + { + mask &= ~MASK_SHIFT; + } + if (key == KEY_ALT) + { + mask &= ~MASK_ALT; + } + } + if ((mKeyFilterMask & ALLOW_KEYS) == 0) + { + // basic keys not allowed + return false; + } + else if ((mKeyFilterMask & ALLOW_MASK_KEYS) == 0 && mask != 0) + { + // masked keys not allowed + return false; + } + + if (LLKeyConflictHandler::isReservedByMenu(key, mask)) + { + pDesription->setText(getString("reserved_by_menu")); + pDesription->setTextArg("[KEYSTR]", LLKeyboard::stringFromAccelerator(mask,key)); + mLastMaskKey = 0; + return true; + } + + setKeyBind(CLICK_NONE, key, mask, pCheckBox->getValue().asBoolean()); + // Note/Todo: To warranty zero interference we should also consume + // an 'up' event if we recorded on 'down', not just close floater + // on first recorded combination. + sRecordKeys = false; + closeFloater(); + return true; +} + +BOOL LLSetKeyBindDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) +{ + BOOL result = FALSE; + if (!pParent) + { + // we already processed 'down' event, this is 'up', consume + closeFloater(); + result = TRUE; + } + if (!result && clicktype == CLICK_LEFT) + { + // try handling buttons first + if (down) + { + result = LLView::handleMouseDown(x, y, mask); + } + else + { + result = LLView::handleMouseUp(x, y, mask); + } + if (result) + { + setFocus(TRUE); + gFocusMgr.setKeystrokesOnly(TRUE); + } + // ignore selection related combinations + else if (down && (mask & (MASK_SHIFT | MASK_CONTROL)) == 0) + { + // this can be a double click, wait a bit; + if (!pUpdater) + { + // Note: default doubleclick time is 500ms, but can stretch up to 5s + pUpdater = new Updater(boost::bind(&onClickTimeout, this, _1), 0.7f, mask); + result = TRUE; + } + } + } + + if (!result + && (clicktype != CLICK_LEFT) // subcases were handled above + && ((mKeyFilterMask & ALLOW_MOUSE) != 0) + && (clicktype != CLICK_RIGHT || mask != 0) // reassigning menu button is not supported + && ((mKeyFilterMask & ALLOW_MASK_MOUSE) != 0 || mask == 0)) // reserved for selection + { + setKeyBind(clicktype, KEY_NONE, mask, pCheckBox->getValue().asBoolean()); + result = TRUE; + if (!down) + { + // wait for 'up' event before closing + // alternative: set pUpdater + closeFloater(); + } + } + + return result; +} + +//static +void LLSetKeyBindDialog::onCancel(void* user_data) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + self->closeFloater(); +} + +//static +void LLSetKeyBindDialog::onBlank(void* user_data) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + // tmp needs 'no key' button + self->setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false); + self->closeFloater(); +} + +//static +void LLSetKeyBindDialog::onDefault(void* user_data) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + if (self->pParent) + { + self->pParent->onDefaultKeyBind(self->pCheckBox->getValue().asBoolean()); + self->pParent = NULL; + } + self->closeFloater(); +} + +//static +void LLSetKeyBindDialog::onClickTimeout(void* user_data, MASK mask) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + + // timer will delete itself after timeout + self->pUpdater = NULL; + + self->setKeyBind(CLICK_LEFT, KEY_NONE, mask, self->pCheckBox->getValue().asBoolean()); + self->closeFloater(); +} + +void LLSetKeyBindDialog::setKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) +{ + if (pParent) + { + pParent->onSetKeyBind(click, key, mask, all_modes); + pParent = NULL; + } +} + diff --git a/indra/newview/llsetkeybinddialog.h b/indra/newview/llsetkeybinddialog.h new file mode 100644 index 0000000000..a34b952233 --- /dev/null +++ b/indra/newview/llsetkeybinddialog.h @@ -0,0 +1,106 @@ +/** + * @file llsetkeybinddialog.h + * @brief LLSetKeyBindDialog class definition + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef LL_LLSETKEYBINDDIALOG_H +#define LL_LLSETKEYBINDDIALOG_H + +#include "llmodaldialog.h" + +class LLCheckBoxCtrl; +class LLTextBase; + +// Filters for LLSetKeyBindDialog +static const U32 ALLOW_MOUSE = 1; +static const U32 ALLOW_MASK_MOUSE = 2; +static const U32 ALLOW_KEYS = 4; //keyboard +static const U32 ALLOW_MASK_KEYS = 8; +static const U32 ALLOW_MASKS = 16; +static const U32 DEFAULT_KEY_FILTER = ALLOW_MOUSE | ALLOW_MASK_MOUSE | ALLOW_KEYS | ALLOW_MASKS | ALLOW_MASK_KEYS; + + +class LLKeyBindResponderInterface +{ +public: + virtual ~LLKeyBindResponderInterface() {}; + + virtual void onCancelKeyBind() = 0; + virtual void onDefaultKeyBind(bool all_modes) = 0; + // returns true if parent failed to set key due to key being in use + virtual bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) = 0; +}; + +class LLSetKeyBindDialog : public LLModalDialog +{ +public: + LLSetKeyBindDialog(const LLSD& key); + ~LLSetKeyBindDialog(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& data); + /*virtual*/ void onClose(bool app_quiting); + /*virtual*/ void draw(); + + void setParent(LLKeyBindResponderInterface* parent, LLView* frustum_origin, U32 key_mask = DEFAULT_KEY_FILTER); + + // Wrapper around recordAndHandleKey + // It does not record, it handles, but handleKey function is already in use + static bool recordKey(KEY key, MASK mask, BOOL down); + + BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); + static void onCancel(void* user_data); + static void onBlank(void* user_data); + static void onDefault(void* user_data); + static void onClickTimeout(void* user_data, MASK mask); + + class Updater; + +private: + bool recordAndHandleKey(KEY key, MASK mask, BOOL down); + void setKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes); + LLKeyBindResponderInterface *pParent; + LLCheckBoxCtrl *pCheckBox; + LLTextBase *pDesription; + + U32 mKeyFilterMask; + Updater *pUpdater; + KEY mLastMaskKey; + + static bool sRecordKeys; // for convinience and not to check instance each time + + // drawFrustum +private: + void drawFrustum(); + + LLHandle <LLView> mFrustumOrigin; + F32 mContextConeOpacity; + F32 mContextConeInAlpha; + F32 mContextConeOutAlpha; + F32 mContextConeFadeTime; +}; + + +#endif // LL_LLSETKEYBINDDIALOG_H diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 97b5b2a57d..1e5b893cbc 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -678,8 +678,17 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force) { shader->uniform4fv(LLViewerShaderMgr::LIGHTNORM, 1, light_direction.mV); + // Legacy? SETTING_CLOUD_SCROLL_RATE("cloud_scroll_rate") LLVector4 vect_c_p_d1(mSettings[SETTING_CLOUD_POS_DENSITY1]); - vect_c_p_d1 += LLVector4(LLEnvironment::instance().getCloudScrollDelta()); + LLVector4 cloud_scroll( LLEnvironment::instance().getCloudScrollDelta() ); + + // SL-13084 EEP added support for custom cloud textures -- flip them horizontally to match the preview of Clouds > Cloud Scroll + // Keep in Sync! + // * indra\newview\llsettingsvo.cpp + // * indra\newview\app_settings\shaders\class2\windlight\cloudsV.glsl + // * indra\newview\app_settings\shaders\class1\deferred\cloudsV.glsl + cloud_scroll[0] = -cloud_scroll[0]; + vect_c_p_d1 += cloud_scroll; shader->uniform4fv(LLShaderMgr::CLOUD_POS_DENSITY1, 1, vect_c_p_d1.mV); LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 6e2b4a00fc..48151c17ea 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -140,6 +140,8 @@ BOOL LLSidepanelAppearance::postBuild() setVisibleCallback(boost::bind(&LLSidepanelAppearance::onVisibilityChanged,this,_2)); + setWearablesLoading(gAgentWearables.isCOFChangeInProgress()); + return TRUE; } diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp index 1fb63c7444..f325315933 100644 --- a/indra/newview/llskinningutil.cpp +++ b/indra/newview/llskinningutil.cpp @@ -309,7 +309,8 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a if (vol_face.mJointRiggingInfoTab.needsUpdate()) { S32 num_verts = vol_face.mNumVertices; - if (num_verts>0 && vol_face.mWeights && (skin->mJointNames.size()>0)) + S32 num_joints = skin->mJointNames.size(); + if (num_verts > 0 && vol_face.mWeights && num_joints > 0) { initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar); if (vol_face.mJointRiggingInfoTab.size()==0) @@ -343,7 +344,7 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a for (U32 k=0; k<4; ++k) { S32 joint_index = idx[k]; - if (wght[k] > 0.0f) + if (wght[k] > 0.0f && num_joints > joint_index) { S32 joint_num = skin->mJointNums[joint_index]; if (joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS) @@ -394,35 +395,6 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a } } -void LLSkinningUtil::updateRiggingInfo_(LLMeshSkinInfo* skin, LLVOAvatar *avatar, S32 num_verts, LLVector4a* weights, LLVector4a* positions, U8* joint_indices, LLJointRiggingInfoTab &rig_info_tab) -{ - LL_RECORD_BLOCK_TIME(FTM_FACE_RIGGING_INFO); - for (S32 i=0; i < num_verts; i++) - { - LLVector4a& pos = positions[i]; - LLVector4a& wght = weights[i]; - for (U32 k=0; k<4; ++k) - { - S32 joint_num = skin->mJointNums[joint_indices[k]]; - llassert(joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS); - { - rig_info_tab[joint_num].setIsRiggedTo(true); - LLMatrix4a bind_shape; - bind_shape.loadu(skin->mBindShapeMatrix); - LLMatrix4a inv_bind; - inv_bind.loadu(skin->mInvBindMatrix[joint_indices[k]]); - LLMatrix4a mat; - matMul(bind_shape, inv_bind, mat); - LLVector4a pos_joint_space; - mat.affineTransform(pos, pos_joint_space); - pos_joint_space.mul(wght[k]); - LLVector4a *extents = rig_info_tab[joint_num].getRiggedExtents(); - update_min_max(extents[0], extents[1], pos_joint_space); - } - } - } -} - // This is used for extracting rotation from a bind shape matrix that // already has scales baked in LLQuaternion LLSkinningUtil::getUnscaledQuaternion(const LLMatrix4& mat4) diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h index 549aa6a29f..efe7c85997 100644 --- a/indra/newview/llskinningutil.h +++ b/indra/newview/llskinningutil.h @@ -67,7 +67,6 @@ namespace LLSkinningUtil void initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar); void updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face); - void updateRiggingInfo_(LLMeshSkinInfo* skin, LLVOAvatar *avatar, S32 num_verts, LLVector4a* weights, LLVector4a* positions, U8* joint_indices, LLJointRiggingInfoTab &rig_info_tab); LLQuaternion getUnscaledQuaternion(const LLMatrix4& mat4); }; diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index f3439daee9..8369def968 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -155,7 +155,7 @@ F32 LLSnapshotLivePreview::getImageAspect() void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail, F32 delay) { - LL_DEBUGS() << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << LL_ENDL; + LL_DEBUGS("Snapshot") << "updateSnapshot: mSnapshotUpToDate = " << getSnapshotUpToDate() << LL_ENDL; // Update snapshot if requested. if (new_snapshot) @@ -343,7 +343,7 @@ void LLSnapshotLivePreview::draw() } else if (mShineAnimTimer.getStarted()) { - LL_DEBUGS() << "Drawing shining animation" << LL_ENDL; + LL_DEBUGS("Snapshot") << "Drawing shining animation" << LL_ENDL; F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME); // draw "shine" effect @@ -391,7 +391,7 @@ void LLSnapshotLivePreview::draw() S32 old_image_index = (mCurImageIndex + 1) % 2; if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME) { - LL_DEBUGS() << "Drawing fall animation" << LL_ENDL; + LL_DEBUGS("Snapshot") << "Drawing fall animation" << LL_ENDL; F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME; F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f); LLColor4 image_color(1.f, 1.f, 1.f, alpha); @@ -435,7 +435,7 @@ void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_pare LLView::reshape(width, height, called_from_parent); if (old_rect.getWidth() != width || old_rect.getHeight() != height) { - LL_DEBUGS() << "window reshaped, updating thumbnail" << LL_ENDL; + LL_DEBUGS("Window", "Snapshot") << "window reshaped, updating thumbnail" << LL_ENDL; if (mViewContainer && mViewContainer->isInVisibleChain()) { // We usually resize only on window reshape, so give it a chance to redraw, assign delay @@ -580,7 +580,7 @@ void LLSnapshotLivePreview::generateThumbnailImage(BOOL force_update) } else { - LL_WARNS() << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; + LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; } } // Scale to a power of 2 so it can be mapped to a texture @@ -628,7 +628,7 @@ LLViewerTexture* LLSnapshotLivePreview::getBigThumbnailImage() } else { - LL_WARNS() << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; + LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; } } // Scale to a power of 2 so it can be mapped to a texture @@ -648,7 +648,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview; if (previewp->getWidth() == 0 || previewp->getHeight() == 0) { - LL_WARNS() << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << LL_ENDL; + LL_WARNS("Snapshot") << "Incorrect dimensions: " << previewp->getWidth() << "x" << previewp->getHeight() << LL_ENDL; return FALSE; } @@ -679,7 +679,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) previewp->mCameraRot = new_camera_rot; // request a new snapshot whenever the camera moves, with a time delay BOOL new_snapshot = gSavedSettings.getBOOL("AutoSnapshot") || previewp->mForceUpdateSnapshot; - LL_DEBUGS() << "camera moved, updating thumbnail" << LL_ENDL; + LL_DEBUGS("Snapshot") << "camera moved, updating thumbnail" << LL_ENDL; previewp->updateSnapshot( new_snapshot, // whether a new snapshot is needed or merely invalidate the existing one FALSE, // or if 1st arg is false, whether to produce a new thumbnail image. @@ -695,7 +695,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) // time to produce a snapshot if(!previewp->getSnapshotUpToDate()) { - LL_DEBUGS() << "producing snapshot" << LL_ENDL; + LL_DEBUGS("Snapshot") << "producing snapshot" << LL_ENDL; if (!previewp->mPreviewImage) { previewp->mPreviewImage = new LLImageRaw; @@ -745,7 +745,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) previewp->getWindow()->decBusyCount(); previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode previewp->mSnapshotActive = FALSE; - LL_DEBUGS() << "done creating snapshot" << LL_ENDL; + LL_DEBUGS("Snapshot") << "done creating snapshot" << LL_ENDL; } if (!previewp->getThumbnailUpToDate()) @@ -836,7 +836,7 @@ LLPointer<LLImageRaw> LLSnapshotLivePreview::getEncodedImage() if (getSnapshotType() == LLSnapshotModel::SNAPSHOT_TEXTURE) { // We don't store the intermediate formatted image in mFormattedImage in the J2C case - LL_DEBUGS() << "Encoding new image of format J2C" << LL_ENDL; + LL_DEBUGS("Snapshot") << "Encoding new image of format J2C" << LL_ENDL; LLPointer<LLImageJ2C> formatted = new LLImageJ2C; // Copy the preview LLPointer<LLImageRaw> scaled = new LLImageRaw( @@ -921,13 +921,13 @@ LLPointer<LLImageFormatted> LLSnapshotLivePreview::getFormattedImage() } else { - LL_WARNS() << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; + LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; } } // Create the new formatted image of the appropriate format. LLSnapshotModel::ESnapshotFormat format = getSnapshotFormat(); - LL_DEBUGS() << "Encoding new image of format " << format << LL_ENDL; + LL_DEBUGS("Snapshot") << "Encoding new image of format " << format << LL_ENDL; switch (format) { @@ -952,7 +952,7 @@ LLPointer<LLImageFormatted> LLSnapshotLivePreview::getFormattedImage() void LLSnapshotLivePreview::setSize(S32 w, S32 h) { - LL_DEBUGS() << "setSize(" << w << ", " << h << ")" << LL_ENDL; + LL_DEBUGS("Snapshot") << "setSize(" << w << ", " << h << ")" << LL_ENDL; setWidth(w); setHeight(h); } @@ -974,7 +974,7 @@ void LLSnapshotLivePreview::getSize(S32& w, S32& h) const void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) { - LL_DEBUGS() << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << LL_ENDL; + LL_DEBUGS("Snapshot") << "saving texture: " << mPreviewImage->getWidth() << "x" << mPreviewImage->getHeight() << LL_ENDL; // gen a new uuid for this asset LLTransactionID tid; tid.generate(); @@ -997,12 +997,12 @@ void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) } else { - LL_WARNS() << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; + LL_WARNS("Snapshot") << "Couldn't find a path to the following filter : " << getFilter() << LL_ENDL; } } scaled->biasedScaleToPowerOfTwo(MAX_TEXTURE_SIZE); - LL_DEBUGS() << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << LL_ENDL; + LL_DEBUGS("Snapshot") << "scaled texture to " << scaled->getWidth() << "x" << scaled->getHeight() << LL_ENDL; if (formatted->encode(scaled, 0.0f)) { @@ -1030,7 +1030,7 @@ void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) else { LLNotificationsUtil::add("ErrorEncodingSnapshot"); - LL_WARNS() << "Error encoding snapshot" << LL_ENDL; + LL_WARNS("Snapshot") << "Error encoding snapshot" << LL_ENDL; } add(LLStatViewer::SNAPSHOT, 1); diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 77bbcdada6..2cb27d85bc 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -556,7 +556,9 @@ void LLSpatialGroup::shift(const LLVector4a &offset) if (!getSpatialPartition()->mRenderByGroup && getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_TREE && getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_TERRAIN && - getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_BRIDGE) + getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_BRIDGE && + getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_AVATAR && + getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_CONTROL_AV) { setState(GEOM_DIRTY); gPipeline.markRebuild(this, TRUE); @@ -994,7 +996,7 @@ LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible) } LLSpatialGroup* group = drawablep->getSpatialGroup(); - llassert(group != NULL); + //llassert(group != NULL); if (group && was_visible && group->isOcclusionState(LLSpatialGroup::QUERY_PENDING)) { @@ -3123,13 +3125,13 @@ void renderRaycast(LLDrawable* drawablep) LLGLEnable blend(GL_BLEND); gGL.diffuseColor4f(0,1,1,0.5f); - if (drawablep->getVOVolume()) + LLVOVolume* vobj = drawablep->getVOVolume(); + if (vobj && !vobj->isDead()) { //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //pushVerts(drawablep->getFace(gDebugRaycastFaceHit), LLVertexBuffer::MAP_VERTEX); //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - LLVOVolume* vobj = drawablep->getVOVolume(); LLVolume* volume = vobj->getVolume(); bool transform = true; @@ -3358,7 +3360,7 @@ public: for (OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i) { LLDrawable* drawable = (LLDrawable*)(*i)->getDrawable(); - if(!drawable) + if(!drawable || drawable->isDead()) { continue; } @@ -3448,7 +3450,7 @@ public: U8 index = facep->getTextureIndex(); if (facep->mDrawInfo) { - if (index < 255) + if (index < FACE_DO_NOT_BATCH_TEXTURES) { if (facep->mDrawInfo->mTextureList.size() <= index) { diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h index 7e65da42f7..919f386d29 100644 --- a/indra/newview/llspatialpartition.h +++ b/indra/newview/llspatialpartition.h @@ -685,6 +685,18 @@ public: virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count) { LLVolumeGeometryManager::addGeometryCount(group, vertex_count, index_count); } }; +class LLAvatarBridge : public LLVolumeBridge +{ +public: + LLAvatarBridge(LLDrawable* drawablep, LLViewerRegion* regionp); +}; + +class LLControlAVBridge : public LLVolumeBridge +{ +public: + LLControlAVBridge(LLDrawable* drawablep, LLViewerRegion* regionp); +}; + class LLHUDBridge : public LLVolumeBridge { public: @@ -702,6 +714,18 @@ public: virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count) { } }; +class LLAvatarPartition : public LLBridgePartition +{ +public: + LLAvatarPartition(LLViewerRegion* regionp); +}; + +class LLControlAVPartition : public LLBridgePartition +{ +public: + LLControlAVPartition(LLViewerRegion* regionp); +}; + class LLHUDPartition : public LLBridgePartition { public: diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 6d20dcf188..af0421b4c5 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -917,9 +917,9 @@ bool idle_startup() } // Set PerAccountSettingsFile to the default value. - gSavedSettings.setString("PerAccountSettingsFile", - gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, - LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount"))); + std::string settings_per_account = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount")); + gSavedSettings.setString("PerAccountSettingsFile", settings_per_account); + gDebugInfo["PerAccountSettingsFilename"] = settings_per_account; // Note: can't store warnings files per account because some come up before login @@ -1105,6 +1105,8 @@ bool idle_startup() // Its either downloading or declined. // If optional was skipped this case shouldn't // be reached. + + LL_INFOS("LLStartup") << "Forcing a quit due to update." << LL_ENDL; LLLoginInstance::getInstance()->disconnect(); LLAppViewer::instance()->forceQuit(); } @@ -1125,7 +1127,24 @@ bool idle_startup() { // This was a certificate error, so grab the certificate // and throw up the appropriate dialog. - LLPointer<LLCertificate> certificate = gSecAPIHandler->getCertificate(response["certificate"]); + LLPointer<LLCertificate> certificate; + try + { + certificate = gSecAPIHandler->getCertificate(response["certificate"]); + } + catch (LLCertException &cert_exception) + { + LL_WARNS("LLStartup", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL; + LLSD args; + args["REASON"] = LLTrans::getString(cert_exception.what()); + + LLNotificationsUtil::add("GeneralCertificateErrorShort", args, response, + general_cert_done); + + reset_login(); + gSavedSettings.setBOOL("AutoLogin", FALSE); + show_connect_box = true; + } if(certificate) { LLSD args = transform_cert_args(certificate); @@ -1211,7 +1230,6 @@ bool idle_startup() display_startup(); gAgentCamera.init(); display_startup(); - set_underclothes_menu_options(); display_startup(); // Since we connected, save off the settings so the user doesn't have to @@ -1407,7 +1425,10 @@ bool idle_startup() // update the voice settings *after* gCacheName initialization // so that we can construct voice UI that relies on the name cache - LLVoiceClient::getInstance()->updateSettings(); + if (LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->updateSettings(); + } display_startup(); // create a container's instance for start a controlling conversation windows @@ -3519,11 +3540,6 @@ bool process_login_success_response() } // Request the map server url - // Non-agni grids have a different default location. - if (!LLGridManager::getInstance()->isInProductionGrid()) - { - gSavedSettings.setString("MapServerURL", "http://test.map.secondlife.com.s3.amazonaws.com/"); - } std::string map_server_url = response["map-server-url"]; if(!map_server_url.empty()) { diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 2e52414d71..a9f19dc32d 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -52,7 +52,8 @@ const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE;//1024; const F32 TEXTURE_CACHE_PURGE_AMOUNT = .20f; // % amount to reduce the cache by when it exceeds its limit const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead to regenerate) const S32 TEXTURE_FAST_CACHE_ENTRY_OVERHEAD = sizeof(S32) * 4; //w, h, c, level -const S32 TEXTURE_FAST_CACHE_ENTRY_SIZE = 16 * 16 * 4 + TEXTURE_FAST_CACHE_ENTRY_OVERHEAD; +const S32 TEXTURE_FAST_CACHE_DATA_SIZE = 16 * 16 * 4; +const S32 TEXTURE_FAST_CACHE_ENTRY_SIZE = TEXTURE_FAST_CACHE_DATA_SIZE + TEXTURE_FAST_CACHE_ENTRY_OVERHEAD; const F32 TEXTURE_LAZY_PURGE_TIME_LIMIT = .004f; // 4ms. Would be better to autoadjust, but there is a major cache rework in progress. class LLTextureCacheWorker : public LLWorkerClass @@ -616,6 +617,9 @@ bool LLTextureCacheRemoteWorker::doWrite() if(idx >= 0) { // write to the fast cache. + // mRawImage is not entirely safe here since it is a pointer to one owned by cache worker, + // it could have been retrieved via getRequestFinished() and then modified. + // If writeToFastCache crashes, something is wrong around fetch worker. if(!mCache->writeToFastCache(mID, idx, mRawImage, mRawDiscardLevel)) { LL_WARNS() << "writeToFastCache failed" << LL_ENDL; @@ -2065,7 +2069,9 @@ LLPointer<LLImageRaw> LLTextureCache::readFromFastCache(const LLUUID& id, S32& d } S32 image_size = head[0] * head[1] * head[2]; - if(!image_size) //invalid + if(image_size <= 0 + || image_size > TEXTURE_FAST_CACHE_DATA_SIZE + || head[3] < 0) //invalid { closeFastCache(); return NULL; @@ -2087,46 +2093,6 @@ LLPointer<LLImageRaw> LLTextureCache::readFromFastCache(const LLUUID& id, S32& d return raw; } -#if LL_WINDOWS - -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific - -U32 exception_dupe_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) -{ - if (code == STATUS_MSC_EXCEPTION) - { - // C++ exception, go on - return EXCEPTION_CONTINUE_SEARCH; - } - else - { - // handle it - return EXCEPTION_EXECUTE_HANDLER; - } -} - -//due to unwinding -void dupe(LLPointer<LLImageRaw> &raw) -{ - raw = raw->duplicate(); -} - -void logExceptionDupplicate(LLPointer<LLImageRaw> &raw) -{ - __try - { - dupe(raw); - } - __except (exception_dupe_filter(GetExceptionCode(), GetExceptionInformation())) - { - // convert to C++ styled exception - char integer_string[32]; - sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); - throw std::exception(integer_string); - } -} -#endif - //return the fast cache location bool LLTextureCache::writeToFastCache(LLUUID image_id, S32 id, LLPointer<LLImageRaw> raw, S32 discardlevel) { @@ -2143,8 +2109,9 @@ bool LLTextureCache::writeToFastCache(LLUUID image_id, S32 id, LLPointer<LLImage c = raw->getComponents(); S32 i = 0 ; - - while(((w >> i) * (h >> i) * c) > TEXTURE_FAST_CACHE_ENTRY_SIZE - TEXTURE_FAST_CACHE_ENTRY_OVERHEAD) + + // Search for a discard level that will fit into fast cache + while(((w >> i) * (h >> i) * c) > TEXTURE_FAST_CACHE_DATA_SIZE) { ++i ; } @@ -2155,31 +2122,8 @@ bool LLTextureCache::writeToFastCache(LLUUID image_id, S32 id, LLPointer<LLImage h >>= i; if(w * h *c > 0) //valid { - //make a duplicate to keep the original raw image untouched. - - try - { -#if LL_WINDOWS - // Temporary diagnostics for scale/duplicate crash - logExceptionDupplicate(raw); -#else - raw = raw->duplicate(); -#endif - } - catch (...) - { - removeFromCache(image_id); - LL_ERRS() << "Failed to cache image: " << image_id - << " local id: " << id - << " Exception: " << boost::current_exception_diagnostic_information() - << " Image new width: " << w - << " Image new height: " << h - << " Image new components: " << c - << " Image discard difference: " << i - << LL_ENDL; - - return false; - } + // Make a duplicate to keep the original raw image untouched. + raw = raw->duplicate(); if (raw->isBufferInvalid()) { diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 6a0464c657..1c4a56b549 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -73,6 +73,7 @@ #include "llavatarappearancedefines.h" + static const S32 LOCAL_TRACKING_ID_COLUMN = 1; //static const char CURRENT_IMAGE_NAME[] = "Current Texture"; @@ -139,17 +140,17 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selecti if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID)) { - if ( mBakeTextureEnabled && mModeSelector->getSelectedIndex() != 2) + if ( mBakeTextureEnabled && mModeSelector->getValue().asInteger() != 2) { - mModeSelector->setSelectedIndex(2, 0); + mModeSelector->selectByValue(2); onModeSelect(0,this); } } else { - if (mModeSelector->getSelectedIndex() == 2) + if (mModeSelector->getValue().asInteger() == 2) { - mModeSelector->setSelectedIndex(0, 0); + mModeSelector->selectByValue(0); onModeSelect(0,this); } @@ -346,7 +347,7 @@ BOOL LLFloaterTexturePicker::postBuild() } mTentativeLabel = getChild<LLTextBox>("Multiple"); - mResolutionLabel = getChild<LLTextBox>("unknown"); + mResolutionLabel = getChild<LLTextBox>("size_lbl"); childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this); @@ -362,9 +363,9 @@ BOOL LLFloaterTexturePicker::postBuild() mInventoryPanel = getChild<LLInventoryPanel>("inventory panel"); - mModeSelector = getChild<LLRadioGroup>("mode_selection"); + mModeSelector = getChild<LLComboBox>("mode_selection"); mModeSelector->setCommitCallback(onModeSelect, this); - mModeSelector->setSelectedIndex(0, 0); + mModeSelector->selectByValue(0); if(mInventoryPanel) { @@ -431,7 +432,7 @@ BOOL LLFloaterTexturePicker::postBuild() getChild<LLComboBox>("l_bake_use_texture_combo_box")->setCommitCallback(onBakeTextureSelect, this); getChild<LLCheckBoxCtrl>("hide_base_mesh_region")->setCommitCallback(onHideBaseMeshRegionCheck, this); - setBakeTextureEnabled(FALSE); + setBakeTextureEnabled(TRUE); return TRUE; } @@ -755,7 +756,7 @@ void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem void LLFloaterTexturePicker::onModeSelect(LLUICtrl* ctrl, void *userdata) { LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; - int index = self->mModeSelector->getSelectedIndex(); + int index = self->mModeSelector->getValue().asInteger(); self->getChild<LLButton>("Default")->setVisible(index == 0 ? TRUE : FALSE); self->getChild<LLButton>("Blank")->setVisible(index == 0 ? TRUE : FALSE); @@ -1082,7 +1083,7 @@ void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string ) void LLFloaterTexturePicker::setLocalTextureEnabled(BOOL enabled) { - mModeSelector->setIndexEnabled(1,enabled); + mModeSelector->setEnabledByValue(1, enabled); } void LLFloaterTexturePicker::setBakeTextureEnabled(BOOL enabled) @@ -1090,18 +1091,18 @@ void LLFloaterTexturePicker::setBakeTextureEnabled(BOOL enabled) BOOL changed = (enabled != mBakeTextureEnabled); mBakeTextureEnabled = enabled; - mModeSelector->setIndexEnabled(2, enabled); + mModeSelector->setEnabledByValue(2, enabled); - if (!mBakeTextureEnabled && (mModeSelector->getSelectedIndex() == 2)) + if (!mBakeTextureEnabled && (mModeSelector->getValue().asInteger() == 2)) { - mModeSelector->setSelectedIndex(0, 0); + mModeSelector->selectByValue(0); } if (changed && mBakeTextureEnabled && LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID)) { - if (mModeSelector->getSelectedIndex() != 2) + if (mModeSelector->getValue().asInteger() != 2) { - mModeSelector->setSelectedIndex(2, 0); + mModeSelector->selectByValue(2); } } onModeSelect(0, this); @@ -1156,8 +1157,7 @@ LLTextureCtrl::LLTextureCtrl(const LLTextureCtrl::Params& p) mImageAssetID(p.image_id), mDefaultImageAssetID(p.default_image_id), mDefaultImageName(p.default_image_name), - mFallbackImage(p.fallback_image), - mBakeTextureEnabled(FALSE) + mFallbackImage(p.fallback_image) { // Default of defaults is white image for diff tex @@ -1350,7 +1350,7 @@ void LLTextureCtrl::showPicker(BOOL take_focus) } if (texture_floaterp) { - texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled); + texture_floaterp->setBakeTextureEnabled(TRUE); } LLFloater* root_floater = gFloaterView->getParentFloater(this); @@ -1529,7 +1529,6 @@ void LLTextureCtrl::setImageAssetID( const LLUUID& asset_id ) void LLTextureCtrl::setBakeTextureEnabled(BOOL enabled) { - mBakeTextureEnabled = enabled; LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get(); if (floaterp) { diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index b2a34a37c4..92f6f89af6 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -36,7 +36,6 @@ #include "llstring.h" #include "lluictrl.h" #include "llpermissionsflags.h" -#include "llradiogroup.h" #include "lltextbox.h" // for params #include "llviewerinventory.h" #include "llviewborder.h" // for params @@ -44,7 +43,7 @@ #include "llviewertexture.h" #include "llwindow.h" -class LLButton; +class LLComboBox; class LLFloaterTexturePicker; class LLInventoryItem; class LLViewerFetchedTexture; @@ -239,7 +238,6 @@ private: BOOL mShowLoadingPlaceholder; std::string mLoadingPlaceholderString; S32 mLabelWidth; - BOOL mBakeTextureEnabled; }; ////////////////////////////////////////////////////////////////////////////////////////// @@ -367,7 +365,7 @@ protected: LLSaveFolderState mSavedFolderState; BOOL mSelectedItemPinned; - LLRadioGroup* mModeSelector; + LLComboBox* mModeSelector; LLScrollListCtrl* mLocalScrollCtrl; private: diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index fe058024f6..f64db7beb5 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1977,6 +1977,11 @@ bool LLTextureFetchWorker::doWork(S32 param) setState(WAIT_ON_WRITE); ++mCacheWriteCount; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); + // This call might be under work mutex, but mRawImage is not nessesary safe here. + // If something retrieves it via getRequestFinished() and modifies, image won't + // be protected by work mutex and won't be safe to use here nor in cache worker. + // So make sure users of getRequestFinished() does not attempt to modify image while + // fetcher is working mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, mFormattedImage->getData(), datasize, mFileSize, mRawImage, mDecodedDiscard, responder); diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index cdf8868597..2aa194e141 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -92,6 +92,7 @@ public: void deleteAllRequests(); // Threads: T* + // keep in mind that if fetcher isn't done, it still might need original raw image bool getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux, LLCore::HttpStatus& last_http_get_status); diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 0d2edc0268..7a0f69fed5 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -423,8 +423,8 @@ void LLAvatarTexBar::draw() LLColor4 color; U32 line_num = 1; - for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearance::getDictionary()->getBakedTextures().begin(); + baked_iter != LLAvatarAppearance::getDictionary()->getBakedTextures().end(); ++baked_iter) { const LLAvatarAppearanceDefines::EBakedTextureIndex baked_index = baked_iter->first; @@ -549,7 +549,7 @@ void LLGLTexMemBar::draw() U32 texFetchLatMed = U32(recording.getMean(LLTextureFetch::sTexFetchLatency).value() * 1000.0f); U32 texFetchLatMax = U32(recording.getMax(LLTextureFetch::sTexFetchLatency).value() * 1000.0f); - text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB", + text = llformat("GL Tot: %d/%d MB Bound: %4d/%4d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB", total_mem.value(), max_total_mem.value(), bound_mem.value(), diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index c5e31ff8e6..5235914c34 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -60,7 +60,7 @@ LLTool::~LLTool() } } -BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); @@ -83,9 +83,9 @@ BOOL LLTool::handleMouseDown(S32 x, S32 y, MASK mask) LL_INFOS() << "LLTool left mouse down" << LL_ENDL; } // by default, didn't handle it + // AGENT_CONTROL_LBUTTON_DOWN is handled by scanMouse() and scanKey() // LL_INFOS() << "LLTool::handleMouseDown" << LL_ENDL; - gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN); - return TRUE; + return FALSE; } BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask) @@ -95,8 +95,8 @@ BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask) LL_INFOS() << "LLTool left mouse up" << LL_ENDL; } // by default, didn't handle it + // AGENT_CONTROL_LBUTTON_UP is handled by scanMouse() and scanKey() // LL_INFOS() << "LLTool::handleMouseUp" << LL_ENDL; - gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP); return TRUE; } diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h index 308983afda..41a38804ce 100644 --- a/indra/newview/lltool.h +++ b/indra/newview/lltool.h @@ -49,7 +49,7 @@ public: virtual BOOL isView() const { return FALSE; } // Virtual functions inherited from LLMouseHandler - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltoolgrab.cpp b/indra/newview/lltoolgrab.cpp index a4806ceaf6..f01b374db1 100644 --- a/indra/newview/lltoolgrab.cpp +++ b/indra/newview/lltoolgrab.cpp @@ -142,8 +142,9 @@ BOOL LLToolGrabBase::handleMouseDown(S32 x, S32 y, MASK mask) // call the base class to propogate info to sim LLTool::handleMouseDown(x, y, mask); - - if (!gAgent.leftButtonGrabbed()) + + // leftButtonGrabbed() checks if controls are reserved by scripts, but does not take masks into account + if (!gAgent.leftButtonGrabbed() || ((mask & DEFAULT_GRAB_MASK) != 0 && !gAgentCamera.cameraMouselook())) { // can grab transparent objects (how touch event propagates, scripters rely on this) gViewerWindow->pickAsync(x, y, mask, pickCallback, /*BOOL pick_transparent*/ TRUE); diff --git a/indra/newview/lltoolgrab.h b/indra/newview/lltoolgrab.h index 02ed5c26d7..ce0de0f946 100644 --- a/indra/newview/lltoolgrab.h +++ b/indra/newview/lltoolgrab.h @@ -44,6 +44,7 @@ class LLPickInfo; void send_ObjectGrab_message(LLViewerObject* object, const LLPickInfo & pick, const LLVector3 &grab_offset); void send_ObjectDeGrab_message(LLViewerObject* object, const LLPickInfo & pick); +const MASK DEFAULT_GRAB_MASK = MASK_CONTROL; /** * LLToolGrabBase contains most of the semantics of LLToolGrab. It's just that diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index f499c34ca4..322d0bc727 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -63,6 +63,7 @@ #include "llviewerobject.h" #include "llviewerparcelmgr.h" #include "llviewerwindow.h" +#include "llviewerinput.h" #include "llviewermedia.h" #include "llvoavatarself.h" #include "llviewermediafocus.h" @@ -83,7 +84,6 @@ LLToolPie::LLToolPie() mMouseOutsideSlop( false ), mMouseSteerX(-1), mMouseSteerY(-1), - mBlockClickToWalk(false), mClickAction(0), mClickActionBuyEnabled( gSavedSettings.getBOOL("ClickActionBuyEnabled") ), mClickActionPayEnabled( gSavedSettings.getBOOL("ClickActionPayEnabled") ), @@ -91,7 +91,7 @@ LLToolPie::LLToolPie() { } -BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) +BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); @@ -172,10 +172,8 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) mPick.mKeyMask = mask; mMouseButtonDown = true; - - handleLeftClickPick(); - return TRUE; + return handleLeftClickPick(); } // Spawn context menus on right mouse down so you can drag over and select @@ -391,8 +389,6 @@ BOOL LLToolPie::handleLeftClickPick() // put focus back "in world" if (gFocusMgr.getKeyboardFocus()) { - // don't click to walk on attempt to give focus to world - mBlockClickToWalk = true; gFocusMgr.setKeyboardFocus(NULL); } @@ -436,7 +432,7 @@ BOOL LLToolPie::handleLeftClickPick() } object = (LLViewerObject*)object->getParent(); } - if (object && object == gAgentAvatarp && !gSavedSettings.getBOOL("ClickToWalk")) + if (object && object == gAgentAvatarp) { // we left clicked on avatar, switch to focus mode mMouseButtonDown = false; @@ -453,7 +449,6 @@ BOOL LLToolPie::handleLeftClickPick() // LLFirstUse::useLeftClickNoHit(); ///////// - // Eat the event return LLTool::handleMouseDown(x, y, mask); } @@ -560,17 +555,120 @@ void LLToolPie::resetSelection() mClickAction = 0; } -void LLToolPie::walkToClickedLocation() +bool LLToolPie::walkToClickedLocation() { - if(mAutoPilotDestination) { mAutoPilotDestination->markDead(); } - mAutoPilotDestination = (LLHUDEffectBlob *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BLOB, FALSE); - mAutoPilotDestination->setPositionGlobal(mPick.mPosGlobal); - mAutoPilotDestination->setPixelSize(5); - mAutoPilotDestination->setColor(LLColor4U(170, 210, 190)); - mAutoPilotDestination->setDuration(3.f); - - LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal; - gAgent.startAutoPilotGlobal(pos, std::string(), NULL, NULL, NULL, 0.f, 0.03f, FALSE); + if (gAgent.getFlying() // don't auto-navigate while flying until that works + || !gAgentAvatarp + || gAgentAvatarp->isSitting()) + { + return false; + } + + LLPickInfo saved_pick = mPick; + if (gAgentCamera.getCameraMode() != CAMERA_MODE_MOUSELOOK) + { + mPick = gViewerWindow->pickImmediate(mHoverPick.mMousePt.mX, mHoverPick.mMousePt.mY, + FALSE /* ignore transparent */, + FALSE /* ignore rigged */, + FALSE /* ignore particles */); + } + else + { + // We do not handle hover in mouselook as we do in other modes, so + // use croshair's position to do a pick + mPick = gViewerWindow->pickImmediate(gViewerWindow->getWorldViewRectScaled().getWidth() / 2, + gViewerWindow->getWorldViewRectScaled().getHeight() / 2, + FALSE /* ignore transparent */, + FALSE /* ignore rigged */, + FALSE /* ignore particles */); + } + + if (mPick.mPickType == LLPickInfo::PICK_OBJECT) + { + if (mPick.getObject() && mPick.getObject()->isHUDAttachment()) + { + mPick = saved_pick; + return false; + } + } + + LLViewerObject* avatar_object = mPick.getObject(); + + // get pointer to avatar + while (avatar_object && !avatar_object->isAvatar()) + { + avatar_object = (LLViewerObject*)avatar_object->getParent(); + } + + if (avatar_object && ((LLVOAvatar*)avatar_object)->isSelf()) + { + const F64 SELF_CLICK_WALK_DISTANCE = 3.0; + // pretend we picked some point a bit in front of avatar + mPick.mPosGlobal = gAgent.getPositionGlobal() + LLVector3d(LLViewerCamera::instance().getAtAxis()) * SELF_CLICK_WALK_DISTANCE; + } + + if ((mPick.mPickType == LLPickInfo::PICK_LAND && !mPick.mPosGlobal.isExactlyZero()) || + (mPick.mObjectID.notNull() && !mPick.mPosGlobal.isExactlyZero())) + { + gAgentCamera.setFocusOnAvatar(TRUE, TRUE); + + if (mAutoPilotDestination) { mAutoPilotDestination->markDead(); } + mAutoPilotDestination = (LLHUDEffectBlob *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BLOB, FALSE); + mAutoPilotDestination->setPositionGlobal(mPick.mPosGlobal); + mAutoPilotDestination->setPixelSize(5); + mAutoPilotDestination->setColor(LLColor4U(170, 210, 190)); + mAutoPilotDestination->setDuration(3.f); + + LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal; + gAgent.startAutoPilotGlobal(pos, std::string(), NULL, NULL, NULL, 0.f, 0.03f, FALSE); + LLFirstUse::notMoving(false); + showVisualContextMenuEffect(); + return true; + } + else + { + LL_DEBUGS() << "walk target was " + << (mPick.mPosGlobal.isExactlyZero() ? "zero" : "not zero") + << ", pick type was " << (mPick.mPickType == LLPickInfo::PICK_LAND ? "land" : "not land") + << ", pick object was " << mPick.mObjectID + << LL_ENDL; + mPick = saved_pick; + return false; + } + return true; +} + +bool LLToolPie::teleportToClickedLocation() +{ + if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) + { + // We do not handle hover in mouselook as we do in other modes, so + // use croshair's position to do a pick + BOOL pick_rigged = false; + mHoverPick = gViewerWindow->pickImmediate(gViewerWindow->getWorldViewRectScaled().getWidth() / 2, + gViewerWindow->getWorldViewRectScaled().getHeight() / 2, + FALSE, + pick_rigged); + } + LLViewerObject* objp = mHoverPick.getObject(); + LLViewerObject* parentp = objp ? objp->getRootEdit() : NULL; + + bool is_in_world = mHoverPick.mObjectID.notNull() && objp && !objp->isHUDAttachment(); + bool is_land = mHoverPick.mPickType == LLPickInfo::PICK_LAND; + bool pos_non_zero = !mHoverPick.mPosGlobal.isExactlyZero(); + bool has_touch_handler = (objp && objp->flagHandleTouch()) || (parentp && parentp->flagHandleTouch()); + bool has_click_action = final_click_action(objp); + + if (pos_non_zero && (is_land || (is_in_world && !has_touch_handler && !has_click_action))) + { + LLVector3d pos = mHoverPick.mPosGlobal; + pos.mdV[VZ] += gAgentAvatarp->getPelvisToFoot(); + gAgent.teleportViaLocationLookAt(pos); + mPick = mHoverPick; + showVisualContextMenuEffect(); + return true; + } + return false; } // When we get object properties after left-clicking on an object @@ -646,8 +744,9 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) LL_DEBUGS("UserInput") << "hover handled by LLToolPie (inactive)" << LL_ENDL; } else if (!mMouseOutsideSlop - && mMouseButtonDown - && gSavedSettings.getBOOL("ClickToWalk")) + && mMouseButtonDown + // disable camera steering if click on land is not used for moving + && gViewerInput.isMouseBindUsed(CLICK_LEFT)) { S32 delta_x = x - mMouseDownX; S32 delta_y = y - mMouseDownY; @@ -671,7 +770,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) else { // perform a separate pick that detects transparent objects since they respond to 1-click actions - LLPickInfo click_action_pick = gViewerWindow->pickImmediate(x, y, TRUE, pick_rigged); + LLPickInfo click_action_pick = gViewerWindow->pickImmediate(x, y, FALSE, pick_rigged); LLViewerObject* click_action_object = click_action_pick.getObject(); @@ -732,70 +831,10 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask) mDoubleClickTimer.reset(); } LLViewerObject* obj = mPick.getObject(); - U8 click_action = final_click_action(obj); - // let media have first pass at click - if (handleMediaMouseUp() || LLViewerMediaFocus::getInstance()->getFocus()) - { - mBlockClickToWalk = true; - } stopCameraSteering(); mMouseButtonDown = false; - if (click_action == CLICK_ACTION_NONE // not doing 1-click action - && gSavedSettings.getBOOL("ClickToWalk") // click to walk enabled - && !gAgent.getFlying() // don't auto-navigate while flying until that works - && gAgentAvatarp - && !gAgentAvatarp->isSitting() - && !mBlockClickToWalk // another behavior hasn't cancelled click to walk - ) - { - // We may be doing click to walk, but we don't want to use a target on - // a transparent object because the user thought they were clicking on - // whatever they were seeing through it, so recompute what was clicked on - // ignoring transparent objects - LLPickInfo savedPick = mPick; - mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY, - FALSE /* ignore transparent */, - FALSE /* ignore rigged */, - FALSE /* ignore particles */); - - if (!mPick.mPosGlobal.isExactlyZero() // valid coordinates for pick - && (mPick.mPickType == LLPickInfo::PICK_LAND // we clicked on land - || mPick.mObjectID.notNull())) // or on an object - { - // handle special cases of steering picks - LLViewerObject* avatar_object = mPick.getObject(); - - // get pointer to avatar - while (avatar_object && !avatar_object->isAvatar()) - { - avatar_object = (LLViewerObject*)avatar_object->getParent(); - } - - if (avatar_object && ((LLVOAvatar*)avatar_object)->isSelf()) - { - const F64 SELF_CLICK_WALK_DISTANCE = 3.0; - // pretend we picked some point a bit in front of avatar - mPick.mPosGlobal = gAgent.getPositionGlobal() + LLVector3d(LLViewerCamera::instance().getAtAxis()) * SELF_CLICK_WALK_DISTANCE; - } - gAgentCamera.setFocusOnAvatar(TRUE, TRUE); - walkToClickedLocation(); - LLFirstUse::notMoving(false); - - return TRUE; - } - else - { - LL_DEBUGS("maint5901") << "walk target was " - << (mPick.mPosGlobal.isExactlyZero() ? "zero" : "not zero") - << ", pick type was " << (mPick.mPickType == LLPickInfo::PICK_LAND ? "land" : "not land") - << ", pick object was " << mPick.mObjectID - << LL_ENDL; - // we didn't click to walk, so restore the original target - mPick = savedPick; - } - } gViewerWindow->setCursor(UI_CURSOR_ARROW); if (hasMouseCapture()) { @@ -805,7 +844,6 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask) LLToolMgr::getInstance()->clearTransientTool(); gAgentCamera.setLookAt(LOOKAT_TARGET_CONVERSATION, obj); // maybe look at object/person clicked on - mBlockClickToWalk = false; return LLTool::handleMouseUp(x, y, mask); } @@ -831,66 +869,13 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask) return TRUE; } - if (!mDoubleClickTimer.getStarted() || (mDoubleClickTimer.getElapsedTimeF32() > 0.3f)) + if (!mDoubleClickTimer.getStarted() || (mDoubleClickTimer.getElapsedTimeF32() > 0.3f)) { mDoubleClickTimer.stop(); return FALSE; } mDoubleClickTimer.stop(); - if (gSavedSettings.getBOOL("DoubleClickAutoPilot")) - { - // We may be doing double click to walk, but we don't want to use a target on - // a transparent object because the user thought they were clicking on - // whatever they were seeing through it, so recompute what was clicked on - // ignoring transparent objects - LLPickInfo savedPick = mPick; - mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY, - FALSE /* ignore transparent */, - FALSE /* ignore rigged */, - FALSE /* ignore particles */); - - if(mPick.mPickType == LLPickInfo::PICK_OBJECT) - { - if (mPick.getObject() && mPick.getObject()->isHUDAttachment()) - { - mPick = savedPick; - return FALSE; - } - } - - if ((mPick.mPickType == LLPickInfo::PICK_LAND && !mPick.mPosGlobal.isExactlyZero()) || - (mPick.mObjectID.notNull() && !mPick.mPosGlobal.isExactlyZero())) - { - walkToClickedLocation(); - return TRUE; - } - else - { - // restore the original pick for any other purpose - mPick = savedPick; - } - } - else if (gSavedSettings.getBOOL("DoubleClickTeleport")) - { - LLViewerObject* objp = mPick.getObject(); - LLViewerObject* parentp = objp ? objp->getRootEdit() : NULL; - - bool is_in_world = mPick.mObjectID.notNull() && objp && !objp->isHUDAttachment(); - bool is_land = mPick.mPickType == LLPickInfo::PICK_LAND; - bool pos_non_zero = !mPick.mPosGlobal.isExactlyZero(); - bool has_touch_handler = (objp && objp->flagHandleTouch()) || (parentp && parentp->flagHandleTouch()); - bool has_click_action = final_click_action(objp); - - if (pos_non_zero && (is_land || (is_in_world && !has_touch_handler && !has_click_action))) - { - LLVector3d pos = mPick.mPosGlobal; - pos.mdV[VZ] += gAgentAvatarp->getPelvisToFoot(); - gAgent.teleportViaLocationLookAt(pos); - return TRUE; - } - } - return FALSE; } @@ -1422,7 +1407,6 @@ void LLToolPie::VisitHomePage(const LLPickInfo& info) void LLToolPie::handleSelect() { // tool is reselected when app gets focus, etc. - mBlockClickToWalk = true; } void LLToolPie::handleDeselect() @@ -1449,7 +1433,7 @@ LLTool* LLToolPie::getOverrideTool(MASK mask) { if (gSavedSettings.getBOOL("EnableGrab")) { - if (mask == MASK_CONTROL) + if (mask == DEFAULT_GRAB_MASK) { return LLToolGrab::getInstance(); } @@ -1483,7 +1467,7 @@ void LLToolPie::stopCameraSteering() bool LLToolPie::inCameraSteerMode() { - return mMouseButtonDown && mMouseOutsideSlop && gSavedSettings.getBOOL("ClickToWalk"); + return mMouseButtonDown && mMouseOutsideSlop; } // true if x,y outside small box around start_x,start_y @@ -1934,7 +1918,6 @@ void LLToolPie::startCameraSteering() { LLFirstUse::notMoving(false); mMouseOutsideSlop = true; - mBlockClickToWalk = true; if (gAgentCamera.getFocusOnAvatar()) { diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index 2d6c22f425..8f6100e4b4 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -42,7 +42,7 @@ class LLToolPie : public LLTool, public LLSingleton<LLToolPie> public: // Virtual functions inherited from LLMouseHandler - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); @@ -68,8 +68,8 @@ public: LLViewerObject* getClickActionObject() { return mClickActionObject; } LLObjectSelection* getLeftClickSelection() { return (LLObjectSelection*)mLeftClickSelection; } void resetSelection(); - void walkToClickedLocation(); - void blockClickToWalk() { mBlockClickToWalk = true; } + bool walkToClickedLocation(); + bool teleportToClickedLocation(); void stopClickToWalk(); static void selectionPropertiesReceived(); @@ -111,7 +111,6 @@ private: LLPointer<LLHUDEffectBlob> mAutoPilotDestination; LLPointer<LLHUDEffectBlob> mMouseSteerGrabPoint; bool mClockwise; - bool mBlockClickToWalk; LLUUID mMediaMouseCaptureID; LLPickInfo mPick; LLPickInfo mHoverPick; diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index cacdee7e83..7842d24279 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -49,7 +49,8 @@ /// LLViewerAssetRequest ///---------------------------------------------------------------------------- -static const std::string VIEWER_ASSET_STORAGE_CORO_POOL = "VAssetStorage"; + // There is also PoolSizeAssetStorage value in setting that should mirror this name +static const std::string VIEWER_ASSET_STORAGE_CORO_POOL = "AssetStorage"; /** * @brief Local class to encapsulate asset fetch requests with a timestamp. @@ -136,6 +137,14 @@ LLViewerAssetStorage::~LLViewerAssetStorage() // This class has dedicated coroutine pool, clean it up, otherwise coroutines will crash later. LLCoprocedureManager::instance().close(VIEWER_ASSET_STORAGE_CORO_POOL); } + + while (mCoroWaitList.size() > 0) + { + CoroWaitList &request = mCoroWaitList.front(); + // Clean up pending downloads, delete request and trigger callbacks + removeAndCallbackPendingDownloads(request.mId, request.mType, request.mId, request.mType, LL_ERR_NOERR, LLExtStat::NONE); + mCoroWaitList.pop_front(); + } } // virtual @@ -349,6 +358,27 @@ void LLViewerAssetStorage::storeAssetData( } } +void LLViewerAssetStorage::checkForTimeouts() +{ + LLAssetStorage::checkForTimeouts(); + + // Restore requests + LLCoprocedureManager* manager = LLCoprocedureManager::getInstance(); + while (mCoroWaitList.size() > 0 + && manager->count(VIEWER_ASSET_STORAGE_CORO_POOL) < LLCoprocedureManager::DEFAULT_QUEUE_SIZE) + { + CoroWaitList &request = mCoroWaitList.front(); + + bool with_http = true; + bool is_temp = false; + LLViewerAssetStatsFF::record_enqueue(request.mType, with_http, is_temp); + + manager->enqueueCoprocedure(VIEWER_ASSET_STORAGE_CORO_POOL, "LLViewerAssetStorage::assetRequestCoro", + boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, request.mRequest, request.mId, request.mType, request.mCallback, request.mUserData)); + + mCoroWaitList.pop_front(); + } +} /** * @brief Allocate and queue an asset fetch request for the viewer @@ -406,12 +436,20 @@ void LLViewerAssetStorage::queueRequestHttp( // This is the same as the current UDP logic - don't re-request a duplicate. if (!duplicate) { - bool with_http = true; - bool is_temp = false; - LLViewerAssetStatsFF::record_enqueue(atype, with_http, is_temp); + // Coroutine buffer has fixed size (synchronization buffer, so we have no alternatives), so buffer any request above limit + if (LLCoprocedureManager::instance().count(VIEWER_ASSET_STORAGE_CORO_POOL) < LLCoprocedureManager::DEFAULT_QUEUE_SIZE) + { + bool with_http = true; + bool is_temp = false; + LLViewerAssetStatsFF::record_enqueue(atype, with_http, is_temp); - LLCoprocedureManager::instance().enqueueCoprocedure(VIEWER_ASSET_STORAGE_CORO_POOL,"LLViewerAssetStorage::assetRequestCoro", - boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, req, uuid, atype, callback, user_data)); + LLCoprocedureManager::instance().enqueueCoprocedure(VIEWER_ASSET_STORAGE_CORO_POOL, "LLViewerAssetStorage::assetRequestCoro", + boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, req, uuid, atype, callback, user_data)); + } + else + { + mCoroWaitList.emplace_back(req, uuid, atype, callback, user_data); + } } } diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index ef01d179b7..c1a5534b81 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -45,7 +45,7 @@ public: ~LLViewerAssetStorage(); - virtual void storeAssetData( + void storeAssetData( const LLTransactionID& tid, LLAssetType::EType atype, LLStoreAssetCallback callback, @@ -54,9 +54,9 @@ public: bool is_priority = false, bool store_local = false, bool user_waiting=FALSE, - F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); - - virtual void storeAssetData( + F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT) override; + + void storeAssetData( const std::string& filename, const LLTransactionID& tid, LLAssetType::EType type, @@ -65,16 +65,17 @@ public: bool temp_file = false, bool is_priority = false, bool user_waiting=FALSE, - F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); + F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT) override; + + void checkForTimeouts() override; protected: - // virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, void *user_data, BOOL duplicate, - BOOL is_priority); + BOOL is_priority) override; void queueRequestHttp(const LLUUID& uuid, LLAssetType::EType type, @@ -93,8 +94,35 @@ protected: std::string getAssetURL(const std::string& cap_url, const LLUUID& uuid, LLAssetType::EType atype); - void logAssetStorageInfo(); - + void logAssetStorageInfo() override; + + // Asset storage works through coroutines and coroutines have limited queue capacity + // This class is meant to temporary store requests when fiber's queue is full + class CoroWaitList + { + public: + CoroWaitList(LLViewerAssetRequest *req, + const LLUUID& uuid, + LLAssetType::EType atype, + LLGetAssetCallback &callback, + void *user_data) + : mRequest(req), + mId(uuid), + mType(atype), + mCallback(callback), + mUserData(user_data) + { + } + + LLViewerAssetRequest* mRequest; + LLUUID mId; + LLAssetType::EType mType; + LLGetAssetCallback mCallback; + void *mUserData; + }; + typedef std::list<CoroWaitList> wait_list_t; + wait_list_t mCoroWaitList; + std::string mViewerAssetUrl; S32 mAssetCoroCount; S32 mCountRequests; diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index d53cc3f745..bc07821ccd 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -316,24 +316,25 @@ bool LLResourceUploadInfo::findAssetTypeOfExtension(const std::string& exten, LL bool LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(const std::string& exten, LLAssetType::EType& asset_type, U32& codec, bool bulk_upload) { bool succ = false; - - codec = LLImageBase::getCodecFromExtension(exten); + std::string exten_lc(exten); + LLStringUtil::toLower(exten_lc); + codec = LLImageBase::getCodecFromExtension(exten_lc); if (codec != IMG_CODEC_INVALID) { asset_type = LLAssetType::AT_TEXTURE; succ = true; } - else if (exten == "wav") + else if (exten_lc == "wav") { asset_type = LLAssetType::AT_SOUND; succ = true; } - else if (exten == "anim") + else if (exten_lc == "anim") { asset_type = LLAssetType::AT_ANIMATION; succ = true; } - else if (!bulk_upload && (exten == "bvh")) + else if (!bulk_upload && (exten_lc == "bvh")) { asset_type = LLAssetType::AT_ANIMATION; succ = true; diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index c65431d6f6..76dc9a6790 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -187,6 +187,16 @@ static bool handleRenderPerfTestChanged(const LLSD& newvalue) bool handleRenderTransparentWaterChanged(const LLSD& newvalue) { + LLRenderTarget::sUseFBO = newvalue.asBoolean(); + if (gPipeline.isInit()) + { + gPipeline.updateRenderTransparentWater(); + gPipeline.updateRenderDeferred(); + gPipeline.releaseGLBuffers(); + gPipeline.createGLBuffers(); + gPipeline.resetVertexBuffers(); + LLViewerShaderMgr::instance()->setShaders(); + } LLWorld::getInstance()->updateWaterObjects(); return true; } @@ -500,7 +510,10 @@ bool handleHighResSnapshotChanged(const LLSD& newvalue) bool handleVoiceClientPrefsChanged(const LLSD& newvalue) { - LLVoiceClient::getInstance()->updateSettings(); + if (LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->updateSettings(); + } return true; } diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index f025863072..d314b1477a 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -423,6 +423,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) if( (gAgent.getTeleportState() != LLAgent::TELEPORT_START) && (teleport_percent > 100.f) ) { // Give up. Don't keep the UI locked forever. + LL_WARNS("Teleport") << "Giving up on teleport. elapsed time " << teleport_elapsed << " exceeds max time " << teleport_save_time << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); gAgent.setTeleportMessage(std::string()); } @@ -444,6 +445,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) gTeleportDisplayTimer.reset(); gViewerWindow->setShowProgress(TRUE); gViewerWindow->setProgressPercent(llmin(teleport_percent, 0.0f)); + LL_INFOS("Teleport") << "A teleport request has been sent, setting state to TELEPORT_REQUESTED" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_REQUESTED ); gAgent.setTeleportMessage( LLAgent::sTeleportProgressMessages["requesting"]); @@ -468,6 +470,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) gTeleportArrivalTimer.reset(); gViewerWindow->setProgressCancelButtonVisible(FALSE, LLTrans::getString("Cancel")); gViewerWindow->setProgressPercent(75.f); + LL_INFOS("Teleport") << "Changing state to TELEPORT_ARRIVING" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_ARRIVING ); gAgent.setTeleportMessage( LLAgent::sTeleportProgressMessages["arriving"]); @@ -484,6 +487,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) { arrival_fraction = 1.f; //LLFirstUse::useTeleport(); + LL_INFOS("Teleport") << "arrival_fraction is " << arrival_fraction << " changing state to TELEPORT_NONE" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } gViewerWindow->setProgressCancelButtonVisible(FALSE, LLTrans::getString("Cancel")); @@ -499,6 +503,10 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) if( gTeleportDisplayTimer.getElapsedTimeF32() > teleport_local_delay() ) { //LLFirstUse::useTeleport(); + LL_INFOS("Teleport") << "State is local and gTeleportDisplayTimer " << gTeleportDisplayTimer.getElapsedTimeF32() + << " exceeds teleport_local_delete " << teleport_local_delay + << "; setting state to TELEPORT_NONE" + << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } } @@ -710,9 +718,6 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) LLGLState::checkTextureChannels(); LLGLState::checkClientArrays(); - BOOL to_texture = gPipeline.canUseVertexShaders() && - LLPipeline::sRenderGlow; - LLAppViewer::instance()->pingMainloopTimeout("Display:Swap"); { @@ -919,31 +924,28 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) stop_glerror(); - if (to_texture) - { - gGL.setColorMask(true, true); - - if (LLPipeline::sRenderDeferred) - { - gPipeline.mDeferredScreen.bindTarget(); - glClearColor(1,0,1,1); - gPipeline.mDeferredScreen.clear(); - } - else - { - gPipeline.mScreen.bindTarget(); - if (LLPipeline::sUnderWaterRender && !gPipeline.canUseWindLightShaders()) - { - const LLColor4 &col = LLEnvironment::instance().getCurrentWater()->getWaterFogColor(); - glClearColor(col.mV[0], col.mV[1], col.mV[2], 0.f); - } - gPipeline.mScreen.clear(); - } - - gGL.setColorMask(true, false); - } - - LLAppViewer::instance()->pingMainloopTimeout("Display:RenderGeom"); + gGL.setColorMask(true, true); + + if (LLPipeline::sRenderDeferred) + { + gPipeline.mDeferredScreen.bindTarget(); + glClearColor(1, 0, 1, 1); + gPipeline.mDeferredScreen.clear(); + } + else + { + gPipeline.mScreen.bindTarget(); + if (LLPipeline::sUnderWaterRender && !gPipeline.canUseWindLightShaders()) + { + const LLColor4 &col = LLEnvironment::instance().getCurrentWater()->getWaterFogColor(); + glClearColor(col.mV[0], col.mV[1], col.mV[2], 0.f); + } + gPipeline.mScreen.clear(); + } + + gGL.setColorMask(true, false); + + LLAppViewer::instance()->pingMainloopTimeout("Display:RenderGeom"); if (!(LLAppViewer::instance()->logoutRequestSent() && LLAppViewer::instance()->hasSavedFinalSnapshot()) && !gRestoreGL) @@ -1005,38 +1007,20 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) } } - LLAppViewer::instance()->pingMainloopTimeout("Display:RenderFlush"); - - if (to_texture) - { - if (LLPipeline::sRenderDeferred) - { - gPipeline.mDeferredScreen.flush(); - if(LLRenderTarget::sUseFBO) - { - LLRenderTarget::copyContentsToFramebuffer(gPipeline.mDeferredScreen, 0, 0, gPipeline.mDeferredScreen.getWidth(), - gPipeline.mDeferredScreen.getHeight(), 0, 0, - gPipeline.mDeferredScreen.getWidth(), - gPipeline.mDeferredScreen.getHeight(), - GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); - } - } - else - { - gPipeline.mScreen.flush(); - if(LLRenderTarget::sUseFBO) - { - LLRenderTarget::copyContentsToFramebuffer(gPipeline.mScreen, 0, 0, gPipeline.mScreen.getWidth(), - gPipeline.mScreen.getHeight(), 0, 0, - gPipeline.mScreen.getWidth(), - gPipeline.mScreen.getHeight(), - GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); - } - } - } + LLAppViewer::instance()->pingMainloopTimeout("Display:RenderFlush"); - if (LLPipeline::sRenderDeferred) - { + LLRenderTarget &rt = (gPipeline.sRenderDeferred ? gPipeline.mDeferredScreen : gPipeline.mScreen); + rt.flush(); + + if (rt.sUseFBO) + { + LLRenderTarget::copyContentsToFramebuffer(rt, 0, 0, rt.getWidth(), rt.getHeight(), 0, 0, rt.getWidth(), + rt.getHeight(), GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, + GL_NEAREST); + } + + if (LLPipeline::sRenderDeferred) + { gPipeline.renderDeferredLighting(&gPipeline.mScreen); } @@ -1300,19 +1284,12 @@ void render_ui(F32 zoom_factor, int subfield) gGL.popMatrix(); } - { - BOOL to_texture = gPipeline.canUseVertexShaders() && - LLPipeline::sRenderGlow; + // Finalize scene + gPipeline.renderFinalize(); - if (to_texture) - { - gPipeline.renderBloom(gSnapshot, zoom_factor, subfield); - } - - LL_RECORD_BLOCK_TIME(FTM_RENDER_HUD); - render_hud_elements(); - render_hud_attachments(); - } + LL_RECORD_BLOCK_TIME(FTM_RENDER_HUD); + render_hud_elements(); + render_hud_attachments(); LLGLSDefault gls_default; LLGLSUIDefault gls_ui; diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp new file mode 100644 index 0000000000..c0eaa88f54 --- /dev/null +++ b/indra/newview/llviewerinput.cpp @@ -0,0 +1,1540 @@ +/** + * @file llviewerinput.cpp + * @brief LLViewerInput class implementation + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerinput.h" + +#include "llappviewer.h" +#include "llfloaterreg.h" +#include "llmath.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llfloaterimnearbychat.h" +#include "llfocusmgr.h" +#include "llkeybind.h" // LLKeyData +#include "llmorphview.h" +#include "llmoveview.h" +#include "lltoolfocus.h" +#include "lltoolpie.h" +#include "llviewercontrol.h" +#include "llviewerwindow.h" +#include "llvoavatarself.h" +#include "llfloatercamera.h" +#include "llinitparam.h" +#include "llselectmgr.h" + +// +// Constants +// + +const F32 FLY_TIME = 0.5f; +const F32 FLY_FRAMES = 4; + +const F32 NUDGE_TIME = 0.25f; // in seconds +const S32 NUDGE_FRAMES = 2; +const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed + +const LLKeyData agent_control_lbutton(CLICK_LEFT, KEY_NONE, MASK_NONE, true); + +struct LLKeyboardActionRegistry +: public LLRegistrySingleton<std::string, boost::function<bool (EKeystate keystate)>, LLKeyboardActionRegistry> +{ + LLSINGLETON_EMPTY_CTOR(LLKeyboardActionRegistry); +}; + +LLViewerInput gViewerInput; + +bool agent_jump( EKeystate s ) +{ + static BOOL first_fly_attempt(TRUE); + if (KEYSTATE_UP == s) + { + first_fly_attempt = TRUE; + return true; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + if( time < FLY_TIME + || frame_count <= FLY_FRAMES + || gAgent.upGrabbed() + || !gSavedSettings.getBOOL("AutomaticFly")) + { + gAgent.moveUp(1); + } + else + { + gAgent.setFlying(TRUE, first_fly_attempt); + first_fly_attempt = FALSE; + gAgent.moveUp(1); + } + return true; +} + +bool agent_push_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgent.moveUp(-1); + return true; +} + +static void agent_check_temporary_run(LLAgent::EDoubleTapRunMode mode) +{ + if (gAgent.mDoubleTapRunMode == mode && + gAgent.getRunning() && + !gAgent.getAlwaysRun()) + { + // Turn off temporary running. + gAgent.clearRunning(); + gAgent.sendWalkRun(gAgent.getRunning()); + } +} + +static void agent_handle_doubletap_run(EKeystate s, LLAgent::EDoubleTapRunMode mode) +{ + if (KEYSTATE_UP == s) + { + // Note: in case shift is already released, slide left/right run + // will be released in agent_turn_left()/agent_turn_right() + agent_check_temporary_run(mode); + } + else if (gSavedSettings.getBOOL("AllowTapTapHoldRun") && + KEYSTATE_DOWN == s && + !gAgent.getRunning()) + { + if (gAgent.mDoubleTapRunMode == mode && + gAgent.mDoubleTapRunTimer.getElapsedTimeF32() < NUDGE_TIME) + { + // Same walk-key was pushed again quickly; this is a + // double-tap so engage temporary running. + gAgent.setRunning(); + gAgent.sendWalkRun(gAgent.getRunning()); + } + + // Pressing any walk-key resets the double-tap timer + gAgent.mDoubleTapRunTimer.reset(); + gAgent.mDoubleTapRunMode = mode; + } +} + +static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) +{ + agent_handle_doubletap_run(s, mode); + if (KEYSTATE_UP == s) return; + + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) + { + gAgent.moveAtNudge(direction); + } + else + { + gAgent.moveAt(direction); + } +} + +bool camera_move_forward( EKeystate s ); + +bool agent_push_forward( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return true; + + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_move_forward(s); + } + else + { + agent_push_forwardbackward(s, 1, LLAgent::DOUBLETAP_FORWARD); + } + return true; +} + +bool camera_move_backward( EKeystate s ); + +bool agent_push_backward( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return true; + + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_move_backward(s); + } + else if (!gAgent.backwardGrabbed() && gAgentAvatarp->isSitting() && gSavedSettings.getBOOL("LeaveMouselook")) + { + gAgentCamera.changeCameraToThirdPerson(); + } + else + { + agent_push_forwardbackward(s, -1, LLAgent::DOUBLETAP_BACKWARD); + } + return true; +} + +static void agent_slide_leftright( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) +{ + agent_handle_doubletap_run(s, mode); + if( KEYSTATE_UP == s ) return; + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) + { + gAgent.moveLeftNudge(direction); + } + else + { + gAgent.moveLeft(direction); + } +} + + +bool agent_slide_left( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return true; + agent_slide_leftright(s, 1, LLAgent::DOUBLETAP_SLIDELEFT); + return true; +} + + +bool agent_slide_right( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return true; + agent_slide_leftright(s, -1, LLAgent::DOUBLETAP_SLIDERIGHT); + return true; +} + +bool camera_spin_around_cw( EKeystate s ); + +bool agent_turn_left(EKeystate s) +{ + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_spin_around_cw(s); + return true; + } + + if(gAgent.isMovementLocked()) return false; + + if (LLToolCamera::getInstance()->mouseSteerMode()) + { + agent_slide_left(s); + } + else + { + if (KEYSTATE_UP == s) + { + // Check temporary running. In case user released 'left' key with shift already released. + agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDELEFT); + return true; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + gAgent.moveYaw( LLFloaterMove::getYawRate( time ) ); + } + return true; +} + +bool camera_spin_around_ccw( EKeystate s ); + +bool agent_turn_right( EKeystate s ) +{ + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_spin_around_ccw(s); + return true; + } + + if(gAgent.isMovementLocked()) return false; + + if (LLToolCamera::getInstance()->mouseSteerMode()) + { + agent_slide_right(s); + } + else + { + if (KEYSTATE_UP == s) + { + // Check temporary running. In case user released 'right' key with shift already released. + agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDERIGHT); + return true; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) ); + } + return true; +} + +bool agent_look_up( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgent.movePitch(-1); + //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); + return true; +} + + +bool agent_look_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgent.movePitch(1); + //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); + return true; +} + +bool agent_toggle_fly( EKeystate s ) +{ + // Only catch the edge + if (KEYSTATE_DOWN == s ) + { + LLAgent::toggleFlying(); + } + return true; +} + +F32 get_orbit_rate() +{ + F32 time = gKeyboard->getCurKeyElapsedTime(); + if( time < NUDGE_TIME ) + { + F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME; + //LL_INFOS() << rate << LL_ENDL; + return rate; + } + else + { + return 1; + } +} + +bool camera_spin_around_ccw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); + return true; +} + + +bool camera_spin_around_cw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); + return true; +} + +bool camera_spin_around_ccw_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT ) return true; + if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) + { + //send keystrokes, but do not change camera + agent_turn_right(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.unlockView(); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); + } + return true; +} + + +bool camera_spin_around_cw_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT ) return true; + if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) + { + //send keystrokes, but do not change camera + agent_turn_left(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.unlockView(); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); + } + return true; +} + + +bool camera_spin_over( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); + return true; +} + + +bool camera_spin_under( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); + return true; +} + +bool camera_spin_over_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + if (gAgent.upGrabbed() || gAgentCamera.sitCameraEnabled()) + { + //send keystrokes, but do not change camera + agent_jump(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); + } + return true; +} + + +bool camera_spin_under_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + if (gAgent.downGrabbed() || gAgentCamera.sitCameraEnabled()) + { + //send keystrokes, but do not change camera + agent_push_down(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); + } + return true; +} + +bool camera_move_forward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitInKey( get_orbit_rate() ); + return true; +} + + +bool camera_move_backward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); + return true; +} + +bool camera_move_forward_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD ) return true; + if (gAgent.forwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) + { + agent_push_forward(s); + } + else + { + gAgentCamera.setOrbitInKey( get_orbit_rate() ); + } + return true; +} + +bool camera_move_backward_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD ) return true; + + if (gAgent.backwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) + { + agent_push_backward(s); + } + else + { + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); + } + return true; +} + +bool camera_pan_up( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanUpKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanDownKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_left( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanLeftKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_right( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanRightKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_in( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanInKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_out( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanOutKey( get_orbit_rate() ); + return true; +} + +bool camera_move_forward_fast( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitInKey(2.5f); + return true; +} + +bool camera_move_backward_fast( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitOutKey(2.5f); + return true; +} + + +bool edit_avatar_spin_ccw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); + //gMorphView->orbitLeft( get_orbit_rate() ); + return true; +} + + +bool edit_avatar_spin_cw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); + //gMorphView->orbitRight( get_orbit_rate() ); + return true; +} + +bool edit_avatar_spin_over( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); + //gMorphView->orbitUp( get_orbit_rate() ); + return true; +} + + +bool edit_avatar_spin_under( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); + //gMorphView->orbitDown( get_orbit_rate() ); + return true; +} + +bool edit_avatar_move_forward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitInKey( get_orbit_rate() ); + //gMorphView->orbitIn(); + return true; +} + + +bool edit_avatar_move_backward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); + //gMorphView->orbitOut(); + return true; +} + +bool stop_moving( EKeystate s ) +{ + //it's supposed that 'stop moving' key will be held down for some time + if( KEYSTATE_UP == s ) return true; + // stop agent + gAgent.setControlFlags(AGENT_CONTROL_STOP); + + // cancel autopilot + gAgent.stopAutoPilot(); + return true; +} + +bool start_chat( EKeystate s ) +{ + if (LLAppViewer::instance()->quitRequested()) + { + return true; // can't talk, gotta go, kthxbye! + } + if (KEYSTATE_DOWN != s) return true; + + // start chat + LLFloaterIMNearbyChat::startChat(NULL); + return true; +} + +bool start_gesture( EKeystate s ) +{ + LLUICtrl* focus_ctrlp = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus()); + if (KEYSTATE_UP == s && + ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) + { + if ((LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->getCurrentChat().empty()) + { + // No existing chat in chat editor, insert '/' + LLFloaterIMNearbyChat::startChat("/"); + } + else + { + // Don't overwrite existing text in chat editor + LLFloaterIMNearbyChat::startChat(NULL); + } + } + return true; +} + +bool run_forward(EKeystate s) +{ + if (KEYSTATE_UP != s) + { + if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD) + { + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_FORWARD; + } + if (!gAgent.getRunning()) + { + gAgent.setRunning(); + gAgent.sendWalkRun(true); + } + } + else if(KEYSTATE_UP == s) + { + if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_FORWARD) + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; + gAgent.clearRunning(); + gAgent.sendWalkRun(false); + } + agent_push_forward(s); + return true; +} + +bool run_backward(EKeystate s) +{ + if (KEYSTATE_UP != s) + { + if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD) + { + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_BACKWARD; + } + if (!gAgent.getRunning()) + { + gAgent.setRunning(); + gAgent.sendWalkRun(true); + } + } + else if (KEYSTATE_UP == s) + { + if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_BACKWARD) + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; + gAgent.clearRunning(); + gAgent.sendWalkRun(false); + } + agent_push_backward(s); + return true; +} + +bool run_left(EKeystate s) +{ + if (KEYSTATE_UP != s) + { + if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT) + { + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_SLIDELEFT; + } + if (!gAgent.getRunning()) + { + gAgent.setRunning(); + gAgent.sendWalkRun(true); + } + } + else if (KEYSTATE_UP == s) + { + if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_SLIDELEFT) + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; + gAgent.clearRunning(); + gAgent.sendWalkRun(false); + } + agent_slide_left(s); + return true; +} + +bool run_right(EKeystate s) +{ + if (KEYSTATE_UP != s) + { + if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT) + { + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_SLIDERIGHT; + } + if (!gAgent.getRunning()) + { + gAgent.setRunning(); + gAgent.sendWalkRun(true); + } + } + else if (KEYSTATE_UP == s) + { + if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_SLIDERIGHT) + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; + gAgent.clearRunning(); + gAgent.sendWalkRun(false); + } + agent_slide_right(s); + return true; +} + +bool toggle_run(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + bool run = gAgent.getAlwaysRun(); + if (run) + { + gAgent.clearAlwaysRun(); + gAgent.clearRunning(); + } + else + { + gAgent.setAlwaysRun(); + gAgent.setRunning(); + } + gAgent.sendWalkRun(!run); + return true; +} + +bool toggle_sit(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + if (gAgent.isSitting()) + { + gAgent.standUp(); + } + else + { + gAgent.sitDown(); + } + return true; +} + +bool toggle_pause_media(EKeystate s) // analogue of play/pause button in top bar +{ + if (KEYSTATE_DOWN != s) return true; + bool pause = LLViewerMedia::getInstance()->isAnyMediaPlaying(); + LLViewerMedia::getInstance()->setAllMediaPaused(pause); + return true; +} + +bool toggle_enable_media(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + bool pause = LLViewerMedia::getInstance()->isAnyMediaPlaying() || LLViewerMedia::getInstance()->isAnyMediaShowing(); + LLViewerMedia::getInstance()->setAllMediaEnabled(!pause); + return true; +} + +bool walk_to(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + return LLToolPie::getInstance()->walkToClickedLocation(); +} + +bool teleport_to(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + return LLToolPie::getInstance()->teleportToClickedLocation(); +} + +bool toggle_voice(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + if (!LLAgent::isActionAllowed("speak")) return false; + LLVoiceClient::getInstance()->toggleUserPTTState(); + return true; +} + +bool voice_follow_key(EKeystate s) +{ + if (KEYSTATE_DOWN == s) + { + if (!LLAgent::isActionAllowed("speak")) return false; + LLVoiceClient::getInstance()->setUserPTTState(true); + return true; + } + else if (KEYSTATE_UP == s && LLVoiceClient::getInstance()->getUserPTTState()) + { + LLVoiceClient::getInstance()->setUserPTTState(false); + return true; + } + return false; +} + +bool agen_control_lbutton_handle(EKeystate s) +{ + switch (s) + { + case KEYSTATE_DOWN: + gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN); + break; + case KEYSTATE_UP: + gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP); + break; + default: + break; + } + return true; +} + +#define REGISTER_KEYBOARD_ACTION(KEY, ACTION) LLREGISTER_STATIC(LLKeyboardActionRegistry, KEY, ACTION); +REGISTER_KEYBOARD_ACTION("jump", agent_jump); +REGISTER_KEYBOARD_ACTION("push_down", agent_push_down); +REGISTER_KEYBOARD_ACTION("push_forward", agent_push_forward); +REGISTER_KEYBOARD_ACTION("push_backward", agent_push_backward); +REGISTER_KEYBOARD_ACTION("look_up", agent_look_up); +REGISTER_KEYBOARD_ACTION("look_down", agent_look_down); +REGISTER_KEYBOARD_ACTION("toggle_fly", agent_toggle_fly); +REGISTER_KEYBOARD_ACTION("turn_left", agent_turn_left); +REGISTER_KEYBOARD_ACTION("turn_right", agent_turn_right); +REGISTER_KEYBOARD_ACTION("slide_left", agent_slide_left); +REGISTER_KEYBOARD_ACTION("slide_right", agent_slide_right); +REGISTER_KEYBOARD_ACTION("spin_around_ccw", camera_spin_around_ccw); +REGISTER_KEYBOARD_ACTION("spin_around_cw", camera_spin_around_cw); +REGISTER_KEYBOARD_ACTION("spin_around_ccw_sitting", camera_spin_around_ccw_sitting); +REGISTER_KEYBOARD_ACTION("spin_around_cw_sitting", camera_spin_around_cw_sitting); +REGISTER_KEYBOARD_ACTION("spin_over", camera_spin_over); +REGISTER_KEYBOARD_ACTION("spin_under", camera_spin_under); +REGISTER_KEYBOARD_ACTION("spin_over_sitting", camera_spin_over_sitting); +REGISTER_KEYBOARD_ACTION("spin_under_sitting", camera_spin_under_sitting); +REGISTER_KEYBOARD_ACTION("move_forward", camera_move_forward); +REGISTER_KEYBOARD_ACTION("move_backward", camera_move_backward); +REGISTER_KEYBOARD_ACTION("move_forward_sitting", camera_move_forward_sitting); +REGISTER_KEYBOARD_ACTION("move_backward_sitting", camera_move_backward_sitting); +REGISTER_KEYBOARD_ACTION("pan_up", camera_pan_up); +REGISTER_KEYBOARD_ACTION("pan_down", camera_pan_down); +REGISTER_KEYBOARD_ACTION("pan_left", camera_pan_left); +REGISTER_KEYBOARD_ACTION("pan_right", camera_pan_right); +REGISTER_KEYBOARD_ACTION("pan_in", camera_pan_in); +REGISTER_KEYBOARD_ACTION("pan_out", camera_pan_out); +REGISTER_KEYBOARD_ACTION("move_forward_fast", camera_move_forward_fast); +REGISTER_KEYBOARD_ACTION("move_backward_fast", camera_move_backward_fast); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_ccw", edit_avatar_spin_ccw); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_cw", edit_avatar_spin_cw); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_over", edit_avatar_spin_over); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_under", edit_avatar_spin_under); +REGISTER_KEYBOARD_ACTION("edit_avatar_move_forward", edit_avatar_move_forward); +REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward); +REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving); +REGISTER_KEYBOARD_ACTION("start_chat", start_chat); +REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture); +REGISTER_KEYBOARD_ACTION("run_forward", run_forward); +REGISTER_KEYBOARD_ACTION("run_backward", run_backward); +REGISTER_KEYBOARD_ACTION("run_left", run_left); +REGISTER_KEYBOARD_ACTION("run_right", run_right); +REGISTER_KEYBOARD_ACTION("toggle_run", toggle_run); +REGISTER_KEYBOARD_ACTION("toggle_sit", toggle_sit); +REGISTER_KEYBOARD_ACTION("toggle_pause_media", toggle_pause_media); +REGISTER_KEYBOARD_ACTION("toggle_enable_media", toggle_enable_media); +REGISTER_KEYBOARD_ACTION("teleport_to", teleport_to); +REGISTER_KEYBOARD_ACTION("walk_to", walk_to); +REGISTER_KEYBOARD_ACTION("toggle_voice", toggle_voice); +REGISTER_KEYBOARD_ACTION("voice_follow_key", voice_follow_key); +#undef REGISTER_KEYBOARD_ACTION + +LLViewerInput::LLViewerInput() +{ + resetBindings(); + + for (S32 i = 0; i < KEY_COUNT; i++) + { + mKeyHandledByUI[i] = FALSE; + } + for (S32 i = 0; i < CLICK_COUNT; i++) + { + mMouseLevel[i] = MOUSE_STATE_SILENT; + } + // we want the UI to never see these keys so that they can always control the avatar/camera + for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++) + { + mKeysSkippedByUI.insert(k); + } +} + +// static +BOOL LLViewerInput::modeFromString(const std::string& string, S32 *mode) +{ + if (string == "FIRST_PERSON") + { + *mode = MODE_FIRST_PERSON; + return TRUE; + } + else if (string == "THIRD_PERSON") + { + *mode = MODE_THIRD_PERSON; + return TRUE; + } + else if (string == "EDIT_AVATAR") + { + *mode = MODE_EDIT_AVATAR; + return TRUE; + } + else if (string == "SITTING") + { + *mode = MODE_SITTING; + return TRUE; + } + else + { + *mode = MODE_THIRD_PERSON; + return FALSE; + } +} + +// static +BOOL LLViewerInput::mouseFromString(const std::string& string, EMouseClickType *mode) +{ + if (string == "LMB") + { + *mode = CLICK_LEFT; + return TRUE; + } + else if (string == "Double LMB") + { + *mode = CLICK_DOUBLELEFT; + return TRUE; + } + else if (string == "MMB") + { + *mode = CLICK_MIDDLE; + return TRUE; + } + else if (string == "MB4") + { + *mode = CLICK_BUTTON4; + return TRUE; + } + else if (string == "MB5") + { + *mode = CLICK_BUTTON5; + return TRUE; + } + else + { + *mode = CLICK_NONE; + return FALSE; + } +} + +BOOL LLViewerInput::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated) +{ + // check for re-map + EKeyboardMode mode = gViewerInput.getMode(); + U32 keyidx = (translated_mask<<16) | translated_key; + key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx); + if (iter != mRemapKeys[mode].end()) + { + translated_key = (iter->second) & 0xff; + translated_mask = (iter->second)>>16; + } + + // No repeats of F-keys + BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12); + if (!repeatable_key && repeated) + { + return FALSE; + } + + LL_DEBUGS("UserInput") << "keydown -" << translated_key << "-" << LL_ENDL; + // skip skipped keys + if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end()) + { + mKeyHandledByUI[translated_key] = FALSE; + LL_INFOS("KeyboardHandling") << "Key wasn't handled by UI!" << LL_ENDL; + } + else + { + // it is sufficient to set this value once per call to handlekey + // without clearing it, as it is only used in the subsequent call to scanKey + mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); + // mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress) + // NOT whether some UI shortcut wishes to handle the keypress + + } + return mKeyHandledByUI[translated_key]; +} + +BOOL LLViewerInput::handleKeyUp(KEY translated_key, MASK translated_mask) +{ + return gViewerWindow->handleKeyUp(translated_key, translated_mask); +} + +BOOL LLViewerInput::bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name) +{ + S32 index; + typedef boost::function<bool(EKeystate)> function_t; + function_t function = NULL; + std::string name; + + // Allow remapping of F2-F12 + if (function_name[0] == 'F') + { + int c1 = function_name[1] - '0'; + int c2 = function_name[2] ? function_name[2] - '0' : -1; + if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9) + { + int idx = c1; + if (c2 >= 0) + idx = idx*10 + c2; + if (idx >=2 && idx <= 12) + { + U32 keyidx = ((mask<<16)|key); + (mRemapKeys[mode])[keyidx] = ((0<<16)|(KEY_F1+(idx-1))); + return TRUE; + } + } + } + + // Not remapped, look for a function + + function_t* result = LLKeyboardActionRegistry::getValue(function_name); + if (result) + { + function = *result; + } + + if (!function) + { + LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; + return FALSE; + } + + // check for duplicate first and overwrite + S32 size = mKeyBindings[mode].size(); + for (index = 0; index < size; index++) + { + if (key == mKeyBindings[mode][index].mKey && mask == mKeyBindings[mode][index].mMask) + break; + } + + if (mode >= MODE_COUNT) + { + LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; + return FALSE; + } + + LLKeyboardBinding bind; + bind.mKey = key; + bind.mMask = mask; + bind.mFunction = function; + + mKeyBindings[mode].push_back(bind); + + return TRUE; +} + +BOOL LLViewerInput::bindMouse(const S32 mode, const EMouseClickType mouse, const MASK mask, const std::string& function_name) +{ + S32 index; + typedef boost::function<bool(EKeystate)> function_t; + function_t function = NULL; + + function_t* result = LLKeyboardActionRegistry::getValue(function_name); + if (result) + { + function = *result; + } + + if (!function) + { + LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; + return FALSE; + } + + // check for duplicate first and overwrite + S32 size = mMouseBindings[mode].size(); + for (index = 0; index < size; index++) + { + if (mouse == mMouseBindings[mode][index].mMouse && mask == mMouseBindings[mode][index].mMask) + break; + } + + if (mode >= MODE_COUNT) + { + LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; + return FALSE; + } + + LLMouseBinding bind; + bind.mMouse = mouse; + bind.mMask = mask; + bind.mFunction = function; + + mMouseBindings[mode].push_back(bind); + + return TRUE; +} + +LLViewerInput::KeyBinding::KeyBinding() +: key("key"), + mouse("mouse"), + mask("mask"), + command("command") +{} + +LLViewerInput::KeyMode::KeyMode() +: bindings("binding") +{} + +LLViewerInput::Keys::Keys() +: first_person("first_person"), + third_person("third_person"), + sitting("sitting"), + edit_avatar("edit_avatar") +{} + +void LLViewerInput::resetBindings() +{ + for (S32 i = 0; i < MODE_COUNT; i++) + { + mKeyBindings[i].clear(); + mMouseBindings[i].clear(); + } +} + +S32 LLViewerInput::loadBindingsXML(const std::string& filename) +{ + resetBindings(); + + S32 binding_count = 0; + Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + binding_count += loadBindingMode(keys.first_person, MODE_FIRST_PERSON); + binding_count += loadBindingMode(keys.third_person, MODE_THIRD_PERSON); + binding_count += loadBindingMode(keys.sitting, MODE_SITTING); + binding_count += loadBindingMode(keys.edit_avatar, MODE_EDIT_AVATAR); + } + return binding_count; +} + +S32 count_masks(const MASK &mask) +{ + S32 res = 0; + if (mask & MASK_CONTROL) + { + res++; + } + if (mask & MASK_SHIFT) + { + res++; + } + if (mask & MASK_ALT) + { + res++; + } + return res; +} + +bool compare_key_by_mask(LLKeyboardBinding i1, LLKeyboardBinding i2) +{ + return (count_masks(i1.mMask) > count_masks(i2.mMask)); +} + +bool compare_mouse_by_mask(LLMouseBinding i1, LLMouseBinding i2) +{ + return (count_masks(i1.mMask) > count_masks(i2.mMask)); +} + +S32 LLViewerInput::loadBindingMode(const LLViewerInput::KeyMode& keymode, S32 mode) +{ + S32 binding_count = 0; + for (LLInitParam::ParamIterator<KeyBinding>::const_iterator it = keymode.bindings.begin(), + end_it = keymode.bindings.end(); + it != end_it; + ++it) + { + bool processed = false; + std::string key_str = it->key.getValue(); + if (!key_str.empty() && key_str != "NONE") + { + KEY key; + LLKeyboard::keyFromString(key_str, &key); + if (key != KEY_NONE) + { + MASK mask; + LLKeyboard::maskFromString(it->mask, &mask); + bindKey(mode, key, mask, it->command); + processed = true; + } + else + { + LL_WARNS_ONCE() << "There might be issues in keybindings' file" << LL_ENDL; + } + } + if (!processed && it->mouse.isProvided() && !it->mouse.getValue().empty()) + { + EMouseClickType mouse; + mouseFromString(it->mouse.getValue(), &mouse); + if (mouse != CLICK_NONE) + { + MASK mask; + LLKeyboard::maskFromString(it->mask, &mask); + bindMouse(mode, mouse, mask, it->command); + processed = true; + } + else + { + LL_WARNS_ONCE() << "There might be issues in keybindings' file" << LL_ENDL; + } + } + if (processed) + { + // total + binding_count++; + } + } + + // sort lists by mask (so that Shift+W is executed before W, if both are assigned, but if Shift+W is not assigned W should be executed) + std::sort(mKeyBindings[mode].begin(), mKeyBindings[mode].end(), compare_key_by_mask); + std::sort(mMouseBindings[mode].begin(), mMouseBindings[mode].end(), compare_mouse_by_mask); + + return binding_count; +} + +EKeyboardMode LLViewerInput::getMode() const +{ + if ( gAgentCamera.cameraMouselook() ) + { + return MODE_FIRST_PERSON; + } + else if ( gMorphView && gMorphView->getVisible()) + { + return MODE_EDIT_AVATAR; + } + else if (isAgentAvatarValid() && gAgentAvatarp->isSitting()) + { + return MODE_SITTING; + } + else + { + return MODE_THIRD_PERSON; + } +} + +bool LLViewerInput::scanKey(const std::vector<LLKeyboardBinding> &binding, + S32 binding_count, + KEY key, + MASK mask, + BOOL key_down, + BOOL key_up, + BOOL key_level, + bool repeat) const +{ + for (S32 i = 0; i < binding_count; i++) + { + if (binding[i].mKey == key) + { + if ((binding[i].mMask & mask) == binding[i].mMask) + { + bool res = false; + if (key_down && !repeat) + { + // ...key went down this frame, call function + res = binding[i].mFunction( KEYSTATE_DOWN ); + return true; + } + else if (key_up) + { + // ...key went down this frame, call function + res = binding[i].mFunction( KEYSTATE_UP ); + } + else if (key_level) + { + // ...key held down from previous frame + // Not windows, just call the function. + res = binding[i].mFunction( KEYSTATE_LEVEL ); + }//if + // Key+Mask combinations are supposed to be unique, so we won't find anything else + return res; + }//if + }//if + }//for + return false; +} + +// Called from scanKeyboard. +bool LLViewerInput::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) const +{ + if (LLApp::isExiting()) + { + return false; + } + + S32 mode = getMode(); + // Consider keyboard scanning as NOT mouse event. JC + MASK mask = gKeyboard->currentMask(FALSE); + + if (mKeyHandledByUI[key]) + { + return false; + } + + // don't process key down on repeated keys + BOOL repeat = gKeyboard->getKeyRepeated(key); + + bool res = scanKey(mKeyBindings[mode], mKeyBindings[mode].size(), key, mask, key_down, key_up, key_level, repeat); + + if (!res && agent_control_lbutton.canHandle(CLICK_NONE, key, mask)) + { + if (key_down && !repeat) + { + res = agen_control_lbutton_handle(KEYSTATE_DOWN); + } + if (key_up) + { + res = agen_control_lbutton_handle(KEYSTATE_UP); + } + } + return res; +} + +BOOL LLViewerInput::handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) +{ + BOOL handled = gViewerWindow->handleAnyMouseClick(window_impl, pos, mask, clicktype, down); + + if (clicktype != CLICK_NONE) + { + // Special case + // If UI doesn't handle double click, LMB click is issued, so supres LMB 'down' when doubleclick is set + // handle !down as if we are handling doubleclick + + bool double_click_sp = (clicktype == CLICK_LEFT + && (mMouseLevel[CLICK_DOUBLELEFT] != MOUSE_STATE_SILENT) + && mMouseLevel[CLICK_LEFT] == MOUSE_STATE_SILENT); + if (double_click_sp && !down) + { + // Process doubleclick instead + clicktype = CLICK_DOUBLELEFT; + } + + + if (double_click_sp && down) + { + // Consume click. + // Due to handling, double click that is not handled will be immediately followed by LMB click + } + // If UI handled 'down', it should handle 'up' as well + // If we handle 'down' not by UI, then we should handle 'up'/'level' regardless of UI + else if (handled) + { + // UI handled new 'down' so iterupt whatever state we were in. + if (mMouseLevel[clicktype] != MOUSE_STATE_SILENT) + { + if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) + { + mMouseLevel[clicktype] = MOUSE_STATE_CLICK; + } + else + { + mMouseLevel[clicktype] = MOUSE_STATE_UP; + } + } + } + else if (down) + { + if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) + { + // this is repeated hit (mouse does not repeat event until release) + // for now treat rapid clicking like mouse being held + mMouseLevel[clicktype] = MOUSE_STATE_LEVEL; + } + else + { + mMouseLevel[clicktype] = MOUSE_STATE_DOWN; + } + } + else if (mMouseLevel[clicktype] != MOUSE_STATE_SILENT) + { + // Released mouse key + if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) + { + mMouseLevel[clicktype] = MOUSE_STATE_CLICK; + } + else + { + mMouseLevel[clicktype] = MOUSE_STATE_UP; + } + } + } + + return handled; +} + +bool LLViewerInput::scanMouse(const std::vector<LLMouseBinding> &binding, S32 binding_count, EMouseClickType mouse, MASK mask, EMouseState state) const +{ + for (S32 i = 0; i < binding_count; i++) + { + if (binding[i].mMouse == mouse && (binding[i].mMask & mask) == binding[i].mMask) + { + bool res = false; + switch (state) + { + case MOUSE_STATE_DOWN: + res = binding[i].mFunction(KEYSTATE_DOWN); + break; + case MOUSE_STATE_CLICK: + // Button went down and up in scope of single frame + // might not work best with some functions, + // but some function need specific states specifically + res = binding[i].mFunction(KEYSTATE_DOWN); + res |= binding[i].mFunction(KEYSTATE_UP); + break; + case MOUSE_STATE_LEVEL: + res = binding[i].mFunction(KEYSTATE_LEVEL); + break; + case MOUSE_STATE_UP: + res = binding[i].mFunction(KEYSTATE_UP); + break; + default: + break; + } + // Key+Mask combinations are supposed to be unique, no need to continue + return res; + } + } + return false; +} + +// todo: this recods key, scanMouse() triggers functions with EKeystate +bool LLViewerInput::scanMouse(EMouseClickType click, EMouseState state) const +{ + bool res = false; + S32 mode = getMode(); + MASK mask = gKeyboard->currentMask(TRUE); + res = scanMouse(mMouseBindings[mode], mMouseBindings[mode].size(), click, mask, state); + // no user defined actions found or those actions can't handle the key/button, handle control if nessesary + if (!res && agent_control_lbutton.canHandle(click, KEY_NONE, mask)) + { + switch (state) + { + case MOUSE_STATE_DOWN: + agen_control_lbutton_handle(KEYSTATE_DOWN); + res = true; + break; + case MOUSE_STATE_CLICK: + // might not work best with some functions, + // but some function need specific states too specifically + agen_control_lbutton_handle(KEYSTATE_DOWN); + agen_control_lbutton_handle(KEYSTATE_UP); + res = true; + break; + case MOUSE_STATE_UP: + agen_control_lbutton_handle(KEYSTATE_UP); + res = true; + break; + default: + break; + } + } + return res; +} + +void LLViewerInput::scanMouse() +{ + for (S32 i = 0; i < CLICK_COUNT; i++) + { + if (mMouseLevel[i] != MOUSE_STATE_SILENT) + { + scanMouse((EMouseClickType)i, mMouseLevel[i]); + if (mMouseLevel[i] == MOUSE_STATE_DOWN) + { + // mouse doesn't support 'continued' state, so after handling, switch to LEVEL + mMouseLevel[i] = MOUSE_STATE_LEVEL; + } + else if (mMouseLevel[i] == MOUSE_STATE_UP || mMouseLevel[i] == MOUSE_STATE_CLICK) + { + mMouseLevel[i] = MOUSE_STATE_SILENT; + } + } + } +} + +bool LLViewerInput::isMouseBindUsed(const EMouseClickType mouse, const MASK mask, const S32 mode) +{ + S32 size = mMouseBindings[mode].size(); + for (S32 index = 0; index < size; index++) + { + if (mouse == mMouseBindings[mode][index].mMouse && mask == mMouseBindings[mode][index].mMask) + return true; + } + return false; +} diff --git a/indra/newview/llviewerinput.h b/indra/newview/llviewerinput.h new file mode 100644 index 0000000000..281a209896 --- /dev/null +++ b/indra/newview/llviewerinput.h @@ -0,0 +1,179 @@ +/** + * @file llviewerinput.h + * @brief LLViewerInput class header file + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLVIEWERINPUT_H +#define LL_LLVIEWERINPUT_H + +#include "llkeyboard.h" // For EKeystate +#include "llinitparam.h" + +const S32 MAX_KEY_BINDINGS = 128; // was 60 + +class LLNamedFunction +{ +public: + LLNamedFunction() : mFunction(NULL) { }; + ~LLNamedFunction() { }; + + std::string mName; + LLKeyFunc mFunction; +}; + +class LLKeyboardBinding +{ +public: + KEY mKey; + MASK mMask; + + LLKeyFunc mFunction; +}; + +class LLMouseBinding +{ +public: + EMouseClickType mMouse; + MASK mMask; + + LLKeyFunc mFunction; +}; + + +typedef enum e_keyboard_mode +{ + MODE_FIRST_PERSON, + MODE_THIRD_PERSON, + MODE_EDIT_AVATAR, + MODE_SITTING, + MODE_COUNT +} EKeyboardMode; + +class LLWindow; + +void bind_keyboard_functions(); + +class LLViewerInput +{ +public: + struct KeyBinding : public LLInitParam::Block<KeyBinding> + { + Mandatory<std::string> key, + mask, + command; + Optional<std::string> mouse; // Note, not mandatory for the sake of backward campatibility with keys.xml + + KeyBinding(); + }; + + struct KeyMode : public LLInitParam::Block<KeyMode> + { + Multiple<KeyBinding> bindings; + + KeyMode(); + }; + + struct Keys : public LLInitParam::Block<Keys> + { + Optional<KeyMode> first_person, + third_person, + sitting, + edit_avatar; + + Keys(); + }; + + LLViewerInput(); + + BOOL handleKey(KEY key, MASK mask, BOOL repeated); + BOOL handleKeyUp(KEY key, MASK mask); + + S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error + EKeyboardMode getMode() const; + + static BOOL modeFromString(const std::string& string, S32 *mode); // False on failure + static BOOL mouseFromString(const std::string& string, EMouseClickType *mode);// False on failure + + bool scanKey(KEY key, + BOOL key_down, + BOOL key_up, + BOOL key_level) const; + + // handleMouse() records state, scanMouse() goes through states, scanMouse(click) processes individual saved states after UI is done with them + BOOL handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); + void scanMouse(); + + bool isMouseBindUsed(const EMouseClickType mouse, const MASK mask = MASK_NONE, const S32 mode = MODE_THIRD_PERSON); + +private: + bool scanKey(const std::vector<LLKeyboardBinding> &binding, + S32 binding_count, + KEY key, + MASK mask, + BOOL key_down, + BOOL key_up, + BOOL key_level, + bool repeat) const; + + enum EMouseState + { + MOUSE_STATE_DOWN, // key down this frame + MOUSE_STATE_CLICK, // key went up and down in scope of same frame + MOUSE_STATE_LEVEL, // clicked again fast, or never released + MOUSE_STATE_UP, // went up this frame + MOUSE_STATE_SILENT // notified about 'up', do not notify again + }; + bool scanMouse(EMouseClickType click, EMouseState state) const; + bool scanMouse(const std::vector<LLMouseBinding> &binding, + S32 binding_count, + EMouseClickType mouse, + MASK mask, + EMouseState state) const; + + S32 loadBindingMode(const LLViewerInput::KeyMode& keymode, S32 mode); + BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name); + BOOL bindMouse(const S32 mode, const EMouseClickType mouse, const MASK mask, const std::string& function_name); + void resetBindings(); + + // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here + + // TODO: at some point it is better to remake this, especially keyaboard part + // would be much better to send to functions actual state of the button than + // to send what we think function wants based on collection of bools (mKeyRepeated, mKeyLevel, mKeyDown) + std::vector<LLKeyboardBinding> mKeyBindings[MODE_COUNT]; + std::vector<LLMouseBinding> mMouseBindings[MODE_COUNT]; + + typedef std::map<U32, U32> key_remap_t; + key_remap_t mRemapKeys[MODE_COUNT]; + std::set<KEY> mKeysSkippedByUI; + BOOL mKeyHandledByUI[KEY_COUNT]; // key processed successfully by UI + + // This is indentical to what llkeyboard does (mKeyRepeated, mKeyLevel, mKeyDown e t c), + // just instead of remembering individually as bools, we record state as enum + EMouseState mMouseLevel[CLICK_COUNT]; // records of key state +}; + +extern LLViewerInput gViewerInput; + +#endif // LL_LLVIEWERINPUT_H diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index d0cbd1181b..bbed741a33 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -81,6 +81,9 @@ static const char * const LOG_INV("Inventory"); static const char * const LOG_LOCAL("InventoryLocalize"); static const char * const LOG_NOTECARD("copy_inventory_from_notecard"); +static const std::string INV_OWNER_ID("owner_id"); +static const std::string INV_VERSION("version"); + #if 1 // *TODO$: LLInventoryCallback should be deprecated to conform to the new boost::bind/coroutine model. // temp code in transition @@ -519,14 +522,6 @@ void LLViewerInventoryItem::packMessage(LLMessageSystem* msg) const } // virtual -BOOL LLViewerInventoryItem::importFile(LLFILE* fp) -{ - BOOL rv = LLInventoryItem::importFile(fp); - mIsComplete = TRUE; - return rv; -} - -// virtual BOOL LLViewerInventoryItem::importLegacyStream(std::istream& input_stream) { BOOL rv = LLInventoryItem::importLegacyStream(input_stream); @@ -534,32 +529,6 @@ BOOL LLViewerInventoryItem::importLegacyStream(std::istream& input_stream) return rv; } -bool LLViewerInventoryItem::importFileLocal(LLFILE* fp) -{ - // TODO: convert all functions that return BOOL to return bool - bool rv = (LLInventoryItem::importFile(fp) ? true : false); - mIsComplete = false; - return rv; -} - -bool LLViewerInventoryItem::exportFileLocal(LLFILE* fp) const -{ - std::string uuid_str; - fprintf(fp, "\tinv_item\t0\n\t{\n"); - mUUID.toString(uuid_str); - fprintf(fp, "\t\titem_id\t%s\n", uuid_str.c_str()); - mParentUUID.toString(uuid_str); - fprintf(fp, "\t\tparent_id\t%s\n", uuid_str.c_str()); - mPermissions.exportFile(fp); - fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); - const std::string inv_type_str = LLInventoryType::lookup(mInventoryType); - if(!inv_type_str.empty()) fprintf(fp, "\t\tinv_type\t%s\n", inv_type_str.c_str()); - fprintf(fp, "\t\tname\t%s|\n", mName.c_str()); - fprintf(fp, "\t\tcreation_date\t%d\n", (S32) mCreationDate); - fprintf(fp,"\t}\n"); - return true; -} - void LLViewerInventoryItem::updateParentOnServer(BOOL restamp) const { LLMessageSystem* msg = gMessageSystem; @@ -720,90 +689,26 @@ S32 LLViewerInventoryCategory::getViewerDescendentCount() const return descendents_actual; } -bool LLViewerInventoryCategory::importFileLocal(LLFILE* fp) +LLSD LLViewerInventoryCategory::exportLLSD() const { - // *NOTE: This buffer size is hard coded into scanf() below. - char buffer[MAX_STRING]; /* Flawfinder: ignore */ - char keyword[MAX_STRING]; /* Flawfinder: ignore */ - char valuestr[MAX_STRING]; /* Flawfinder: ignore */ + LLSD cat_data = LLInventoryCategory::exportLLSD(); + cat_data[INV_OWNER_ID] = mOwnerID; + cat_data[INV_VERSION] = mVersion; - keyword[0] = '\0'; - valuestr[0] = '\0'; - while(!feof(fp)) - { - if (fgets(buffer, MAX_STRING, fp) == NULL) - { - buffer[0] = '\0'; - } - - sscanf( /* Flawfinder: ignore */ - buffer, " %254s %254s", keyword, valuestr); - if(0 == strcmp("{",keyword)) - { - continue; - } - if(0 == strcmp("}", keyword)) - { - break; - } - else if(0 == strcmp("cat_id", keyword)) - { - mUUID.set(valuestr); - } - else if(0 == strcmp("parent_id", keyword)) - { - mParentUUID.set(valuestr); - } - else if(0 == strcmp("type", keyword)) - { - mType = LLAssetType::lookup(valuestr); - } - else if(0 == strcmp("pref_type", keyword)) - { - mPreferredType = LLFolderType::lookup(valuestr); - } - else if(0 == strcmp("name", keyword)) - { - //strcpy(valuestr, buffer + strlen(keyword) + 3); - // *NOTE: Not ANSI C, but widely supported. - sscanf( /* Flawfinder: ignore */ - buffer, " %254s %254[^|]", keyword, valuestr); - mName.assign(valuestr); - LLStringUtil::replaceNonstandardASCII(mName, ' '); - LLStringUtil::replaceChar(mName, '|', ' '); - } - else if(0 == strcmp("owner_id", keyword)) - { - mOwnerID.set(valuestr); - } - else if(0 == strcmp("version", keyword)) - { - sscanf(valuestr, "%d", &mVersion); - } - else - { - LL_WARNS(LOG_INV) << "unknown keyword '" << keyword - << "' in inventory import category " << mUUID << LL_ENDL; - } - } - return true; + return cat_data; } -bool LLViewerInventoryCategory::exportFileLocal(LLFILE* fp) const -{ - std::string uuid_str; - fprintf(fp, "\tinv_category\t0\n\t{\n"); - mUUID.toString(uuid_str); - fprintf(fp, "\t\tcat_id\t%s\n", uuid_str.c_str()); - mParentUUID.toString(uuid_str); - fprintf(fp, "\t\tparent_id\t%s\n", uuid_str.c_str()); - fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); - fprintf(fp, "\t\tpref_type\t%s\n", LLFolderType::lookup(mPreferredType).c_str()); - fprintf(fp, "\t\tname\t%s|\n", mName.c_str()); - mOwnerID.toString(uuid_str); - fprintf(fp, "\t\towner_id\t%s\n", uuid_str.c_str()); - fprintf(fp, "\t\tversion\t%d\n", mVersion); - fprintf(fp,"\t}\n"); +bool LLViewerInventoryCategory::importLLSD(const LLSD& cat_data) +{ + LLInventoryCategory::importLLSD(cat_data); + if (cat_data.has(INV_OWNER_ID)) + { + mOwnerID = cat_data[INV_OWNER_ID].asUUID(); + } + if (cat_data.has(INV_VERSION)) + { + setVersion(cat_data[INV_VERSION].asInteger()); + } return true; } diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index d537b28682..24b632632b 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -131,14 +131,8 @@ public: virtual void packMessage(LLMessageSystem* msg) const; virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); virtual BOOL unpackMessage(const LLSD& item); - virtual BOOL importFile(LLFILE* fp); virtual BOOL importLegacyStream(std::istream& input_stream); - // file handling on the viewer. These are not meant for anything - // other than cacheing. - bool exportFileLocal(LLFILE* fp) const; - bool importFileLocal(LLFILE* fp); - // new methods BOOL isFinished() const { return mIsComplete; } void setComplete(BOOL complete) { mIsComplete = complete; } @@ -226,10 +220,9 @@ public: // How many descendents do we currently have information for in the InventoryModel? S32 getViewerDescendentCount() const; - // file handling on the viewer. These are not meant for anything - // other than caching. - bool exportFileLocal(LLFILE* fp) const; - bool importFileLocal(LLFILE* fp); + LLSD exportLLSD() const; + bool importLLSD(const LLSD& cat_data); + void determineFolderType(); void changeType(LLFolderType::EType new_folder_type); virtual void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp index 6990f56a08..fdfd22c117 100644 --- a/indra/newview/llviewerjointmesh.cpp +++ b/indra/newview/llviewerjointmesh.cpp @@ -253,7 +253,6 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy) //---------------------------------------------------------------- llassert( !(mTexture.notNull() && mLayerSet) ); // mutually exclusive - LLTexUnit::eTextureAddressMode old_mode = LLTexUnit::TAM_WRAP; LLViewerTexLayerSet *layerset = dynamic_cast<LLViewerTexLayerSet*>(mLayerSet); if (mTestImageName) { @@ -280,22 +279,15 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy) gGL.getTexUnit(diffuse_channel)->bind(LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT)); } } - else - if ( !is_dummy && mTexture.notNull() ) + else if ( !is_dummy && mTexture.notNull() ) { - if(mTexture->hasGLTexture()) - { - old_mode = mTexture->getAddressMode(); - } gGL.getTexUnit(diffuse_channel)->bind(mTexture); - gGL.getTexUnit(diffuse_channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); } else { gGL.getTexUnit(diffuse_channel)->bind(LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT)); } - U32 mask = sRenderMask; U32 start = mMesh->mFaceVertexOffset; @@ -341,12 +333,6 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy) gGL.getTexUnit(diffuse_channel)->setTextureBlendType(LLTexUnit::TB_MULT); } - if (mTexture.notNull() && !is_dummy) - { - gGL.getTexUnit(diffuse_channel)->bind(mTexture); - gGL.getTexUnit(diffuse_channel)->setTextureAddressMode(old_mode); - } - return triangle_count; } diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 491ad7e3b2..e35cb26ce1 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -36,10 +36,19 @@ #include "lltoolmgr.h" #include "llselectmgr.h" #include "llviewermenu.h" +#include "llviewerwindow.h" +#include "llwindow.h" #include "llagent.h" #include "llagentcamera.h" #include "llfocusmgr.h" +#if LL_WINDOWS && !LL_MESA_HEADLESS +// Require DirectInput version 8 +#define DIRECTINPUT_VERSION 0x0800 + +#include <dinput.h> +#endif + // ---------------------------------------------------------------------------- // Constants @@ -62,6 +71,7 @@ F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0}; #define MAX_SPACENAVIGATOR_INPUT 3000.0f #define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT + #if LIB_NDOF std::ostream& operator<<(std::ostream& out, NDOF_Device* ptr) { @@ -99,6 +109,126 @@ std::ostream& operator<<(std::ostream& out, NDOF_Device* ptr) } #endif // LIB_NDOF + +#if LL_WINDOWS && !LL_MESA_HEADLESS +// this should reflect ndof and set axises, see ndofdev_win.cpp from ndof package +BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* inst, VOID* user_data) +{ + if (inst->dwType & DIDFT_AXIS) + { + LPDIRECTINPUTDEVICE8 device = *((LPDIRECTINPUTDEVICE8 *)user_data); + DIPROPRANGE diprg; + diprg.diph.dwSize = sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); + diprg.diph.dwHow = DIPH_BYID; + diprg.diph.dwObj = inst->dwType; // specify the enumerated axis + + // Set the range for the axis + diprg.lMin = (long)-MAX_JOYSTICK_INPUT_VALUE; + diprg.lMax = (long)+MAX_JOYSTICK_INPUT_VALUE; + HRESULT hr = device->SetProperty(DIPROP_RANGE, &diprg.diph); + + if (FAILED(hr)) + { + return DIENUM_STOP; + } + } + + return DIENUM_CONTINUE; +} + +BOOL CALLBACK di8_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, LPVOID pvRef) +{ + // Note: If a single device can function as more than one DirectInput + // device type, it is enumerated as each device type that it supports. + // Capable of detecting devices like Oculus Rift + if (device_instance_ptr) + { + std::string product_name = utf16str_to_utf8str(llutf16string(device_instance_ptr->tszProductName)); + + LLSD guid = LLViewerJoystick::getInstance()->getDeviceUUID(); + + bool init_device = false; + if (guid.isBinary()) + { + std::vector<U8> bin_bucket = guid.asBinary(); + init_device = memcmp(&bin_bucket[0], &device_instance_ptr->guidInstance, sizeof(GUID)) == 0; + } + else + { + // It might be better to init space navigator here, but if system doesn't has one, + // ndof will pick a random device, it is simpler to pick first device now to have an id + init_device = true; + } + + if (init_device) + { + LL_DEBUGS("Joystick") << "Found and attempting to use device: " << product_name << LL_ENDL; + LPDIRECTINPUT8 di8_interface = *((LPDIRECTINPUT8 *)gViewerWindow->getWindow()->getDirectInput8()); + LPDIRECTINPUTDEVICE8 device = NULL; + + HRESULT status = di8_interface->CreateDevice( + device_instance_ptr->guidInstance, // REFGUID rguid, + &device, // LPDIRECTINPUTDEVICE * lplpDirectInputDevice, + NULL // LPUNKNOWN pUnkOuter + ); + + if (status == DI_OK) + { + // prerequisite for aquire() + LL_DEBUGS("Joystick") << "Device created" << LL_ENDL; + status = device->SetDataFormat(&c_dfDIJoystick); // c_dfDIJoystick2 + } + + if (status == DI_OK) + { + // set properties + LL_DEBUGS("Joystick") << "Format set" << LL_ENDL; + status = device->EnumObjects(EnumObjectsCallback, &device, DIDFT_ALL); + } + + if (status == DI_OK) + { + LL_DEBUGS("Joystick") << "Properties updated" << LL_ENDL; + + S32 size = sizeof(GUID); + LLSD::Binary data; //just an std::vector + data.resize(size); + memcpy(&data[0], &device_instance_ptr->guidInstance /*POD _GUID*/, size); + LLViewerJoystick::getInstance()->initDevice(&device, product_name, LLSD(data)); + return DIENUM_STOP; + } + } + else + { + LL_DEBUGS("Joystick") << "Found device: " << product_name << LL_ENDL; + } + } + return DIENUM_CONTINUE; +} + +// Windows guids +// This is GUID2 so teoretically it can be memcpy copied into LLUUID +void guid_from_string(GUID &guid, const std::string &input) +{ + CLSIDFromString(utf8str_to_utf16str(input).c_str(), &guid); +} + +std::string string_from_guid(const GUID &guid) +{ + OLECHAR* guidString; //wchat + StringFromCLSID(guid, &guidString); + + // use guidString... + + std::string res = utf16str_to_utf8str(llutf16string(guidString)); + // ensure memory is freed + ::CoTaskMemFree(guidString); + + return res; +} +#endif + // ----------------------------------------------------------------------------- void LLViewerJoystick::updateEnabled(bool autoenable) { @@ -108,7 +238,8 @@ void LLViewerJoystick::updateEnabled(bool autoenable) } else { - if (isLikeSpaceNavigator() && autoenable) + // autoenable if user specifically chose this device + if (autoenable && (isLikeSpaceNavigator() || isDeviceUUIDSet())) { gSavedSettings.setBOOL("JoystickEnabled", TRUE ); } @@ -144,7 +275,7 @@ NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev) LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); if (joystick->mDriverState == JDS_UNINITIALIZED) { - LL_INFOS("joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL; + LL_INFOS("Joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL; ndof_dump(stderr, dev); joystick->mNdofDev = dev; joystick->mDriverState = JDS_INITIALIZED; @@ -162,7 +293,7 @@ void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev) LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); if (joystick->mNdofDev == dev) { - LL_INFOS("joystick") << "HotPlugRemovalCallback: joystick->mNdofDev=" + LL_INFOS("Joystick") << "HotPlugRemovalCallback: joystick->mNdofDev=" << joystick->mNdofDev << "; removed device:" << LL_ENDL; ndof_dump(stderr, dev); joystick->mDriverState = JDS_UNINITIALIZED; @@ -189,6 +320,8 @@ LLViewerJoystick::LLViewerJoystick() // factor in bandwidth? bandwidth = gViewerStats->mKBitStat mPerfScale = 4000.f / gSysCPU.getMHz(); // hmm. why? + + mLastDeviceUUID = LLSD::Integer(1); } // ----------------------------------------------------------------------------- @@ -207,12 +340,14 @@ void LLViewerJoystick::init(bool autoenable) static bool libinit = false; mDriverState = JDS_INITIALIZING; + loadDeviceIdFromSettings(); + if (libinit == false) { // Note: The HotPlug callbacks are not actually getting called on Windows if (ndof_libinit(HotPlugAddCallback, HotPlugRemovalCallback, - NULL)) + gViewerWindow->getWindow()->getDirectInput8())) { mDriverState = JDS_UNINITIALIZED; } @@ -229,39 +364,33 @@ void LLViewerJoystick::init(bool autoenable) if (libinit) { if (mNdofDev) - { - LL_DEBUGS("joystick") << "ndof_create() returned: " << mNdofDev << LL_ENDL; - // Different joysticks will return different ranges of raw values. - // Since we want to handle every device in the same uniform way, - // we initialize the mNdofDev struct and we set the range - // of values we would like to receive. - // - // HACK: On Windows, libndofdev passes our range to DI with a - // SetProperty call. This works but with one notable exception, the - // SpaceNavigator, who doesn't seem to care about the SetProperty - // call. In theory, we should handle this case inside libndofdev. - // However, the range we're setting here is arbitrary anyway, - // so let's just use the SpaceNavigator range for our purposes. - mNdofDev->axes_min = (long)-MAX_JOYSTICK_INPUT_VALUE; - mNdofDev->axes_max = (long)+MAX_JOYSTICK_INPUT_VALUE; - - // libndofdev could be used to return deltas. Here we choose to - // just have the absolute values instead. - mNdofDev->absolute = 1; - - LL_DEBUGS("joystick") << "ndof_init_first() received: " << mNdofDev << LL_ENDL; - // init & use the first suitable NDOF device found on the USB chain - if (ndof_init_first(mNdofDev, NULL)) - { - mDriverState = JDS_UNINITIALIZED; - LL_WARNS("joystick") << "ndof_init_first FAILED" << LL_ENDL; - ndof_dump_list(stderr); - } - else - { - mDriverState = JDS_INITIALIZED; - } - LL_DEBUGS("joystick") << "ndof_init_first() left: " << mNdofDev << LL_ENDL; + { + // di8_devices_callback callback is immediate and happens in scope of getInputDevices() +#if LL_WINDOWS && !LL_MESA_HEADLESS + // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib + U32 device_type = DI8DEVCLASS_GAMECTRL; + void* callback = &di8_devices_callback; +#else + // MAC doesn't support device search yet + // On MAC there is an ndof_idsearch and it is possible to specify product + // and manufacturer in NDOF_Device for ndof_init_first to pick specific one + U32 device_type = 0; + void* callback = NULL; +#endif + if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL)) + { + LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL; + // Failed to gather devices from windows, init first suitable one + mLastDeviceUUID = LLSD(); + void *preffered_device = NULL; + initDevice(preffered_device); + } + + if (mDriverState == JDS_INITIALIZING) + { + LL_INFOS("Joystick") << "Found no matching joystick devices." << LL_ENDL; + mDriverState = JDS_UNINITIALIZED; + } } else { @@ -300,11 +429,92 @@ void LLViewerJoystick::init(bool autoenable) // No device connected, don't change any settings } - LL_INFOS("joystick") << "ndof: mDriverState=" << mDriverState << "; mNdofDev=" + LL_INFOS("Joystick") << "ndof: mDriverState=" << mDriverState << "; mNdofDev=" << mNdofDev << "; libinit=" << libinit << LL_ENDL; #endif } +void LLViewerJoystick::initDevice(LLSD &guid) +{ +#if LIB_NDOF + mLastDeviceUUID = guid; + +#if LL_WINDOWS && !LL_MESA_HEADLESS + // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib + U32 device_type = DI8DEVCLASS_GAMECTRL; + void* callback = &di8_devices_callback; +#else + // MAC doesn't support device search yet + // On MAC there is an ndof_idsearch and it is possible to specify product + // and manufacturer in NDOF_Device for ndof_init_first to pick specific one + U32 device_type = 0; + void* callback = NULL; +#endif + + mDriverState = JDS_INITIALIZING; + if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL)) + { + LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL; + // Failed to gather devices from windows, init first suitable one + void *preffered_device = NULL; + mLastDeviceUUID = LLSD(); + initDevice(preffered_device); + } + + if (mDriverState == JDS_INITIALIZING) + { + LL_INFOS("Joystick") << "Found no matching joystick devices." << LL_ENDL; + mDriverState = JDS_UNINITIALIZED; + } +#endif +} + +void LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid) +{ +#if LIB_NDOF + mLastDeviceUUID = guid; + + strncpy(mNdofDev->product, name.c_str(), sizeof(mNdofDev->product)); + mNdofDev->manufacturer[0] = '\0'; + + initDevice(preffered_device); +#endif +} + +void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */) +{ +#if LIB_NDOF + // Different joysticks will return different ranges of raw values. + // Since we want to handle every device in the same uniform way, + // we initialize the mNdofDev struct and we set the range + // of values we would like to receive. + // + // HACK: On Windows, libndofdev passes our range to DI with a + // SetProperty call. This works but with one notable exception, the + // SpaceNavigator, who doesn't seem to care about the SetProperty + // call. In theory, we should handle this case inside libndofdev. + // However, the range we're setting here is arbitrary anyway, + // so let's just use the SpaceNavigator range for our purposes. + mNdofDev->axes_min = (long)-MAX_JOYSTICK_INPUT_VALUE; + mNdofDev->axes_max = (long)+MAX_JOYSTICK_INPUT_VALUE; + + // libndofdev could be used to return deltas. Here we choose to + // just have the absolute values instead. + mNdofDev->absolute = 1; + // init & use the first suitable NDOF device found on the USB chain + // On windows preffered_device needs to be a pointer to LPDIRECTINPUTDEVICE8 + if (ndof_init_first(mNdofDev, preffered_device)) + { + mDriverState = JDS_UNINITIALIZED; + LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + } + else + { + mDriverState = JDS_INITIALIZED; + } +#endif +} + // ----------------------------------------------------------------------------- void LLViewerJoystick::terminate() { @@ -314,7 +524,7 @@ void LLViewerJoystick::terminate() ndof_libcleanup(); // frees alocated memory in mNdofDev mDriverState = JDS_UNINITIALIZED; mNdofDev = NULL; - LL_INFOS("joystick") << "Terminated connection with NDOF device." << LL_ENDL; + LL_INFOS("Joystick") << "Terminated connection with NDOF device." << LL_ENDL; } #endif } @@ -1105,6 +1315,74 @@ void LLViewerJoystick::scanJoystick() } // ----------------------------------------------------------------------------- +bool LLViewerJoystick::isDeviceUUIDSet() +{ +#if LL_WINDOWS && !LL_MESA_HEADLESS + // for ease of comparison and to dial less with platform specific variables, we store id as LLSD binary + return mLastDeviceUUID.isBinary(); +#else + return false; +#endif +} + +LLSD LLViewerJoystick::getDeviceUUID() +{ + return mLastDeviceUUID; +} + +std::string LLViewerJoystick::getDeviceUUIDString() +{ +#if LL_WINDOWS && !LL_MESA_HEADLESS + // Might be simpler to just convert _GUID into string everywhere, store and compare as string + if (mLastDeviceUUID.isBinary()) + { + S32 size = sizeof(GUID); + LLSD::Binary data = mLastDeviceUUID.asBinary(); + GUID guid; + memcpy(&guid, &data[0], size); + return string_from_guid(guid); + } + else + { + return std::string(); + } +#else + return std::string(); + // return mLastDeviceUUID; +#endif +} + +void LLViewerJoystick::loadDeviceIdFromSettings() +{ +#if LL_WINDOWS && !LL_MESA_HEADLESS + // We can't save binary data to gSavedSettings, somebody editing the file will corrupt it, + // so _GUID data gets converted to string (we probably can convert it to LLUUID with memcpy) + // and here we need to convert it back to binary from string + std::string device_string = gSavedSettings.getString("JoystickDeviceUUID"); + if (device_string.empty()) + { + mLastDeviceUUID = LLSD(); + } + else + { + LL_DEBUGS("Joystick") << "Looking for device by id: " << device_string << LL_ENDL; + GUID guid; + guid_from_string(guid, device_string); + S32 size = sizeof(GUID); + LLSD::Binary data; //just an std::vector + data.resize(size); + memcpy(&data[0], &guid /*POD _GUID*/, size); + // We store this data in LLSD since LLSD is versatile and will be able to handle both GUID2 + // and any data MAC will need for device selection + mLastDeviceUUID = LLSD(data); + } +#else + mLastDeviceUUID = LLSD(); + //mLastDeviceUUID = gSavedSettings.getLLSD("JoystickDeviceUUID"); +#endif +} + +// ----------------------------------------------------------------------------- std::string LLViewerJoystick::getDescription() { std::string res; @@ -1145,7 +1423,7 @@ void LLViewerJoystick::setSNDefaults() #endif //gViewerWindow->alertXml("CacheWillClear"); - LL_INFOS("joystick") << "restoring SpaceNavigator defaults..." << LL_ENDL; + LL_INFOS("Joystick") << "restoring SpaceNavigator defaults..." << LL_ENDL; gSavedSettings.setS32("JoystickAxis0", 1); // z (at) gSavedSettings.setS32("JoystickAxis1", 0); // x (slide) diff --git a/indra/newview/llviewerjoystick.h b/indra/newview/llviewerjoystick.h index 016b435ee8..782c523d4f 100644 --- a/indra/newview/llviewerjoystick.h +++ b/indra/newview/llviewerjoystick.h @@ -50,6 +50,9 @@ class LLViewerJoystick : public LLSingleton<LLViewerJoystick> public: void init(bool autoenable); + void initDevice(LLSD &guid); + void initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/); + void initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid); void terminate(); void updateStatus(); @@ -68,8 +71,11 @@ public: void setOverrideCamera(bool val); bool toggleFlycam(); void setSNDefaults(); + bool isDeviceUUIDSet(); + LLSD getDeviceUUID(); //unconverted, OS dependent value wrapped into LLSD, for comparison/search + std::string getDeviceUUIDString(); // converted readable value for settings std::string getDescription(); - + protected: void updateEnabled(bool autoenable); void handleRun(F32 inc); @@ -80,6 +86,7 @@ protected: void agentYaw(F32 yaw_inc); void agentJump(); void resetDeltas(S32 axis[]); + void loadDeviceIdFromSettings(); #if LIB_NDOF static NDOF_HotPlugResult HotPlugAddCallback(NDOF_Device *dev); static void HotPlugRemovalCallback(NDOF_Device *dev); @@ -95,6 +102,7 @@ private: bool mCameraUpdated; bool mOverrideCamera; U32 mJoystickRun; + LLSD mLastDeviceUUID; // _GUID as U8 binary map, integer 1 for no device/ndof's device static F32 sLastDelta[7]; static F32 sDelta[7]; diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp deleted file mode 100644 index 6914e0fc2b..0000000000 --- a/indra/newview/llviewerkeyboard.cpp +++ /dev/null @@ -1,1039 +0,0 @@ -/** - * @file llviewerkeyboard.cpp - * @brief LLViewerKeyboard class implementation - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llappviewer.h" -#include "llfloaterreg.h" -#include "llviewerkeyboard.h" -#include "llmath.h" -#include "llagent.h" -#include "llagentcamera.h" -#include "llfloaterimnearbychat.h" -#include "llviewercontrol.h" -#include "llfocusmgr.h" -#include "llmorphview.h" -#include "llmoveview.h" -#include "lltoolfocus.h" -#include "llviewerwindow.h" -#include "llvoavatarself.h" -#include "llfloatercamera.h" -#include "llinitparam.h" - -// -// Constants -// - -const F32 FLY_TIME = 0.5f; -const F32 FLY_FRAMES = 4; - -const F32 NUDGE_TIME = 0.25f; // in seconds -const S32 NUDGE_FRAMES = 2; -const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed - -struct LLKeyboardActionRegistry -: public LLRegistrySingleton<std::string, boost::function<void (EKeystate keystate)>, LLKeyboardActionRegistry> -{ - LLSINGLETON_EMPTY_CTOR(LLKeyboardActionRegistry); -}; - -LLViewerKeyboard gViewerKeyboard; - -void agent_jump( EKeystate s ) -{ - static BOOL first_fly_attempt(TRUE); - if (KEYSTATE_UP == s) - { - first_fly_attempt = TRUE; - return; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - if( time < FLY_TIME - || frame_count <= FLY_FRAMES - || gAgent.upGrabbed() - || !gSavedSettings.getBOOL("AutomaticFly")) - { - gAgent.moveUp(1); - } - else - { - gAgent.setFlying(TRUE, first_fly_attempt); - first_fly_attempt = FALSE; - gAgent.moveUp(1); - } -} - -void agent_push_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgent.moveUp(-1); -} - -static void agent_check_temporary_run(LLAgent::EDoubleTapRunMode mode) -{ - if (gAgent.mDoubleTapRunMode == mode && - gAgent.getRunning() && - !gAgent.getAlwaysRun()) - { - // Turn off temporary running. - gAgent.clearRunning(); - gAgent.sendWalkRun(gAgent.getRunning()); - } -} - -static void agent_handle_doubletap_run(EKeystate s, LLAgent::EDoubleTapRunMode mode) -{ - if (KEYSTATE_UP == s) - { - // Note: in case shift is already released, slide left/right run - // will be released in agent_turn_left()/agent_turn_right() - agent_check_temporary_run(mode); - } - else if (gSavedSettings.getBOOL("AllowTapTapHoldRun") && - KEYSTATE_DOWN == s && - !gAgent.getRunning()) - { - if (gAgent.mDoubleTapRunMode == mode && - gAgent.mDoubleTapRunTimer.getElapsedTimeF32() < NUDGE_TIME) - { - // Same walk-key was pushed again quickly; this is a - // double-tap so engage temporary running. - gAgent.setRunning(); - gAgent.sendWalkRun(gAgent.getRunning()); - } - - // Pressing any walk-key resets the double-tap timer - gAgent.mDoubleTapRunTimer.reset(); - gAgent.mDoubleTapRunMode = mode; - } -} - -static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) -{ - agent_handle_doubletap_run(s, mode); - if (KEYSTATE_UP == s) return; - - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) - { - gAgent.moveAtNudge(direction); - } - else - { - gAgent.moveAt(direction); - } -} - -void camera_move_forward( EKeystate s ); - -void agent_push_forward( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return; - - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_move_forward(s); - } - else - { - agent_push_forwardbackward(s, 1, LLAgent::DOUBLETAP_FORWARD); - } -} - -void camera_move_backward( EKeystate s ); - -void agent_push_backward( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return; - - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_move_backward(s); - } - else if (!gAgent.backwardGrabbed() && gAgentAvatarp->isSitting() && gSavedSettings.getBOOL("LeaveMouselook")) - { - gAgentCamera.changeCameraToThirdPerson(); - } - else - { - agent_push_forwardbackward(s, -1, LLAgent::DOUBLETAP_BACKWARD); - } -} - -static void agent_slide_leftright( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) -{ - agent_handle_doubletap_run(s, mode); - if( KEYSTATE_UP == s ) return; - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) - { - gAgent.moveLeftNudge(direction); - } - else - { - gAgent.moveLeft(direction); - } -} - - -void agent_slide_left( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return; - agent_slide_leftright(s, 1, LLAgent::DOUBLETAP_SLIDELEFT); -} - - -void agent_slide_right( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return; - agent_slide_leftright(s, -1, LLAgent::DOUBLETAP_SLIDERIGHT); -} - -void camera_spin_around_cw( EKeystate s ); - -void agent_turn_left( EKeystate s ) -{ - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_spin_around_cw(s); - return; - } - - if(gAgent.isMovementLocked()) return; - - if (LLToolCamera::getInstance()->mouseSteerMode()) - { - agent_slide_left(s); - } - else - { - if (KEYSTATE_UP == s) - { - // Check temporary running. In case user released 'left' key with shift already released. - agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDELEFT); - return; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - gAgent.moveYaw( LLFloaterMove::getYawRate( time ) ); - } -} - -void camera_spin_around_ccw( EKeystate s ); - -void agent_turn_right( EKeystate s ) -{ - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_spin_around_ccw(s); - return; - } - - if(gAgent.isMovementLocked()) return; - - if (LLToolCamera::getInstance()->mouseSteerMode()) - { - agent_slide_right(s); - } - else - { - if (KEYSTATE_UP == s) - { - // Check temporary running. In case user released 'right' key with shift already released. - agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDERIGHT); - return; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) ); - } -} - -void agent_look_up( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgent.movePitch(-1); - //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); -} - - -void agent_look_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgent.movePitch(1); - //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); -} - -void agent_toggle_fly( EKeystate s ) -{ - // Only catch the edge - if (KEYSTATE_DOWN == s ) - { - LLAgent::toggleFlying(); - } -} - -F32 get_orbit_rate() -{ - F32 time = gKeyboard->getCurKeyElapsedTime(); - if( time < NUDGE_TIME ) - { - F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME; - //LL_INFOS() << rate << LL_ENDL; - return rate; - } - else - { - return 1; - } -} - -void camera_spin_around_ccw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); -} - - -void camera_spin_around_cw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); -} - -void camera_spin_around_ccw_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT ) return; - if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) - { - //send keystrokes, but do not change camera - agent_turn_right(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.unlockView(); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); - } -} - - -void camera_spin_around_cw_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT ) return; - if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) - { - //send keystrokes, but do not change camera - agent_turn_left(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.unlockView(); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); - } -} - - -void camera_spin_over( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); -} - - -void camera_spin_under( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); -} - -void camera_spin_over_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - if (gAgent.upGrabbed() || gAgentCamera.sitCameraEnabled()) - { - //send keystrokes, but do not change camera - agent_jump(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); - } -} - - -void camera_spin_under_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - if (gAgent.downGrabbed() || gAgentCamera.sitCameraEnabled()) - { - //send keystrokes, but do not change camera - agent_push_down(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); - } -} - -void camera_move_forward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitInKey( get_orbit_rate() ); -} - - -void camera_move_backward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); -} - -void camera_move_forward_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD ) return; - if (gAgent.forwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) - { - agent_push_forward(s); - } - else - { - gAgentCamera.setOrbitInKey( get_orbit_rate() ); - } -} - - -void camera_move_backward_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD ) return; - - if (gAgent.backwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) - { - agent_push_backward(s); - } - else - { - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); - } -} - -void camera_pan_up( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanUpKey( get_orbit_rate() ); -} - -void camera_pan_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanDownKey( get_orbit_rate() ); -} - -void camera_pan_left( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanLeftKey( get_orbit_rate() ); -} - -void camera_pan_right( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanRightKey( get_orbit_rate() ); -} - -void camera_pan_in( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanInKey( get_orbit_rate() ); -} - -void camera_pan_out( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanOutKey( get_orbit_rate() ); -} - -void camera_move_forward_fast( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitInKey(2.5f); -} - -void camera_move_backward_fast( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitOutKey(2.5f); -} - - -void edit_avatar_spin_ccw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); - //gMorphView->orbitLeft( get_orbit_rate() ); -} - - -void edit_avatar_spin_cw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); - //gMorphView->orbitRight( get_orbit_rate() ); -} - -void edit_avatar_spin_over( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); - //gMorphView->orbitUp( get_orbit_rate() ); -} - - -void edit_avatar_spin_under( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); - //gMorphView->orbitDown( get_orbit_rate() ); -} - -void edit_avatar_move_forward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitInKey( get_orbit_rate() ); - //gMorphView->orbitIn(); -} - - -void edit_avatar_move_backward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); - //gMorphView->orbitOut(); -} - -void stop_moving( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - // stop agent - gAgent.setControlFlags(AGENT_CONTROL_STOP); - - // cancel autopilot - gAgent.stopAutoPilot(); -} - -void start_chat( EKeystate s ) -{ - if (LLAppViewer::instance()->quitRequested()) - { - return; // can't talk, gotta go, kthxbye! - } - - // start chat - LLFloaterIMNearbyChat::startChat(NULL); -} - -void start_gesture( EKeystate s ) -{ - LLUICtrl* focus_ctrlp = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus()); - if (KEYSTATE_UP == s && - ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) - { - if ((LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->getCurrentChat().empty()) - { - // No existing chat in chat editor, insert '/' - LLFloaterIMNearbyChat::startChat("/"); - } - else - { - // Don't overwrite existing text in chat editor - LLFloaterIMNearbyChat::startChat(NULL); - } - } -} - -#define REGISTER_KEYBOARD_ACTION(KEY, ACTION) LLREGISTER_STATIC(LLKeyboardActionRegistry, KEY, ACTION); -REGISTER_KEYBOARD_ACTION("jump", agent_jump); -REGISTER_KEYBOARD_ACTION("push_down", agent_push_down); -REGISTER_KEYBOARD_ACTION("push_forward", agent_push_forward); -REGISTER_KEYBOARD_ACTION("push_backward", agent_push_backward); -REGISTER_KEYBOARD_ACTION("look_up", agent_look_up); -REGISTER_KEYBOARD_ACTION("look_down", agent_look_down); -REGISTER_KEYBOARD_ACTION("toggle_fly", agent_toggle_fly); -REGISTER_KEYBOARD_ACTION("turn_left", agent_turn_left); -REGISTER_KEYBOARD_ACTION("turn_right", agent_turn_right); -REGISTER_KEYBOARD_ACTION("slide_left", agent_slide_left); -REGISTER_KEYBOARD_ACTION("slide_right", agent_slide_right); -REGISTER_KEYBOARD_ACTION("spin_around_ccw", camera_spin_around_ccw); -REGISTER_KEYBOARD_ACTION("spin_around_cw", camera_spin_around_cw); -REGISTER_KEYBOARD_ACTION("spin_around_ccw_sitting", camera_spin_around_ccw_sitting); -REGISTER_KEYBOARD_ACTION("spin_around_cw_sitting", camera_spin_around_cw_sitting); -REGISTER_KEYBOARD_ACTION("spin_over", camera_spin_over); -REGISTER_KEYBOARD_ACTION("spin_under", camera_spin_under); -REGISTER_KEYBOARD_ACTION("spin_over_sitting", camera_spin_over_sitting); -REGISTER_KEYBOARD_ACTION("spin_under_sitting", camera_spin_under_sitting); -REGISTER_KEYBOARD_ACTION("move_forward", camera_move_forward); -REGISTER_KEYBOARD_ACTION("move_backward", camera_move_backward); -REGISTER_KEYBOARD_ACTION("move_forward_sitting", camera_move_forward_sitting); -REGISTER_KEYBOARD_ACTION("move_backward_sitting", camera_move_backward_sitting); -REGISTER_KEYBOARD_ACTION("pan_up", camera_pan_up); -REGISTER_KEYBOARD_ACTION("pan_down", camera_pan_down); -REGISTER_KEYBOARD_ACTION("pan_left", camera_pan_left); -REGISTER_KEYBOARD_ACTION("pan_right", camera_pan_right); -REGISTER_KEYBOARD_ACTION("pan_in", camera_pan_in); -REGISTER_KEYBOARD_ACTION("pan_out", camera_pan_out); -REGISTER_KEYBOARD_ACTION("move_forward_fast", camera_move_forward_fast); -REGISTER_KEYBOARD_ACTION("move_backward_fast", camera_move_backward_fast); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_ccw", edit_avatar_spin_ccw); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_cw", edit_avatar_spin_cw); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_over", edit_avatar_spin_over); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_under", edit_avatar_spin_under); -REGISTER_KEYBOARD_ACTION("edit_avatar_move_forward", edit_avatar_move_forward); -REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward); -REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving); -REGISTER_KEYBOARD_ACTION("start_chat", start_chat); -REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture); -#undef REGISTER_KEYBOARD_ACTION - -LLViewerKeyboard::LLViewerKeyboard() -{ - for (S32 i = 0; i < MODE_COUNT; i++) - { - mBindingCount[i] = 0; - } - - for (S32 i = 0; i < KEY_COUNT; i++) - { - mKeyHandledByUI[i] = FALSE; - } - // we want the UI to never see these keys so that they can always control the avatar/camera - for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++) - { - mKeysSkippedByUI.insert(k); - } -} - -BOOL LLViewerKeyboard::modeFromString(const std::string& string, S32 *mode) -{ - if (string == "FIRST_PERSON") - { - *mode = MODE_FIRST_PERSON; - return TRUE; - } - else if (string == "THIRD_PERSON") - { - *mode = MODE_THIRD_PERSON; - return TRUE; - } - else if (string == "EDIT") - { - *mode = MODE_EDIT; - return TRUE; - } - else if (string == "EDIT_AVATAR") - { - *mode = MODE_EDIT_AVATAR; - return TRUE; - } - else if (string == "SITTING") - { - *mode = MODE_SITTING; - return TRUE; - } - else - { - *mode = MODE_THIRD_PERSON; - return FALSE; - } -} - -BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated) -{ - // check for re-map - EKeyboardMode mode = gViewerKeyboard.getMode(); - U32 keyidx = (translated_mask<<16) | translated_key; - key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx); - if (iter != mRemapKeys[mode].end()) - { - translated_key = (iter->second) & 0xff; - translated_mask = (iter->second)>>16; - } - - // No repeats of F-keys - BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12); - if (!repeatable_key && repeated) - { - return FALSE; - } - - LL_DEBUGS("UserInput") << "keydown -" << translated_key << "-" << LL_ENDL; - // skip skipped keys - if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end()) - { - mKeyHandledByUI[translated_key] = FALSE; - LL_INFOS("KeyboardHandling") << "Key wasn't handled by UI!" << LL_ENDL; - } - else - { - // it is sufficient to set this value once per call to handlekey - // without clearing it, as it is only used in the subsequent call to scanKey - mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); - // mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress) - // NOT whether some UI shortcut wishes to handle the keypress - - } - return mKeyHandledByUI[translated_key]; -} - -BOOL LLViewerKeyboard::handleKeyUp(KEY translated_key, MASK translated_mask) -{ - return gViewerWindow->handleKeyUp(translated_key, translated_mask); -} - -BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name) -{ - S32 index; - typedef boost::function<void(EKeystate)> function_t; - function_t function = NULL; - std::string name; - - // Allow remapping of F2-F12 - if (function_name[0] == 'F') - { - int c1 = function_name[1] - '0'; - int c2 = function_name[2] ? function_name[2] - '0' : -1; - if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9) - { - int idx = c1; - if (c2 >= 0) - idx = idx*10 + c2; - if (idx >=2 && idx <= 12) - { - U32 keyidx = ((mask<<16)|key); - (mRemapKeys[mode])[keyidx] = ((0<<16)|(KEY_F1+(idx-1))); - return TRUE; - } - } - } - - // Not remapped, look for a function - - function_t* result = LLKeyboardActionRegistry::getValue(function_name); - if (result) - { - function = *result; - } - - if (!function) - { - LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; - return FALSE; - } - - // check for duplicate first and overwrite - for (index = 0; index < mBindingCount[mode]; index++) - { - if (key == mBindings[mode][index].mKey && mask == mBindings[mode][index].mMask) - break; - } - - if (index >= MAX_KEY_BINDINGS) - { - LL_ERRS() << "LLKeyboard::bindKey() - too many keys for mode " << mode << LL_ENDL; - return FALSE; - } - - if (mode >= MODE_COUNT) - { - LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; - return FALSE; - } - - mBindings[mode][index].mKey = key; - mBindings[mode][index].mMask = mask; - mBindings[mode][index].mFunction = function; - - if (index == mBindingCount[mode]) - mBindingCount[mode]++; - - return TRUE; -} - -LLViewerKeyboard::KeyBinding::KeyBinding() -: key("key"), - mask("mask"), - command("command") -{} - -LLViewerKeyboard::KeyMode::KeyMode(EKeyboardMode _mode) -: bindings("binding"), - mode(_mode) -{} - -LLViewerKeyboard::Keys::Keys() -: first_person("first_person", KeyMode(MODE_FIRST_PERSON)), - third_person("third_person", KeyMode(MODE_THIRD_PERSON)), - edit("edit", KeyMode(MODE_EDIT)), - sitting("sitting", KeyMode(MODE_SITTING)), - edit_avatar("edit_avatar", KeyMode(MODE_EDIT_AVATAR)) -{} - -S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename) -{ - S32 binding_count = 0; - Keys keys; - LLSimpleXUIParser parser; - - if (parser.readXUI(filename, keys) - && keys.validateBlock()) - { - binding_count += loadBindingMode(keys.first_person); - binding_count += loadBindingMode(keys.third_person); - binding_count += loadBindingMode(keys.edit); - binding_count += loadBindingMode(keys.sitting); - binding_count += loadBindingMode(keys.edit_avatar); - } - return binding_count; -} - -S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode) -{ - S32 binding_count = 0; - for (LLInitParam::ParamIterator<KeyBinding>::const_iterator it = keymode.bindings.begin(), - end_it = keymode.bindings.end(); - it != end_it; - ++it) - { - KEY key; - MASK mask; - LLKeyboard::keyFromString(it->key, &key); - LLKeyboard::maskFromString(it->mask, &mask); - bindKey(keymode.mode, key, mask, it->command); - binding_count++; - } - - return binding_count; -} - -S32 LLViewerKeyboard::loadBindings(const std::string& filename) -{ - LLFILE *fp; - const S32 BUFFER_SIZE = 2048; - char buffer[BUFFER_SIZE]; /* Flawfinder: ignore */ - // *NOTE: This buffer size is hard coded into scanf() below. - char mode_string[MAX_STRING] = ""; /* Flawfinder: ignore */ - char key_string[MAX_STRING] = ""; /* Flawfinder: ignore */ - char mask_string[MAX_STRING] = ""; /* Flawfinder: ignore */ - char function_string[MAX_STRING] = ""; /* Flawfinder: ignore */ - S32 mode = MODE_THIRD_PERSON; - KEY key = 0; - MASK mask = 0; - S32 tokens_read; - S32 binding_count = 0; - S32 line_count = 0; - - if(filename.empty()) - { - LL_ERRS() << " No filename specified" << LL_ENDL; - return 0; - } - - fp = LLFile::fopen(filename, "r"); - - if (!fp) - { - return 0; - } - - - while (!feof(fp)) - { - line_count++; - if (!fgets(buffer, BUFFER_SIZE, fp)) - break; - - // skip over comments, blank lines - if (buffer[0] == '#' || buffer[0] == '\n') continue; - - // grab the binding strings - tokens_read = sscanf( /* Flawfinder: ignore */ - buffer, - "%254s %254s %254s %254s", - mode_string, - key_string, - mask_string, - function_string); - - if (tokens_read == EOF) - { - LL_INFOS() << "Unexpected end-of-file at line " << line_count << " of key binding file " << filename << LL_ENDL; - fclose(fp); - return 0; - } - else if (tokens_read < 4) - { - LL_INFOS() << "Can't read line " << line_count << " of key binding file " << filename << LL_ENDL; - continue; - } - - // convert mode - if (!modeFromString(mode_string, &mode)) - { - LL_INFOS() << "Unknown mode on line " << line_count << " of key binding file " << filename << LL_ENDL; - LL_INFOS() << "Mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR" << LL_ENDL; - continue; - } - - // convert key - if (!LLKeyboard::keyFromString(key_string, &key)) - { - LL_INFOS() << "Can't interpret key on line " << line_count << " of key binding file " << filename << LL_ENDL; - continue; - } - - // convert mask - if (!LLKeyboard::maskFromString(mask_string, &mask)) - { - LL_INFOS() << "Can't interpret mask on line " << line_count << " of key binding file " << filename << LL_ENDL; - continue; - } - - // bind key - if (bindKey(mode, key, mask, function_string)) - { - binding_count++; - } - } - - fclose(fp); - - return binding_count; -} - - -EKeyboardMode LLViewerKeyboard::getMode() -{ - if ( gAgentCamera.cameraMouselook() ) - { - return MODE_FIRST_PERSON; - } - else if ( gMorphView && gMorphView->getVisible()) - { - return MODE_EDIT_AVATAR; - } - else if (isAgentAvatarValid() && gAgentAvatarp->isSitting()) - { - return MODE_SITTING; - } - else - { - return MODE_THIRD_PERSON; - } -} - - -// Called from scanKeyboard. -void LLViewerKeyboard::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) -{ - if (LLApp::isExiting()) - { - return; - } - - S32 mode = getMode(); - // Consider keyboard scanning as NOT mouse event. JC - MASK mask = gKeyboard->currentMask(FALSE); - - LLKeyBinding* binding = mBindings[mode]; - S32 binding_count = mBindingCount[mode]; - - - if (mKeyHandledByUI[key]) - { - return; - } - - // don't process key down on repeated keys - BOOL repeat = gKeyboard->getKeyRepeated(key); - - for (S32 i = 0; i < binding_count; i++) - { - //for (S32 key = 0; key < KEY_COUNT; key++) - if (binding[i].mKey == key) - { - //if (binding[i].mKey == key && binding[i].mMask == mask) - if (binding[i].mMask == mask) - { - if (key_down && !repeat) - { - // ...key went down this frame, call function - binding[i].mFunction( KEYSTATE_DOWN ); - } - else if (key_up) - { - // ...key went down this frame, call function - binding[i].mFunction( KEYSTATE_UP ); - } - else if (key_level) - { - // ...key held down from previous frame - // Not windows, just call the function. - binding[i].mFunction( KEYSTATE_LEVEL ); - }//if - }//if - }//for - }//for -} diff --git a/indra/newview/llviewerkeyboard.h b/indra/newview/llviewerkeyboard.h deleted file mode 100644 index 110dc89d28..0000000000 --- a/indra/newview/llviewerkeyboard.h +++ /dev/null @@ -1,118 +0,0 @@ -/** - * @file llviewerkeyboard.h - * @brief LLViewerKeyboard class header file - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVIEWERKEYBOARD_H -#define LL_LLVIEWERKEYBOARD_H - -#include "llkeyboard.h" // For EKeystate -#include "llinitparam.h" - -const S32 MAX_NAMED_FUNCTIONS = 100; -const S32 MAX_KEY_BINDINGS = 128; // was 60 - -class LLNamedFunction -{ -public: - LLNamedFunction() : mFunction(NULL) { }; - ~LLNamedFunction() { }; - - std::string mName; - LLKeyFunc mFunction; -}; - -typedef enum e_keyboard_mode -{ - MODE_FIRST_PERSON, - MODE_THIRD_PERSON, - MODE_EDIT, - MODE_EDIT_AVATAR, - MODE_SITTING, - MODE_COUNT -} EKeyboardMode; - - -void bind_keyboard_functions(); - -class LLViewerKeyboard -{ -public: - struct KeyBinding : public LLInitParam::Block<KeyBinding> - { - Mandatory<std::string> key, - mask, - command; - - KeyBinding(); - }; - - struct KeyMode : public LLInitParam::Block<KeyMode> - { - Multiple<KeyBinding> bindings; - EKeyboardMode mode; - KeyMode(EKeyboardMode mode); - }; - - struct Keys : public LLInitParam::Block<Keys> - { - Optional<KeyMode> first_person, - third_person, - edit, - sitting, - edit_avatar; - - Keys(); - }; - - LLViewerKeyboard(); - - BOOL handleKey(KEY key, MASK mask, BOOL repeated); - BOOL handleKeyUp(KEY key, MASK mask); - - S32 loadBindings(const std::string& filename); // returns number bound, 0 on error - S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error - EKeyboardMode getMode(); - - BOOL modeFromString(const std::string& string, S32 *mode); // False on failure - - void scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level); - -private: - S32 loadBindingMode(const LLViewerKeyboard::KeyMode& keymode); - BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name); - - // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here - S32 mBindingCount[MODE_COUNT]; - LLKeyBinding mBindings[MODE_COUNT][MAX_KEY_BINDINGS]; - - typedef std::map<U32, U32> key_remap_t; - key_remap_t mRemapKeys[MODE_COUNT]; - std::set<KEY> mKeysSkippedByUI; - BOOL mKeyHandledByUI[KEY_COUNT]; // key processed successfully by UI -}; - -extern LLViewerKeyboard gViewerKeyboard; - -#endif // LL_LLVIEWERKEYBOARD_H diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index c36d877a59..9ed2df2759 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -194,7 +194,6 @@ static F32 sGlobalVolume = 1.0f; static bool sForceUpdate = false; static LLUUID sOnlyAudibleTextureID = LLUUID::null; static F64 sLowestLoadableImplInterest = 0.0f; -static bool sAnyMediaShowing = false; ////////////////////////////////////////////////////////////////////////////////////////// static void add_media_impl(LLViewerMediaImpl* media) @@ -866,7 +865,7 @@ void LLViewerMedia::updateMedia(void *dummy_arg) if (!pimpl->getUsedInUI() && pimpl->hasMedia()) { - sAnyMediaShowing = true; + mAnyMediaShowing = true; } if (!pimpl->getUsedInUI() && pimpl->hasMedia() && (pimpl->isMediaPlaying() || !pimpl->isMediaTimeBased())) @@ -1719,23 +1718,8 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ std::string user_data_path_cache = gDirUtilp->getCacheDir(false); user_data_path_cache += gDirUtilp->getDirDelimiter(); - std::string user_data_path_cookies = gDirUtilp->getOSUserAppDir(); - user_data_path_cookies += gDirUtilp->getDirDelimiter(); - std::string user_data_path_cef_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef_log.txt"); - // Fix for EXT-5960 - make browser profile specific to user (cache, cookies etc.) - // If the linden username returned is blank, that can only mean we are - // at the login page displaying login Web page or Web browser test via Develop menu. - // In this case we just use whatever gDirUtilp->getOSUserAppDir() gives us (this - // is what we always used before this change) - std::string linden_user_dir = gDirUtilp->getLindenUserDir(); - if ( ! linden_user_dir.empty() ) - { - user_data_path_cookies = linden_user_dir; - user_data_path_cookies += gDirUtilp->getDirDelimiter(); - }; - // See if the plugin executable exists llstat s; if(LLFile::stat(launcher_name, &s)) @@ -1752,7 +1736,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ { media_source = new LLPluginClassMedia(owner); media_source->setSize(default_width, default_height); - media_source->setUserDataPath(user_data_path_cache, user_data_path_cookies, user_data_path_cef_log); + media_source->setUserDataPath(user_data_path_cache, gDirUtilp->getUserName(), user_data_path_cef_log); media_source->setLanguageCode(LLUI::getLanguage()); media_source->setZoomFactor(zoom_factor); diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index 71ae7bfbc3..ba80eeb6b6 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -205,8 +205,9 @@ bool LLViewerMediaFocus::getFocus() } // This function selects an ideal viewing distance based on the focused object, pick normal, and padding value -void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only) +LLVector3d LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only) { + LLVector3d camera_pos; if (object) { gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); @@ -254,7 +255,7 @@ void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, distance += depth * 0.5; // Finally animate the camera to this new position and focal point - LLVector3d camera_pos, target_pos; + LLVector3d target_pos; // The target lookat position is the center of the selection (in global coords) target_pos = center; // Target look-from (camera) position is "distance" away from the target along the normal @@ -287,7 +288,7 @@ void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, if (zoom_in_only && (dist_vec_squared(gAgentCamera.getCameraPositionGlobal(), target_pos) < dist_vec_squared(camera_pos, target_pos))) { - return; + return camera_pos; } gAgentCamera.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() ); @@ -298,6 +299,7 @@ void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, // If we have no object, focus back on the avatar. gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); } + return camera_pos; } void LLViewerMediaFocus::onFocusReceived() { @@ -338,15 +340,6 @@ BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent) clearFocus(); } - - if ( KEY_F1 == key && LLUI::getInstance()->mHelpImpl && mMediaControls.get()) - { - std::string help_topic; - if (mMediaControls.get()->findHelpTopic(help_topic)) - { - LLUI::getInstance()->mHelpImpl->showTopic(help_topic); - } - } } return true; diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index fa469c36e3..effd08a559 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -63,7 +63,7 @@ public: void update(); - static void setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only = false); + static LLVector3d setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only = false); static F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); bool isFocusedOnFace(LLPointer<LLViewerObject> objectp, S32 face); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index b6c7be2ed3..9d05f59b09 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -375,20 +375,6 @@ void initialize_menus(); // Break up groups of more than 6 items with separators //----------------------------------------------------------------------------- -void set_underclothes_menu_options() -{ - if (gMenuHolder && gAgent.isTeen()) - { - gMenuHolder->getChild<LLView>("Self Underpants")->setVisible(FALSE); - gMenuHolder->getChild<LLView>("Self Undershirt")->setVisible(FALSE); - } - if (gMenuBarView && gAgent.isTeen()) - { - gMenuBarView->getChild<LLView>("Menu Underpants")->setVisible(FALSE); - gMenuBarView->getChild<LLView>("Menu Undershirt")->setVisible(FALSE); - } -} - void set_merchant_SLM_menu() { // All other cases (new merchant, not merchant, migrated merchant): show the new Marketplace Listings menu and enable the tool @@ -740,6 +726,10 @@ U32 render_type_from_string(std::string render_type) { return LLPipeline::RENDER_TYPE_AVATAR; } + else if ("controlAV" == render_type) // Animesh + { + return LLPipeline::RENDER_TYPE_CONTROL_AV; + } else if ("surfacePatch" == render_type) { return LLPipeline::RENDER_TYPE_TERRAIN; @@ -2777,7 +2767,6 @@ class LLObjectBuild : public view_listener_t } }; - void handle_object_edit() { LLViewerParcelMgr::getInstance()->deselectLand(); @@ -2822,6 +2811,57 @@ void handle_object_edit() return; } +void handle_attachment_edit(const LLUUID& inv_item_id) +{ + if (isAgentAvatarValid()) + { + if (LLViewerObject* attached_obj = gAgentAvatarp->getWornAttachment(inv_item_id)) + { + LLSelectMgr::getInstance()->deselectAll(); + LLSelectMgr::getInstance()->selectObjectAndFamily(attached_obj); + + handle_object_edit(); + } + } +} + +void handle_attachment_touch(const LLUUID& inv_item_id) +{ + if ( (isAgentAvatarValid()) && (enable_attachment_touch(inv_item_id)) ) + { + if (LLViewerObject* attach_obj = gAgentAvatarp->getWornAttachment(gInventory.getLinkedItemID(inv_item_id))) + { + LLSelectMgr::getInstance()->deselectAll(); + + LLObjectSelectionHandle sel = LLSelectMgr::getInstance()->selectObjectAndFamily(attach_obj); + if (!LLToolMgr::getInstance()->inBuildMode()) + { + struct SetTransient : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) + { + node->setTransient(TRUE); + return true; + } + } f; + sel->applyToNodes(&f); + } + + handle_object_touch(); + } + } +} + +bool enable_attachment_touch(const LLUUID& inv_item_id) +{ + if (isAgentAvatarValid()) + { + const LLViewerObject* attach_obj = gAgentAvatarp->getWornAttachment(gInventory.getLinkedItemID(inv_item_id)); + return (attach_obj) && (attach_obj->flagHandleTouch()); + } + return false; +} + void handle_object_inspect() { LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); @@ -3144,6 +3184,7 @@ class LLObjectMute : public view_listener_t if (avatar) { avatar->mNeedsImpostorUpdate = TRUE; + avatar->mLastImpostorUpdateReason = 9; id = avatar->getID(); @@ -4104,7 +4145,7 @@ void handle_reset_view() // switching to outfit selector should automagically save any currently edited wearable LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "my_outfits")); } - + gAgentCamera.setFocusOnAvatar(TRUE, FALSE, FALSE); reset_view_final( TRUE ); LLFloaterCamera::resetCameraMode(); } @@ -4595,6 +4636,18 @@ void handle_take_copy() derez_objects(DRD_ACQUIRE_TO_AGENT_INVENTORY, category_id); } +void handle_link_objects() +{ + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + LLFloaterReg::toggleInstanceOrBringToFront("places"); + } + else + { + LLSelectMgr::getInstance()->linkObjects(); + } +} + // You can return an object to its owner if it is on your land. class LLObjectReturn : public view_listener_t { @@ -5353,7 +5406,7 @@ class LLToolsSelectNextPartFace : public view_listener_t new_te = to_select->getNumTEs() - 1; } } - LLSelectMgr::getInstance()->addAsIndividual(to_select, new_te, FALSE); + LLSelectMgr::getInstance()->selectObjectOnly(to_select, new_te); } else { @@ -7858,7 +7911,7 @@ void handle_grab_baked_texture(void* data) if(folder_id.notNull()) { std::string name; - name = "Baked " + LLAvatarAppearanceDictionary::getInstance()->getBakedTexture(baked_tex_index)->mNameCapitalized + " Texture"; + name = "Baked " + LLAvatarAppearance::getDictionary()->getBakedTexture(baked_tex_index)->mNameCapitalized + " Texture"; LLUUID item_id; item_id.generate(); @@ -8031,7 +8084,6 @@ BOOL LLViewerMenuHolderGL::hideMenus() if (LLMenuHolderGL::hideMenus()) { - LLToolPie::instance().blockClickToWalk(); handled = TRUE; } @@ -8981,7 +9033,7 @@ void initialize_menus() view_listener_t::addMenu(new LLToolsSnapObjectXY(), "Tools.SnapObjectXY"); view_listener_t::addMenu(new LLToolsUseSelectionForGrid(), "Tools.UseSelectionForGrid"); view_listener_t::addMenu(new LLToolsSelectNextPartFace(), "Tools.SelectNextPart"); - commit.add("Tools.Link", boost::bind(&LLSelectMgr::linkObjects, LLSelectMgr::getInstance())); + commit.add("Tools.Link", boost::bind(&handle_link_objects)); commit.add("Tools.Unlink", boost::bind(&LLSelectMgr::unlinkObjects, LLSelectMgr::getInstance())); view_listener_t::addMenu(new LLToolsStopAllAnimations(), "Tools.StopAllAnimations"); view_listener_t::addMenu(new LLToolsReleaseKeys(), "Tools.ReleaseKeys"); diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index 6882405407..0f63c8cf58 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -82,7 +82,6 @@ void handle_detach(void*); BOOL enable_god_full(void* user_data); BOOL enable_god_liaison(void* user_data); BOOL enable_god_basic(void* user_data); -void set_underclothes_menu_options(); void check_merchant_status(bool force = false); void exchange_callingcard(const LLUUID& dest_id); @@ -110,6 +109,10 @@ void handle_object_return(); void handle_object_delete(); void handle_object_edit(); +void handle_attachment_edit(const LLUUID& inv_item_id); +void handle_attachment_touch(const LLUUID& inv_item_id); +bool enable_attachment_touch(const LLUUID& inv_item_id); + void handle_buy_land(); // Takes avatar UUID, or if no UUID passed, uses last selected object diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index cd48b1e8e7..4b231c7067 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -391,7 +391,9 @@ const void upload_single_file(const std::vector<std::string>& filenames, LLFileP } if (type == LLFilePicker::FFLOAD_ANIM) { - if (filename.rfind(".anim") != std::string::npos) + std::string filename_lc(filename); + LLStringUtil::toLower(filename_lc); + if (filename_lc.rfind(".anim") != std::string::npos) { LLFloaterReg::showInstance("upload_anim_anim", LLSD(filename)); } @@ -552,7 +554,7 @@ class LLFileUploadModel : public view_listener_t LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::getInstance("upload_model"); if (fmp && !fmp->isModelLoading()) { - fmp->loadModel(3); + fmp->loadHighLodModel(); } return TRUE; @@ -686,11 +688,12 @@ class LLFileTakeSnapshotToDisk : public view_listener_t bool render_ui = gSavedSettings.getBOOL("RenderUIInSnapshot"); bool render_hud = gSavedSettings.getBOOL("RenderHUDInSnapshot"); - if (gSavedSettings.getBOOL("HighResSnapshot")) + BOOL high_res = gSavedSettings.getBOOL("HighResSnapshot"); + if (high_res) { width *= 2; height *= 2; - // not compatible wirh UI/HUD + // not compatible with UI/HUD render_ui = false; render_hud = false; } @@ -702,7 +705,9 @@ class LLFileTakeSnapshotToDisk : public view_listener_t FALSE, render_ui, render_hud, - FALSE)) + FALSE, + LLSnapshotModel::SNAPSHOT_TYPE_COLOR, + high_res ? S32_MAX : MAX_SNAPSHOT_IMAGE_SIZE)) //per side { LLPointer<LLImageFormatted> formatted; LLSnapshotModel::ESnapshotFormat fmt = (LLSnapshotModel::ESnapshotFormat) gSavedSettings.getS32("SnapshotFormat"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 06a8ebbe89..458fc3b13d 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -406,6 +406,7 @@ void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_ void send_complete_agent_movement(const LLHost& sim_host) { + LL_DEBUGS("Teleport", "Messaging") << "Sending CompleteAgentMovement to sim_host " << sim_host << LL_ENDL; LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_CompleteAgentMovement); msg->nextBlockFast(_PREHASH_AgentData); @@ -2220,8 +2221,12 @@ protected: } }; +static LLTrace::BlockTimerStatHandle FTM_PROCESS_IMPROVED_IM("Process IM"); + void process_improved_im(LLMessageSystem *msg, void **user_data) { + LL_RECORD_BLOCK_TIME(FTM_PROCESS_IMPROVED_IM); + LLUUID from_id; BOOL from_group; LLUUID to_id; @@ -2873,12 +2878,12 @@ BOOL LLPostTeleportNotifiers::tick() // We're going to pretend to be a new agent void process_teleport_finish(LLMessageSystem* msg, void**) { - LL_DEBUGS("Messaging") << "Got teleport location message" << LL_ENDL; + LL_DEBUGS("Teleport","Messaging") << "Received TeleportFinish message" << LL_ENDL; LLUUID agent_id; msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); if (agent_id != gAgent.getID()) { - LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; + LL_WARNS("Teleport","Messaging") << "Got teleport notification for wrong agent " << agent_id << " expected " << gAgent.getID() << ", ignoring!" << LL_ENDL; return; } @@ -2888,12 +2893,13 @@ void process_teleport_finish(LLMessageSystem* msg, void**) { // Server either ignored teleport cancel message or did not receive it in time. // This message can't be ignored since teleport is complete at server side + LL_INFOS("Teleport") << "Restoring canceled teleport request" << LL_ENDL; gAgent.restoreCanceledTeleportRequest(); } else { // Race condition? Make sure all variables are set correctly for teleport to work - LL_WARNS("Messaging") << "Teleport 'finish' message without 'start'" << LL_ENDL; + LL_WARNS("Teleport","Messaging") << "Teleport 'finish' message without 'start'. Setting state to TELEPORT_REQUESTED" << LL_ENDL; gTeleportDisplay = TRUE; LLViewerMessage::getInstance()->mTeleportStartedSignal(); gAgent.setTeleportState(LLAgent::TELEPORT_REQUESTED); @@ -2902,7 +2908,7 @@ void process_teleport_finish(LLMessageSystem* msg, void**) } else if (gAgent.getTeleportState() == LLAgent::TELEPORT_MOVING) { - LL_WARNS("Messaging") << "Teleport message in the middle of other teleport" << LL_ENDL; + LL_WARNS("Teleport","Messaging") << "Teleport message in the middle of other teleport" << LL_ENDL; } // Teleport is finished; it can't be cancelled now. @@ -2930,11 +2936,18 @@ void process_teleport_finish(LLMessageSystem* msg, void**) msg->getU64Fast(_PREHASH_Info, _PREHASH_RegionHandle, region_handle); U32 teleport_flags; msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); - - + std::string seedCap; msg->getStringFast(_PREHASH_Info, _PREHASH_SeedCapability, seedCap); + LL_DEBUGS("Teleport") << "TeleportFinish message params are:" + << " sim_ip " << sim_ip + << " sim_port " << sim_port + << " region_handle " << region_handle + << " teleport_flags " << teleport_flags + << " seedCap " << seedCap + << LL_ENDL; + // update home location if we are teleporting out of prelude - specific to teleporting to welcome area if((teleport_flags & TELEPORT_FLAGS_SET_HOME_TO_TARGET) && (!gAgent.isGodlike())) @@ -2976,7 +2989,7 @@ void process_teleport_finish(LLMessageSystem* msg, void**) gAgent.standUp(); // now, use the circuit info to tell simulator about us! - LL_INFOS("Messaging") << "process_teleport_finish() Enabling " + LL_INFOS("Teleport","Messaging") << "process_teleport_finish() sending UseCircuitCode to enable sim_host " << sim_host << " with code " << msg->mOurCircuitCode << LL_ENDL; msg->newMessageFast(_PREHASH_UseCircuitCode); msg->nextBlockFast(_PREHASH_CircuitCode); @@ -2985,11 +2998,12 @@ void process_teleport_finish(LLMessageSystem* msg, void**) msg->addUUIDFast(_PREHASH_ID, gAgent.getID()); msg->sendReliable(sim_host); + LL_INFOS("Teleport") << "Calling send_complete_agent_movement() and setting state to TELEPORT_MOVING" << LL_ENDL; send_complete_agent_movement(sim_host); gAgent.setTeleportState( LLAgent::TELEPORT_MOVING ); gAgent.setTeleportMessage(LLAgent::sTeleportProgressMessages["contacting"]); - LL_DEBUGS("CrossingCaps") << "Calling setSeedCapability from process_teleport_finish(). Seed cap == " + LL_DEBUGS("CrossingCaps") << "Calling setSeedCapability(). Seed cap == " << seedCap << LL_ENDL; regionp->setSeedCapability(seedCap); @@ -3022,6 +3036,8 @@ void process_avatar_init_complete(LLMessageSystem* msg, void**) void process_agent_movement_complete(LLMessageSystem* msg, void**) { + LL_INFOS("Teleport","Messaging") << "Received ProcessAgentMovementComplete" << LL_ENDL; + gShiftFrame = true; gAgentMovementCompleted = true; @@ -3031,13 +3047,13 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) { - LL_WARNS("Messaging") << "Incorrect id in process_agent_movement_complete()" - << LL_ENDL; + LL_WARNS("Teleport", "Messaging") << "Incorrect agent or session id in process_agent_movement_complete()" + << " agent " << agent_id << " expected " << gAgent.getID() + << " session " << session_id << " expected " << gAgent.getSessionID() + << ", ignoring" << LL_ENDL; return; } - LL_DEBUGS("Messaging") << "process_agent_movement_complete()" << LL_ENDL; - // *TODO: check timestamp to make sure the movement compleation // makes sense. LLVector3 agent_pos; @@ -3054,7 +3070,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) { // Could happen if you were immediately god-teleported away on login, // maybe other cases. Continue, but warn. - LL_WARNS("Messaging") << "agent_movement_complete() with NULL avatarp." << LL_ENDL; + LL_WARNS("Teleport", "Messaging") << "agent_movement_complete() with NULL avatarp." << LL_ENDL; } F32 x, y; @@ -3064,19 +3080,21 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) { if (gAgent.getRegion()) { - LL_WARNS("Messaging") << "current region " << gAgent.getRegion()->getOriginGlobal() << LL_ENDL; + LL_WARNS("Teleport", "Messaging") << "current region origin " + << gAgent.getRegion()->getOriginGlobal() << " id " << gAgent.getRegion()->getRegionID() << LL_ENDL; } - LL_WARNS("Messaging") << "Agent being sent to invalid home region: " - << x << ":" << y - << " current pos " << gAgent.getPositionGlobal() - << LL_ENDL; + LL_WARNS("Teleport", "Messaging") << "Agent being sent to invalid home region: " + << x << ":" << y + << " current pos " << gAgent.getPositionGlobal() + << ", calling forceDisconnect()" + << LL_ENDL; LLAppViewer::instance()->forceDisconnect(LLTrans::getString("SentToInvalidRegion")); return; } - LL_INFOS("Messaging") << "Changing home region to " << x << ":" << y << LL_ENDL; + LL_INFOS("Teleport","Messaging") << "Changing home region to region id " << regionp->getRegionID() << " handle " << region_handle << " == x,y " << x << "," << y << LL_ENDL; // set our upstream host the new simulator and shuffle things as // appropriate. @@ -3104,6 +3122,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) gAgentCamera.slamLookAt(look_at); gAgentCamera.updateCamera(); + LL_INFOS("Teleport") << "Agent movement complete, setting state to TELEPORT_START_ARRIVAL" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_START_ARRIVAL ); if (isAgentAvatarValid()) @@ -3117,6 +3136,8 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) else { // This is initial log-in or a region crossing + LL_INFOS("Teleport") << "State is not TELEPORT_MOVING, so this is initial log-in or region crossing. " + << "Setting state to TELEPORT_NONE" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); if(LLStartUp::getStartupState() < STATE_STARTED) @@ -5715,8 +5736,9 @@ void process_script_question(LLMessageSystem *msg, void **user_data) // so we'll reuse the same namespace for both throttle types. std::string throttle_name = owner_name; std::string self_name; - LLAgentUI::buildFullname( self_name ); - if( owner_name == self_name ) + LLAgentUI::buildFullname( self_name ); // does not include ' Resident' + std::string clean_owner_name = LLCacheName::cleanFullName(owner_name); // removes ' Resident' + if( clean_owner_name == self_name ) { throttle_name = taskid.getString(); } @@ -5751,7 +5773,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data) S32 count = 0; LLSD args; args["OBJECTNAME"] = object_name; - args["NAME"] = LLCacheName::cleanFullName(owner_name); + args["NAME"] = clean_owner_name; S32 known_questions = 0; bool has_not_only_debit = questions ^ SCRIPT_PERMISSIONS[SCRIPT_PERMISSION_DEBIT].permbit; // check the received permission flags against each permission @@ -5951,6 +5973,8 @@ std::string formatted_time(const time_t& the_time) void process_teleport_failed(LLMessageSystem *msg, void**) { + LL_WARNS("Teleport","Messaging") << "Received TeleportFailed message" << LL_ENDL; + std::string message_id; // Tag from server, like "RegionEntryAccessBlocked" std::string big_reason; // Actual message to display LLSD args; @@ -5969,6 +5993,7 @@ void process_teleport_failed(LLMessageSystem *msg, void**) // Nothing found in the map - use what the server returned in the original message block msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, big_reason); } + LL_WARNS("Teleport") << "AlertInfo message_id " << message_id << " reason: " << big_reason << LL_ENDL; LLSD llsd_block; std::string llsd_raw; @@ -5978,10 +6003,11 @@ void process_teleport_failed(LLMessageSystem *msg, void**) std::istringstream llsd_data(llsd_raw); if (!LLSDSerialize::deserialize(llsd_block, llsd_data, llsd_raw.length())) { - LL_WARNS() << "process_teleport_failed: Attempted to read alert parameter data into LLSD but failed:" << llsd_raw << LL_ENDL; + LL_WARNS("Teleport") << "process_teleport_failed: Attempted to read alert parameter data into LLSD but failed:" << llsd_raw << LL_ENDL; } else { + LL_WARNS("Teleport") << "AlertInfo llsd block received: " << llsd_block << LL_ENDL; if(llsd_block.has("REGION_NAME")) { std::string region_name = llsd_block["REGION_NAME"].asString(); @@ -5997,6 +6023,7 @@ void process_teleport_failed(LLMessageSystem *msg, void**) { if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { + LL_WARNS("Teleport") << "called handle_teleport_access_blocked, setting state to TELEPORT_NONE" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } return; @@ -6019,22 +6046,27 @@ void process_teleport_failed(LLMessageSystem *msg, void**) args["REASON"] = message_id; } } + LL_WARNS("Teleport") << "Displaying CouldNotTeleportReason string, REASON= " << args["REASON"] << LL_ENDL; LLNotificationsUtil::add("CouldNotTeleportReason", args); if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { + LL_WARNS("Teleport") << "End of process_teleport_failed(). Reason message arg is " << args["REASON"] + << ". Setting state to TELEPORT_NONE" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } } void process_teleport_local(LLMessageSystem *msg,void**) { + LL_INFOS("Teleport","Messaging") << "Received TeleportLocal message" << LL_ENDL; + LLUUID agent_id; msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); if (agent_id != gAgent.getID()) { - LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; + LL_WARNS("Teleport", "Messaging") << "Got teleport notification for wrong agent " << agent_id << " expected " << gAgent.getID() << ", ignoring!" << LL_ENDL; return; } @@ -6046,6 +6078,7 @@ void process_teleport_local(LLMessageSystem *msg,void**) msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at); msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); + LL_INFOS("Teleport") << "Message params are location_id " << location_id << " teleport_flags " << teleport_flags << LL_ENDL; if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) { if( gAgent.getTeleportState() == LLAgent::TELEPORT_LOCAL ) @@ -6058,6 +6091,8 @@ void process_teleport_local(LLMessageSystem *msg,void**) } else { + LL_WARNS("Teleport") << "State is not TELEPORT_LOCAL: " << gAgent.getTeleportStateName() + << ", setting state to TELEPORT_NONE" << LL_ENDL; gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } } diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index a593905060..be05ac0d3a 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -130,7 +130,7 @@ void LLGridManager::initialize(const std::string& grid_file) "https://secondlife.aditi.lindenlab.com/helpers/", DEFAULT_LOGIN_PAGE, SL_UPDATE_QUERY_URL, - "https://my.aditi.lindenlab.com/", + "https://my.secondlife-beta.com/", "Aditi"); LLSD other_grids; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 9c91cde09a..395dd0495f 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -284,6 +284,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mOnActiveList(FALSE), mOnMap(FALSE), mStatic(FALSE), + mSeatCount(0), mNumFaces(0), mRotTime(0.f), mAngularVelocityRot(), @@ -895,7 +896,12 @@ void LLViewerObject::addChild(LLViewerObject *childp) if(childp->setParent(this)) { mChildList.push_back(childp); - childp->afterReparent(); + childp->afterReparent(); + + if (childp->isAvatar()) + { + mSeatCount++; + } } } @@ -924,6 +930,11 @@ void LLViewerObject::removeChild(LLViewerObject *childp) { childp->setParent(NULL); } + + if (childp->isAvatar()) + { + mSeatCount--; + } break; } } @@ -981,21 +992,10 @@ BOOL LLViewerObject::isChild(LLViewerObject *childp) const return FALSE; } - // returns TRUE if at least one avatar is sitting on this object BOOL LLViewerObject::isSeat() const { - for (child_list_t::const_iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - if (child->isAvatar()) - { - return TRUE; - } - } - return FALSE; - + return mSeatCount > 0; } BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) @@ -4800,9 +4800,7 @@ LLViewerTexture* LLViewerObject::getBakedTextureForMagicId(const LLUUID& id) } LLVOAvatar* avatar = getAvatar(); - if (avatar && !isHUDAttachment() - && isMesh() - && getVolume() && getVolume()->getParams().getSculptID().notNull()) // checking for the rigged mesh by params instead of using isRiggedMesh() to avoid false negatives when skin info isn't ready + if (avatar && !isHUDAttachment()) { LLAvatarAppearanceDefines::EBakedTextureIndex texIndex = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::assetIdToBakedTextureIndex(id); LLViewerTexture* bakedTexture = avatar->getBakedTexture(texIndex); @@ -4882,22 +4880,78 @@ void LLViewerObject::refreshBakeTexture() } } +void LLViewerObject::updateDiffuseMatParams(const U8 te, LLMaterial* mat, LLViewerTexture *imagep, bool baked_texture) +{ + // Objects getting non-alpha texture and alpha mask can result in graphical bugs, like white or red alphas. + // To resolve the issue this function provides image format to material and based on format material's + // getDiffuseAlphaModeRender() function will decide what value to provide to render + // + // Unfortunately LLMaterial has no access to diffuse image, so we have to set this data in LLViewerObject + // regardles of object being used/seen or frequency of image-updates. + mat->setDiffuseBaked(baked_texture); + + if (!baked_texture) + { + if (imagep->isMissingAsset()) + { + mat->setDiffuseFormatPrimary(0); + } + else if (0 == imagep->getPrimaryFormat()) + { + // We don't have information about this texture, wait for it + mWaitingTextureInfo.insert(uuid_material_mmap_t::value_type(imagep->getID(), material_info(LLRender::DIFFUSE_MAP, te))); + // Temporary assume RGBA image + mat->setDiffuseFormatPrimary(GL_RGBA); + } + else + { + mat->setDiffuseFormatPrimary(imagep->getPrimaryFormat()); + } + } +} + +S32 LLViewerObject::setDiffuseImageAndParams(const U8 te, LLViewerTexture *imagep) +{ + LLUUID new_id = imagep->getID(); + S32 retval = LLPrimitive::setTETexture(te, new_id); + + LLTextureEntry* tep = getTE(te); + LLUUID old_image_id = tep->getID(); + + LLViewerTexture* baked_texture = getBakedTextureForMagicId(new_id); + mTEImages[te] = baked_texture ? baked_texture : imagep; + updateAvatarMeshVisibility(new_id, old_image_id); + + LLMaterial* mat = tep->getMaterialParams(); + if (mat) + { + // Don't update format from texture (and don't shedule one) if material has no alpha mode set, + // just assume RGBA format, format will get updated with setTEMaterialParams call if mode changes + if (mat->getDiffuseAlphaMode() != LLMaterial::DIFFUSE_ALPHA_MODE_NONE) + { + bool baked = baked_texture != NULL; + updateDiffuseMatParams(te, mat, imagep, baked); + } + else + { + mat->setDiffuseFormatPrimary(GL_RGBA); + } + } + + setChanged(TEXTURE); + if (mDrawable.notNull()) + { + gPipeline.markTextured(mDrawable); + } + + return retval; +} + void LLViewerObject::setTEImage(const U8 te, LLViewerTexture *imagep) { if (mTEImages[te] != imagep) { - LLUUID old_image_id = getTE(te) ? getTE(te)->getID() : LLUUID::null; - - LLPrimitive::setTETexture(te, imagep->getID()); - - LLViewerTexture* baked_texture = getBakedTextureForMagicId(imagep->getID()); - mTEImages[te] = baked_texture ? baked_texture : imagep; - updateAvatarMeshVisibility(imagep->getID(), old_image_id); - setChanged(TEXTURE); - if (mDrawable.notNull()) - { - gPipeline.markTextured(mDrawable); - } + setDiffuseImageAndParams(te, imagep); } } @@ -4909,15 +4963,7 @@ S32 LLViewerObject::setTETextureCore(const U8 te, LLViewerTexture *image) if (uuid != getTE(te)->getID() || uuid == LLUUID::null) { - retval = LLPrimitive::setTETexture(te, uuid); - LLViewerTexture* baked_texture = getBakedTextureForMagicId(uuid); - mTEImages[te] = baked_texture ? baked_texture : image; - updateAvatarMeshVisibility(uuid,old_image_id); - setChanged(TEXTURE); - if (mDrawable.notNull()) - { - gPipeline.markTextured(mDrawable); - } + retval = setDiffuseImageAndParams(te, image); } return retval; } @@ -5212,6 +5258,29 @@ S32 LLViewerObject::setTEMaterialParams(const U8 te, const LLMaterialPtr pMateri return 0; } + if (pMaterialParams.notNull() + && pMaterialParams->getDiffuseAlphaMode() != LLMaterial::DIFFUSE_ALPHA_MODE_NONE) + { + // Don't update if no alpha is set. If alpha changes, this function will run again, + // no point in sheduling additional texture callbacks (in updateDiffuseMatParams) + LLTextureEntry* tex_entry = getTE(te); + bool is_baked = tex_entry && LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(tex_entry->getID()); + + LLViewerTexture *img_diffuse = getTEImage(te); + llassert(NULL != img_diffuse); + + if (NULL != img_diffuse) + { + // Will modify alpha mask provided to renderer to fit image + updateDiffuseMatParams(te, pMaterialParams.get(), img_diffuse, is_baked); + } + else + { + LLMaterial *mat = pMaterialParams.get(); // to avoid const + mat->setDiffuseFormatPrimary(0); + } + } + retval = LLPrimitive::setTEMaterialParams(te, pMaterialParams); LL_DEBUGS("Material") << "Changing material params for te " << (S32)te << ", object " << mID @@ -5224,6 +5293,84 @@ S32 LLViewerObject::setTEMaterialParams(const U8 te, const LLMaterialPtr pMateri return retval; } +bool LLViewerObject::notifyAboutCreatingTexture(LLViewerTexture *texture) +{ + // Confirmation about texture creation, check wait-list + // and make changes, or return false + + std::pair<uuid_material_mmap_t::iterator, uuid_material_mmap_t::iterator> range = mWaitingTextureInfo.equal_range(texture->getID()); + + bool refresh_materials = false; + + // RGB textures without alpha channels won't work right with alpha, + // we provide format to material for material to decide when to drop alpha + for (uuid_material_mmap_t::iterator range_it = range.first; range_it != range.second; ++range_it) + { + LLMaterialPtr cur_material = getTEMaterialParams(range_it->second.te); + if (cur_material.notNull() + && LLRender::DIFFUSE_MAP == range_it->second.map) + { + U32 format = texture->getPrimaryFormat(); + if (format != cur_material->getDiffuseFormatPrimary()) + { + cur_material->setDiffuseFormatPrimary(format); + refresh_materials = true; + } + } + } //for + + if (refresh_materials) + { + LLViewerObject::refreshMaterials(); + } + + //clear wait-list + mWaitingTextureInfo.erase(range.first, range.second); + + return refresh_materials; +} + +bool LLViewerObject::notifyAboutMissingAsset(LLViewerTexture *texture) +{ + // When waiting information about texture it turned out to be missing. + // Confirm the state, update values accordingly + std::pair<uuid_material_mmap_t::iterator, uuid_material_mmap_t::iterator> range = mWaitingTextureInfo.equal_range(texture->getID()); + if (range.first == range.second) return false; + + bool refresh_materials = false; + + for (uuid_material_mmap_t::iterator range_it = range.first; range_it != range.second; ++range_it) + { + LLMaterialPtr cur_material = getTEMaterialParams(range_it->second.te); + if (cur_material.isNull()) + continue; + + if (range_it->second.map == LLRender::DIFFUSE_MAP) + { + LLMaterialPtr cur_material = getTEMaterialParams(range_it->second.te); + if (cur_material.notNull() + && LLRender::DIFFUSE_MAP == range_it->second.map) + { + if (0 != cur_material->getDiffuseFormatPrimary()) + { + cur_material->setDiffuseFormatPrimary(0); + refresh_materials = true; + } + } + } + } //for + + if (refresh_materials) + { + LLViewerObject::refreshMaterials(); + } + + //clear wait-list + mWaitingTextureInfo.erase(range.first, range.second); + + return refresh_materials; +} + void LLViewerObject::refreshMaterials() { setChanged(TEXTURE); diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 03c5403a1e..9444c4f788 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -114,7 +114,7 @@ class LLViewerObject public LLTrace::MemTrackable<LLViewerObject> { protected: - ~LLViewerObject(); // use unref() + virtual ~LLViewerObject(); // use unref() // TomY: Provide for a list of extra parameter structures, mapped by structure name struct ExtraParameter @@ -768,7 +768,12 @@ protected: void unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id, bool legacy); void deleteParticleSource(); void setParticleSource(const LLPartSysData& particle_parameters, const LLUUID& owner_id); - + + // Helper function to modify alpha mask provided to render according to image (ex: RGB image will drop alpha mask) + void updateDiffuseMatParams(const U8 te, LLMaterial* mat, LLViewerTexture *imagep, bool baked_texture); + // Shared part of code from setTEImage and setTETextureCore + S32 setDiffuseImageAndParams(const U8 te, LLViewerTexture *imagep); + private: void setNameValueList(const std::string& list); // clears nv pairs and then individually adds \n separated NV pairs from \0 terminated string void deleteTEImages(); // correctly deletes list of images @@ -832,6 +837,7 @@ protected: BOOL mOnActiveList; BOOL mOnMap; // On the map. BOOL mStatic; // Object doesn't move. + S32 mSeatCount; S32 mNumFaces; F32 mRotTime; // Amount (in seconds) that object has rotated according to angular velocity (llSetTargetOmega) @@ -900,10 +906,27 @@ public: LLJointRiggingInfoTab mJointRiggingInfoTab; + bool notifyAboutCreatingTexture(LLViewerTexture *texture); + bool notifyAboutMissingAsset(LLViewerTexture *texture); + private: LLUUID mAttachmentItemID; // ItemID of the associated object is in user inventory. EObjectUpdateType mLastUpdateType; BOOL mLastUpdateCached; + + struct material_info + { + LLRender::eTexIndex map; + U8 te; + + material_info(LLRender::eTexIndex map_, U8 te_) + : map(map_) + , te(te_) + {} + }; + + typedef std::multimap<LLUUID, material_info> uuid_material_mmap_t; + uuid_material_mmap_t mWaitingTextureInfo; }; /////////////////// diff --git a/indra/newview/llviewerparcelaskplay.cpp b/indra/newview/llviewerparcelaskplay.cpp index 74586dadc3..afbe2c94de 100644 --- a/indra/newview/llviewerparcelaskplay.cpp +++ b/indra/newview/llviewerparcelaskplay.cpp @@ -59,7 +59,10 @@ void LLViewerParcelAskPlay::initSingleton() } void LLViewerParcelAskPlay::cleanupSingleton() { - cancelNotification(); + if (LLNotifications::instanceExists()) + { + cancelNotification(); + } } void LLViewerParcelAskPlay::askToPlay(const LLUUID ®ion_id, const S32 &parcel_id, const std::string &url, ask_callback cb) diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index c966b7d4f9..d5365e4ee8 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1906,7 +1906,10 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use || music_url.substr(0, 8) == "https://") { LLViewerRegion *region = LLWorld::getInstance()->getRegion(msg->getSender()); - optionally_start_music(music_url, parcel->mLocalID, region->getRegionID()); + if (region) + { + optionally_start_music(music_url, parcel->mLocalID, region->getRegionID()); + } } else { diff --git a/indra/newview/llviewerpartsource.cpp b/indra/newview/llviewerpartsource.cpp index 998ae52fe0..f042040e98 100644 --- a/indra/newview/llviewerpartsource.cpp +++ b/indra/newview/llviewerpartsource.cpp @@ -136,7 +136,7 @@ void LLViewerPartSourceScript::update(const F32 dt) { mOwnerAvatarp = find_avatar(mOwnerUUID); } - if (mOwnerAvatarp.notNull() && LLVOAvatar::AV_DO_NOT_RENDER == mOwnerAvatarp->getVisualMuteSettings()) + if (mOwnerAvatarp.notNull() && LLVOAvatar::AOA_NORMAL != mOwnerAvatarp->getOverallAppearance()) { return; } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index e67826454b..2fde4fe49c 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -102,6 +102,7 @@ const U32 DEFAULT_MAX_REGION_WIDE_PRIM_COUNT = 15000; BOOL LLViewerRegion::sVOCacheCullingEnabled = FALSE; S32 LLViewerRegion::sLastCameraUpdated = 0; S32 LLViewerRegion::sNewObjectCreationThrottle = -1; +LLViewerRegion::vocache_entry_map_t LLViewerRegion::sRegionCacheCleanup; typedef std::map<std::string, std::string> CapabilityMap; @@ -290,6 +291,8 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) LL_INFOS("AppInit", "Capabilities") << "Requesting seed from " << url << " region name " << regionp->getName() + << " region id " << regionp->getRegionID() + << " handle " << regionp->getHandle() << " (attempt #" << mSeedCapAttempts + 1 << ")" << LL_ENDL; LL_DEBUGS("AppInit", "Capabilities") << "Capabilities requested: " << capabilityNames << LL_ENDL; @@ -298,6 +301,11 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) ++mSeedCapAttempts; + if (LLApp::isExiting()) + { + return; + } + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); if (!regionp) //region was removed { @@ -344,9 +352,9 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) log_capabilities(mCapabilities); #endif + LL_DEBUGS("AppInit", "Capabilities", "Teleport") << "received caps for handle " << regionHandle + << " region name " << regionp->getName() << LL_ENDL; regionp->setCapabilitiesReceived(true); - LL_DEBUGS("AppInit", "Capabilities") << "received caps for handle " << regionHandle - << " region name " << regionp->getName() << LL_ENDL; if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) { @@ -399,7 +407,7 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle) LLSD capabilityNames = LLSD::emptyArray(); buildCapabilityNames(capabilityNames); - LL_INFOS("AppInit", "Capabilities") << "Requesting second Seed from " << url << LL_ENDL; + LL_INFOS("AppInit", "Capabilities") << "Requesting second Seed from " << url << " for region " << regionp->getRegionID() << LL_ENDL; regionp = NULL; result = httpAdapter->postAndSuspend(httpRequest, url, capabilityNames); @@ -412,6 +420,11 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle) break; // no retry } + if (LLApp::isExiting()) + { + break; + } + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); if (!regionp) //region was removed { @@ -515,6 +528,11 @@ void LLViewerRegionImpl::requestSimulatorFeatureCoro(std::string url, U64 region continue; } + if (LLApp::isExiting()) + { + break; + } + // remove the http_result from the llsd result.erase("http_result"); @@ -609,6 +627,8 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, mImpl->mObjectPartition.push_back(new LLGrassPartition(this)); //PARTITION_GRASS mImpl->mObjectPartition.push_back(new LLVolumePartition(this)); //PARTITION_VOLUME mImpl->mObjectPartition.push_back(new LLBridgePartition(this)); //PARTITION_BRIDGE + mImpl->mObjectPartition.push_back(new LLAvatarPartition(this)); //PARTITION_AVATAR + mImpl->mObjectPartition.push_back(new LLControlAVPartition(this)); //PARTITION_CONTROL_AV mImpl->mObjectPartition.push_back(new LLHUDParticlePartition(this));//PARTITION_HUD_PARTICLE mImpl->mObjectPartition.push_back(new LLVOCachePartition(this)); //PARTITION_VO_CACHE mImpl->mObjectPartition.push_back(NULL); //PARTITION_NONE @@ -633,6 +653,9 @@ void LLViewerRegion::initStats() mAlive = false; // can become false if circuit disconnects } +static LLTrace::BlockTimerStatHandle FTM_CLEANUP_REGION_OBJECTS("Cleanup Region Objects"); +static LLTrace::BlockTimerStatHandle FTM_SAVE_REGION_CACHE("Save Region Cache"); + LLViewerRegion::~LLViewerRegion() { mDead = TRUE; @@ -647,7 +670,10 @@ LLViewerRegion::~LLViewerRegion() disconnectAllNeighbors(); LLViewerPartSim::getInstance()->cleanupRegion(this); - gObjectList.killObjects(this); + { + LL_RECORD_BLOCK_TIME(FTM_CLEANUP_REGION_OBJECTS); + gObjectList.killObjects(this); + } delete mImpl->mCompositionp; delete mParcelOverlay; @@ -658,7 +684,10 @@ LLViewerRegion::~LLViewerRegion() #endif std::for_each(mImpl->mObjectPartition.begin(), mImpl->mObjectPartition.end(), DeletePointer()); - saveObjectCache(); + { + LL_RECORD_BLOCK_TIME(FTM_SAVE_REGION_CACHE); + saveObjectCache(); + } delete mImpl; mImpl = NULL; @@ -727,6 +756,8 @@ void LLViewerRegion::saveObjectCache() mCacheDirty = FALSE; } + // Map of LLVOCacheEntry takes time to release, store map for cleanup on idle + sRegionCacheCleanup.insert(mImpl->mCacheMap.begin(), mImpl->mCacheMap.end()); mImpl->mCacheMap.clear(); } @@ -1488,6 +1519,16 @@ void LLViewerRegion::idleUpdate(F32 max_update_time) return; } +// static +void LLViewerRegion::idleCleanup(F32 max_update_time) +{ + LLTimer update_timer; + while (!sRegionCacheCleanup.empty() && (max_update_time - update_timer.getElapsedTimeF32() > 0)) + { + sRegionCacheCleanup.erase(sRegionCacheCleanup.begin()); + } +} + //update the throttling number for new object creation void LLViewerRegion::calcNewObjectCreationThrottle() { @@ -2182,7 +2223,7 @@ void LLViewerRegion::requestSimulatorFeatures() LLCoros::instance().launch("LLViewerRegionImpl::requestSimulatorFeatureCoro", boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, mImpl, url, getHandle())); - LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << LL_ENDL; + LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << " for region " << getRegionID() << LL_ENDL; } else { @@ -2989,7 +3030,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) if (getCapability("Seed") == url) { setCapabilityDebug("Seed", url); - LL_WARNS("CrossingCaps") << "Received duplicate seed capability, posting to seed " << + LL_WARNS("CrossingCaps") << "Received duplicate seed capability for " << getRegionID() << ", posting to seed " << url << LL_ENDL; //Instead of just returning we build up a second set of seed caps and compare them @@ -3010,7 +3051,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) LLCoros::instance().launch("LLViewerRegionImpl::requestBaseCapabilitiesCoro", boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCoro, mImpl, getHandle())); - LL_INFOS("AppInit", "Capabilities") << "Launching " << coroname << " requesting seed capabilities from " << url << LL_ENDL; + LL_INFOS("AppInit", "Capabilities") << "Launching " << coroname << " requesting seed capabilities from " << url << " for region " << getRegionID() << LL_ENDL; } S32 LLViewerRegion::getNumSeedCapRetries() @@ -3144,7 +3185,7 @@ void LLViewerRegion::setCapabilitiesReceived(bool received) { mCapabilitiesReceivedSignal(getRegionID()); - //LLFloaterPermsDefault::sendInitialPerms(); + LLFloaterPermsDefault::sendInitialPerms(); // This is a single-shot signal. Forget callbacks to save resources. mCapabilitiesReceivedSignal.disconnect_all_slots(); @@ -3320,5 +3361,12 @@ U32 LLViewerRegion::getMaxMaterialsPerTransaction() const return max_entries; } - +std::string LLViewerRegion::getSimHostName() +{ + if (mSimulatorFeaturesReceived) + { + return mSimulatorFeatures.has("HostName") ? mSimulatorFeatures["HostName"].asString() : getHost().getHostName(); + } + return std::string("..."); +} diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 1b226ac2c6..dfd8c64f76 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -86,6 +86,8 @@ public: PARTITION_GRASS, PARTITION_VOLUME, PARTITION_BRIDGE, + PARTITION_AVATAR, + PARTITION_CONTROL_AV, // Animesh PARTITION_HUD_PARTICLE, PARTITION_VO_CACHE, PARTITION_NONE, @@ -230,6 +232,9 @@ public: F32 getWidth() const { return mWidth; } + // regions are expensive to release, this function gradually releases cache from memory + static void idleCleanup(F32 max_update_time); + void idleUpdate(F32 max_update_time); void lightIdleUpdate(); bool addVisibleGroup(LLViewerOctreeGroup* group); @@ -388,6 +393,8 @@ public: BOOL isPaused() const {return mPaused;} S32 getLastUpdate() const {return mLastUpdate;} + std::string getSimHostName(); + static BOOL isNewObjectCreationThrottleDisabled() {return sNewObjectCreationThrottle < 0;} private: @@ -548,6 +555,9 @@ private: LLSD mSimulatorFeatures; + typedef std::map<U32, LLPointer<LLVOCacheEntry> > vocache_entry_map_t; + static vocache_entry_map_t sRegionCacheCleanup; + // the materials capability throttle LLFrameTimer mMaterialsCapThrottleTimer; LLFrameTimer mRenderInfoRequestTimer; diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index f108d96320..be5c22e7c3 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -400,6 +400,15 @@ void LLViewerShaderMgr::setShaders() return; } + if (!gGLManager.mHasShaderObjects + || !gGLManager.mHasVertexShader + || !gGLManager.mHasFragmentShader) + { + // Viewer will show 'hardware requirements' warning later + LL_INFOS("ShaderLoading") << "Shaders not supported" << LL_ENDL; + return; + } + static LLCachedControl<U32> max_texture_index(gSavedSettings, "RenderMaxTextureIndex", 16); LLGLSLShader::sIndexedTextureChannels = llmax(llmin(gGLManager.mNumTextureImageUnits, (S32) max_texture_index), 1); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index f7ded00318..05f88b0a75 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -60,6 +60,7 @@ #include "llviewernetwork.h" #include "llmeshrepository.h" //for LLMeshRepository::sBytesReceived #include "llsdserialize.h" +#include "llsdutil.h" #include "llcorehttputil.h" #include "llvoicevivox.h" @@ -425,7 +426,7 @@ void update_statistics() * If you move stats around here, make the corresponding changes in * those locations, too. */ -void send_stats() +void send_viewer_stats(bool include_preferences) { // IW 9/23/02 I elected not to move this into LLViewerStats // because it depends on too many viewer.cpp globals. @@ -513,6 +514,8 @@ void send_stats() system["gpu_version"] = gGLManager.mDriverVersionVendorString; system["opengl_version"] = gGLManager.mGLVersionString; + gGLManager.asLLSD(system["gl"]); + S32 shader_level = 0; if (LLPipeline::sRenderDeferred) { @@ -572,6 +575,7 @@ void send_stats() fail["failed_resends"] = (S32) gMessageSystem->mFailedResendPackets; fail["off_circuit"] = (S32) gMessageSystem->mOffCircuitPackets; fail["invalid"] = (S32) gMessageSystem->mInvalidOnCircuitPackets; + fail["missing_updater"] = (S32) LLAppViewer::instance()->isUpdaterMissing(); body["stats"]["voice"] = LLVoiceVivoxStats::getInstance()->read(); @@ -617,10 +621,25 @@ void send_stats() body["DisplayNamesEnabled"] = gSavedSettings.getBOOL("UseDisplayNames"); body["DisplayNamesShowUsername"] = gSavedSettings.getBOOL("NameTagShowUsernames"); - + + // Preferences + if (include_preferences) + { + bool diffs_only = true; // only log preferences that differ from default + body["preferences"]["settings"] = gSavedSettings.asLLSD(diffs_only); + body["preferences"]["settings_per_account"] = gSavedPerAccountSettings.asLLSD(diffs_only); + } + body["MinimalSkin"] = false; + LL_INFOS("LogViewerStatsPacket") << "Sending viewer statistics: " << body << LL_ENDL; + if (debugLoggingEnabled("LogViewerStatsPacket")) + { + std::string filename("viewer_stats_packet.xml"); + llofstream of(filename.c_str()); + LLSDSerialize::toPrettyXML(body,of); + } // The session ID token must never appear in logs body["session_id"] = gAgentSessionID; diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index d8d92d61d3..04870e0c26 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -294,7 +294,7 @@ static const F32 SEND_STATS_PERIOD = 300.0f; // The following are from (older?) statistics code found in appviewer. void update_statistics(); -void send_stats(); +void send_viewer_stats(bool include_preferences); extern LLFrameTimer gTextureTimer; extern U32Bytes gTotalTextureData; diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp index 7f7d190b92..c501dd0035 100644 --- a/indra/newview/llviewertexlayer.cpp +++ b/indra/newview/llviewertexlayer.cpp @@ -54,7 +54,7 @@ LLViewerTexLayerSetBuffer::LLViewerTexLayerSetBuffer(LLTexLayerSet* const owner, S32 width, S32 height) : // ORDER_LAST => must render these after the hints are created. LLTexLayerSetBuffer(owner), - LLViewerDynamicTexture( width, height, 4, LLViewerDynamicTexture::ORDER_LAST, TRUE ), + LLViewerDynamicTexture(width, height, 4, LLViewerDynamicTexture::ORDER_LAST, FALSE), mNeedsUpdate(TRUE), mNumLowresUpdates(0) { diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index a2cec9a613..20a22ba45e 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1238,6 +1238,8 @@ void LLViewerFetchedTexture::loadFromFastCache() { if (mBoostLevel == LLGLTexture::BOOST_ICON) { + // Shouldn't do anything usefull since texures in fast cache are 16x16, + // it is here in case fast cache changes. S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS; S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS; if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) @@ -1485,7 +1487,8 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/) mOrigWidth = mRawImage->getWidth(); mOrigHeight = mRawImage->getHeight(); - + // This is only safe because it's a local image and fetcher doesn't use raw data + // from local images, but this might become unsafe in case of changes to fetcher if (mBoostLevel == BOOST_PREVIEW) { mRawImage->biasedScaleToPowerOfTwo(1024); @@ -1541,6 +1544,26 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/) return FALSE; } + if (mGLTexturep->getHasExplicitFormat()) + { + LLGLenum format = mGLTexturep->getPrimaryFormat(); + S8 components = mRawImage->getComponents(); + if ((format == GL_RGBA && components < 4) + || (format == GL_RGB && components < 3)) + { + LL_WARNS() << "Can't create a texture " << mID << ": invalid image format " << std::hex << format << " vs components " << (U32)components << LL_ENDL; + // Was expecting specific format but raw texture has insufficient components for + // such format, using such texture will result in crash or will display wrongly + // if we change format. Texture might be corrupted server side, so just set as + // missing and clear cashed texture (do not cause reload loop, will retry&recover + // during new session) + setIsMissingAsset(); + destroyRawImage(); + LLAppViewer::getTextureCache()->removeFromCache(mID); + return FALSE; + } + } + res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel); notifyAboutCreatingTexture(); @@ -1989,6 +2012,7 @@ bool LLViewerFetchedTexture::updateFetch() if (mRawImage.notNull()) sRawCount--; if (mAuxRawImage.notNull()) sAuxCount--; + // keep in mind that fetcher still might need raw image, don't modify original bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage, mLastHttpGetStatus); if (mRawImage.notNull()) sRawCount++; @@ -2048,7 +2072,8 @@ bool LLViewerFetchedTexture::updateFetch() if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) { // scale oversized icon, no need to give more work to gl - mRawImage->scale(expected_width, expected_height); + // since we got mRawImage from thread worker and image may be in use (ex: writing cache), make a copy + mRawImage = mRawImage->scaled(expected_width, expected_height); } } diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 2d7a0f920f..9c4dfd1ca2 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -178,7 +178,7 @@ BOOL LLViewerWearable::isOldVersion() const S32 te_count = 0; for( S32 te = 0; te < TEX_NUM_INDICES; te++ ) { - if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex) te) == mType) + if (LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex) te) == mType) { te_count++; if( !is_in_map(mTEMap, te ) ) @@ -230,7 +230,7 @@ BOOL LLViewerWearable::isDirty() const for( S32 te = 0; te < TEX_NUM_INDICES; te++ ) { - if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex) te) == mType) + if (LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex) te) == mType) { te_map_t::const_iterator current_iter = mTEMap.find(te); if(current_iter != mTEMap.end()) @@ -276,7 +276,7 @@ void LLViewerWearable::setTexturesToDefaults() { for( S32 te = 0; te < TEX_NUM_INDICES; te++ ) { - if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex) te) == mType) + if (LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex) te) == mType) { LLUUID id = getDefaultTextureImageID((ETextureIndex) te); LLViewerFetchedTexture * image = LLViewerTextureManager::getFetchedTexture( id ); @@ -300,7 +300,7 @@ void LLViewerWearable::setTexturesToDefaults() // virtual LLUUID LLViewerWearable::getDefaultTextureImageID(ETextureIndex index) const { - const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearanceDictionary::getInstance()->getTexture(index); + const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearance::getDictionary()->getTexture(index); const std::string &default_image_name = texture_dict ? texture_dict->mDefaultImageName : ""; if (default_image_name == "") { @@ -331,7 +331,7 @@ void LLViewerWearable::writeToAvatar(LLAvatarAppearance *avatarp) // Pull texture entries for( S32 te = 0; te < TEX_NUM_INDICES; te++ ) { - if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex) te) == mType) + if (LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex) te) == mType) { te_map_t::const_iterator iter = mTEMap.find(te); LLUUID image_id; @@ -424,7 +424,7 @@ void LLViewerWearable::copyDataFrom(const LLViewerWearable* src) // Deep copy of mTEMap (copies only those tes that are current, filling in defaults where needed) for (S32 te = 0; te < TEX_NUM_INDICES; te++) { - if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex) te) == mType) + if (LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex) te) == mType) { te_map_t::const_iterator iter = src->mTEMap.find(te); LLUUID image_id; @@ -463,8 +463,7 @@ void LLViewerWearable::revertValues() { LLWearable::revertValues(); - - LLSidepanelAppearance *panel = dynamic_cast<LLSidepanelAppearance*>(LLFloaterSidePanelContainer::getPanel("appearance")); + LLSidepanelAppearance *panel = dynamic_cast<LLSidepanelAppearance*>(LLFloaterSidePanelContainer::findPanel("appearance")); if( panel ) { panel->updateScrollingPanelList(); @@ -480,7 +479,7 @@ void LLViewerWearable::saveValues() { LLWearable::saveValues(); - LLSidepanelAppearance *panel = dynamic_cast<LLSidepanelAppearance*>(LLFloaterSidePanelContainer::getPanel("appearance")); + LLSidepanelAppearance *panel = dynamic_cast<LLSidepanelAppearance*>(LLFloaterSidePanelContainer::findPanel("appearance")); if( panel ) { panel->updateScrollingPanelList(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 556f16e0b4..2ecbe0e68b 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -45,7 +45,8 @@ #include "llmeshrepository.h" #include "llnotificationhandler.h" #include "llpanellogin.h" -#include "llviewerkeyboard.h" +#include "llsetkeybinddialog.h" +#include "llviewerinput.h" #include "llviewermenu.h" #include "llviewquery.h" @@ -173,7 +174,7 @@ #include "llviewergesture.h" #include "llviewertexturelist.h" #include "llviewerinventory.h" -#include "llviewerkeyboard.h" +#include "llviewerinput.h" #include "llviewermedia.h" #include "llviewermediafocus.h" #include "llviewermenu.h" @@ -346,6 +347,12 @@ public: void update() { + if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + clearText(); + return; + } + static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic", false) ; std::string wind_vel_text; @@ -914,7 +921,18 @@ LLViewerWindow::Params::Params() {} -BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +void LLViewerWindow::handlePieMenu(S32 x, S32 y, MASK mask) +{ + if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance() && gAgent.isInitialized()) + { + // If the current tool didn't process the click, we should show + // the pie menu. This can be done by passing the event to the pie + // menu tool. + LLToolPie::getInstance()->handleRightMouseDown(x, y, mask); + } +} + +BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) { const char* buttonname = ""; const char* buttonstatestr = ""; @@ -938,28 +956,30 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK switch (clicktype) { - case LLMouseHandler::CLICK_LEFT: + case CLICK_LEFT: mLeftMouseDown = down; buttonname = "Left"; break; - case LLMouseHandler::CLICK_RIGHT: + case CLICK_RIGHT: mRightMouseDown = down; buttonname = "Right"; break; - case LLMouseHandler::CLICK_MIDDLE: + case CLICK_MIDDLE: mMiddleMouseDown = down; buttonname = "Middle"; break; - case LLMouseHandler::CLICK_DOUBLELEFT: + case CLICK_DOUBLELEFT: mLeftMouseDown = down; buttonname = "Left Double Click"; break; - case LLMouseHandler::CLICK_BUTTON4: + case CLICK_BUTTON4: buttonname = "Button 4"; break; - case LLMouseHandler::CLICK_BUTTON5: + case CLICK_BUTTON5: buttonname = "Button 5"; break; + default: + break; // COUNT and NONE } LLView::sMouseHandlerMessage.clear(); @@ -1010,6 +1030,11 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK LLViewerEventRecorder::instance().logMouseEvent(std::string(buttonstatestr),std::string(buttonname)); } + else if (down && clicktype == CLICK_RIGHT) + { + handlePieMenu(x, y, mask); + r = TRUE; + } return r; } @@ -1056,7 +1081,12 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK return TRUE; } - + if (down && clicktype == CLICK_RIGHT) + { + handlePieMenu(x, y, mask); + return TRUE; + } + // If we got this far on a down-click, it wasn't handled. // Up-clicks, though, are always handled as far as the OS is concerned. BOOL default_rtn = !down; @@ -1075,7 +1105,8 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask mMouseDownTimer.reset(); } BOOL down = TRUE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); + //handleMouse() loops back to LLViewerWindow::handleAnyMouseClick + return gViewerInput.handleMouse(window, pos, mask, CLICK_LEFT, down); } BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask) @@ -1083,8 +1114,7 @@ BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK ma // try handling as a double-click first, then a single-click if that // wasn't handled. BOOL down = TRUE; - if (handleAnyMouseClick(window, pos, mask, - LLMouseHandler::CLICK_DOUBLELEFT, down)) + if (gViewerInput.handleMouse(window, pos, mask, CLICK_DOUBLELEFT, down)) { return TRUE; } @@ -1098,47 +1128,24 @@ BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) mMouseDownTimer.stop(); } BOOL down = FALSE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); + return gViewerInput.handleMouse(window, pos, mask, CLICK_LEFT, down); } - - BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { - S32 x = pos.mX; - S32 y = pos.mY; - x = ll_round((F32)x / mDisplayScale.mV[VX]); - y = ll_round((F32)y / mDisplayScale.mV[VY]); - BOOL down = TRUE; - BOOL handle = handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); - if (handle) - return handle; - - // *HACK: this should be rolled into the composite tool logic, not - // hardcoded at the top level. - if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance() && gAgent.isInitialized()) - { - // If the current tool didn't process the click, we should show - // the pie menu. This can be done by passing the event to the pie - // menu tool. - LLToolPie::getInstance()->handleRightMouseDown(x, y, mask); - // show_context_menu( x, y, mask ); - } - - return TRUE; + return gViewerInput.handleMouse(window, pos, mask, CLICK_RIGHT, down); } BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); + return gViewerInput.handleMouse(window, pos, mask, CLICK_RIGHT, down); } BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = TRUE; - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_MIDDLE, true); - handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); + gViewerInput.handleMouse(window, pos, mask, CLICK_MIDDLE, down); // Always handled as far as the OS is concerned. return TRUE; @@ -1293,8 +1300,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_MIDDLE, false); - handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); + gViewerInput.handleMouse(window, pos, mask, CLICK_MIDDLE, down); // Always handled as far as the OS is concerned. return TRUE; @@ -1305,12 +1311,10 @@ BOOL LLViewerWindow::handleOtherMouse(LLWindow *window, LLCoordGL pos, MASK mask switch (button) { case 4: - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_BUTTON4, down); - handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_BUTTON4, down); + gViewerInput.handleMouse(window, pos, mask, CLICK_BUTTON4, down); break; case 5: - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_BUTTON5, down); - handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_BUTTON5, down); + gViewerInput.handleMouse(window, pos, mask, CLICK_BUTTON5, down); break; default: break; @@ -1396,6 +1400,7 @@ BOOL LLViewerWindow::handleCloseRequest(LLWindow *window) void LLViewerWindow::handleQuit(LLWindow *window) { + LL_INFOS() << "Window forced quit" << LL_ENDL; LLAppViewer::instance()->forceQuit(); } @@ -1456,9 +1461,6 @@ void LLViewerWindow::handleFocusLost(LLWindow *window) BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) { - // Let the voice chat code check for its PTT key. Note that this never affects event processing. - LLVoiceClient::getInstance()->keyDown(key, mask); - if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) { gAgent.clearAFK(); @@ -1477,14 +1479,12 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) return FALSE; } - return gViewerKeyboard.handleKey(key, mask, repeated); + // remaps, handles ignored cases and returns back to viewer window. + return gViewerInput.handleKey(key, mask, repeated); } BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) { - // Let the voice chat code check for its PTT key. Note that this never affects event processing. - LLVoiceClient::getInstance()->keyUp(key, mask); - // Let the inspect tool code check for ALT key to set LLToolSelectRect active instead LLToolCamera LLToolCompInspect * tool_inspectp = LLToolCompInspect::getInstance(); if (LLToolMgr::getInstance()->getCurrentTool() == tool_inspectp) @@ -1492,13 +1492,13 @@ BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) tool_inspectp->keyUp(key, mask); } - return gViewerKeyboard.handleKeyUp(key, mask); + return gViewerInput.handleKeyUp(key, mask); } void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) { LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true); - gViewerKeyboard.scanKey(key, key_down, key_up, key_level); + gViewerInput.scanKey(key, key_down, key_up, key_level); return; // Be clear this function returns nothing } @@ -2317,7 +2317,6 @@ void LLViewerWindow::shutdownGL() LLViewerWindow::~LLViewerWindow() { LL_INFOS() << "Destroying Window" << LL_ENDL; - gDebugWindowProc = TRUE; // event catching, at this point it shouldn't output at all destroyWindow(); delete mDebugText; @@ -2408,6 +2407,11 @@ void LLViewerWindow::reshape(S32 width, S32 height) // round up when converting coordinates to make sure there are no gaps at edge of window LLView::sForceReshape = display_scale_changed; mRootView->reshape(llceil((F32)width / mDisplayScale.mV[VX]), llceil((F32)height / mDisplayScale.mV[VY])); + if (display_scale_changed) + { + // Needs only a 'scale change' update, everything else gets handled by LLLayoutStack::updateClass() + LLPanelLogin::reshapePanel(); + } LLView::sForceReshape = FALSE; // clear font width caches @@ -2696,6 +2700,13 @@ void LLViewerWindow::draw() // Takes a single keyup event, usually when UI is visible BOOL LLViewerWindow::handleKeyUp(KEY key, MASK mask) { + if (LLSetKeyBindDialog::recordKey(key, mask, FALSE)) + { + LL_DEBUGS() << "KeyUp handled by LLSetKeyBindDialog" << LL_ENDL; + LLViewerEventRecorder::instance().logKeyEvent(key, mask); + return TRUE; + } + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); if (keyboard_focus @@ -2739,23 +2750,74 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // hide tooltips on keypress LLToolTipMgr::instance().blockToolTips(); - LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); + // Menus get handled on key down instead of key up + // so keybindings have to be recorded before that + if (LLSetKeyBindDialog::recordKey(key, mask, TRUE)) + { + LL_DEBUGS() << "Key handled by LLSetKeyBindDialog" << LL_ENDL; + LLViewerEventRecorder::instance().logKeyEvent(key,mask); + return TRUE; + } + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); + if (keyboard_focus - && !(mask & (MASK_CONTROL | MASK_ALT)) - && !gFocusMgr.getKeystrokesOnly()) - { - // We have keyboard focus, and it's not an accelerator - if (keyboard_focus && keyboard_focus->wantsKeyUpKeyDown()) + && !gFocusMgr.getKeystrokesOnly()) + { +#ifdef LL_WINDOWS + // On windows Alt Gr key generates additional Ctrl event, as result handling situations + // like 'AltGr + D' will result in 'Alt+Ctrl+D'. If it results in WM_CHAR, don't let it + // pass into menu or it will trigger 'develop' menu assigned to this combination on top + // of character handling. + // Alt Gr can be additionally modified by Shift + const MASK alt_gr = MASK_CONTROL | MASK_ALT; + if ((mask & alt_gr) != 0 + && key >= 0x30 + && key <= 0x5A + && (GetKeyState(VK_RMENU) & 0x8000) != 0 + && (GetKeyState(VK_RCONTROL) & 0x8000) == 0) // ensure right control is not pressed, only left one { - return keyboard_focus->handleKey(key, mask, FALSE ); + // Alt Gr key is represented as right alt and left control. + // Any alt+ctrl combination is treated as Alt Gr by TranslateMessage() and + // will generate a WM_CHAR message, but here we only treat virtual Alt Graph + // key by checking if this specific combination has unicode char. + // + // I decided to handle only virtual RAlt+LCtrl==AltGr combination to minimize + // impact on menu, but the right way might be to handle all Alt+Ctrl calls. + + BYTE keyboard_state[256]; + if (GetKeyboardState(keyboard_state)) + { + const int char_count = 6; + wchar_t chars[char_count]; + HKL layout = GetKeyboardLayout(0); + // ToUnicodeEx changes buffer state on OS below Win10, which is undesirable, + // but since we already did a TranslateMessage() in gatherInput(), this + // should have no negative effect + int res = ToUnicodeEx(key, 0, keyboard_state, chars, char_count, 1 << 2 /*do not modify buffer flag*/, layout); + if (res == 1 && chars[0] >= 0x20) + { + // Let it fall through to character handler and get a WM_CHAR. + return TRUE; + } + } } - else if (key < 0x80) - { - // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. - return (keyboard_focus != NULL); - } - } +#endif + + if (!(mask & (MASK_CONTROL | MASK_ALT))) + { + // We have keyboard focus, and it's not an accelerator + if (keyboard_focus && keyboard_focus->wantsKeyUpKeyDown()) + { + return keyboard_focus->handleKey(key, mask, FALSE); + } + else if (key < 0x80) + { + // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. + return TRUE; + } + } + } // let menus handle navigation keys for navigation if ((gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE)) @@ -2954,7 +3016,8 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) { if (mask != MASK_ALT) { - return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); + // remaps, handles ignored cases and returns back to viewer window. + return gViewerInput.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); } } @@ -3925,12 +3988,12 @@ void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL draw_handles = TRUE; - if (tool == LLToolCompTranslate::getInstance() && !all_selected_objects_move && !LLSelectMgr::getInstance()->isSelfAvatarSelected()) + if (tool == LLToolCompTranslate::getInstance() && !all_selected_objects_move && !LLSelectMgr::getInstance()->isMovableAvatarSelected()) { draw_handles = FALSE; } - if (tool == LLToolCompRotate::getInstance() && !all_selected_objects_move) + if (tool == LLToolCompRotate::getInstance() && !all_selected_objects_move && !LLSelectMgr::getInstance()->isMovableAvatarSelected()) { draw_handles = FALSE; } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index e901245f92..8a6df613dc 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -137,7 +137,7 @@ private: }; -static const U32 MAX_SNAPSHOT_IMAGE_SIZE = 6 * 1024; // max snapshot image size 6144 * 6144 +static const U32 MAX_SNAPSHOT_IMAGE_SIZE = 7680; // max snapshot image size 7680 * 7680 UHDTV2 class LLViewerWindow : public LLWindowCallbacks { @@ -175,8 +175,9 @@ public: void initWorldUI(); void setUIVisibility(bool); bool getUIVisibility(); + void handlePieMenu(S32 x, S32 y, MASK mask); - BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); + BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); // // LLWindowCallback interface implementation diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index d567623ac0..f69b9b3861 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -207,6 +207,8 @@ enum ERenderName RENDER_NAME_FADE }; +#define JELLYDOLLS_SHOULD_IMPOSTOR + //----------------------------------------------------------------------------- // Callback data //----------------------------------------------------------------------------- @@ -573,9 +575,9 @@ private: //----------------------------------------------------------------------------- // Static Data //----------------------------------------------------------------------------- -LLAvatarAppearanceDictionary *LLVOAvatar::sAvatarDictionary = NULL; S32 LLVOAvatar::sFreezeCounter = 0; -U32 LLVOAvatar::sMaxNonImpostors = 12; // overridden based on graphics setting +U32 LLVOAvatar::sMaxNonImpostors = 12; // Set from RenderAvatarMaxNonImpostors +bool LLVOAvatar::sLimitNonImpostors = false; // True unless RenderAvatarMaxNonImpostors is 0 (unlimited) F32 LLVOAvatar::sRenderDistance = 256.f; S32 LLVOAvatar::sNumVisibleAvatars = 0; S32 LLVOAvatar::sNumLODChangesThisFrame = 0; @@ -602,7 +604,6 @@ BOOL LLVOAvatar::sShowFootPlane = FALSE; BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE; F32 LLVOAvatar::sLODFactor = 1.f; F32 LLVOAvatar::sPhysicsLODFactor = 1.f; -bool LLVOAvatar::sUseImpostors = false; // overwridden by RenderAvatarMaxNonImpostors BOOL LLVOAvatar::sJointDebug = FALSE; F32 LLVOAvatar::sUnbakedTime = 0.f; F32 LLVOAvatar::sUnbakedUpdateTime = 0.f; @@ -635,6 +636,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mMeshValid(FALSE), mVisible(FALSE), mLastImpostorUpdateFrameTime(0.f), + mLastImpostorUpdateReason(0), mWindFreq(0.f), mRipplePhase( 0.f ), mBelowWater(FALSE), @@ -657,6 +659,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mNeedsSkin(FALSE), mLastSkinTime(0.f), mUpdatePeriod(1), + mOverallAppearance(AOA_INVISIBLE), mVisualComplexityStale(true), mVisuallyMuteSetting(AV_RENDER_NORMALLY), mMutedAVColor(LLColor4::white /* used for "uninitialize" */), @@ -701,6 +704,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, setAnimationData("Speed", &mSpeed); mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 0; mNeedsAnimUpdate = TRUE; mNeedsExtentUpdate = true; @@ -756,8 +760,8 @@ std::string LLVOAvatar::avString() const } else { - std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus()); - return " Avatar '" + getFullname() + "' " + viz_string + " "; + std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus()); + return " Avatar '" + getFullname() + "' " + viz_string + " "; } } @@ -1025,15 +1029,15 @@ void LLVOAvatar::dumpBakedStatus() { LL_CONT << " Unbaked ("; - for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator iter = LLAvatarAppearance::getDictionary()->getBakedTextures().begin(); + iter != LLAvatarAppearance::getDictionary()->getBakedTextures().end(); ++iter) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = iter->second; const ETextureIndex index = baked_dict->mTextureIndex; if (!inst->isTextureDefined(index)) { - LL_CONT << " " << (LLAvatarAppearanceDictionary::getInstance()->getTexture(index) ? LLAvatarAppearanceDictionary::getInstance()->getTexture(index)->mName : ""); + LL_CONT << " " << (LLAvatarAppearance::getDictionary()->getTexture(index) ? LLAvatarAppearance::getDictionary()->getTexture(index)->mName : ""); } } LL_CONT << " ) " << inst->getUnbakedPixelAreaRank(); @@ -1076,6 +1080,7 @@ void LLVOAvatar::resetImpostors() LLVOAvatar* avatar = (LLVOAvatar*) *iter; avatar->mImpostor.release(); avatar->mNeedsImpostorUpdate = TRUE; + avatar->mLastImpostorUpdateReason = 1; } } @@ -1119,6 +1124,7 @@ void LLVOAvatar::initClass() LLJoint::setDebugJointNames(gSavedSettings.getString("DebugAvatarJoints")); LLControlAvatar::sRegionChangedSlot = gAgent.addRegionChangedCallback(&LLControlAvatar::onRegionChanged); + } @@ -1319,72 +1325,84 @@ void LLVOAvatar::calculateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) LL_RECORD_BLOCK_TIME(FTM_AVATAR_EXTENT_UPDATE); S32 box_detail = gSavedSettings.getS32("AvatarBoundingBoxComplexity"); + if (getOverallAppearance() != AOA_NORMAL) + { + if (isControlAvatar()) + { + // Animated objects don't show system avatar but do need to include rigged meshes in their bounding box. + box_detail = 3; + } + else + { + // Jellydolled avatars ignore attachments, etc, use only system avatar. + box_detail = 1; + } + } // FIXME the update_min_max function used below assumes there is a // known starting point, but in general there isn't. Ideally the // box update logic should be modified to handle the no-point-yet // case. For most models, starting with the pelvis is safe though. LLVector3 zero_pos; - LLVector4a pos; + LLVector4a pos; if (dist_vec(zero_pos, mPelvisp->getWorldPosition())<0.001) { // Don't use pelvis until av initialized - pos.load3(getRenderPosition().mV); + pos.load3(getRenderPosition().mV); } else { pos.load3(mPelvisp->getWorldPosition().mV); } - newMin = pos; - newMax = pos; + newMin = pos; + newMax = pos; - //stretch bounding box by joint positions. Doing this for - //control avs, where the polymeshes aren't maintained or - //displayed, can give inaccurate boxes due to joints stuck at (0,0,0). - if ((box_detail>=1) && !isControlAvatar()) + if (box_detail>=1 && !isControlAvatar()) { - for (polymesh_map_t::iterator i = mPolyMeshes.begin(); i != mPolyMeshes.end(); ++i) - { - LLPolyMesh* mesh = i->second; - for (S32 joint_num = 0; joint_num < mesh->mJointRenderData.size(); joint_num++) - { - LLVector4a trans; - trans.load3( mesh->mJointRenderData[joint_num]->mWorldMatrix->getTranslation().mV); - update_min_max(newMin, newMax, trans); - } - } - + //stretch bounding box by joint positions. Doing this for + //control avs, where the polymeshes aren't maintained or + //displayed, can give inaccurate boxes due to joints stuck at (0,0,0). + for (polymesh_map_t::iterator i = mPolyMeshes.begin(); i != mPolyMeshes.end(); ++i) + { + LLPolyMesh* mesh = i->second; + for (S32 joint_num = 0; joint_num < mesh->mJointRenderData.size(); joint_num++) + { + LLVector4a trans; + trans.load3( mesh->mJointRenderData[joint_num]->mWorldMatrix->getTranslation().mV); + update_min_max(newMin, newMax, trans); + } + } } - // Pad bounding box for starting joint, plus polymesh if - // applicable. Subsequent calcs should be accurate enough to not - // need padding. - LLVector4a padding(0.25); - newMin.sub(padding); - newMax.add(padding); + // Pad bounding box for starting joint, plus polymesh if + // applicable. Subsequent calcs should be accurate enough to not + // need padding. + LLVector4a padding(0.25); + newMin.sub(padding); + newMax.add(padding); - //stretch bounding box by static attachments + //stretch bounding box by static attachments if (box_detail >= 2) { float max_attachment_span = get_default_max_prim_scale() * 5.0f; - - for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); - ++iter) - { - LLViewerJointAttachment* attachment = iter->second; + + for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); + ++iter) + { + LLViewerJointAttachment* attachment = iter->second; - if (attachment->getValid()) - { - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { + if (attachment->getValid()) + { + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { // Don't we need to look at children of attached_object as well? - const LLViewerObject* attached_object = attachment_iter->get(); - if (attached_object && !attached_object->isHUDAttachment()) - { + const LLViewerObject* attached_object = attachment_iter->get(); + if (attached_object && !attached_object->isHUDAttachment()) + { const LLVOVolume *vol = dynamic_cast<const LLVOVolume*>(attached_object); if (vol && vol->isAnimatedObject()) { @@ -1406,39 +1424,39 @@ void LLVOAvatar::calculateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) { continue; } - LLDrawable* drawable = attached_object->mDrawable; - if (drawable && !drawable->isState(LLDrawable::RIGGED)) - { - LLSpatialBridge* bridge = drawable->getSpatialBridge(); - if (bridge) - { - const LLVector4a* ext = bridge->getSpatialExtents(); - LLVector4a distance; - distance.setSub(ext[1], ext[0]); - LLVector4a max_span(max_attachment_span); - - S32 lt = distance.lessThan(max_span).getGatheredBits() & 0x7; - - // Only add the prim to spatial extents calculations if it isn't a megaprim. - // max_attachment_span calculated at the start of the function - // (currently 5 times our max prim size) - if (lt == 0x7) - { - update_min_max(newMin,newMax,ext[0]); - update_min_max(newMin,newMax,ext[1]); - } - } - } - } - } - } - } + LLDrawable* drawable = attached_object->mDrawable; + if (drawable && !drawable->isState(LLDrawable::RIGGED)) + { + LLSpatialBridge* bridge = drawable->getSpatialBridge(); + if (bridge) + { + const LLVector4a* ext = bridge->getSpatialExtents(); + LLVector4a distance; + distance.setSub(ext[1], ext[0]); + LLVector4a max_span(max_attachment_span); + + S32 lt = distance.lessThan(max_span).getGatheredBits() & 0x7; + + // Only add the prim to spatial extents calculations if it isn't a megaprim. + // max_attachment_span calculated at the start of the function + // (currently 5 times our max prim size) + if (lt == 0x7) + { + update_min_max(newMin,newMax,ext[0]); + update_min_max(newMin,newMax,ext[1]); + } + } + } + } + } + } + } } // Stretch bounding box by rigged mesh joint boxes if (box_detail>=3) { - updateRiggingInfo(); + updateRiggingInfo(); for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++) { LLJoint *joint = getJoint(joint_num); @@ -1578,13 +1596,16 @@ void LLVOAvatar::renderCollisionVolumes() } } -void LLVOAvatar::renderBones() +void LLVOAvatar::renderBones(const std::string &selected_joint) { LLGLEnable blend(GL_BLEND); avatar_joint_list_t::iterator iter = mSkeleton.begin(); - avatar_joint_list_t::iterator end = mSkeleton.end(); + avatar_joint_list_t::iterator end = mSkeleton.end(); + // For selected joints + static LLVector3 SELECTED_COLOR_OCCLUDED(1.0f, 1.0f, 0.0f); + static LLVector3 SELECTED_COLOR_VISIBLE(0.5f, 0.5f, 0.5f); // For bones with position overrides defined static LLVector3 OVERRIDE_COLOR_OCCLUDED(1.0f, 0.0f, 0.0f); static LLVector3 OVERRIDE_COLOR_VISIBLE(0.5f, 0.5f, 0.5f); @@ -1611,7 +1632,18 @@ void LLVOAvatar::renderBones() LLVector3 pos; LLUUID mesh_id; - if (jointp->hasAttachmentPosOverride(pos,mesh_id)) + F32 sphere_scale = SPHERE_SCALEF; + + // We are in render, so it is preferable to implement selection + // in a different way, but since this is for debug/preview, this + // is low priority + if (jointp->getName() == selected_joint) + { + sphere_scale *= 16; + occ_color = SELECTED_COLOR_OCCLUDED; + visible_color = SELECTED_COLOR_VISIBLE; + } + else if (jointp->hasAttachmentPosOverride(pos,mesh_id)) { occ_color = OVERRIDE_COLOR_OCCLUDED; visible_color = OVERRIDE_COLOR_VISIBLE; @@ -1632,7 +1664,6 @@ void LLVOAvatar::renderBones() LLVector3 begin_pos(0,0,0); LLVector3 end_pos(jointp->getEnd()); - F32 sphere_scale = SPHERE_SCALEF; gGL.pushMatrix(); gGL.multMatrix( &jointp->getXform()->getWorldMatrix().mMatrix[0][0] ); @@ -2011,6 +2042,38 @@ void LLVOAvatar::resetVisualParams() } } +void LLVOAvatar::applyDefaultParams() +{ + // These are params from avs with newly created copies of shape, + // skin, hair, eyes, plus gender set as noted. Run arche_tool.py + // to get params from some other xml appearance dump. + std::map<S32, U8> male_params = { + {1,33}, {2,61}, {4,85}, {5,23}, {6,58}, {7,127}, {8,63}, {10,85}, {11,63}, {12,42}, {13,0}, {14,85}, {15,63}, {16,36}, {17,85}, {18,95}, {19,153}, {20,63}, {21,34}, {22,0}, {23,63}, {24,109}, {25,88}, {27,132}, {31,63}, {33,136}, {34,81}, {35,85}, {36,103}, {37,136}, {38,127}, {80,255}, {93,203}, {98,0}, {99,0}, {105,127}, {108,0}, {110,0}, {111,127}, {112,0}, {113,0}, {114,127}, {115,0}, {116,0}, {117,0}, {119,127}, {130,114}, {131,127}, {132,99}, {133,63}, {134,127}, {135,140}, {136,127}, {137,127}, {140,0}, {141,0}, {142,0}, {143,191}, {150,0}, {155,104}, {157,0}, {162,0}, {163,0}, {165,0}, {166,0}, {167,0}, {168,0}, {169,0}, {177,0}, {181,145}, {182,216}, {183,133}, {184,0}, {185,127}, {192,0}, {193,127}, {196,170}, {198,0}, {503,0}, {505,127}, {506,127}, {507,109}, {508,85}, {513,127}, {514,127}, {515,63}, {517,85}, {518,42}, {603,100}, {604,216}, {605,214}, {606,204}, {607,204}, {608,204}, {609,51}, {616,25}, {617,89}, {619,76}, {624,204}, {625,0}, {629,127}, {637,0}, {638,0}, {646,144}, {647,85}, {649,127}, {650,132}, {652,127}, {653,85}, {654,0}, {656,127}, {659,127}, {662,127}, {663,127}, {664,127}, {665,127}, {674,59}, {675,127}, {676,85}, {678,127}, {682,127}, {683,106}, {684,47}, {685,79}, {690,127}, {692,127}, {693,204}, {700,63}, {701,0}, {702,0}, {703,0}, {704,0}, {705,127}, {706,127}, {707,0}, {708,0}, {709,0}, {710,0}, {711,127}, {712,0}, {713,159}, {714,0}, {715,0}, {750,178}, {752,127}, {753,36}, {754,85}, {755,131}, {756,127}, {757,127}, {758,127}, {759,153}, {760,95}, {762,0}, {763,140}, {764,74}, {765,27}, {769,127}, {773,127}, {775,0}, {779,214}, {780,204}, {781,198}, {785,0}, {789,0}, {795,63}, {796,30}, {799,127}, {800,226}, {801,255}, {802,198}, {803,255}, {804,255}, {805,255}, {806,255}, {807,255}, {808,255}, {812,255}, {813,255}, {814,255}, {815,204}, {816,0}, {817,255}, {818,255}, {819,255}, {820,255}, {821,255}, {822,255}, {823,255}, {824,255}, {825,255}, {826,255}, {827,255}, {828,0}, {829,255}, {830,255}, {834,255}, {835,255}, {836,255}, {840,0}, {841,127}, {842,127}, {844,255}, {848,25}, {858,100}, {859,255}, {860,255}, {861,255}, {862,255}, {863,84}, {868,0}, {869,0}, {877,0}, {879,51}, {880,132}, {921,255}, {922,255}, {923,255}, {10000,0}, {10001,0}, {10002,25}, {10003,0}, {10004,25}, {10005,23}, {10006,51}, {10007,0}, {10008,25}, {10009,23}, {10010,51}, {10011,0}, {10012,0}, {10013,25}, {10014,0}, {10015,25}, {10016,23}, {10017,51}, {10018,0}, {10019,0}, {10020,25}, {10021,0}, {10022,25}, {10023,23}, {10024,51}, {10025,0}, {10026,25}, {10027,23}, {10028,51}, {10029,0}, {10030,25}, {10031,23}, {10032,51}, {11000,1}, {11001,127} + }; + std::map<S32, U8> female_params = { + {1,33}, {2,61}, {4,85}, {5,23}, {6,58}, {7,127}, {8,63}, {10,85}, {11,63}, {12,42}, {13,0}, {14,85}, {15,63}, {16,36}, {17,85}, {18,95}, {19,153}, {20,63}, {21,34}, {22,0}, {23,63}, {24,109}, {25,88}, {27,132}, {31,63}, {33,136}, {34,81}, {35,85}, {36,103}, {37,136}, {38,127}, {80,0}, {93,203}, {98,0}, {99,0}, {105,127}, {108,0}, {110,0}, {111,127}, {112,0}, {113,0}, {114,127}, {115,0}, {116,0}, {117,0}, {119,127}, {130,114}, {131,127}, {132,99}, {133,63}, {134,127}, {135,140}, {136,127}, {137,127}, {140,0}, {141,0}, {142,0}, {143,191}, {150,0}, {155,104}, {157,0}, {162,0}, {163,0}, {165,0}, {166,0}, {167,0}, {168,0}, {169,0}, {177,0}, {181,145}, {182,216}, {183,133}, {184,0}, {185,127}, {192,0}, {193,127}, {196,170}, {198,0}, {503,0}, {505,127}, {506,127}, {507,109}, {508,85}, {513,127}, {514,127}, {515,63}, {517,85}, {518,42}, {603,100}, {604,216}, {605,214}, {606,204}, {607,204}, {608,204}, {609,51}, {616,25}, {617,89}, {619,76}, {624,204}, {625,0}, {629,127}, {637,0}, {638,0}, {646,144}, {647,85}, {649,127}, {650,132}, {652,127}, {653,85}, {654,0}, {656,127}, {659,127}, {662,127}, {663,127}, {664,127}, {665,127}, {674,59}, {675,127}, {676,85}, {678,127}, {682,127}, {683,106}, {684,47}, {685,79}, {690,127}, {692,127}, {693,204}, {700,63}, {701,0}, {702,0}, {703,0}, {704,0}, {705,127}, {706,127}, {707,0}, {708,0}, {709,0}, {710,0}, {711,127}, {712,0}, {713,159}, {714,0}, {715,0}, {750,178}, {752,127}, {753,36}, {754,85}, {755,131}, {756,127}, {757,127}, {758,127}, {759,153}, {760,95}, {762,0}, {763,140}, {764,74}, {765,27}, {769,127}, {773,127}, {775,0}, {779,214}, {780,204}, {781,198}, {785,0}, {789,0}, {795,63}, {796,30}, {799,127}, {800,226}, {801,255}, {802,198}, {803,255}, {804,255}, {805,255}, {806,255}, {807,255}, {808,255}, {812,255}, {813,255}, {814,255}, {815,204}, {816,0}, {817,255}, {818,255}, {819,255}, {820,255}, {821,255}, {822,255}, {823,255}, {824,255}, {825,255}, {826,255}, {827,255}, {828,0}, {829,255}, {830,255}, {834,255}, {835,255}, {836,255}, {840,0}, {841,127}, {842,127}, {844,255}, {848,25}, {858,100}, {859,255}, {860,255}, {861,255}, {862,255}, {863,84}, {868,0}, {869,0}, {877,0}, {879,51}, {880,132}, {921,255}, {922,255}, {923,255}, {10000,0}, {10001,0}, {10002,25}, {10003,0}, {10004,25}, {10005,23}, {10006,51}, {10007,0}, {10008,25}, {10009,23}, {10010,51}, {10011,0}, {10012,0}, {10013,25}, {10014,0}, {10015,25}, {10016,23}, {10017,51}, {10018,0}, {10019,0}, {10020,25}, {10021,0}, {10022,25}, {10023,23}, {10024,51}, {10025,0}, {10026,25}, {10027,23}, {10028,51}, {10029,0}, {10030,25}, {10031,23}, {10032,51}, {11000,1}, {11001,127} + }; + std::map<S32, U8> *params = NULL; + if (getSex() == SEX_MALE) + params = &male_params; + else + params = &female_params; + + for( auto it = params->begin(); it != params->end(); ++it) + { + LLVisualParam* param = getVisualParam(it->first); + if( !param ) + { + // invalid id + break; + } + + U8 value = it->second; + F32 newWeight = U8_to_F32(value, param->getMinWeight(), param->getMaxWeight()); + param->setWeight(newWeight); + } +} + //----------------------------------------------------------------------------- // resetSkeleton() //----------------------------------------------------------------------------- @@ -2073,15 +2136,30 @@ void LLVOAvatar::resetSkeleton(bool reset_animations) } // Reset tweakable params to preserved state - if (mLastProcessedAppearance) - { - bool slam_params = true; - applyParsedAppearanceMessage(*mLastProcessedAppearance, slam_params); - } + if (getOverallAppearance() == AOA_NORMAL) + { + if (mLastProcessedAppearance) + { + bool slam_params = true; + applyParsedAppearanceMessage(*mLastProcessedAppearance, slam_params); + } + } + else + { + // Stripped down approximation of + // applyParsedAppearanceMessage, but with alternative default + // (jellydoll) params + setCompositeUpdatesEnabled( FALSE ); + gPipeline.markGLRebuild(this); + applyDefaultParams(); + setCompositeUpdatesEnabled( TRUE ); + updateMeshTextures(); + updateMeshVisibility(); + } updateVisualParams(); // Restore attachment pos overrides - updateAttachmentOverrides(); + updateAttachmentOverrides(); // Animations if (reset_animations) @@ -2414,6 +2492,7 @@ S32 LLVOAvatar::setTETexture(const U8 te, const LLUUID& uuid) } static LLTrace::BlockTimerStatHandle FTM_AVATAR_UPDATE("Avatar Update"); +static LLTrace::BlockTimerStatHandle FTM_AVATAR_UPDATE_COMPLEXITY("Avatar Update Complexity"); static LLTrace::BlockTimerStatHandle FTM_JOINT_UPDATE("Update Joints"); //------------------------------------------------------------------------ @@ -2456,14 +2535,13 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) return; } - if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR)) + if (!(gPipeline.hasRenderType(mIsControlAvatar ? LLPipeline::RENDER_TYPE_CONTROL_AV : LLPipeline::RENDER_TYPE_AVATAR)) && !(gSavedSettings.getBOOL("DisableAllRenderTypes")) && !isSelf()) { return; } // Update should be happening max once per frame. - const S32 upd_freq = 4; // force update every upd_freq frames. if ((mLastAnimExtents[0]==LLVector3())|| (mLastAnimExtents[1])==LLVector3()) { @@ -2471,6 +2549,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) } else { + const S32 upd_freq = 4; // force update every upd_freq frames. mNeedsExtentUpdate = ((LLDrawable::getCurrentFrame()+mID.mData[0])%upd_freq==0); } @@ -2555,8 +2634,41 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) } idleUpdateNameTag( mLastRootPos ); + + // Complexity has stale mechanics, but updates still can be very rapid + // so spread avatar complexity calculations over frames to lesen load from + // rapid updates and to make sure all avatars are not calculated at once. + S32 compl_upd_freq = 20; + if (isControlAvatar()) + { + // animeshes do not (or won't) have impostors nor change outfis, + // no need for high frequency + compl_upd_freq = 100; + } + else if (mLastRezzedStatus <= 0) //cloud or init + { + compl_upd_freq = 60; + } + else if (isSelf()) + { + compl_upd_freq = 5; + } + else if (mLastRezzedStatus == 1) //'grey', not fully loaded + { + compl_upd_freq = 40; + } + else if (isInMuteList()) //cheap, buffers value from search + { + compl_upd_freq = 100; + } + + if ((LLFrameTimer::getFrameCount() + mID.mData[0]) % compl_upd_freq == 0) + { + LL_RECORD_BLOCK_TIME(FTM_AVATAR_UPDATE_COMPLEXITY); idleUpdateRenderComplexity(); } + idleUpdateDebugInfo(); +} void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) { @@ -2682,7 +2794,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) BOOL visible = isVisible() || mNeedsAnimUpdate; // update attachments positions - if (detailed_update || !sUseImpostors) + if (detailed_update) { LL_RECORD_BLOCK_TIME(FTM_ATTACHMENT_UPDATE); for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); @@ -2742,6 +2854,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) if (angle_diff > F_PI/512.f*distance*mUpdatePeriod) { mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 2; } } @@ -2753,6 +2866,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) if (dist_diff/mImpostorDistance > 0.1f) { mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 3; } else { @@ -2765,6 +2879,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) if (diff.getLength3().getF32() > 0.05f) { mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 4; } else { @@ -2772,6 +2887,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) if (diff.getLength3().getF32() > 0.05f) { mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 5; } } } @@ -2780,13 +2896,13 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) if (mDrawable.notNull()) { - mDrawable->movePartition(); + mDrawable->movePartition(); - //force a move if sitting on an active object - if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive()) - { - gPipeline.markMoved(mDrawable, TRUE); - } + //force a move if sitting on an active object + if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive()) + { + gPipeline.markMoved(mDrawable, TRUE); + } } } @@ -2866,7 +2982,10 @@ F32 LLVOAvatar::calcMorphAmount() void LLVOAvatar::idleUpdateLipSync(bool voice_enabled) { // Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync - if ( voice_enabled && (LLVoiceClient::getInstance()->lipSyncEnabled()) && LLVoiceClient::getInstance()->getIsSpeaking( mID ) ) + if ( voice_enabled + && mLastRezzedStatus > 0 // no point updating lip-sync for clouds + && (LLVoiceClient::getInstance()->lipSyncEnabled()) + && LLVoiceClient::getInstance()->getIsSpeaking( mID ) ) { F32 ooh_morph_amount = 0.0f; F32 aah_morph_amount = 0.0f; @@ -3214,7 +3333,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) std::string title_str = title->getString(); LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR); addNameTagLine(title_str, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall(), true); } static LLUICachedControl<bool> show_display_names("NameTagShowDisplayNames", true); @@ -3234,7 +3353,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) if (show_display_names) { addNameTagLine(av_name.getDisplayName(), name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerif()); + LLFontGL::getFontSansSerif(), true); } // Suppress SLID display if display name matches exactly (ugh) if (show_usernames && !av_name.isDisplayNameDefault()) @@ -3242,14 +3361,14 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // *HACK: Desaturate the color LLColor4 username_color = name_tag_color * 0.83f; addNameTagLine(av_name.getUserName(), username_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall(), true); } } else { const LLFontGL* font = LLFontGL::getFontSansSerif(); std::string full_name = LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); - addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font); + addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font, true); } mNameAway = is_away; @@ -3341,7 +3460,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) } } -void LLVOAvatar::addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font) +void LLVOAvatar::addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font, const bool use_ellipses) { llassert(mNameText); if (mVisibleChat) @@ -3350,7 +3469,7 @@ void LLVOAvatar::addNameTagLine(const std::string& line, const LLColor4& color, } else { - mNameText->addLine(line, color, (LLFontGL::StyleFlags)style, font); + mNameText->addLine(line, color, (LLFontGL::StyleFlags)style, font, use_ellipses); } mNameIsSet |= !line.empty(); } @@ -3512,14 +3631,19 @@ bool LLVOAvatar::isVisuallyMuted() muted = false; } else if (mVisuallyMuteSetting == AV_DO_NOT_RENDER) - { // Always want to see this AV as an impostor + { +#ifdef JELLYDOLLS_SHOULD_IMPOSTOR muted = true; + // Always want to see this AV as an impostor +#else + muted = false; +#endif } else if (isInMuteList()) { muted = true; } - else + else { muted = isTooComplex(); } @@ -3528,7 +3652,7 @@ bool LLVOAvatar::isVisuallyMuted() return muted; } -bool LLVOAvatar::isInMuteList() +bool LLVOAvatar::isInMuteList() const { bool muted = false; F64 now = LLFrameTimer::getTotalSeconds(); @@ -3590,9 +3714,14 @@ void LLVOAvatar::updateAppearanceMessageDebugText() if (hover_offset[2] != 0.0) { debug_line += llformat(" hov_z: %.3f", hover_offset[2]); - debug_line += llformat(" %s", (isSitting() ? "S" : "T")); + debug_line += llformat(" %s", (isSitting() ? "S" : "T")); debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-")); } + if (mInAir) + { + debug_line += " A"; + + } LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); LLVector3 normal; @@ -3605,9 +3734,38 @@ void LLVOAvatar::updateAppearanceMessageDebugText() LLVector3 pelvis_pos = mPelvisp->getPosition(); debug_line += llformat(" rp %.3f pp %.3f", root_pos[2], pelvis_pos[2]); - S32 is_visible = (S32) isVisible(); - S32 is_m_visible = (S32) mVisible; - debug_line += llformat(" v %d/%d", is_visible, is_m_visible); + const LLVector3& scale = getScale(); + debug_line += llformat(" scale-z %.3f", scale[2]); + S32 is_visible = (S32) isVisible(); + S32 is_m_visible = (S32) mVisible; + debug_line += llformat(" v %d/%d", is_visible, is_m_visible); + + AvatarOverallAppearance aoa = getOverallAppearance(); + if (aoa == AOA_NORMAL) + { + debug_line += " N"; + } + else if (aoa == AOA_JELLYDOLL) + { + debug_line += " J"; + } + else + { + debug_line += " I"; + } + + if (mMeshValid) + { + debug_line += "m"; + } + else + { + debug_line += "-"; + } + if (isImpostor()) + { + debug_line += " Imp" + llformat("%d[%d]:%.1f", mUpdatePeriod, mLastImpostorUpdateReason, ((F32)(gFrameTimeSeconds-mLastImpostorUpdateFrameTime))); + } addDebugText(debug_line); } @@ -3649,13 +3807,13 @@ LLViewerInventoryItem* recursiveGetObjectInventoryItem(LLViewerObject *vobj, LLU void LLVOAvatar::updateAnimationDebugText() { - for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin(); - iter != mMotionController.getActiveMotions().end(); ++iter) + for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin(); + iter != mMotionController.getActiveMotions().end(); ++iter) + { + LLMotion* motionp = *iter; + if (motionp->getMinPixelArea() < getPixelArea()) { - LLMotion* motionp = *iter; - if (motionp->getMinPixelArea() < getPixelArea()) - { - std::string output; + std::string output; std::string motion_name = motionp->getName(); if (motion_name.empty()) { @@ -3672,73 +3830,74 @@ void LLVOAvatar::updateAnimationDebugText() } } if (motion_name.empty()) + { + std::string name; + if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf()) { - std::string name; - if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf()) + name = motionp->getID().asString(); + LLVOAvatar::AnimSourceIterator anim_it = mAnimationSources.begin(); + for (; anim_it != mAnimationSources.end(); ++anim_it) { - name = motionp->getID().asString(); - LLVOAvatar::AnimSourceIterator anim_it = mAnimationSources.begin(); - for (; anim_it != mAnimationSources.end(); ++anim_it) + if (anim_it->second == motionp->getID()) { - if (anim_it->second == motionp->getID()) + LLViewerObject* object = gObjectList.findObject(anim_it->first); + if (!object) + { + break; + } + if (object->isAvatar()) { - LLViewerObject* object = gObjectList.findObject(anim_it->first); - if (!object) + if (mMotionController.mIsSelf) + { + // Searching inventory by asset id is really long + // so just mark as inventory + // Also item is likely to be named by LLPreviewAnim + name += "(inventory)"; + } + } + else + { + LLViewerInventoryItem* item = NULL; + if (!object->isInventoryDirty()) + { + item = object->getInventoryItemByAsset(motionp->getID()); + } + if (item) { - break; + name = item->getName(); } - if (object->isAvatar()) + else if (object->isAttachment()) { - if (mMotionController.mIsSelf) - { - // Searching inventory by asset id is really long - // so just mark as inventory - // Also item is likely to be named by LLPreviewAnim - name += "(inventory)"; - } + name += "(att:" + getAttachmentItemName() + ")"; } else { - LLViewerInventoryItem* item = NULL; - if (!object->isInventoryDirty()) - { - item = object->getInventoryItemByAsset(motionp->getID()); - } - if (item) - { - name = item->getName(); - } - else if (object->isAttachment()) - { - name += "(" + getAttachmentItemName() + ")"; - } - else - { - // in-world object, name or content unknown - name += "(in-world)"; - } + // in-world object, name or content unknown + name += "(in-world)"; } - break; } + break; } } - else - { - name = LLUUID::null.asString(); - } - output = llformat("%s - %d", - name.c_str(), - (U32)motionp->getPriority()); } else { - output = llformat("%s - %d", - motion_name.c_str(), - (U32)motionp->getPriority()); + name = LLUUID::null.asString(); } - addDebugText(output); + motion_name = name; + } + std::string motion_tag = ""; + if (mPlayingAnimations.find(motionp->getID()) != mPlayingAnimations.end()) + { + motion_tag = "*"; } + output = llformat("%s%s - %d", + motion_name.c_str(), + motion_tag.c_str(), + (U32)motionp->getPriority()); + addDebugText(output); } + } } void LLVOAvatar::updateDebugText() @@ -3867,7 +4026,14 @@ void LLVOAvatar::updateFootstepSounds() // computeUpdatePeriod() // Factored out from updateCharacter() // Set new value for mUpdatePeriod based on distance and various other factors. -//------------------------------------------------------------------------ +// +// Note 10-2020: it turns out that none of these update period +// calculations have been having any effect, because +// mNeedsImpostorUpdate was not being set in updateCharacter(). So +// it's really open to question whether we want to enable time based updates, and if +// so, at what rate. Leaving the rates as given would lead to +// drastically more frequent impostor updates than we've been doing all these years. +// ------------------------------------------------------------------------ void LLVOAvatar::computeUpdatePeriod() { bool visually_muted = isVisuallyMuted(); @@ -3875,7 +4041,7 @@ void LLVOAvatar::computeUpdatePeriod() && isVisible() && (!isSelf() || visually_muted) && !isUIAvatar() - && sUseImpostors + && (sLimitNonImpostors || visually_muted) && !mNeedsAnimUpdate && !sFreezeCounter) { @@ -3883,11 +4049,14 @@ void LLVOAvatar::computeUpdatePeriod() LLVector4a size; size.setSub(ext[1],ext[0]); F32 mag = size.getLength3().getF32()*0.5f; + + const S32 UPDATE_RATE_SLOW = 64; + const S32 UPDATE_RATE_MED = 48; + const S32 UPDATE_RATE_FAST = 32; - F32 impostor_area = 256.f*512.f*(8.125f - LLVOAvatar::sLODFactor*8.f); if (visually_muted) - { // visually muted avatars update at 16 hz - mUpdatePeriod = 16; + { // visually muted avatars update at lowest rate + mUpdatePeriod = UPDATE_RATE_SLOW; } else if (! shouldImpostor() || mDrawable->mDistanceWRTCamera < 1.f + mag) @@ -3896,29 +4065,29 @@ void LLVOAvatar::computeUpdatePeriod() // impostor camera near clip plane mUpdatePeriod = 1; } - else if ( shouldImpostor(4) ) + else if ( shouldImpostor(4.0) ) { //background avatars are REALLY slow updating impostors - mUpdatePeriod = 16; + mUpdatePeriod = UPDATE_RATE_SLOW; } - else if ( shouldImpostor(3) ) - { //back 25% of max visible avatars are slow updating impostors - mUpdatePeriod = 8; + else if (mLastRezzedStatus <= 0) + { + // Don't update cloud avatars too often + mUpdatePeriod = UPDATE_RATE_SLOW; } - else if (mImpostorPixelArea <= impostor_area) - { // stuff in between gets an update period based on pixel area - mUpdatePeriod = llclamp((S32) sqrtf(impostor_area*4.f/mImpostorPixelArea), 2, 8); + else if ( shouldImpostor(3.0) ) + { //back 25% of max visible avatars are slow updating impostors + mUpdatePeriod = UPDATE_RATE_MED; } - else + else { //nearby avatars, update the impostors more frequently. - mUpdatePeriod = 4; + mUpdatePeriod = UPDATE_RATE_FAST; } } else { mUpdatePeriod = 1; } - } //------------------------------------------------------------------------ @@ -4189,7 +4358,33 @@ void LLVOAvatar::updateRootPositionAndRotation(LLAgent& agent, F32 speed, bool w if (!isSitting() && !was_sit_ground_constrained) { root_pos += LLVector3d(getHoverOffset()); - } + if (getOverallAppearance() == AOA_JELLYDOLL) + { + F32 offz = -0.5 * (getScale()[VZ] - mBodySize.mV[VZ]); + root_pos[2] += offz; + // if (!isSelf() && !isControlAvatar()) + // { + // LL_DEBUGS("Avatar") << "av " << getFullname() + // << " frame " << LLFrameTimer::getFrameCount() + // << " root adjust offz " << offz + // << " scalez " << getScale()[VZ] + // << " bsz " << mBodySize.mV[VZ] + // << LL_ENDL; + // } + } + } + // if (!isSelf() && !isControlAvatar()) + // { + // LL_DEBUGS("Avatar") << "av " << getFullname() << " aoa " << (S32) getOverallAppearance() + // << " frame " << LLFrameTimer::getFrameCount() + // << " scalez " << getScale()[VZ] + // << " bsz " << mBodySize.mV[VZ] + // << " root pos " << root_pos[2] + // << " curr rootz " << mRoot->getPosition()[2] + // << " pp-z " << mPelvisp->getPosition()[2] + // << " renderpos " << getRenderPosition() + // << LL_ENDL; + // } LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); if (cav) @@ -4200,6 +4395,14 @@ void LLVOAvatar::updateRootPositionAndRotation(LLAgent& agent, F32 speed, bool w else { LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); + // if (!isSelf() && !isControlAvatar()) + // { + // LL_DEBUGS("Avatar") << "av " << getFullname() + // << " frame " << LLFrameTimer::getFrameCount() + // << " newPosition " << newPosition + // << " renderpos " << getRenderPosition() + // << LL_ENDL; + // } if (newPosition != mRoot->getXform()->getWorldPosition()) { mRoot->touch(); @@ -4230,6 +4433,37 @@ void LLVOAvatar::updateRootPositionAndRotation(LLAgent& agent, F32 speed, bool w } //------------------------------------------------------------------------ +// LLVOAvatar::computeNeedsUpdate() +// +// Most of the logic here is to figure out when to periodically update impostors. +// Non-impostors have mUpdatePeriod == 1 and will need update every frame. +//------------------------------------------------------------------------ +bool LLVOAvatar::computeNeedsUpdate() +{ + const F32 MAX_IMPOSTOR_INTERVAL = 4.0f; + computeUpdatePeriod(); + + bool needs_update_by_frame_count = ((LLDrawable::getCurrentFrame()+mID.mData[0])%mUpdatePeriod == 0); + + bool needs_update_by_max_time = ((gFrameTimeSeconds-mLastImpostorUpdateFrameTime)> MAX_IMPOSTOR_INTERVAL); + bool needs_update = needs_update_by_frame_count || needs_update_by_max_time; + + if (needs_update && !isSelf()) + { + if (needs_update_by_max_time) + { + mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 11; + } + else + { + //mNeedsImpostorUpdate = TRUE; + //mLastImpostorUpdateReason = 10; + } + } + return needs_update; +} + // updateCharacter() // // This is called for all avatars, so there are 4 possible situations: @@ -4252,7 +4486,7 @@ void LLVOAvatar::updateRootPositionAndRotation(LLAgent& agent, F32 speed, bool w // simulator. // //------------------------------------------------------------------------ -BOOL LLVOAvatar::updateCharacter(LLAgent &agent) +bool LLVOAvatar::updateCharacter(LLAgent &agent) { updateDebugText(); @@ -4264,6 +4498,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) BOOL visible = isVisible(); bool is_control_avatar = isControlAvatar(); // capture state to simplify tracing bool is_attachment = false; + if (is_control_avatar) { LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); @@ -4283,24 +4518,30 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) //-------------------------------------------------------------------- // The rest should only be done occasionally for far away avatars. - // Set mUpdatePeriod and visible based on distance and other criteria. + // Set mUpdatePeriod and visible based on distance and other criteria, + // and flag for impostor update if needed. //-------------------------------------------------------------------- - computeUpdatePeriod(); - visible = (LLDrawable::getCurrentFrame()+mID.mData[0])%mUpdatePeriod == 0 ? TRUE : FALSE; - + bool needs_update = computeNeedsUpdate(); + //-------------------------------------------------------------------- - // Early out if not visible and not self + // Early out if does not need update and not self // don't early out for your own avatar, as we rely on your animations playing reliably // for example, the "turn around" animation when entering customize avatar needs to trigger // even when your avatar is offscreen //-------------------------------------------------------------------- - if (!visible && !isSelf()) + if (!needs_update && !isSelf()) { updateMotions(LLCharacter::HIDDEN_UPDATE); return FALSE; } //-------------------------------------------------------------------- + // Handle transitions between regular rendering, jellydoll, or invisible. + // Can trigger skeleton reset or animation changes + //-------------------------------------------------------------------- + updateOverallAppearance(); + + //-------------------------------------------------------------------- // change animation time quanta based on avatar render load //-------------------------------------------------------------------- // SL-763 the time step quantization does not currently work. @@ -4343,12 +4584,17 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) mSpeed = speed; // update animations - if (mSpecialRenderMode == 1) // Animation Preview + if (!visible) + { + updateMotions(LLCharacter::HIDDEN_UPDATE); + } + else if (mSpecialRenderMode == 1) // Animation Preview { updateMotions(LLCharacter::FORCE_UPDATE); } else { + // Might be better to do HIDDEN_UPDATE if cloud updateMotions(LLCharacter::NORMAL_UPDATE); } @@ -4376,10 +4622,13 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) // Update child joints as needed. mRoot->updateWorldMatrixChildren(); - // System avatar mesh vertices need to be reskinned. - mNeedsSkin = TRUE; + if (visible) + { + // System avatar mesh vertices need to be reskinned. + mNeedsSkin = TRUE; + } - return TRUE; + return visible; } //----------------------------------------------------------------------------- @@ -4822,7 +5071,8 @@ U32 LLVOAvatar::renderSkinned() } if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender) { - if (isTextureVisible(TEX_HEAD_BAKED) || isUIAvatar()) + + if (isTextureVisible(TEX_HEAD_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar()) { LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD); if (head_mesh) @@ -4832,7 +5082,7 @@ U32 LLVOAvatar::renderSkinned() first_pass = FALSE; } } - if (isTextureVisible(TEX_UPPER_BAKED) || isUIAvatar()) + if (isTextureVisible(TEX_UPPER_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar()) { LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY); if (upper_mesh) @@ -4842,7 +5092,7 @@ U32 LLVOAvatar::renderSkinned() first_pass = FALSE; } - if (isTextureVisible(TEX_LOWER_BAKED) || isUIAvatar()) + if (isTextureVisible(TEX_LOWER_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar()) { LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY); if (lower_mesh) @@ -4899,7 +5149,7 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass) } first_pass = FALSE; } - if (isTextureVisible(TEX_HAIR_BAKED)) + if (isTextureVisible(TEX_HAIR_BAKED) && (getOverallAppearance() != AOA_JELLYDOLL)) { LLViewerJoint* hair_mesh = getViewerJoint(MESH_ID_HAIR); if (hair_mesh) @@ -4947,7 +5197,7 @@ U32 LLVOAvatar::renderRigid() gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); } - if (isTextureVisible(TEX_EYES_BAKED) || isUIAvatar()) + if (isTextureVisible(TEX_EYES_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar()) { LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT); LLViewerJoint* eyeball_right = getViewerJoint(MESH_ID_EYEBALL_RIGHT); @@ -5125,7 +5375,7 @@ void LLVOAvatar::collectLocalTextureUUIDs(std::set<LLUUID>& ids) const { for (U32 texture_index = 0; texture_index < getNumTEs(); texture_index++) { - LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex)texture_index); + LLWearableType::EType wearable_type = LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex)texture_index); U32 num_wearables = gAgentWearables.getWearableCount(wearable_type); LLViewerFetchedTexture *imagep = NULL; @@ -5134,7 +5384,7 @@ void LLVOAvatar::collectLocalTextureUUIDs(std::set<LLUUID>& ids) const imagep = LLViewerTextureManager::staticCastToFetchedTexture(getImage(texture_index, wearable_index), TRUE); if (imagep) { - const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearanceDictionary::getInstance()->getTexture((ETextureIndex)texture_index); + const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)texture_index); if (texture_dict && texture_dict->mIsLocalTexture) { ids.insert(imagep->getID()); @@ -5268,7 +5518,7 @@ void LLVOAvatar::updateTextures() mHasGrey = FALSE; // debug for (U32 texture_index = 0; texture_index < getNumTEs(); texture_index++) { - LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex)texture_index); + LLWearableType::EType wearable_type = LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex)texture_index); U32 num_wearables = gAgentWearables.getWearableCount(wearable_type); const LLTextureEntry *te = getTE(texture_index); @@ -5291,7 +5541,7 @@ void LLVOAvatar::updateTextures() imagep = LLViewerTextureManager::staticCastToFetchedTexture(getImage(texture_index, wearable_index), TRUE); if (imagep) { - const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearanceDictionary::getInstance()->getTexture((ETextureIndex)texture_index); + const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)texture_index); const EBakedTextureIndex baked_index = texture_dict ? texture_dict->mBakedTextureIndex : EBakedTextureIndex::BAKED_NUM_INDICES; if (texture_dict && texture_dict->mIsLocalTexture) { @@ -5466,7 +5716,7 @@ const std::string LLVOAvatar::getImageURL(const U8 te, const LLUUID &uuid) return url; } - const LLAvatarAppearanceDictionary::TextureEntry* texture_entry = LLAvatarAppearanceDictionary::getInstance()->getTexture((ETextureIndex)te); + const LLAvatarAppearanceDictionary::TextureEntry* texture_entry = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)te); if (texture_entry != NULL) { url = appearance_service_url + "texture/" + getID().asString() + "/" + texture_entry->mDefaultImageName + "/" + uuid.asString(); @@ -5576,8 +5826,8 @@ void LLVOAvatar::processAnimationStateChanges() stopMotion(ANIM_AGENT_TARGET); if (mEnableDefaultMotions) { - startMotion(ANIM_AGENT_BODY_NOISE); - } + startMotion(ANIM_AGENT_BODY_NOISE); + } } // clear all current animations @@ -5597,23 +5847,32 @@ void LLVOAvatar::processAnimationStateChanges() ++anim_it; } + // if jellydolled, shelve all playing animations + if (getOverallAppearance() != AOA_NORMAL) + { + mPlayingAnimations.clear(); + } + // start up all new anims - for (anim_it = mSignaledAnimations.begin(); anim_it != mSignaledAnimations.end();) + if (getOverallAppearance() == AOA_NORMAL) { - AnimIterator found_anim = mPlayingAnimations.find(anim_it->first); - - // signaled but not playing, or different sequence id, start motion - if (found_anim == mPlayingAnimations.end() || found_anim->second != anim_it->second) + for (anim_it = mSignaledAnimations.begin(); anim_it != mSignaledAnimations.end();) { - if (processSingleAnimationStateChange(anim_it->first, TRUE)) + AnimIterator found_anim = mPlayingAnimations.find(anim_it->first); + + // signaled but not playing, or different sequence id, start motion + if (found_anim == mPlayingAnimations.end() || found_anim->second != anim_it->second) { - mPlayingAnimations[anim_it->first] = anim_it->second; - ++anim_it; - continue; + if (processSingleAnimationStateChange(anim_it->first, TRUE)) + { + mPlayingAnimations[anim_it->first] = anim_it->second; + ++anim_it; + continue; + } } - } - ++anim_it; + ++anim_it; + } } // clear source information for animations which have been stopped @@ -6176,6 +6435,11 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo, std::set<LL } LLScopedContextString str("addAttachmentOverridesForObject " + getFullname()); + + if (getOverallAppearance() != AOA_NORMAL) + { + return; + } LL_DEBUGS("AnimatedObjects") << "adding" << LL_ENDL; dumpStack("AnimatedObjectsStack"); @@ -6834,13 +7098,13 @@ LLDrawable *LLVOAvatar::createDrawable(LLPipeline *pipeline) pipeline->allocDrawable(this); mDrawable->setLit(FALSE); - LLDrawPoolAvatar *poolp = (LLDrawPoolAvatar*) gPipeline.getPool(LLDrawPool::POOL_AVATAR); + LLDrawPoolAvatar *poolp = (LLDrawPoolAvatar*)gPipeline.getPool(mIsControlAvatar ? LLDrawPool::POOL_CONTROL_AV : LLDrawPool::POOL_AVATAR); // Only a single face (one per avatar) //this face will be splitted into several if its vertex buffer is too long. mDrawable->setState(LLDrawable::ACTIVE); mDrawable->addFace(poolp, NULL); - mDrawable->setRenderType(LLPipeline::RENDER_TYPE_AVATAR); + mDrawable->setRenderType(mIsControlAvatar ? LLPipeline::RENDER_TYPE_CONTROL_AV : LLPipeline::RENDER_TYPE_AVATAR); mNumInitFaces = mDrawable->getNumFaces() ; @@ -6865,7 +7129,7 @@ static LLTrace::BlockTimerStatHandle FTM_UPDATE_AVATAR("Update Avatar"); BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable) { LL_RECORD_BLOCK_TIME(FTM_UPDATE_AVATAR); - if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR))) + if (!(gPipeline.hasRenderType(mIsControlAvatar ? LLPipeline::RENDER_TYPE_CONTROL_AV : LLPipeline::RENDER_TYPE_AVATAR))) { return TRUE; } @@ -7501,8 +7765,8 @@ BOOL LLVOAvatar::isWearingWearableType(LLWearableType::EType type) const break; // Do nothing } - for (LLAvatarAppearanceDictionary::Textures::const_iterator tex_iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - tex_iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator tex_iter = LLAvatarAppearance::getDictionary()->getTextures().begin(); + tex_iter != LLAvatarAppearance::getDictionary()->getTextures().end(); ++tex_iter) { const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = tex_iter->second; @@ -7515,7 +7779,7 @@ BOOL LLVOAvatar::isWearingWearableType(LLWearableType::EType type) const if (texture_dict->mIsUsedByBakedTexture) { const EBakedTextureIndex baked_index = texture_dict->mBakedTextureIndex; - return isTextureDefined(LLAvatarAppearanceDictionary::getInstance()->getBakedTexture(baked_index)->mTextureIndex); + return isTextureDefined(LLAvatarAppearance::getDictionary()->getBakedTexture(baked_index)->mTextureIndex); } return FALSE; } @@ -7595,9 +7859,15 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color) } // virtual +// Do rigged mesh attachments display with this av? bool LLVOAvatar::shouldRenderRigged() const { - return true; + if (getOverallAppearance() == AOA_NORMAL) + { + return true; + } + // TBD - render for AOA_JELLYDOLL? + return false; } // FIXME: We have an mVisible member, set in updateVisibility(), but this @@ -7878,12 +8148,14 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) } // did our loading state "change" from last call? - // runway - why are we updating every 30 calls even if nothing has changed? + // FIXME runway - why are we updating every 30 calls even if nothing has changed? + // This causes updateLOD() to run every 30 frames, among other things. const S32 UPDATE_RATE = 30; BOOL changed = ((mFullyLoaded != mPreviousFullyLoaded) || // if the value is different from the previous call (!mFullyLoadedInitialized) || // if we've never been called before (mFullyLoadedFrameCounter % UPDATE_RATE == 0)); // every now and then issue a change + BOOL fully_loaded_changed = (mFullyLoaded != mPreviousFullyLoaded); mPreviousFullyLoaded = mFullyLoaded; mFullyLoadedInitialized = TRUE; @@ -7894,7 +8166,13 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) // to know about outfit switching LLAvatarRenderNotifier::getInstance()->updateNotificationState(); } - + + if (fully_loaded_changed && !isSelf() && mFullyLoaded && isImpostor()) + { + // Fix for jellydoll initially invisible + mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 6; + } return changed; } @@ -7966,47 +8244,25 @@ void LLVOAvatar::updateMeshVisibility() bool bake_flag[BAKED_NUM_INDICES]; memset(bake_flag, 0, BAKED_NUM_INDICES*sizeof(bool)); - for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); - ++iter) + if (getOverallAppearance() == AOA_NORMAL) { - LLViewerJointAttachment* attachment = iter->second; - if (attachment) + for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); + ++iter) { - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) + LLViewerJointAttachment* attachment = iter->second; + if (attachment) { - LLViewerObject *objectp = attachment_iter->get(); - if (objectp) - { - for (int face_index = 0; face_index < objectp->getNumTEs(); face_index++) - { - LLTextureEntry* tex_entry = objectp->getTE(face_index); - bake_flag[BAKED_HEAD] |= (tex_entry->getID() == IMG_USE_BAKED_HEAD); - bake_flag[BAKED_EYES] |= (tex_entry->getID() == IMG_USE_BAKED_EYES); - bake_flag[BAKED_HAIR] |= (tex_entry->getID() == IMG_USE_BAKED_HAIR); - bake_flag[BAKED_LOWER] |= (tex_entry->getID() == IMG_USE_BAKED_LOWER); - bake_flag[BAKED_UPPER] |= (tex_entry->getID() == IMG_USE_BAKED_UPPER); - bake_flag[BAKED_SKIRT] |= (tex_entry->getID() == IMG_USE_BAKED_SKIRT); - bake_flag[BAKED_LEFT_ARM] |= (tex_entry->getID() == IMG_USE_BAKED_LEFTARM); - bake_flag[BAKED_LEFT_LEG] |= (tex_entry->getID() == IMG_USE_BAKED_LEFTLEG); - bake_flag[BAKED_AUX1] |= (tex_entry->getID() == IMG_USE_BAKED_AUX1); - bake_flag[BAKED_AUX2] |= (tex_entry->getID() == IMG_USE_BAKED_AUX2); - bake_flag[BAKED_AUX3] |= (tex_entry->getID() == IMG_USE_BAKED_AUX3); - } - } - - LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter1 = child_list.begin(); - iter1 != child_list.end(); ++iter1) + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) { - LLViewerObject* objectchild = *iter1; - if (objectchild) + LLViewerObject *objectp = attachment_iter->get(); + if (objectp) { - for (int face_index = 0; face_index < objectchild->getNumTEs(); face_index++) + for (int face_index = 0; face_index < objectp->getNumTEs(); face_index++) { - LLTextureEntry* tex_entry = objectchild->getTE(face_index); + LLTextureEntry* tex_entry = objectp->getTE(face_index); bake_flag[BAKED_HEAD] |= (tex_entry->getID() == IMG_USE_BAKED_HEAD); bake_flag[BAKED_EYES] |= (tex_entry->getID() == IMG_USE_BAKED_EYES); bake_flag[BAKED_HAIR] |= (tex_entry->getID() == IMG_USE_BAKED_HAIR); @@ -8020,6 +8276,31 @@ void LLVOAvatar::updateMeshVisibility() bake_flag[BAKED_AUX3] |= (tex_entry->getID() == IMG_USE_BAKED_AUX3); } } + + LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter1 = child_list.begin(); + iter1 != child_list.end(); ++iter1) + { + LLViewerObject* objectchild = *iter1; + if (objectchild) + { + for (int face_index = 0; face_index < objectchild->getNumTEs(); face_index++) + { + LLTextureEntry* tex_entry = objectchild->getTE(face_index); + bake_flag[BAKED_HEAD] |= (tex_entry->getID() == IMG_USE_BAKED_HEAD); + bake_flag[BAKED_EYES] |= (tex_entry->getID() == IMG_USE_BAKED_EYES); + bake_flag[BAKED_HAIR] |= (tex_entry->getID() == IMG_USE_BAKED_HAIR); + bake_flag[BAKED_LOWER] |= (tex_entry->getID() == IMG_USE_BAKED_LOWER); + bake_flag[BAKED_UPPER] |= (tex_entry->getID() == IMG_USE_BAKED_UPPER); + bake_flag[BAKED_SKIRT] |= (tex_entry->getID() == IMG_USE_BAKED_SKIRT); + bake_flag[BAKED_LEFT_ARM] |= (tex_entry->getID() == IMG_USE_BAKED_LEFTARM); + bake_flag[BAKED_LEFT_LEG] |= (tex_entry->getID() == IMG_USE_BAKED_LEFTLEG); + bake_flag[BAKED_AUX1] |= (tex_entry->getID() == IMG_USE_BAKED_AUX1); + bake_flag[BAKED_AUX2] |= (tex_entry->getID() == IMG_USE_BAKED_AUX2); + bake_flag[BAKED_AUX3] |= (tex_entry->getID() == IMG_USE_BAKED_AUX3); + } + } + } } } } @@ -8261,8 +8542,8 @@ void LLVOAvatar::updateMeshTextures() for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = - LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + LLAvatarAppearance::getDictionary()->getBakedTextures().begin(); + baked_iter != LLAvatarAppearance::getDictionary()->getBakedTextures().end(); ++baked_iter) { const EBakedTextureIndex baked_index = baked_iter->first; @@ -8440,7 +8721,7 @@ void LLVOAvatar::releaseComponentTextures() for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) { - const LLAvatarAppearanceDictionary::BakedEntry * bakedDicEntry = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index); + const LLAvatarAppearanceDictionary::BakedEntry * bakedDicEntry = LLAvatarAppearance::getDictionary()->getBakedTexture((EBakedTextureIndex)baked_index); // skip if this is a skirt and av is not wearing one, or if we don't have a baked texture UUID if (!isTextureDefined(bakedDicEntry->mTextureIndex) && ( (baked_index != BAKED_SKIRT) || isWearingWearableType(LLWearableType::WT_SKIRT) )) @@ -8459,8 +8740,8 @@ void LLVOAvatar::releaseComponentTextures() void LLVOAvatar::dumpAvatarTEs( const std::string& context ) const { LL_DEBUGS("Avatar") << avString() << (isSelf() ? "Self: " : "Other: ") << context << LL_ENDL; - for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearance::getDictionary()->getTextures().begin(); + iter != LLAvatarAppearance::getDictionary()->getTextures().end(); ++iter) { const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second; @@ -8950,7 +9231,11 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) mLastProcessedAppearance = contents; bool slam_params = false; - applyParsedAppearanceMessage(*contents, slam_params); + applyParsedAppearanceMessage(*contents, slam_params); + if (getOverallAppearance() != AOA_NORMAL) + { + resetSkeleton(false); + } } void LLVOAvatar::applyParsedAppearanceMessage(LLAppearanceMessageContents& contents, bool slam_params) @@ -9209,8 +9494,8 @@ void LLVOAvatar::onBakedTextureMasksLoaded( BOOL success, LLViewerFetchedTexture self->mBakedTextureDatas[BAKED_HEAD].mTexLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1); maskData->mLastDiscardLevel = discard_level; */ BOOL found_texture_id = false; - for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearance::getDictionary()->getTextures().begin(); + iter != LLAvatarAppearance::getDictionary()->getTextures().end(); ++iter) { @@ -9341,7 +9626,7 @@ void LLVOAvatar::useBakedTexture( const LLUUID& id ) } const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = - LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)i); + LLAvatarAppearance::getDictionary()->getBakedTexture((EBakedTextureIndex)i); for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) @@ -9476,7 +9761,7 @@ void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_weara for (U8 te = 0; te < TEX_NUM_INDICES; te++) { - if (LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex)te) == type) + if (LLAvatarAppearance::getDictionary()->getTEWearableType((ETextureIndex)te) == type) { // MULTIPLE_WEARABLES: extend to multiple wearables? LLViewerTexture* te_image = getImage((ETextureIndex)te, 0); @@ -9843,7 +10128,7 @@ BOOL LLVOAvatar::updateLOD() return FALSE; } - if (isImpostor() && 0 != mDrawable->getNumFaces() && mDrawable->getFace(0)->hasGeometry()) + if (!LLPipeline::sImpostorRender && isImpostor() && 0 != mDrawable->getNumFaces() && mDrawable->getFace(0)->hasGeometry()) { return TRUE; } @@ -10021,7 +10306,7 @@ void LLVOAvatar::onActiveOverrideMeshesChanged() U32 LLVOAvatar::getPartitionType() const { // Avatars merely exist as drawables in the bridge partition - return LLViewerRegion::PARTITION_BRIDGE; + return mIsControlAvatar ? LLViewerRegion::PARTITION_CONTROL_AV : LLViewerRegion::PARTITION_AVATAR; } //static @@ -10034,10 +10319,10 @@ void LLVOAvatar::updateImpostors() iter != instances_copy.end(); ++iter) { LLVOAvatar* avatar = (LLVOAvatar*) *iter; - if (!avatar->isDead() && avatar->isVisible() - && ( - (avatar->isImpostor() || LLVOAvatar::AV_DO_NOT_RENDER == avatar->getVisualMuteSettings()) && avatar->needsImpostorUpdate()) - ) + if (!avatar->isDead() + && avatar->isVisible() + && avatar->isImpostor() + && avatar->needsImpostorUpdate()) { avatar->calcMutedAVColor(); gPipeline.generateImpostor(avatar); @@ -10050,12 +10335,20 @@ void LLVOAvatar::updateImpostors() // virtual BOOL LLVOAvatar::isImpostor() { - return sUseImpostors && (isVisuallyMuted() || (mUpdatePeriod >= IMPOSTOR_PERIOD)) ? TRUE : FALSE; + return isVisuallyMuted() || (sLimitNonImpostors && (mUpdatePeriod > 1)); } -BOOL LLVOAvatar::shouldImpostor(const U32 rank_factor) const +BOOL LLVOAvatar::shouldImpostor(const F32 rank_factor) { - return (!isSelf() && sUseImpostors && mVisibilityRank > (sMaxNonImpostors * rank_factor)); + if (isSelf()) + { + return false; + } + if (isVisuallyMuted()) + { + return true; + } + return sLimitNonImpostors && (mVisibilityRank > sMaxNonImpostors * rank_factor); } BOOL LLVOAvatar::needsImpostorUpdate() const @@ -10098,16 +10391,16 @@ void LLVOAvatar::getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& d } // static -const U32 LLVOAvatar::IMPOSTORS_OFF = 66; /* Must equal the maximum allowed the RenderAvatarMaxNonImpostors +const U32 LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER = 66; /* Must equal the maximum allowed the RenderAvatarMaxNonImpostors * slider in panel_preferences_graphics1.xml */ // static void LLVOAvatar::updateImpostorRendering(U32 newMaxNonImpostorsValue) { U32 oldmax = sMaxNonImpostors; - bool oldflg = sUseImpostors; + bool oldflg = sLimitNonImpostors; - if (IMPOSTORS_OFF <= newMaxNonImpostorsValue) + if (NON_IMPOSTORS_MAX_SLIDER <= newMaxNonImpostorsValue) { sMaxNonImpostors = 0; } @@ -10115,13 +10408,13 @@ void LLVOAvatar::updateImpostorRendering(U32 newMaxNonImpostorsValue) { sMaxNonImpostors = newMaxNonImpostorsValue; } - // the sUseImpostors flag depends on whether or not sMaxNonImpostors is set to the no-limit value (0) - sUseImpostors = (0 != sMaxNonImpostors); - if ( oldflg != sUseImpostors ) + // the sLimitNonImpostors flag depends on whether or not sMaxNonImpostors is set to the no-limit value (0) + sLimitNonImpostors = (0 != sMaxNonImpostors); + if ( oldflg != sLimitNonImpostors ) { LL_DEBUGS("AvatarRender") << "was " << (oldflg ? "use" : "don't use" ) << " impostors (max " << oldmax << "); " - << "now " << (sUseImpostors ? "use" : "don't use" ) << " impostors (max " << sMaxNonImpostors << "); " + << "now " << (sLimitNonImpostors ? "use" : "don't use" ) << " impostors (max " << sMaxNonImpostors << "); " << LL_ENDL; } } @@ -10142,7 +10435,10 @@ void LLVOAvatar::idleUpdateRenderComplexity() // Render Complexity calculateUpdateRenderComplexity(); // Update mVisualComplexity if needed +} +void LLVOAvatar::idleUpdateDebugInfo() +{ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AVATAR_DRAW_INFO)) { std::string info_line; @@ -10384,7 +10680,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict - = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index); + = LLAvatarAppearance::getDictionary()->getBakedTexture((EBakedTextureIndex)baked_index); ETextureIndex tex_index = baked_dict->mTextureIndex; if ((tex_index != TEX_SKIRT_BAKED) || (isWearingWearableType(LLWearableType::WT_SKIRT))) { @@ -10449,8 +10745,8 @@ void LLVOAvatar::calculateUpdateRenderComplexity() } // print any avatar textures we didn't already know about - for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearance::getDictionary()->getTextures().begin(); + iter != LLAvatarAppearance::getDictionary()->getTextures().end(); ++iter) { const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second; @@ -10503,10 +10799,178 @@ void LLVOAvatar::setVisualMuteSettings(VisualMuteSettings set) { mVisuallyMuteSetting = set; mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 7; LLRenderMuteList::getInstance()->saveVisualMuteSetting(getID(), S32(set)); } + +void LLVOAvatar::setOverallAppearanceNormal() +{ + if (isControlAvatar()) + return; + + LLVector3 pelvis_pos = getJoint("mPelvis")->getPosition(); + resetSkeleton(false); + getJoint("mPelvis")->setPosition(pelvis_pos); + + for (auto it = mJellyAnims.begin(); it != mJellyAnims.end(); ++it) + { + bool is_playing = (mPlayingAnimations.find(*it) != mPlayingAnimations.end()); + LL_DEBUGS("Avatar") << "jelly anim " << *it << " " << is_playing << LL_ENDL; + if (!is_playing) + { + // Anim was not requested for this av by sim, but may be playing locally + stopMotion(*it); + } + } + mJellyAnims.clear(); + + processAnimationStateChanges(); +} + +void LLVOAvatar::setOverallAppearanceJellyDoll() +{ + if (isControlAvatar()) + return; + + // stop current animations + { + for ( LLVOAvatar::AnimIterator anim_it= mPlayingAnimations.begin(); + anim_it != mPlayingAnimations.end(); + ++anim_it) + { + { + stopMotion(anim_it->first, TRUE); + } + } + } + processAnimationStateChanges(); + + // Start any needed anims for jellydoll + updateOverallAppearanceAnimations(); + + LLVector3 pelvis_pos = getJoint("mPelvis")->getPosition(); + resetSkeleton(false); + getJoint("mPelvis")->setPosition(pelvis_pos); + +} + +void LLVOAvatar::setOverallAppearanceInvisible() +{ +} + +void LLVOAvatar::updateOverallAppearance() +{ + AvatarOverallAppearance new_overall = getOverallAppearance(); + if (new_overall != mOverallAppearance) + { + switch (new_overall) + { + case AOA_NORMAL: + setOverallAppearanceNormal(); + break; + case AOA_JELLYDOLL: + setOverallAppearanceJellyDoll(); + break; + case AOA_INVISIBLE: + setOverallAppearanceInvisible(); + break; + } + mOverallAppearance = new_overall; + if (!isSelf()) + { + mNeedsImpostorUpdate = TRUE; + mLastImpostorUpdateReason = 8; + } + updateMeshVisibility(); + } + + // This needs to be done even if overall appearance has not + // changed, since sit/stand status can be different. + updateOverallAppearanceAnimations(); +} + +void LLVOAvatar::updateOverallAppearanceAnimations() +{ + if (isControlAvatar()) + return; + + if (getOverallAppearance() == AOA_JELLYDOLL) + { + LLUUID motion_id; + if (isSitting() && getParent()) // sitting on object + { + motion_id = ANIM_AGENT_SIT_FEMALE; + } + else if (isSitting()) // sitting on ground + { + motion_id = ANIM_AGENT_SIT_GROUND_CONSTRAINED; + } + else // standing + { + motion_id = ANIM_AGENT_STAND; + } + if (mJellyAnims.find(motion_id) == mJellyAnims.end()) + { + for (auto it = mJellyAnims.begin(); it != mJellyAnims.end(); ++it) + { + bool is_playing = (mPlayingAnimations.find(*it) != mPlayingAnimations.end()); + LL_DEBUGS("Avatar") << "jelly anim " << *it << " " << is_playing << LL_ENDL; + if (!is_playing) + { + // Anim was not requested for this av by sim, but may be playing locally + stopMotion(*it, TRUE); + } + } + mJellyAnims.clear(); + + startMotion(motion_id); + mJellyAnims.insert(motion_id); + + processAnimationStateChanges(); + } + } +} + +// Based on isVisuallyMuted(), but has 3 possible results. +LLVOAvatar::AvatarOverallAppearance LLVOAvatar::getOverallAppearance() const +{ + AvatarOverallAppearance result = AOA_NORMAL; + + // Priority order (highest priority first) + // * own avatar is always drawn normally + // * if on the "always draw normally" list, draw them normally + // * if on the "always visually mute" list, show as jellydoll + // * if explicitly muted (blocked), show as invisible + // * check against the render cost and attachment limits - if too complex, show as jellydoll + if (isSelf()) + { + result = AOA_NORMAL; + } + else // !isSelf() + { + if (isInMuteList()) + { + result = AOA_INVISIBLE; + } + else if (mVisuallyMuteSetting == AV_ALWAYS_RENDER) + { + result = AOA_NORMAL; + } + else if (mVisuallyMuteSetting == AV_DO_NOT_RENDER) + { // Always want to see this AV as an impostor + result = AOA_JELLYDOLL; + } + else if (isTooComplex()) + { + result = AOA_JELLYDOLL; + } + } + + return result; +} + void LLVOAvatar::calcMutedAVColor() { LLColor4 new_color(mMutedAVColor); @@ -10516,8 +10980,8 @@ void LLVOAvatar::calcMutedAVColor() if (getVisualMuteSettings() == AV_DO_NOT_RENDER) { // explicitly not-rendered avatars are light grey - new_color = LLColor4::grey3; - change_msg = " not rendered: color is grey3"; + new_color = LLColor4::grey4; + change_msg = " not rendered: color is grey4"; } else if (LLMuteList::getInstance()->isMuted(av_id)) // the user blocked them { @@ -10530,26 +10994,31 @@ void LLVOAvatar::calcMutedAVColor() new_color = LLColor4::white; change_msg = " simple imposter "; } +#ifdef COLORIZE_JELLYDOLLS else if ( mMutedAVColor == LLColor4::white || mMutedAVColor == LLColor4::grey3 || mMutedAVColor == LLColor4::grey4 ) - { + { // select a color based on the first byte of the agents uuid so any muted agent is always the same color F32 color_value = (F32) (av_id.mData[0]); - F32 spectrum = (color_value / 256.0); // spectrum is between 0 and 1.f + F32 spectrum = (color_value / 256.0); // spectrum is between 0 and 1.f - // Array of colors. These are arranged so only one RGB color changes between each step, + // Array of colors. These are arranged so only one RGB color changes between each step, // and it loops back to red so there is an even distribution. It is not a heat map - const S32 NUM_SPECTRUM_COLORS = 7; + const S32 NUM_SPECTRUM_COLORS = 7; static LLColor4 * spectrum_color[NUM_SPECTRUM_COLORS] = { &LLColor4::red, &LLColor4::magenta, &LLColor4::blue, &LLColor4::cyan, &LLColor4::green, &LLColor4::yellow, &LLColor4::red }; - - spectrum = spectrum * (NUM_SPECTRUM_COLORS - 1); // Scale to range of number of colors - S32 spectrum_index_1 = floor(spectrum); // Desired color will be after this index - S32 spectrum_index_2 = spectrum_index_1 + 1; // and before this index (inclusive) + + spectrum = spectrum * (NUM_SPECTRUM_COLORS - 1); // Scale to range of number of colors + S32 spectrum_index_1 = floor(spectrum); // Desired color will be after this index + S32 spectrum_index_2 = spectrum_index_1 + 1; // and before this index (inclusive) F32 fractBetween = spectrum - (F32)(spectrum_index_1); // distance between the two indexes (0-1) - - new_color = lerp(*spectrum_color[spectrum_index_1], *spectrum_color[spectrum_index_2], fractBetween); - new_color.normalize(); - new_color *= 0.28f; // Tone it down + new_color = lerp(*spectrum_color[spectrum_index_1], *spectrum_color[spectrum_index_2], fractBetween); + new_color.normalize(); + new_color *= 0.28f; // Tone it down + } +#endif + else + { + new_color = LLColor4::grey4; change_msg = " over limit color "; } @@ -10565,7 +11034,7 @@ BOOL LLVOAvatar::isIndexLocalTexture(ETextureIndex index) { return (index < 0 || index >= TEX_NUM_INDICES) ? false - : LLAvatarAppearanceDictionary::getInstance()->getTexture(index)->mIsLocalTexture; + : LLAvatarAppearance::getDictionary()->getTexture(index)->mIsLocalTexture; } // static @@ -10573,15 +11042,15 @@ BOOL LLVOAvatar::isIndexBakedTexture(ETextureIndex index) { return (index < 0 || index >= TEX_NUM_INDICES) ? false - : LLAvatarAppearanceDictionary::getInstance()->getTexture(index)->mIsBakedTexture; + : LLAvatarAppearance::getDictionary()->getTexture(index)->mIsBakedTexture; } const std::string LLVOAvatar::getBakedStatusForPrintout() const { std::string line; - for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearance::getDictionary()->getTextures().begin(); + iter != LLAvatarAppearance::getDictionary()->getTextures().end(); ++iter) { const ETextureIndex index = iter->first; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index ca6ac5c902..74ef589ca4 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -91,6 +91,7 @@ class LLVOAvatar : public: friend class LLVOAvatarSelf; + friend class LLAvatarCheckImpostorMode; /******************************************************************************** ** ** @@ -131,6 +132,7 @@ protected: public: /*virtual*/ void updateGL(); /*virtual*/ LLVOAvatar* asAvatar(); + virtual U32 processUpdateMessage(LLMessageSystem *mesgsys, void **user_data, U32 block_num, @@ -252,6 +254,11 @@ public: virtual bool isControlAvatar() const { return mIsControlAvatar; } // True if this avatar is a control av (no associated user) virtual bool isUIAvatar() const { return mIsUIAvatar; } // True if this avatar is a supplemental av used in some UI views (no associated user) + // If this is an attachment, return the avatar it is attached to. Otherwise NULL. + virtual const LLVOAvatar *getAttachedAvatar() const { return NULL; } + virtual LLVOAvatar *getAttachedAvatar() { return NULL; } + + private: //aligned members LL_ALIGN_16(LLVector4a mImpostorExtents[2]); @@ -262,7 +269,8 @@ public: void updateAppearanceMessageDebugText(); void updateAnimationDebugText(); virtual void updateDebugText(); - virtual BOOL updateCharacter(LLAgent &agent); + virtual bool computeNeedsUpdate(); + virtual bool updateCharacter(LLAgent &agent); void updateFootstepSounds(); void computeUpdatePeriod(); void updateOrientation(LLAgent &agent, F32 speed, F32 delta_time); @@ -284,8 +292,9 @@ public: static void invalidateNameTag(const LLUUID& agent_id); // force all name tags to rebuild, useful when display names turned on/off static void invalidateNameTags(); - void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font); + void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font, const bool use_ellipses = false); void idleUpdateRenderComplexity(); + void idleUpdateDebugInfo(); void accountRenderComplexityForObject(const LLViewerObject *attached_object, const F32 max_attachment_complexity, LLVOVolume::texture_cost_t& textures, @@ -313,12 +322,12 @@ public: public: static S32 sRenderName; static BOOL sRenderGroupTitles; - static const U32 IMPOSTORS_OFF; /* Must equal the maximum allowed the RenderAvatarMaxNonImpostors - * slider in panel_preferences_graphics1.xml */ - static U32 sMaxNonImpostors; //(affected by control "RenderAvatarMaxNonImpostors") - static F32 sRenderDistance; //distance at which avatars will render. + static const U32 NON_IMPOSTORS_MAX_SLIDER; /* Must equal the maximum allowed the RenderAvatarMaxNonImpostors + * slider in panel_preferences_graphics1.xml */ + static U32 sMaxNonImpostors; // affected by control "RenderAvatarMaxNonImpostors" + static bool sLimitNonImpostors; // use impostors for far away avatars + static F32 sRenderDistance; // distance at which avatars will render. static BOOL sShowAnimationDebug; // show animation debug info - static bool sUseImpostors; //use impostors for far away avatars static BOOL sShowFootPlane; // show foot collision plane reported by server static BOOL sShowCollisionVolumes; // show skeletal collision volumes static BOOL sVisibleInFirstPerson; @@ -406,6 +415,7 @@ public: void initAttachmentPoints(bool ignore_hud_joints = false); /*virtual*/ void buildCharacter(); void resetVisualParams(); + void applyDefaultParams(); void resetSkeleton(bool reset_animations); LLVector3 mCurRootToHeadOffset; @@ -426,9 +436,12 @@ public: public: U32 renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0); bool isVisuallyMuted(); - bool isInMuteList(); + bool isInMuteList() const; void forceUpdateVisualMuteSettings(); + // Visual Mute Setting is an input. Does not necessarily determine + // what the avatar looks like, because it interacts with other + // settings like muting, complexity threshold. Should be private or protected. enum VisualMuteSettings { AV_RENDER_NORMALLY = 0, @@ -436,21 +449,50 @@ public: AV_ALWAYS_RENDER = 2 }; void setVisualMuteSettings(VisualMuteSettings set); + +protected: + // If you think you need to access this outside LLVOAvatar, you probably want getOverallAppearance() VisualMuteSettings getVisualMuteSettings() { return mVisuallyMuteSetting; }; +public: + + // Overall Appearance is an output. Depending on whether the + // avatar is blocked/muted, whether it exceeds the complexity + // threshold, etc, avatar will want to be displayed in one of + // these ways. Rendering code that wants to know how to display an + // avatar should be looking at this value, NOT the visual mute + // settings + enum AvatarOverallAppearance + { + AOA_NORMAL, + AOA_JELLYDOLL, + AOA_INVISIBLE + }; + + AvatarOverallAppearance getOverallAppearance() const; + void setOverallAppearanceNormal(); + void setOverallAppearanceJellyDoll(); + void setOverallAppearanceInvisible(); + + void updateOverallAppearance(); + void updateOverallAppearanceAnimations(); + + std::set<LLUUID> mJellyAnims; + U32 renderRigid(); U32 renderSkinned(); F32 getLastSkinTime() { return mLastSkinTime; } U32 renderTransparent(BOOL first_pass); void renderCollisionVolumes(); - void renderBones(); + void renderBones(const std::string &selected_joint = std::string()); void renderJoints(); static void deleteCachedImages(bool clearAll=true); static void destroyGL(); static void restoreGL(); S32 mSpecialRenderMode; // special lighting - private: +private: + AvatarOverallAppearance mOverallAppearance; F32 mAttachmentSurfaceArea; //estimated surface area of attachments U32 mAttachmentVisibleTriangleCount; F32 mAttachmentEstTriangleCount; @@ -467,8 +509,8 @@ public: mutable bool mVisualComplexityStale; U32 mReportedVisualComplexity; // from other viewers through the simulator - bool mCachedInMuteList; - F64 mCachedMuteListUpdateTime; + mutable bool mCachedInMuteList; + mutable F64 mCachedMuteListUpdateTime; VisualMuteSettings mVisuallyMuteSetting; // Always or never visually mute this AV @@ -519,7 +561,7 @@ private: //-------------------------------------------------------------------- public: virtual BOOL isImpostor(); - BOOL shouldImpostor(const U32 rank_factor = 1) const; + BOOL shouldImpostor(const F32 rank_factor = 1.0); BOOL needsImpostorUpdate() const; const LLVector3& getImpostorOffset() const; const LLVector2& getImpostorDim() const; @@ -530,6 +572,7 @@ public: static void updateImpostors(); LLRenderTarget mImpostor; BOOL mNeedsImpostorUpdate; + S32 mLastImpostorUpdateReason; F32SecondsImplicit mLastImpostorUpdateFrameTime; const LLVector3* getLastAnimExtents() const { return mLastAnimExtents; } void setNeedsExtentUpdate(bool val) { mNeedsExtentUpdate = val; } @@ -676,9 +719,6 @@ public: public: static BOOL isIndexLocalTexture(LLAvatarAppearanceDefines::ETextureIndex i); static BOOL isIndexBakedTexture(LLAvatarAppearanceDefines::ETextureIndex i); -private: - static const LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary *getDictionary() { return sAvatarDictionary; } - static LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary* sAvatarDictionary; //-------------------------------------------------------------------- // Messaging diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 16b27fd144..458d8ced65 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -668,7 +668,7 @@ LLVOAvatarSelf::~LLVOAvatarSelf() *********************************************************************************/ // virtual -BOOL LLVOAvatarSelf::updateCharacter(LLAgent &agent) +bool LLVOAvatarSelf::updateCharacter(LLAgent &agent) { // update screen joint size if (mScreenp) @@ -1079,8 +1079,8 @@ void LLVOAvatarSelf::updateAttachmentVisibility(U32 camera_mode) //----------------------------------------------------------------------------- void LLVOAvatarSelf::wearableUpdated(LLWearableType::EType type) { - for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); + baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = baked_iter->second; @@ -1277,7 +1277,7 @@ BOOL LLVOAvatarSelf::detachAttachmentIntoInventory(const LLUUID &item_id) U32 LLVOAvatarSelf::getNumWearables(LLAvatarAppearanceDefines::ETextureIndex i) const { - LLWearableType::EType type = LLAvatarAppearanceDictionary::getInstance()->getTEWearableType(i); + LLWearableType::EType type = sAvatarDictionary->getTEWearableType(i); return gAgentWearables.getWearableCount(type); } @@ -1388,8 +1388,8 @@ BOOL LLVOAvatarSelf::isLocalTextureDataAvailable(const LLViewerTexLayerSet* laye { /* if (layerset == mBakedTextureDatas[BAKED_HEAD].mTexLayerSet) return getLocalDiscardLevel(TEX_HEAD_BODYPAINT) >= 0; */ - for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); + baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const EBakedTextureIndex baked_index = baked_iter->first; @@ -1402,7 +1402,7 @@ BOOL LLVOAvatarSelf::isLocalTextureDataAvailable(const LLViewerTexLayerSet* laye ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; - const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(tex_index); + const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { @@ -1432,13 +1432,13 @@ BOOL LLVOAvatarSelf::isLocalTextureDataFinal(const LLViewerTexLayerSet* layerset { if (layerset == mBakedTextureDatas[i].mTexLayerSet) { - const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)i); + const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = sAvatarDictionary->getBakedTexture((EBakedTextureIndex)i); for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; - const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(tex_index); + const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { @@ -1465,13 +1465,13 @@ BOOL LLVOAvatarSelf::isAllLocalTextureDataFinal() const for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { - const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)i); + const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = sAvatarDictionary->getBakedTexture((EBakedTextureIndex)i); for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; - const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(tex_index); + const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { @@ -1493,7 +1493,7 @@ BOOL LLVOAvatarSelf::isTextureDefined(LLAvatarAppearanceDefines::ETextureIndex t BOOL isDefined = TRUE; if (isIndexLocalTexture(type)) { - const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(type); + const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(type); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); if (index >= wearable_count) { @@ -1710,7 +1710,7 @@ void LLVOAvatarSelf::setLocalTexture(ETextureIndex type, LLViewerTexture* src_te LL_ERRS() << "Tried to set local texture with invalid type: (" << (U32) type << ", " << index << ")" << LL_ENDL; return; } - LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getInstance()->getTEWearableType(type); + LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(type); if (!gAgentWearables.getViewerWearable(wearable_type,index)) { // no wearable is loaded, cannot set the texture. @@ -1787,8 +1787,8 @@ void LLVOAvatarSelf::dumpLocalTextures() const /* ETextureIndex baked_equiv[] = { TEX_UPPER_BAKED, if (isTextureDefined(baked_equiv[i])) */ - for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = sAvatarDictionary->getTextures().begin(); + iter != sAvatarDictionary->getTextures().end(); ++iter) { const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second; @@ -1796,7 +1796,7 @@ void LLVOAvatarSelf::dumpLocalTextures() const continue; const EBakedTextureIndex baked_index = texture_dict->mBakedTextureIndex; - const ETextureIndex baked_equiv = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture(baked_index)->mTextureIndex; + const ETextureIndex baked_equiv = sAvatarDictionary->getBakedTexture(baked_index)->mTextureIndex; const std::string &name = texture_dict->mName; const LLLocalTextureObject *local_tex_obj = getLocalTextureObject(iter->first, 0); @@ -2031,8 +2031,8 @@ const std::string LLVOAvatarSelf::verboseDebugDumpLocalTextureDataInfo(const LLV { std::ostringstream outbuf; for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = - LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + sAvatarDictionary->getBakedTextures().begin(); + baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const EBakedTextureIndex baked_index = baked_iter->first; @@ -2045,9 +2045,9 @@ const std::string LLVOAvatarSelf::verboseDebugDumpLocalTextureDataInfo(const LLV ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; - const std::string tex_name = LLAvatarAppearanceDictionary::getInstance()->getTexture(tex_index)->mName; + const std::string tex_name = sAvatarDictionary->getTexture(tex_index)->mName; outbuf << " tex_index " << (S32) tex_index << " name " << tex_name << "\n"; - const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(tex_index); + const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); if (wearable_count > 0) { @@ -2091,8 +2091,8 @@ const std::string LLVOAvatarSelf::verboseDebugDumpLocalTextureDataInfo(const LLV void LLVOAvatarSelf::dumpAllTextures() const { std::string vd_text = "Local textures per baked index and wearable:\n"; - for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); + baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const LLAvatarAppearanceDefines::EBakedTextureIndex baked_index = baked_iter->first; @@ -2113,8 +2113,8 @@ const std::string LLVOAvatarSelf::debugDumpLocalTextureDataInfo(const LLViewerTe /* if (layerset == mBakedTextureDatas[BAKED_HEAD].mTexLayerSet) return getLocalDiscardLevel(TEX_HEAD_BODYPAINT) >= 0; */ - for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); + baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const EBakedTextureIndex baked_index = baked_iter->first; @@ -2127,7 +2127,7 @@ const std::string LLVOAvatarSelf::debugDumpLocalTextureDataInfo(const LLViewerTe ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; - const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(tex_index); + const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); if (wearable_count > 0) { @@ -2154,14 +2154,14 @@ const std::string LLVOAvatarSelf::debugDumpAllLocalTextureDataInfo() const for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { - const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)i); + const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = sAvatarDictionary->getBakedTexture((EBakedTextureIndex)i); BOOL is_texture_final = TRUE; for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; - const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(tex_index); + const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { @@ -2341,7 +2341,7 @@ const LLUUID& LLVOAvatarSelf::grabBakedTexture(EBakedTextureIndex baked_index) c { if (canGrabBakedTexture(baked_index)) { - ETextureIndex tex_index = LLAvatarAppearanceDictionary::bakedToLocalTextureIndex(baked_index); + ETextureIndex tex_index = sAvatarDictionary->bakedToLocalTextureIndex(baked_index); if (tex_index == TEX_NUM_INDICES) { return LLUUID::null; @@ -2353,7 +2353,7 @@ const LLUUID& LLVOAvatarSelf::grabBakedTexture(EBakedTextureIndex baked_index) c BOOL LLVOAvatarSelf::canGrabBakedTexture(EBakedTextureIndex baked_index) const { - ETextureIndex tex_index = LLAvatarAppearanceDictionary::bakedToLocalTextureIndex(baked_index); + ETextureIndex tex_index = sAvatarDictionary->bakedToLocalTextureIndex(baked_index); if (tex_index == TEX_NUM_INDICES) { return FALSE; @@ -2372,13 +2372,13 @@ BOOL LLVOAvatarSelf::canGrabBakedTexture(EBakedTextureIndex baked_index) const // baked texture. We don't want people copying people's // work via baked textures. - const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture(baked_index); + const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = sAvatarDictionary->getBakedTexture(baked_index); for (texture_vec_t::const_iterator iter = baked_dict->mLocalTextures.begin(); iter != baked_dict->mLocalTextures.end(); ++iter) { const ETextureIndex t_index = (*iter); - LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(t_index); + LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(t_index); U32 count = gAgentWearables.getWearableCount(wearable_type); LL_DEBUGS() << "Checking index " << (U32) t_index << " count: " << count << LL_ENDL; @@ -2466,7 +2466,7 @@ void LLVOAvatarSelf::addLocalTextureStats( ETextureIndex type, LLViewerFetchedTe LLLocalTextureObject* LLVOAvatarSelf::getLocalTextureObject(LLAvatarAppearanceDefines::ETextureIndex i, U32 wearable_index) const { - LLWearableType::EType type = LLAvatarAppearanceDictionary::getInstance()->getTEWearableType(i); + LLWearableType::EType type = sAvatarDictionary->getTEWearableType(i); LLViewerWearable* wearable = gAgentWearables.getViewerWearable(type, wearable_index); if (wearable) { @@ -2540,8 +2540,8 @@ void LLVOAvatarSelf::outputRezDiagnostics() const LL_DEBUGS("Avatar") << "\t\t (" << i << ") \t" << (S32)mDebugBakedTextureTimes[i][0] << " / " << (S32)mDebugBakedTextureTimes[i][1] << LL_ENDL; } - for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin(); - baked_iter != LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end(); + for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); + baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const LLAvatarAppearanceDefines::EBakedTextureIndex baked_index = baked_iter->first; @@ -2607,7 +2607,7 @@ void LLVOAvatarSelf::requestLayerSetUpdate(ETextureIndex index ) case LOCTEX_UPPER_SHIRT: if( mUpperBodyLayerSet ) mUpperBodyLayerSet->requestUpdate(); */ - const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearanceDictionary::getInstance()->getTexture(index); + const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = sAvatarDictionary->getTexture(index); if (!texture_dict) return; if (!texture_dict->mIsLocalTexture || !texture_dict->mIsUsedByBakedTexture) @@ -2625,7 +2625,7 @@ LLViewerTexLayerSet* LLVOAvatarSelf::getLayerSet(ETextureIndex index) const case TEX_HEAD_BAKED: case TEX_HEAD_BODYPAINT: return mHeadLayerSet; */ - const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearanceDictionary::getInstance()->getTexture(index); + const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = sAvatarDictionary->getTexture(index); if (texture_dict && texture_dict->mIsUsedByBakedTexture) { const EBakedTextureIndex baked_index = texture_dict->mBakedTextureIndex; @@ -2667,11 +2667,6 @@ void LLVOAvatarSelf::onCustomizeStart(bool disable_camera_switch) { gAgentCamera.changeCameraToCustomizeAvatar(); } - -#if 0 - gAgentAvatarp->clearVisualParamWeights(); - gAgentAvatarp->idleUpdateAppearanceAnimation(); -#endif gAgentAvatarp->invalidateAll(); // mark all bakes as dirty, request updates gAgentAvatarp->updateMeshTextures(); // make sure correct textures are applied to the avatar mesh. @@ -2716,8 +2711,8 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const { LLUUID texture_id[TEX_NUM_INDICES]; // pack away current TEs to make sure we don't send them out - for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = sAvatarDictionary->getTextures().begin(); + iter != sAvatarDictionary->getTextures().end(); ++iter) { const ETextureIndex index = iter->first; @@ -2733,8 +2728,8 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const bool success = packTEMessage(mesgsys); // unpack TEs to make sure we don't re-trigger a bake - for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin(); - iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end(); + for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = sAvatarDictionary->getTextures().begin(); + iter != sAvatarDictionary->getTextures().end(); ++iter) { const ETextureIndex index = iter->first; diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index b0fdae9bf0..279dbd61a6 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -116,7 +116,7 @@ public: // Updates //-------------------------------------------------------------------- public: - /*virtual*/ BOOL updateCharacter(LLAgent &agent); + /*virtual*/ bool updateCharacter(LLAgent &agent); /*virtual*/ void idleUpdateTractorBeam(); bool checkStuckAppearance(); diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index 07660ca6ac..689eeee0e3 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -347,36 +347,24 @@ void LLVOCacheEntry::dump() const BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const { - BOOL success; - success = check_write(apr_file, (void*)&mLocalID, sizeof(U32)); - if(success) - { - success = check_write(apr_file, (void*)&mCRC, sizeof(U32)); - } - if(success) - { - success = check_write(apr_file, (void*)&mHitCount, sizeof(S32)); - } - if(success) - { - success = check_write(apr_file, (void*)&mDupeCount, sizeof(S32)); - } - if(success) - { - success = check_write(apr_file, (void*)&mCRCChangeCount, sizeof(S32)); - } - if(success) - { - S32 size = mDP.getBufferSize(); - success = check_write(apr_file, (void*)&size, sizeof(S32)); - - if(success) - { - success = check_write(apr_file, (void*)mBuffer, size); - } - } - - return success ; + static const S32 data_buffer_size = 6 * sizeof(S32); + static U8 data_buffer[data_buffer_size]; + S32 size = mDP.getBufferSize(); + + memcpy(data_buffer, &mLocalID, sizeof(U32)); + memcpy(data_buffer + sizeof(U32), &mCRC, sizeof(U32)); + memcpy(data_buffer + (2 * sizeof(U32)), &mHitCount, sizeof(S32)); + memcpy(data_buffer + (3 * sizeof(U32)), &mDupeCount, sizeof(S32)); + memcpy(data_buffer + (4 * sizeof(U32)), &mCRCChangeCount, sizeof(S32)); + memcpy(data_buffer + (5 * sizeof(U32)), &size, sizeof(S32)); + + BOOL success = check_write(apr_file, (void*)data_buffer, data_buffer_size); + if (success) + { + success = check_write(apr_file, (void*)mBuffer, size); + } + + return success; } //static @@ -1537,7 +1525,8 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry: { S32 num_entries = cache_entry_map.size() ; success = check_write(&apr_file, &num_entries, sizeof(S32)); - + + // This can have a lot of entries, so might be better to dump them into buffer first and write in one go. for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); success && iter != cache_entry_map.end(); ++iter) { if(!removal_enabled || iter->second->isValid()) diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index cc590fc947..e2bd1a39c7 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -180,6 +180,13 @@ void LLVoiceClient::terminate() { if (mVoiceModule) mVoiceModule->terminate(); mVoiceModule = NULL; + m_servicePump = NULL; + + // Shutdown speaker volume storage before LLSingletonBase::deleteAll() does it + if (LLSpeakerVolumeStorage::instanceExists()) + { + LLSpeakerVolumeStorage::deleteSingleton(); + } } const LLVoiceVersionInfo LLVoiceClient::getVersion() @@ -200,8 +207,6 @@ const LLVoiceVersionInfo LLVoiceClient::getVersion() void LLVoiceClient::updateSettings() { setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); - std::string keyString = gSavedSettings.getString("PushToTalkButton"); - setPTTKey(keyString); setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); mDisableMic = gSavedSettings.getBOOL("VoiceDisableMic"); @@ -637,32 +642,6 @@ bool LLVoiceClient::getPTTIsToggle() return mPTTIsToggle; } -void LLVoiceClient::setPTTKey(std::string &key) -{ - // Value is stored as text for readability - if(key == "MiddleMouse") - { - mPTTMouseButton = LLMouseHandler::CLICK_MIDDLE; - } - else if(key == "MouseButton4") - { - mPTTMouseButton = LLMouseHandler::CLICK_BUTTON4; - } - else if (key == "MouseButton5") - { - mPTTMouseButton = LLMouseHandler::CLICK_BUTTON5; - } - else - { - mPTTMouseButton = 0; - if(!LLKeyboard::keyFromString(key, &mPTTKey)) - { - // If the call failed, don't match any key. - key = KEY_NONE; - } - } -} - void LLVoiceClient::inputUserControlState(bool down) { if(mPTTIsToggle) @@ -683,43 +662,6 @@ void LLVoiceClient::toggleUserPTTState(void) setUserPTTState(!getUserPTTState()); } -void LLVoiceClient::keyDown(KEY key, MASK mask) -{ - if (gKeyboard->getKeyRepeated(key)) - { - // ignore auto-repeat keys - return; - } - - if (mPTTMouseButton == 0 && LLAgent::isActionAllowed("speak") && (key == mPTTKey)) - { - bool down = gKeyboard->getKeyDown(mPTTKey); - if (down) - { - inputUserControlState(down); - } - } - -} -void LLVoiceClient::keyUp(KEY key, MASK mask) -{ - if (mPTTMouseButton == 0 && (key == mPTTKey)) - { - bool down = gKeyboard->getKeyDown(mPTTKey); - if (!down) - { - inputUserControlState(down); - } - } -} -void LLVoiceClient::updateMouseState(S32 click, bool down) -{ - if(mPTTMouseButton == click && LLAgent::isActionAllowed("speak")) - { - inputUserControlState(down); - } -} - //------------------------------------------- // nearby speaker accessors @@ -981,7 +923,12 @@ LLSpeakerVolumeStorage::LLSpeakerVolumeStorage() LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage() { - save(); +} + +//virtual +void LLSpeakerVolumeStorage::cleanupSingleton() +{ + save(); } void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume) diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 3d04e1f0db..cf527a4464 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -408,16 +408,10 @@ public: void setUsePTT(bool usePTT); void setPTTIsToggle(bool PTTIsToggle); bool getPTTIsToggle(); - void setPTTKey(std::string &key); - + void updateMicMuteLogic(); - + BOOL lipSyncEnabled(); - - // PTT key triggering - void keyDown(KEY key, MASK mask); - void keyUp(KEY key, MASK mask); - void updateMouseState(S32 click, bool down); boost::signals2::connection MicroChangedCallback(const micro_changed_signal_t::slot_type& cb ) { return mMicroChangedSignal.connect(cb); } @@ -499,9 +493,13 @@ protected: **/ class LLSpeakerVolumeStorage : public LLSingleton<LLSpeakerVolumeStorage> { - LLSINGLETON(LLSpeakerVolumeStorage); + LLSINGLETON_C11(LLSpeakerVolumeStorage); ~LLSpeakerVolumeStorage(); LOG_CLASS(LLSpeakerVolumeStorage); + +protected: + virtual void cleanupSingleton() override; + public: /** diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 42a1cf95a7..f9ffefd4a2 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -274,6 +274,8 @@ static void killGateway() /////////////////////////////////////////////////////////////////////////////////////////////// +bool LLVivoxVoiceClient::sShuttingDown = false; + LLVivoxVoiceClient::LLVivoxVoiceClient() : mSessionTerminateRequested(false), mRelogRequested(false), @@ -381,6 +383,7 @@ LLVivoxVoiceClient::~LLVivoxVoiceClient() { mAvatarNameCacheConnection.disconnect(); } + sShuttingDown = true; } //--------------------------------------------------- @@ -388,7 +391,7 @@ LLVivoxVoiceClient::~LLVivoxVoiceClient() void LLVivoxVoiceClient::init(LLPumpIO *pump) { // constructor will set up LLVoiceClient::getInstance() - LLVivoxVoiceClient::getInstance()->mPump = pump; + mPump = pump; // LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro", // boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); @@ -411,8 +414,12 @@ void LLVivoxVoiceClient::terminate() } else { + mRelogRequested = false; killGateway(); } + + sShuttingDown = true; + mPump = NULL; } //--------------------------------------------------- @@ -660,12 +667,18 @@ void LLVivoxVoiceClient::voiceControlCoro() U32 retry = 0; - while (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) + while (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE && !sShuttingDown) { LL_DEBUGS("Voice") << "Suspending voiceControlCoro() momentarily for teleport. Tuning: " << mTuningMode << ". Relog: " << mRelogRequested << LL_ENDL; llcoro::suspendUntilTimeout(1.0); } + if (sShuttingDown) + { + mIsCoroutineActive = false; + return; + } + do { bool success = startAndConnectSession(); @@ -691,7 +704,7 @@ void LLVivoxVoiceClient::voiceControlCoro() << "disconnected" << " RelogRequested=" << mRelogRequested << LL_ENDL; - if (mRelogRequested) + if (mRelogRequested && !sShuttingDown) { if (!success) { @@ -706,14 +719,14 @@ void LLVivoxVoiceClient::voiceControlCoro() LL_INFOS("Voice") << "will attempt to reconnect to voice" << LL_ENDL; } - while (isGatewayRunning() || gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) + while (isGatewayRunning() || (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE && !sShuttingDown)) { LL_INFOS("Voice") << "waiting for SLVoice to exit" << LL_ENDL; llcoro::suspendUntilTimeout(1.0); } } } - while (mVoiceEnabled && mRelogRequested); + while (mVoiceEnabled && mRelogRequested && !sShuttingDown); mIsCoroutineActive = false; LL_INFOS("Voice") << "exiting" << LL_ENDL; } @@ -758,7 +771,7 @@ bool LLVivoxVoiceClient::endAndDisconnectSession() bool LLVivoxVoiceClient::callbackEndDaemon(const LLSD& data) { - if (!LLAppViewer::isExiting() && mVoiceEnabled) + if (!sShuttingDown && mVoiceEnabled) { LL_WARNS("Voice") << "SLVoice terminated " << ll_stream_notation_sd(data) << LL_ENDL; terminateAudioSession(false); @@ -915,7 +928,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL; LLVoiceVivoxStats::getInstance()->reset(); - while (!mConnected) + while (!mConnected && !sShuttingDown) { LLVoiceVivoxStats::getInstance()->connectionAttemptStart(); LL_DEBUGS("Voice") << "Attempting to connect to vivox daemon: " << mDaemonHost << LL_ENDL; @@ -934,12 +947,22 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() } //--------------------------------------------------------------------- + if (sShuttingDown && !mConnected) + { + return false; + } + llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - while (!mPump) - { // Can't do this until we have the pump available. + while (!mPump && !sShuttingDown) + { // Can't use the pump until we have it available. llcoro::suspend(); } + + if (sShuttingDown) + { + return false; + } // MBW -- Note to self: pumps and pipes examples in // indra/test/io.cpp @@ -952,8 +975,10 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); + mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); + //--------------------------------------------------------------------- llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); @@ -970,12 +995,17 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() { LL_INFOS("Voice") << "Provisioning voice account." << LL_ENDL; - while (!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) + while ((!gAgent.getRegion() || !gAgent.getRegion()->capabilitiesReceived()) && !sShuttingDown) { LL_DEBUGS("Voice") << "no capabilities for voice provisioning; waiting " << LL_ENDL; // *TODO* Pump a message for wake up. llcoro::suspend(); } + + if (sShuttingDown) + { + return false; + } std::string url = gAgent.getRegionCapability("ProvisionVoiceAccountRequest"); @@ -1014,10 +1044,15 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() { provisioned = true; } - } while (!provisioned && retryCount <= PROVISION_RETRY_MAX); + } while (!provisioned && retryCount <= PROVISION_RETRY_MAX && !sShuttingDown); + + if (sShuttingDown && !provisioned) + { + return false; + } LLVoiceVivoxStats::getInstance()->provisionAttemptEnd(provisioned); - if (! provisioned ) + if (!provisioned) { LL_WARNS("Voice") << "Could not access voice provision cap after " << retryCount << " attempts." << LL_ENDL; return false; @@ -1058,6 +1093,11 @@ bool LLVivoxVoiceClient::establishVoiceConnection() LL_WARNS("Voice") << "cannot establish connection; enabled "<<mVoiceEnabled<<" initialized "<<mIsInitialized<<LL_ENDL; return false; } + + if (sShuttingDown) + { + return false; + } LLSD result; bool connected(false); @@ -1078,7 +1118,7 @@ bool LLVivoxVoiceClient::establishVoiceConnection() connected = LLSD::Boolean(result["connector"]); if (!connected) { - if (result.has("retry") && ++retries <= CONNECT_RETRY_MAX) + if (result.has("retry") && ++retries <= CONNECT_RETRY_MAX && !sShuttingDown) { F32 timeout = LLSD::Real(result["retry"]); timeout *= retries; @@ -1106,7 +1146,7 @@ bool LLVivoxVoiceClient::establishVoiceConnection() LL_DEBUGS("Voice") << (connected ? "" : "not ") << "connected, " << (giving_up ? "" : "not ") << "giving up" << LL_ENDL; - } while (!connected && !giving_up); + } while (!connected && !giving_up && !sShuttingDown); if (giving_up) { @@ -1193,7 +1233,7 @@ bool LLVivoxVoiceClient::loginToVivox() { std::string loginresp = result["login"]; - if ((loginresp == "retry") || (loginresp == "timeout")) + if (((loginresp == "retry") || (loginresp == "timeout")) && !sShuttingDown) { LL_WARNS("Voice") << "login failed with status '" << loginresp << "' " << " count " << loginRetryCount << "/" << LOGIN_RETRY_MAX @@ -1235,9 +1275,14 @@ bool LLVivoxVoiceClient::loginToVivox() { account_login = true; } + else if (sShuttingDown) + { + mIsLoggingIn = false; + return false; + } } - } while (!response_ok || !account_login); + } while ((!response_ok || !account_login) && !sShuttingDown); mRelogRequested = false; mIsLoggedIn = true; @@ -1690,12 +1735,12 @@ bool LLVivoxVoiceClient::terminateAudioSession(bool wait) // the region chat. mSessionTerminateRequested = false; - bool status=((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !LLApp::isExiting()); + bool status=((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !sShuttingDown); LL_DEBUGS("Voice") << "exiting" << " VoiceEnabled " << mVoiceEnabled << " IsInitialized " << mIsInitialized << " RelogRequested " << mRelogRequested - << " AppExiting " << LLApp::isExiting() + << " ShuttingDown " << (sShuttingDown ? "TRUE" : "FALSE") << " returning " << status << LL_ENDL; return status; @@ -1712,6 +1757,12 @@ bool LLVivoxVoiceClient::waitForChannel() return false; } + if (sShuttingDown) + { + logoutOfVivox(true); + return false; + } + if (LLVoiceClient::instance().getVoiceEffectEnabled()) { retrieveVoiceFonts(); @@ -1733,6 +1784,12 @@ bool LLVivoxVoiceClient::waitForChannel() mIsProcessingChannels = true; llcoro::suspend(); + if (sShuttingDown) + { + mRelogRequested = false; + break; + } + if (mTuningMode) { performMicTuning(); @@ -1777,7 +1834,14 @@ bool LLVivoxVoiceClient::waitForChannel() { llcoro::suspendUntilTimeout(1.0); } - } while (mVoiceEnabled && !mRelogRequested); + + if (sShuttingDown) + { + mRelogRequested = false; + break; + } + + } while (mVoiceEnabled && !mRelogRequested && !sShuttingDown); LL_DEBUGS("Voice") << "leaving inner waitForChannel loop" @@ -1799,14 +1863,14 @@ bool LLVivoxVoiceClient::waitForChannel() return false; } } - } while (mVoiceEnabled && mRelogRequested && isGatewayRunning()); + } while (mVoiceEnabled && mRelogRequested && isGatewayRunning() && !sShuttingDown); LL_DEBUGS("Voice") << "exiting" << " RelogRequested=" << mRelogRequested << " VoiceEnabled=" << mVoiceEnabled << LL_ENDL; - return true; + return !sShuttingDown; } bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 210c726452..699c85066b 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -912,6 +912,8 @@ private: bool mIsProcessingChannels; bool mIsCoroutineActive; + static bool sShuttingDown; // corutines can last longer than vivox so we need a static variable as a shutdown flag + LLEventMailDrop mVivoxPump; }; diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index 8e46ccd555..41099cb570 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -45,6 +45,8 @@ #include "llviewertexturelist.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" +#include "llvolumemgr.h" +#include "llvovolume.h" #include "llworld.h" #include "noise.h" #include "pipeline.h" @@ -85,6 +87,9 @@ LLVOTree::LLVOTree(const LLUUID &id, const LLPCode pcode, LLViewerRegion *region mFrameCount = 0; mWind = mRegionp->mWind.getVelocity(getPositionRegion()); mTrunkLOD = 0; + + // if assert triggers, idleUpdate() needs to be revised and adjusted to new LOD levels + llassert(sMAX_NUM_TREE_LOD_LEVELS == LLVolumeLODGroup::NUM_LODS); } @@ -347,8 +352,11 @@ void LLVOTree::idleUpdate(LLAgent &agent, const F64 &time) return; } - S32 trunk_LOD = sMAX_NUM_TREE_LOD_LEVELS ; + S32 trunk_LOD = sMAX_NUM_TREE_LOD_LEVELS ; // disabled F32 app_angle = getAppAngle()*LLVOTree::sTreeFactor; + F32 distance = mDrawable->mDistanceWRTCamera * LLVOVolume::sDistanceFactor * (F_PI / 3.f); + F32 diameter = getScale().length(); // trees have very broken scale, but length rougtly outlines proper diameter + F32 sz = mBillboardScale * mBillboardRatio * diameter; for (S32 j = 0; j < sMAX_NUM_TREE_LOD_LEVELS; j++) { @@ -357,7 +365,14 @@ void LLVOTree::idleUpdate(LLAgent &agent, const F64 &time) trunk_LOD = j; break; } - } + } + + F32 tan_angle = (LLVOTree::sTreeFactor * 64 * sz) / distance; + S32 cur_detail = LLVolumeLODGroup::getDetailFromTan(ll_round(tan_angle, 0.01f)); // larger value, better quality + + // for trunk_LOD lower value means better quality, but both trunk_LOD and cur_detail have 4 levels + trunk_LOD = llmax(trunk_LOD, LLVolumeLODGroup::NUM_LODS - cur_detail - 1); + trunk_LOD = llmin(trunk_LOD, sMAX_NUM_TREE_LOD_LEVELS); if (mReferenceBuffer.isNull()) { @@ -408,8 +423,9 @@ void LLVOTree::setPixelAreaAndAngle(LLAgent &agent) LLVector3 lookAt = center - viewer_pos_agent; F32 dist = lookAt.normVec() ; F32 cos_angle_to_view_dir = lookAt * LLViewerCamera::getInstance()->getXAxis() ; - - F32 range = dist - getMinScale()/2; + F32 radius = getScale().length()*0.5f; + F32 range = dist - radius; + if (range < F_ALMOST_ZERO || isHUDAttachment()) // range == zero { mAppAngle = 180.f; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 98eb2d3cdc..2d1a882a7e 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -235,7 +235,8 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re mLastFetchedMediaVersion = -1; memset(&mIndexInTex, 0, sizeof(S32) * LLRender::NUM_VOLUME_TEXTURE_CHANNELS); mMDCImplCount = 0; - mLastRiggingInfoLOD = -1; + mLastRiggingInfoLOD = -1; + mResetDebugText = false; } LLVOVolume::~LLVOVolume() @@ -694,6 +695,11 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) LL_RECORD_BLOCK_TIME(FTM_VOLUME_TEXTURES); // Update the pixel area of all faces + if (mDrawable.isNull()) + { + return; + } + if(!forced) { if(!isVisible()) @@ -1391,6 +1397,15 @@ BOOL LLVOVolume::calcLOD() { std::string debug_object_text = get_debug_object_lod_text(this); setDebugText(debug_object_text); + mResetDebugText = true; + } + } + else + { + if (mResetDebugText) + { + restoreHudText(); + mResetDebugText = false; } } @@ -2280,243 +2295,11 @@ S32 LLVOVolume::setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID) return res; } -bool LLVOVolume::notifyAboutCreatingTexture(LLViewerTexture *texture) -{ //Ok, here we have confirmation about texture creation, check our wait-list - //and make changes, or return false - - std::pair<mmap_UUID_MAP_t::iterator, mmap_UUID_MAP_t::iterator> range = mWaitingTextureInfo.equal_range(texture->getID()); - - typedef std::map<U8, LLMaterialPtr> map_te_material; - map_te_material new_material; - - for(mmap_UUID_MAP_t::iterator range_it = range.first; range_it != range.second; ++range_it) - { - LLMaterialPtr cur_material = getTEMaterialParams(range_it->second.te); - - //here we just interesting in DIFFUSE_MAP only! - if(NULL != cur_material.get() && LLRender::DIFFUSE_MAP == range_it->second.map && GL_RGBA != texture->getPrimaryFormat()) - { //ok let's check the diffuse mode - switch(cur_material->getDiffuseAlphaMode()) - { - case LLMaterial::DIFFUSE_ALPHA_MODE_BLEND: - case LLMaterial::DIFFUSE_ALPHA_MODE_EMISSIVE: - case LLMaterial::DIFFUSE_ALPHA_MODE_MASK: - { //uups... we have non 32 bit texture with LLMaterial::DIFFUSE_ALPHA_MODE_* => LLMaterial::DIFFUSE_ALPHA_MODE_NONE - - LLMaterialPtr mat = NULL; - map_te_material::iterator it = new_material.find(range_it->second.te); - if(new_material.end() == it) { - mat = new LLMaterial(cur_material->asLLSD()); - new_material.insert(map_te_material::value_type(range_it->second.te, mat)); - } else { - mat = it->second; - } - - mat->setDiffuseAlphaMode(LLMaterial::DIFFUSE_ALPHA_MODE_NONE); - - } break; - } //switch - } //if - } //for - - //setup new materials - for(map_te_material::const_iterator it = new_material.begin(), end = new_material.end(); it != end; ++it) - { - LLMaterialMgr::getInstance()->put(getID(), it->first, *it->second); - LLViewerObject::setTEMaterialParams(it->first, it->second); - } - - //clear wait-list - mWaitingTextureInfo.erase(range.first, range.second); - - return 0 != new_material.size(); -} - -bool LLVOVolume::notifyAboutMissingAsset(LLViewerTexture *texture) -{ //Ok, here if we wait information about texture and it's missing - //then depending from the texture map (diffuse, normal, or specular) - //make changes in material and confirm it. If not return false. - std::pair<mmap_UUID_MAP_t::iterator, mmap_UUID_MAP_t::iterator> range = mWaitingTextureInfo.equal_range(texture->getID()); - if(range.first == range.second) return false; - - typedef std::map<U8, LLMaterialPtr> map_te_material; - map_te_material new_material; - - for(mmap_UUID_MAP_t::iterator range_it = range.first; range_it != range.second; ++range_it) - { - LLMaterialPtr cur_material = getTEMaterialParams(range_it->second.te); - if (cur_material.isNull()) - continue; - - switch(range_it->second.map) - { - case LLRender::DIFFUSE_MAP: - { - if(LLMaterial::DIFFUSE_ALPHA_MODE_NONE != cur_material->getDiffuseAlphaMode()) - { //missing texture + !LLMaterial::DIFFUSE_ALPHA_MODE_NONE => LLMaterial::DIFFUSE_ALPHA_MODE_NONE - LLMaterialPtr mat = NULL; - map_te_material::iterator it = new_material.find(range_it->second.te); - if(new_material.end() == it) { - mat = new LLMaterial(cur_material->asLLSD()); - new_material.insert(map_te_material::value_type(range_it->second.te, mat)); - } else { - mat = it->second; - } - - mat->setDiffuseAlphaMode(LLMaterial::DIFFUSE_ALPHA_MODE_NONE); - } - } break; - case LLRender::NORMAL_MAP: - { //missing texture => reset material texture id - LLMaterialPtr mat = NULL; - map_te_material::iterator it = new_material.find(range_it->second.te); - if(new_material.end() == it) { - mat = new LLMaterial(cur_material->asLLSD()); - new_material.insert(map_te_material::value_type(range_it->second.te, mat)); - } else { - mat = it->second; - } - - mat->setNormalID(LLUUID::null); - } break; - case LLRender::SPECULAR_MAP: - { //missing texture => reset material texture id - LLMaterialPtr mat = NULL; - map_te_material::iterator it = new_material.find(range_it->second.te); - if(new_material.end() == it) { - mat = new LLMaterial(cur_material->asLLSD()); - new_material.insert(map_te_material::value_type(range_it->second.te, mat)); - } else { - mat = it->second; - } - - mat->setSpecularID(LLUUID::null); - } break; - case LLRender::NUM_TEXTURE_CHANNELS: - //nothing to do, make compiler happy - break; - } //switch - } //for - - //setup new materials - for(map_te_material::const_iterator it = new_material.begin(), end = new_material.end(); it != end; ++it) - { - LLMaterialMgr::getInstance()->setLocalMaterial(getRegion()->getRegionID(), it->second); - LLViewerObject::setTEMaterialParams(it->first, it->second); - } - - //clear wait-list - mWaitingTextureInfo.erase(range.first, range.second); - - return 0 != new_material.size(); -} - S32 LLVOVolume::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams) { - LLMaterialPtr pMaterial = const_cast<LLMaterialPtr&>(pMaterialParams); - - if(pMaterialParams) - { //check all of them according to material settings - - LLViewerTexture *img_diffuse = getTEImage(te); - LLViewerTexture *img_normal = getTENormalMap(te); - LLViewerTexture *img_specular = getTESpecularMap(te); - - llassert(NULL != img_diffuse); - - LLMaterialPtr new_material = NULL; + S32 res = LLViewerObject::setTEMaterialParams(te, pMaterialParams); - //diffuse - if(NULL != img_diffuse) - { //guard - if(0 == img_diffuse->getPrimaryFormat() && !img_diffuse->isMissingAsset()) - { //ok here we don't have information about texture, let's belief and leave material settings - //but we remember this case - mWaitingTextureInfo.insert(mmap_UUID_MAP_t::value_type(img_diffuse->getID(), material_info(LLRender::DIFFUSE_MAP, te))); - } - else - { - bool bSetDiffuseNone = false; - if(img_diffuse->isMissingAsset()) - { - bSetDiffuseNone = true; - } - else - { - switch(pMaterialParams->getDiffuseAlphaMode()) - { - case LLMaterial::DIFFUSE_ALPHA_MODE_BLEND: - case LLMaterial::DIFFUSE_ALPHA_MODE_EMISSIVE: - case LLMaterial::DIFFUSE_ALPHA_MODE_MASK: - { //all of them modes available only for 32 bit textures - LLTextureEntry* tex_entry = getTE(te); - bool bIsBakedImageId = false; - if (tex_entry && LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(tex_entry->getID())) - { - bIsBakedImageId = true; - } - if (GL_RGBA != img_diffuse->getPrimaryFormat() && !bIsBakedImageId) - { - bSetDiffuseNone = true; - } - } break; - } - } //else - - - if(bSetDiffuseNone) - { //upps... we should substitute this material with LLMaterial::DIFFUSE_ALPHA_MODE_NONE - new_material = new LLMaterial(pMaterialParams->asLLSD()); - new_material->setDiffuseAlphaMode(LLMaterial::DIFFUSE_ALPHA_MODE_NONE); - } - } - } - - //normal - if(LLUUID::null != pMaterialParams->getNormalID()) - { - if(img_normal && img_normal->isMissingAsset() && img_normal->getID() == pMaterialParams->getNormalID()) - { - if(!new_material) { - new_material = new LLMaterial(pMaterialParams->asLLSD()); - } - new_material->setNormalID(LLUUID::null); - } - else if(NULL == img_normal || 0 == img_normal->getPrimaryFormat()) - { //ok here we don't have information about texture, let's belief and leave material settings - //but we remember this case - mWaitingTextureInfo.insert(mmap_UUID_MAP_t::value_type(pMaterialParams->getNormalID(), material_info(LLRender::NORMAL_MAP,te))); - } - - } - - - //specular - if(LLUUID::null != pMaterialParams->getSpecularID()) - { - if(img_specular && img_specular->isMissingAsset() && img_specular->getID() == pMaterialParams->getSpecularID()) - { - if(!new_material) { - new_material = new LLMaterial(pMaterialParams->asLLSD()); - } - new_material->setSpecularID(LLUUID::null); - } - else if(NULL == img_specular || 0 == img_specular->getPrimaryFormat()) - { //ok here we don't have information about texture, let's belief and leave material settings - //but we remember this case - mWaitingTextureInfo.insert(mmap_UUID_MAP_t::value_type(pMaterialParams->getSpecularID(), material_info(LLRender::SPECULAR_MAP, te))); - } - } - - if(new_material) { - pMaterial = new_material; - LLMaterialMgr::getInstance()->setLocalMaterial(getRegion()->getRegionID(), pMaterial); - } - } - - S32 res = LLViewerObject::setTEMaterialParams(te, pMaterial); - - LL_DEBUGS("MaterialTEs") << "te " << (S32)te << " material " << ((pMaterial) ? pMaterial->asLLSD() : LLSD("null")) << " res " << res + LL_DEBUGS("MaterialTEs") << "te " << (S32)te << " material " << ((pMaterialParams) ? pMaterialParams->asLLSD() : LLSD("null")) << " res " << res << ( LLSelectMgr::getInstance()->getSelection()->contains(const_cast<LLVOVolume*>(this), te) ? " selected" : " not selected" ) << LL_ENDL; setChanged(ALL_CHANGED); @@ -4583,10 +4366,11 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& LLMaterial* mat = te->getMaterialParams(); if (mat) { - U8 mode = mat->getDiffuseAlphaMode(); + U8 mode = mat->getDiffuseAlphaModeRender(); - if (mode == LLMaterial::DIFFUSE_ALPHA_MODE_EMISSIVE || - mode == LLMaterial::DIFFUSE_ALPHA_MODE_NONE) + if (mode == LLMaterial::DIFFUSE_ALPHA_MODE_EMISSIVE + || mode == LLMaterial::DIFFUSE_ALPHA_MODE_NONE + || (mode == LLMaterial::DIFFUSE_ALPHA_MODE_MASK && mat->getAlphaMaskCutoff() == 0)) { ignore_alpha = true; } @@ -4892,6 +4676,14 @@ U32 LLVOVolume::getPartitionType() const { return LLViewerRegion::PARTITION_HUD; } + if (isAnimatedObject() && getControlAvatar()) + { + return LLViewerRegion::PARTITION_CONTROL_AV; + } + if (isAttachment()) + { + return LLViewerRegion::PARTITION_AVATAR; + } return LLViewerRegion::PARTITION_VOLUME; } @@ -4922,6 +4714,20 @@ LLVolumeGeometryManager() mSlopRatio = 0.25f; } +LLAvatarBridge::LLAvatarBridge(LLDrawable* drawablep, LLViewerRegion* regionp) + : LLVolumeBridge(drawablep, regionp) +{ + mDrawableType = LLPipeline::RENDER_TYPE_AVATAR; + mPartitionType = LLViewerRegion::PARTITION_AVATAR; +} + +LLControlAVBridge::LLControlAVBridge(LLDrawable* drawablep, LLViewerRegion* regionp) + : LLVolumeBridge(drawablep, regionp) +{ + mDrawableType = LLPipeline::RENDER_TYPE_CONTROL_AV; + mPartitionType = LLViewerRegion::PARTITION_CONTROL_AV; +} + bool can_batch_texture(LLFace* facep) { if (facep->getTextureEntry()->getBumpmap()) @@ -5100,7 +4906,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, } - if (index < 255 && idx >= 0) + if (index < FACE_DO_NOT_BATCH_TEXTURES && idx >= 0) { if (mat || draw_vec[idx]->mMaterial) { //can't batch textures when materials are present (yet) @@ -5146,7 +4952,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, draw_vec[idx]->mEnd += facep->getGeomCount(); draw_vec[idx]->mVSize = llmax(draw_vec[idx]->mVSize, facep->getVirtualSize()); - if (index < 255 && index >= draw_vec[idx]->mTextureList.size()) + if (index < FACE_DO_NOT_BATCH_TEXTURES && index >= draw_vec[idx]->mTextureList.size()) { draw_vec[idx]->mTextureList.resize(index+1); draw_vec[idx]->mTextureList[index] = tex; @@ -5206,7 +5012,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, } draw_info->mAlphaMaskCutoff = mat->getAlphaMaskCutoff() * (1.f / 255.f); - draw_info->mDiffuseAlphaMode = mat->getDiffuseAlphaMode(); + draw_info->mDiffuseAlphaMode = mat->getDiffuseAlphaModeRender(); draw_info->mNormalMap = facep->getViewerObject()->getTENormalMap(facep->getTEOffset()); } else @@ -5233,7 +5039,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, draw_info->mDrawMode = LLRender::TRIANGLE_STRIP; } - if (index < 255) + if (index < FACE_DO_NOT_BATCH_TEXTURES) { //initialize texture list for texture batching draw_info->mTextureList.resize(index+1); draw_info->mTextureList[index] = tex; @@ -5266,7 +5072,8 @@ static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj) LLDrawPool* drawpool = face->getPool(); if (drawpool) { - if (drawpool->getType() == LLDrawPool::POOL_AVATAR) + if (drawpool->getType() == LLDrawPool::POOL_AVATAR + || drawpool->getType() == LLDrawPool::POOL_CONTROL_AV) { return (LLDrawPoolAvatar*) drawpool; } @@ -5545,7 +5352,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) //remove face from old pool if it exists LLDrawPool* old_pool = facep->getPool(); - if (old_pool && old_pool->getType() == LLDrawPool::POOL_AVATAR) + if (old_pool + && (old_pool->getType() == LLDrawPool::POOL_AVATAR || old_pool->getType() == LLDrawPool::POOL_CONTROL_AV)) { ((LLDrawPoolAvatar*) old_pool)->removeRiggedFace(facep); } @@ -5566,7 +5374,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) if (mat && LLPipeline::sRenderDeferred) { - U8 alpha_mode = mat->getDiffuseAlphaMode(); + U8 alpha_mode = mat->getDiffuseAlphaModeRender(); bool is_alpha = type == LLDrawPool::POOL_ALPHA && (alpha_mode == LLMaterial::DIFFUSE_ALPHA_MODE_BLEND || @@ -5595,7 +5403,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) else if (mat) { bool is_alpha = type == LLDrawPool::POOL_ALPHA; - U8 mode = mat->getDiffuseAlphaMode(); + U8 mode = mat->getDiffuseAlphaModeRender(); bool can_be_shiny = mode == LLMaterial::DIFFUSE_ALPHA_MODE_NONE || mode == LLMaterial::DIFFUSE_ALPHA_MODE_EMISSIVE; @@ -6365,7 +6173,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace //face has no texture index facep->mDrawInfo = NULL; - facep->setTextureIndex(255); + facep->setTextureIndex(FACE_DO_NOT_BATCH_TEXTURES); if (geom_count + facep->getGeomCount() > max_vertices) { //cut batches on geom count too big @@ -6429,7 +6237,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace facep->setGeomIndex(index_offset); facep->setVertexBuffer(buffer); - if (batch_textures && facep->getTextureIndex() == 255) + if (batch_textures && facep->getTextureIndex() == FACE_DO_NOT_BATCH_TEXTURES) { LL_ERRS() << "Invalid texture index." << LL_ENDL; } @@ -6493,7 +6301,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace bool can_be_shiny = true; if (mat) { - U8 mode = mat->getDiffuseAlphaMode(); + U8 mode = mat->getDiffuseAlphaModeRender(); can_be_shiny = mode == LLMaterial::DIFFUSE_ALPHA_MODE_NONE || mode == LLMaterial::DIFFUSE_ALPHA_MODE_EMISSIVE; } @@ -6515,7 +6323,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace // if (te->getFullbright()) { - if (mat->getDiffuseAlphaMode() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) + if (mat->getDiffuseAlphaModeRender() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) { if (opaque) { @@ -6600,7 +6408,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace } else if (mat) { - U8 mode = mat->getDiffuseAlphaMode(); + U8 mode = mat->getDiffuseAlphaModeRender(); is_alpha = (is_alpha || (mode == LLMaterial::DIFFUSE_ALPHA_MODE_BLEND)); @@ -6699,7 +6507,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace } else if (fullbright || bake_sunlight) { //fullbright - if (mat && mat->getDiffuseAlphaMode() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) + if (mat && mat->getDiffuseAlphaModeRender() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) { registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK); } @@ -6721,7 +6529,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace else { //all around simple llassert(mask & LLVertexBuffer::MAP_NORMAL); - if (mat && mat->getDiffuseAlphaMode() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) + if (mat && mat->getDiffuseAlphaModeRender() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) { //material alpha mask can be respected in non-deferred registerFace(group, facep, LLRenderPass::PASS_ALPHA_MASK); } diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index de00ef494e..97888ed869 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -424,6 +424,8 @@ private: S32 mIndexInTex[LLRender::NUM_VOLUME_TEXTURE_CHANNELS]; S32 mMDCImplCount; + bool mResetDebugText; + LLPointer<LLRiggedVolume> mRiggedVolume; // statics @@ -439,26 +441,6 @@ protected: static S32 sNumLODChanges; friend class LLVolumeImplFlexible; - -public: - bool notifyAboutCreatingTexture(LLViewerTexture *texture); - bool notifyAboutMissingAsset(LLViewerTexture *texture); - -private: - struct material_info - { - LLRender::eTexIndex map; - U8 te; - - material_info(LLRender::eTexIndex map_, U8 te_) - : map(map_) - , te(te_) - {} - }; - - typedef std::multimap<LLUUID, material_info> mmap_UUID_MAP_t; - mmap_UUID_MAP_t mWaitingTextureInfo; - }; #endif // LL_LLVOVOLUME_H diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp index ccda92810e..12def24a0d 100644 --- a/indra/newview/llvowater.cpp +++ b/indra/newview/llvowater.cpp @@ -145,7 +145,7 @@ BOOL LLVOWater::updateGeometry(LLDrawable *drawable) static const unsigned int vertices_per_quad = 4; static const unsigned int indices_per_quad = 6; - const S32 size = gSavedSettings.getBOOL("RenderTransparentWater") && LLGLSLShader::sNoFixedFunction ? 16 : 1; + const S32 size = LLPipeline::sRenderTransparentWater && LLGLSLShader::sNoFixedFunction ? 16 : 1; const S32 num_quads = size * size; face->setSize(vertices_per_quad * num_quads, diff --git a/indra/newview/llvowlsky.cpp b/indra/newview/llvowlsky.cpp index 368a3f2335..d428cb1568 100644 --- a/indra/newview/llvowlsky.cpp +++ b/indra/newview/llvowlsky.cpp @@ -99,6 +99,9 @@ LLDrawable * LLVOWLSky::createDrawable(LLPipeline * pipeline) inline F32 LLVOWLSky::calcPhi(U32 i) { + // Calc: PI/8 * 1-((1-t^4)*(1-t^4)) { 0<t<1 } + // Demos: \pi/8*\left(1-((1-x^{4})*(1-x^{4}))\right)\ \left\{0<x\le1\right\} + // i should range from [0..SKY_STACKS] so t will range from [0.f .. 1.f] F32 t = float(i) / float(getNumStacks()); diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index e7bbee5efd..9acc0f8d2f 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -38,6 +38,7 @@ #include "llgesturemgr.h" #include "lltransutil.h" #include "llviewerattachmenu.h" +#include "llviewermenu.h" #include "llvoavatarself.h" class LLFindOutfitItems : public LLInventoryCollectFunctor @@ -639,6 +640,7 @@ LLWearableItemsList::LLWearableItemsList(const LLWearableItemsList::Params& p) : LLInventoryItemsList(p) { setSortOrder(E_SORT_BY_TYPE_LAYER, false); + mMenuWearableType = LLWearableType::WT_NONE; mIsStandalone = p.standalone; if (mIsStandalone) { @@ -730,10 +732,15 @@ void LLWearableItemsList::onRightClick(S32 x, S32 y) getSelectedUUIDs(selected_uuids); if (selected_uuids.empty()) { - return; + if ((mMenuWearableType != LLWearableType::WT_NONE) && (size() == 0)) + { + ContextMenu::instance().show(this, mMenuWearableType, x, y); + } + } + else + { + ContextMenu::instance().show(this, selected_uuids, x, y); } - - ContextMenu::instance().show(this, selected_uuids, x, y); } void LLWearableItemsList::setSortOrder(ESortOrder sort_order, bool sort_now) @@ -784,6 +791,46 @@ void LLWearableItemsList::ContextMenu::show(LLView* spawning_view, const uuid_ve mParent = NULL; // to avoid dereferencing an invalid pointer } +void LLWearableItemsList::ContextMenu::show(LLView* spawning_view, LLWearableType::EType w_type, S32 x, S32 y) +{ + mParent = dynamic_cast<LLWearableItemsList*>(spawning_view); + LLContextMenu* menup = mMenuHandle.get(); + if (menup) + { + //preventing parent (menu holder) from deleting already "dead" context menus on exit + LLView* parent = menup->getParent(); + if (parent) + { + parent->removeChild(menup); + } + delete menup; + mUUIDs.clear(); + } + + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("Wearable.CreateNew", boost::bind(createNewWearableByType, w_type)); + menup = createFromFile("menu_wearable_list_item.xml"); + if (!menup) + { + LL_WARNS() << "Context menu creation failed" << LL_ENDL; + return; + } + setMenuItemVisible(menup, "create_new", true); + setMenuItemEnabled(menup, "create_new", true); + setMenuItemVisible(menup, "wearable_attach_to", false); + setMenuItemVisible(menup, "wearable_attach_to_hud", false); + + std::string new_label = LLTrans::getString("create_new_" + LLWearableType::getTypeName(w_type)); + LLMenuItemGL* menu_item = menup->getChild<LLMenuItemGL>("create_new"); + menu_item->setLabel(new_label); + + mMenuHandle = menup->getHandle(); + menup->show(x, y); + LLMenuGL::showPopup(spawning_view, menup, x, y); + + mParent = NULL; // to avoid dereferencing an invalid pointer +} + // virtual LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() { @@ -794,7 +841,7 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() // Register handlers common for all wearable types. registrar.add("Wearable.Wear", boost::bind(wear_multiple, ids, true)); registrar.add("Wearable.Add", boost::bind(wear_multiple, ids, false)); - registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids)); + registrar.add("Wearable.Edit", boost::bind(handle_item_edit, selected_id)); registrar.add("Wearable.CreateNew", boost::bind(createNewWearable, selected_id)); registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id)); registrar.add("Wearable.TakeOffDetach", @@ -809,6 +856,7 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() // Register handlers for attachments. registrar.add("Attachment.Detach", boost::bind(&LLAppearanceMgr::removeItemsFromAvatar, LLAppearanceMgr::getInstance(), ids)); + registrar.add("Attachment.Touch", boost::bind(handle_attachment_touch, selected_id)); registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id)); registrar.add("Object.Attach", boost::bind(LLViewerAttachMenu::attachObjects, ids, _2)); @@ -838,6 +886,7 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu U32 n_already_worn = 0; // number of items worn of same type as selected items U32 n_links = 0; // number of links among the selected items U32 n_editable = 0; // number of editable items among the selected ones + U32 n_touchable = 0; // number of touchable items among the selected ones bool can_be_worn = true; @@ -858,12 +907,17 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu const LLWearableType::EType wearable_type = item->getWearableType(); const bool is_link = item->getIsLinkType(); const bool is_worn = get_is_item_worn(id); - const bool is_editable = gAgentWearables.isWearableModifiable(id); + const bool is_editable = get_is_item_editable(id); + const bool is_touchable = enable_attachment_touch(id); const bool is_already_worn = gAgentWearables.selfHasWearable(wearable_type); if (is_worn) { ++n_worn; } + if (is_touchable) + { + ++n_touchable; + } if (is_editable) { ++n_editable; @@ -893,8 +947,10 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu setMenuItemEnabled(menu, "wear_add", LLAppearanceMgr::instance().canAddWearables(ids)); setMenuItemVisible(menu, "wear_replace", n_worn == 0 && n_already_worn != 0 && can_be_worn); //visible only when one item selected and this item is worn - setMenuItemVisible(menu, "edit", !standalone && mask & (MASK_CLOTHING|MASK_BODYPART) && n_worn == n_items && n_worn == 1); - setMenuItemEnabled(menu, "edit", n_editable == 1 && n_worn == 1 && n_items == 1); + setMenuItemVisible(menu, "touch", !standalone && mask == MASK_ATTACHMENT && n_worn == n_items); + setMenuItemEnabled(menu, "touch", n_touchable && n_worn == 1 && n_items == 1); + setMenuItemVisible(menu, "edit", !standalone && mask & (MASK_CLOTHING|MASK_BODYPART|MASK_ATTACHMENT) && n_worn == n_items); + setMenuItemEnabled(menu, "edit", n_editable && n_worn == 1 && n_items == 1); setMenuItemVisible(menu, "create_new", mask & (MASK_CLOTHING|MASK_BODYPART) && n_items == 1); setMenuItemEnabled(menu, "create_new", LLAppearanceMgr::instance().canAddWearables(ids)); setMenuItemVisible(menu, "show_original", !standalone); @@ -1004,4 +1060,10 @@ void LLWearableItemsList::ContextMenu::createNewWearable(const LLUUID& item_id) LLAgentWearables::createWearable(item->getWearableType(), true); } +// static +void LLWearableItemsList::ContextMenu::createNewWearableByType(LLWearableType::EType type) +{ + LLAgentWearables::createWearable(type, true); +} + // EOF diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index f3182ed163..ba8488b237 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -415,6 +415,8 @@ public: public: /*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y); + void show(LLView* spawning_view, LLWearableType::EType w_type, S32 x, S32 y); + protected: enum { MASK_CLOTHING = 0x01, @@ -431,6 +433,7 @@ public: static void setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val); static void updateMask(U32& mask, LLAssetType::EType at); static void createNewWearable(const LLUUID& item_id); + static void createNewWearableByType(LLWearableType::EType type); LLWearableItemsList* mParent; }; @@ -469,6 +472,8 @@ public: void setSortOrder(ESortOrder sort_order, bool sort_now = true); + void setMenuWearableType(LLWearableType::EType type) { mMenuWearableType = type; } + protected: friend class LLUICtrlFactory; LLWearableItemsList(const LLWearableItemsList::Params& p); @@ -479,6 +484,8 @@ protected: bool mWornIndicationEnabled; ESortOrder mSortOrder; + + LLWearableType::EType mMenuWearableType; }; #endif //LL_LLWEARABLEITEMSLIST_H diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp index 9e4297baaf..aa8c79b0d2 100644 --- a/indra/newview/llwindowlistener.cpp +++ b/indra/newview/llwindowlistener.cpp @@ -37,7 +37,7 @@ #include "llview.h" #include "llviewinject.h" #include "llviewerwindow.h" -#include "llviewerkeyboard.h" +#include "llviewerinput.h" #include "llrootview.h" #include "llsdutil.h" #include "stringize.h" @@ -279,7 +279,7 @@ void LLWindowListener::keyDown(LLSD const & evt) response.setResponse(target_view->getInfo()); gFocusMgr.setKeyboardFocus(target_view); - gViewerKeyboard.handleKey(key, mask, false); + gViewerInput.handleKey(key, mask, false); if(key < 0x80) mWindow->handleUnicodeChar(key, mask); } else @@ -291,7 +291,7 @@ void LLWindowListener::keyDown(LLSD const & evt) } else { - gViewerKeyboard.handleKey(key, mask, false); + gViewerInput.handleKey(key, mask, false); if(key < 0x80) mWindow->handleUnicodeChar(key, mask); } } diff --git a/indra/newview/llwlhandlers.cpp b/indra/newview/llwlhandlers.cpp index 6b8374fd48..730aa3774f 100644 --- a/indra/newview/llwlhandlers.cpp +++ b/indra/newview/llwlhandlers.cpp @@ -51,7 +51,7 @@ bool LLEnvironmentRequest::initiate(LLEnvironment::environment_apply_fn cb) if (!cur_region->capabilitiesReceived()) { LL_INFOS("WindlightCaps") << "Deferring windlight settings request until we've got region caps" << LL_ENDL; - cur_region->setCapabilitiesReceivedCallback([cb](LLUUID region_id) { LLEnvironmentRequest::onRegionCapsReceived(region_id, cb); }); + cur_region->setCapabilitiesReceivedCallback([cb](const LLUUID ®ion_id) { LLEnvironmentRequest::onRegionCapsReceived(region_id, cb); }); return false; } diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 8989bae96a..a1a1db35d6 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -730,11 +730,20 @@ void LLWorld::updateRegions(F32 max_update_time) { //perform some necessary but very light updates. (*iter)->lightIdleUpdate(); - } + } + } + + if(max_time > 0.f) + { + max_time = llmin((F32)(max_update_time - update_timer.getElapsedTimeF32()), max_update_time * 0.25f); + } + if(max_time > 0.f) + { + LLViewerRegion::idleCleanup(max_time); } sample(sNumActiveCachedObjects, mNumOfActiveCachedObjects); - } +} void LLWorld::clearAllVisibleObjects() { @@ -1208,11 +1217,14 @@ public: } }; +static LLTrace::BlockTimerStatHandle FTM_DISABLE_REGION("Disable Region"); // disable the circuit to this simulator // Called in response to "DisableSimulator" message. void process_disable_simulator(LLMessageSystem *mesgsys, void **user_data) -{ - LLHost host = mesgsys->getSender(); +{ + LL_RECORD_BLOCK_TIME(FTM_DISABLE_REGION); + + LLHost host = mesgsys->getSender(); //LL_INFOS() << "Disabling simulator with message from " << host << LL_ENDL; LLWorld::getInstance()->removeRegion(host); diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 8e2539606b..32c8ce66a0 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -240,16 +240,16 @@ void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle, if (!status) { + mImpl->setHttpStatus(status); + LLSD errordata = status.getErrorData(); + mImpl->mErrorCertData = errordata; + if ((status.toULong() != CURLE_SSL_PEER_CERTIFICATE) && (status.toULong() != CURLE_SSL_CACERT)) { // if we have a curl error that's not already been handled - // (a non cert error), then generate the error message as + // (a non cert error), then generate the warning message as // appropriate - mImpl->setHttpStatus(status); - LLSD errordata = status.getErrorData(); - mImpl->mErrorCertData = errordata; - LL_WARNS() << "LLXMLRPCTransaction error " << status.toHex() << ": " << status.toString() << LL_ENDL; LL_WARNS() << "LLXMLRPCTransaction request URI: " diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 01438bfb9f..bd667acb53 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -329,6 +329,7 @@ bool LLPipeline::sDelayVBUpdate = true; bool LLPipeline::sAutoMaskAlphaDeferred = true; bool LLPipeline::sAutoMaskAlphaNonDeferred = false; bool LLPipeline::sDisableShaders = false; +bool LLPipeline::sRenderTransparentWater = true; bool LLPipeline::sRenderBump = true; bool LLPipeline::sBakeSunlight = false; bool LLPipeline::sNoAlpha = false; @@ -1044,30 +1045,31 @@ bool LLPipeline::allocateShadowBuffer(U32 resX, U32 resY) } //static +void LLPipeline::updateRenderTransparentWater() +{ + sRenderTransparentWater = gSavedSettings.getBOOL("RenderTransparentWater"); +} + +//static void LLPipeline::updateRenderBump() { sRenderBump = gSavedSettings.getBOOL("RenderObjectBump"); } -//static +// static void LLPipeline::updateRenderDeferred() { - bool deferred = (bool(RenderDeferred && - LLRenderTarget::sUseFBO && - LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && - LLPipeline::sRenderBump && - RenderAvatarVP && - WindLightUseAtmosShaders)) && - !gUseWireframe; - - sRenderDeferred = deferred; - if (deferred) - { //must render glow when rendering deferred since post effect pass is needed to present any lighting at all - sRenderGlow = true; - } + sRenderDeferred = !gUseWireframe && + RenderDeferred && + LLRenderTarget::sUseFBO && + LLPipeline::sRenderBump && + LLPipeline::sRenderTransparentWater && + RenderAvatarVP && + WindLightUseAtmosShaders && + (bool) LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred"); } -//static +// static void LLPipeline::refreshCachedSettings() { LLPipeline::sAutoMaskAlphaDeferred = gSavedSettings.getBOOL("RenderAutoMaskAlphaDeferred"); @@ -1259,24 +1261,20 @@ void LLPipeline::createGLBuffers() GLuint resX = gViewerWindow->getWorldViewWidthRaw(); GLuint resY = gViewerWindow->getWorldViewHeightRaw(); - - if (LLPipeline::sRenderGlow) - { //screen space glow buffers - const U32 glow_res = llmax(1, - llmin(512, 1 << gSavedSettings.getS32("RenderGlowResolutionPow"))); - for (U32 i = 0; i < 3; i++) - { - mGlow[i].allocate(512,glow_res,GL_RGBA,FALSE,FALSE); - } + // allocate screen space glow buffers + const U32 glow_res = llmax(1, llmin(512, 1 << gSavedSettings.getS32("RenderGlowResolutionPow"))); + for (U32 i = 0; i < 3; i++) + { + mGlow[i].allocate(512, glow_res, GL_RGBA, FALSE, FALSE); + } - allocateScreenBuffer(resX,resY); - mScreenWidth = 0; - mScreenHeight = 0; - } - - if (sRenderDeferred) - { + allocateScreenBuffer(resX, resY); + mScreenWidth = 0; + mScreenHeight = 0; + + if (sRenderDeferred) + { if (!mNoiseMap) { const U32 noiseRes = 128; @@ -1609,6 +1607,7 @@ LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerTexture *tex0) break; case LLDrawPool::POOL_AVATAR: + case LLDrawPool::POOL_CONTROL_AV: break; // Do nothing case LLDrawPool::POOL_SKY: @@ -1678,7 +1677,7 @@ U32 LLPipeline::getPoolTypeFromTE(const LLTextureEntry* te, LLViewerTexture* ima if (alpha && mat) { - switch (mat->getDiffuseAlphaMode()) + switch (mat->getDiffuseAlphaModeRender()) { case 1: alpha = true; // Material's alpha mode is set to blend. Toss it into the alpha draw pool. @@ -1995,7 +1994,7 @@ void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list) drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED); if (done) { - if (drawablep->isRoot()) + if (drawablep->isRoot() && !drawablep->isState(LLDrawable::ACTIVE)) { drawablep->makeStatic(); } @@ -3084,9 +3083,10 @@ void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera) if (vobj) // this test may not be needed, see above { LLVOAvatar* av = vobj->asAvatar(); - if (av && (av->isImpostor() - || av->isInMuteList() - || (LLVOAvatar::AV_DO_NOT_RENDER == av->getVisualMuteSettings() && !av->needsImpostorUpdate()) )) + if (av && + ((!sImpostorRender && av->isImpostor()) //ignore impostor flag during impostor pass + || av->isInMuteList() + || (LLVOAvatar::AOA_JELLYDOLL == av->getOverallAppearance() && !av->needsImpostorUpdate()) )) { return; } @@ -3362,6 +3362,7 @@ static LLTrace::BlockTimerStatHandle FTM_RESET_DRAWORDER("Reset Draw Order"); void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) { if (hasAnyRenderType(LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_CONTROL_AV, LLPipeline::RENDER_TYPE_GROUND, LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::RENDER_TYPE_TREE, @@ -5774,6 +5775,7 @@ void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp ) break; case LLDrawPool::POOL_AVATAR: + case LLDrawPool::POOL_CONTROL_AV: break; // Do nothing case LLDrawPool::POOL_SKY: @@ -5922,6 +5924,7 @@ void LLPipeline::removeFromQuickLookup( LLDrawPool* poolp ) break; case LLDrawPool::POOL_AVATAR: + case LLDrawPool::POOL_CONTROL_AV: break; // Do nothing case LLDrawPool::POOL_SKY: @@ -6059,25 +6062,18 @@ static F32 calc_light_dist(LLVOVolume* light, const LLVector3& cam_pos, F32 max_ { return max_dist; } - F32 radius = light->getLightRadius(); bool selected = light->isSelected(); - LLVector3 dpos = light->getRenderPosition() - cam_pos; - F32 dist2 = dpos.lengthSquared(); - if (!selected && dist2 > (max_dist + radius)*(max_dist + radius)) - { - return max_dist; - } - F32 dist = (F32) sqrt(dist2); - dist *= 1.f / inten; - dist -= radius; if (selected) { - dist -= 10000.f; // selected lights get highest priority + return 0.f; // selected lights get highest priority } + F32 radius = light->getLightRadius(); + F32 dist = dist_vec(light->getRenderPosition(), cam_pos); + dist = llmax(dist - radius, 0.f); if (light->mDrawable.notNull() && light->mDrawable->isState(LLDrawable::ACTIVE)) { // moving lights get a little higher priority (too much causes artifacts) - dist -= light->getLightRadius()*0.25f; + dist = llmax(dist - light->getLightRadius()*0.25f, 0.f); } return dist; } @@ -6096,13 +6092,18 @@ void LLPipeline::calcNearbyLights(LLCamera& camera) // mNearbyLight (and all light_set_t's) are sorted such that // begin() == the closest light and rbegin() == the farthest light const S32 MAX_LOCAL_LIGHTS = 6; -// LLVector3 cam_pos = gAgent.getCameraPositionAgent(); - LLVector3 cam_pos = LLViewerJoystick::getInstance()->getOverrideCamera() ? - camera.getOrigin() : - gAgent.getPositionAgent(); - - F32 max_dist = LIGHT_MAX_RADIUS * 4.f; // ignore enitrely lights > 4 * max light rad + LLVector3 cam_pos = camera.getOrigin(); + F32 max_dist; + if (LLPipeline::sRenderDeferred) + { + max_dist = RenderFarClip; + } + else + { + max_dist = llmin(RenderFarClip, LIGHT_MAX_RADIUS * 4.f); + } + // UPDATE THE EXISTING NEARBY LIGHTS light_set_t cur_nearby_lights; for (light_set_t::iterator iter = mNearbyLights.begin(); @@ -6136,8 +6137,38 @@ void LLPipeline::calcNearbyLights(LLCamera& camera) continue; } - F32 dist = calc_light_dist(volight, cam_pos, max_dist); - cur_nearby_lights.insert(Light(drawable, dist, light->fade)); + F32 dist = calc_light_dist(volight, cam_pos, max_dist); + F32 fade = light->fade; + // actual fade gets decreased/increased by setupHWLights + // light->fade value is 'time'. + // >=0 and light will become visible as value increases + // <0 and light will fade out + if (dist < max_dist) + { + if (fade < 0) + { + // mark light to fade in + // if fade was -LIGHT_FADE_TIME - it was fully invisible + // if fade -0 - it was fully visible + // visibility goes up from 0 to LIGHT_FADE_TIME. + fade += LIGHT_FADE_TIME; + } + } + else + { + // mark light to fade out + // visibility goes down from -0 to -LIGHT_FADE_TIME. + if (fade >= LIGHT_FADE_TIME) + { + fade = -0.0001f; // was fully visible + } + else if (fade >= 0) + { + // 0.75 visible light should stay 0.75 visible, but should reverse direction + fade -= LIGHT_FADE_TIME; + } + } + cur_nearby_lights.insert(Light(drawable, dist, fade)); } mNearbyLights = cur_nearby_lights; @@ -6156,17 +6187,23 @@ void LLPipeline::calcNearbyLights(LLCamera& camera) { continue; // no lighting from HUD objects } - F32 dist = calc_light_dist(light, cam_pos, max_dist); - if (dist >= max_dist) + if (!sRenderAttachedLights && light && light->isAttachment()) { continue; } - if (!sRenderAttachedLights && light && light->isAttachment()) + LLVOAvatar * av = light->getAvatar(); + if (av && (av->isTooComplex() || av->isInMuteList())) + { + // avatars that are already in the list will be removed by removeMutedAVsLights + continue; + } + F32 dist = calc_light_dist(light, cam_pos, max_dist); + if (dist >= max_dist) { continue; } new_nearby_lights.insert(Light(drawable, dist, 0.f)); - if (new_nearby_lights.size() > (U32)MAX_LOCAL_LIGHTS) + if (!LLPipeline::sRenderDeferred && new_nearby_lights.size() > (U32)MAX_LOCAL_LIGHTS) { new_nearby_lights.erase(--new_nearby_lights.end()); const Light& last = *new_nearby_lights.rbegin(); @@ -6179,7 +6216,7 @@ void LLPipeline::calcNearbyLights(LLCamera& camera) iter != new_nearby_lights.end(); iter++) { const Light* light = &(*iter); - if (mNearbyLights.size() < (U32)MAX_LOCAL_LIGHTS) + if (LLPipeline::sRenderDeferred || mNearbyLights.size() < (U32)MAX_LOCAL_LIGHTS) { mNearbyLights.insert(*light); ((LLDrawable*) light->drawable)->setState(LLDrawable::NEARBY_LIGHT); @@ -6192,10 +6229,22 @@ void LLPipeline::calcNearbyLights(LLCamera& camera) Light* farthest_light = (const_cast<Light*>(&(*(mNearbyLights.rbegin())))); if (light->dist < farthest_light->dist) { - if (farthest_light->fade >= 0.f) - { - farthest_light->fade = -(gFrameIntervalSeconds.value()); - } + // mark light to fade out + // visibility goes down from -0 to -LIGHT_FADE_TIME. + // + // This is a mess, but for now it needs to be in sync + // with fade code above. Ex: code above detects distance < max, + // sets fade time to positive, this code then detects closer + // lights and sets fade time negative, fully compensating + // for the code above + if (farthest_light->fade >= LIGHT_FADE_TIME) + { + farthest_light->fade = -0.0001f; // was fully visible + } + else if (farthest_light->fade >= 0) + { + farthest_light->fade -= LIGHT_FADE_TIME; + } } else { @@ -6315,12 +6364,6 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) } } - const LLViewerObject *vobj = drawable->getVObj(); - if(vobj && vobj->getAvatar() && vobj->getAvatar()->isInMuteList()) - { - continue; - } - if (drawable->isState(LLDrawable::ACTIVE)) { mLightMovingMask |= (1<<cur_light); @@ -6427,41 +6470,9 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) light->setAmbient(LLColor4::black); light->setSpecular(LLColor4::black); } - if (gAgentAvatarp && - gAgentAvatarp->mSpecialRenderMode == 3) - { - LLColor4 light_color = LLColor4::white; - light_color.mV[3] = 0.0f; - - LLVector3 light_pos(LLViewerCamera::getInstance()->getOrigin()); - LLVector4 light_pos_gl(light_pos, 1.0f); - F32 light_radius = 16.f; - - F32 x = 3.f; - float linatten = x / (light_radius); // % of brightness at radius - - if (LLPipeline::sRenderDeferred) - { - /*light_color.mV[0] = powf(light_color.mV[0], 2.2f); - light_color.mV[1] = powf(light_color.mV[1], 2.2f); - light_color.mV[2] = powf(light_color.mV[2], 2.2f);*/ - } - - mHWLightColors[2] = light_color; - LLLightState* light = gGL.getLight(2); - - light->setPosition(light_pos_gl); - light->setDiffuse(light_color); - light->setDiffuseB(light_color * 0.25f); - light->setAmbient(LLColor4::black); - light->setSpecular(LLColor4::black); - light->setQuadraticAttenuation(0.f); - light->setConstantAttenuation(0.f); - light->setLinearAttenuation(linatten); - light->setSpotExponent(0.f); - light->setSpotCutoff(180.f); - } + // Bookmark comment to allow searching for mSpecialRenderMode == 3 (avatar edit mode), + // prev site of forward (non-deferred) character light injection, removed by SL-13522 09/20 // Init GL state if (!LLGLSLShader::sNoFixedFunction) @@ -6552,7 +6563,7 @@ void LLPipeline::enableLightsDynamic() { gPipeline.enableLightsAvatar(); } - else if (gAgentAvatarp->mSpecialRenderMode >= 1) // anim preview + else if (gAgentAvatarp->mSpecialRenderMode == 2) // anim preview { gPipeline.enableLightsAvatarEdit(LLColor4(0.7f, 0.6f, 0.3f, 1.f)); } @@ -6600,7 +6611,7 @@ void LLPipeline::enableLightsPreview() light->enable(); light->setPosition(light_pos); light->setDiffuse(diffuse0); - light->setAmbient(LLColor4::black); + light->setAmbient(ambient); light->setSpecular(specular0); light->setSpotExponent(0.f); light->setSpotCutoff(180.f); @@ -6611,7 +6622,7 @@ void LLPipeline::enableLightsPreview() light->enable(); light->setPosition(light_pos); light->setDiffuse(diffuse1); - light->setAmbient(LLColor4::black); + light->setAmbient(ambient); light->setSpecular(specular1); light->setSpotExponent(0.f); light->setSpotCutoff(180.f); @@ -6621,7 +6632,7 @@ void LLPipeline::enableLightsPreview() light->enable(); light->setPosition(light_pos); light->setDiffuse(diffuse2); - light->setAmbient(LLColor4::black); + light->setAmbient(ambient); light->setSpecular(specular2); light->setSpotExponent(0.f); light->setSpotCutoff(180.f); @@ -7151,7 +7162,8 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, for (U32 j = 0; j < LLViewerRegion::NUM_PARTITIONS; j++) { if ((j == LLViewerRegion::PARTITION_VOLUME) || - (j == LLViewerRegion::PARTITION_BRIDGE) || + (j == LLViewerRegion::PARTITION_BRIDGE) || + (j == LLViewerRegion::PARTITION_CONTROL_AV) || (j == LLViewerRegion::PARTITION_TERRAIN) || (j == LLViewerRegion::PARTITION_TREE) || (j == LLViewerRegion::PARTITION_GRASS)) // only check these partitions for now @@ -7213,7 +7225,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, { LLViewerRegion* region = *iter; - LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE); + LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_AVATAR); if (part && hasRenderType(part->mDrawableType)) { LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, pick_rigged, face_hit, &position, tex_coord, normal, tangent); @@ -7539,641 +7551,633 @@ void LLPipeline::bindScreenToTexture() static LLTrace::BlockTimerStatHandle FTM_RENDER_BLOOM("Bloom"); -void LLPipeline::renderBloom(bool for_snapshot, F32 zoom_factor, int subfield) +void LLPipeline::renderFinalize() { - if (!(gPipeline.canUseVertexShaders() && - sRenderGlow)) - { - return; - } + LLVertexBuffer::unbind(); + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); - LLVertexBuffer::unbind(); - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); + assertInitialized(); - assertInitialized(); + if (gUseWireframe) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } - if (gUseWireframe) - { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } + LLVector2 tc1(0, 0); + LLVector2 tc2((F32) mScreen.getWidth() * 2, (F32) mScreen.getHeight() * 2); - LLVector2 tc1(0,0); - LLVector2 tc2((F32) mScreen.getWidth()*2, - (F32) mScreen.getHeight()*2); + LL_RECORD_BLOCK_TIME(FTM_RENDER_BLOOM); + gGL.color4f(1, 1, 1, 1); + LLGLDepthTest depth(GL_FALSE); + LLGLDisable blend(GL_BLEND); + LLGLDisable cull(GL_CULL_FACE); - LL_RECORD_BLOCK_TIME(FTM_RENDER_BLOOM); - gGL.color4f(1,1,1,1); - LLGLDepthTest depth(GL_FALSE); - LLGLDisable blend(GL_BLEND); - LLGLDisable cull(GL_CULL_FACE); - - enableLightsFullbright(); + enableLightsFullbright(); - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.pushMatrix(); - gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); - LLGLDisable test(GL_ALPHA_TEST); + LLGLDisable test(GL_ALPHA_TEST); - gGL.setColorMask(true, true); - glClearColor(0,0,0,0); - - { - { - LL_RECORD_BLOCK_TIME(FTM_RENDER_BLOOM_FBO); - mGlow[2].bindTarget(); - mGlow[2].clear(); - } - - gGlowExtractProgram.bind(); - F32 minLum = llmax((F32) RenderGlowMinLuminance, 0.0f); - F32 maxAlpha = RenderGlowMaxExtractAlpha; - F32 warmthAmount = RenderGlowWarmthAmount; - LLVector3 lumWeights = RenderGlowLumWeights; - LLVector3 warmthWeights = RenderGlowWarmthWeights; - - - gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_MIN_LUMINANCE, minLum); - gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_MAX_EXTRACT_ALPHA, maxAlpha); - gGlowExtractProgram.uniform3f(LLShaderMgr::GLOW_LUM_WEIGHTS, lumWeights.mV[0], lumWeights.mV[1], lumWeights.mV[2]); - gGlowExtractProgram.uniform3f(LLShaderMgr::GLOW_WARMTH_WEIGHTS, warmthWeights.mV[0], warmthWeights.mV[1], warmthWeights.mV[2]); - gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_WARMTH_AMOUNT, warmthAmount); - LLGLEnable blend_on(GL_BLEND); - LLGLEnable test(GL_ALPHA_TEST); - - gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); - - mScreen.bindTexture(0, 0, LLTexUnit::TFO_POINT); - - gGL.color4f(1,1,1,1); - gPipeline.enableLightsFullbright(); - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); - - gGL.getTexUnit(0)->unbind(mScreen.getUsage()); + gGL.setColorMask(true, true); + glClearColor(0, 0, 0, 0); - mGlow[2].flush(); - } + if (sRenderGlow) + { + { + LL_RECORD_BLOCK_TIME(FTM_RENDER_BLOOM_FBO); + mGlow[2].bindTarget(); + mGlow[2].clear(); + } - tc1.setVec(0,0); - tc2.setVec(2,2); + gGlowExtractProgram.bind(); + F32 minLum = llmax((F32) RenderGlowMinLuminance, 0.0f); + F32 maxAlpha = RenderGlowMaxExtractAlpha; + F32 warmthAmount = RenderGlowWarmthAmount; + LLVector3 lumWeights = RenderGlowLumWeights; + LLVector3 warmthWeights = RenderGlowWarmthWeights; + + gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_MIN_LUMINANCE, minLum); + gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_MAX_EXTRACT_ALPHA, maxAlpha); + gGlowExtractProgram.uniform3f(LLShaderMgr::GLOW_LUM_WEIGHTS, lumWeights.mV[0], lumWeights.mV[1], + lumWeights.mV[2]); + gGlowExtractProgram.uniform3f(LLShaderMgr::GLOW_WARMTH_WEIGHTS, warmthWeights.mV[0], warmthWeights.mV[1], + warmthWeights.mV[2]); + gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_WARMTH_AMOUNT, warmthAmount); + + { + LLGLEnable blend_on(GL_BLEND); + LLGLEnable test(GL_ALPHA_TEST); - // power of two between 1 and 1024 - U32 glowResPow = RenderGlowResolutionPow; - const U32 glow_res = llmax(1, - llmin(1024, 1 << glowResPow)); + gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); - S32 kernel = RenderGlowIterations*2; - F32 delta = RenderGlowWidth / glow_res; - // Use half the glow width if we have the res set to less than 9 so that it looks - // almost the same in either case. - if (glowResPow < 9) - { - delta *= 0.5f; - } - F32 strength = RenderGlowStrength; + mScreen.bindTexture(0, 0, LLTexUnit::TFO_POINT); - gGlowProgram.bind(); - gGlowProgram.uniform1f(LLShaderMgr::GLOW_STRENGTH, strength); + gGL.color4f(1, 1, 1, 1); + gPipeline.enableLightsFullbright(); + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1, -1); - for (S32 i = 0; i < kernel; i++) - { - { - LL_RECORD_BLOCK_TIME(FTM_RENDER_BLOOM_FBO); - mGlow[i%2].bindTarget(); - mGlow[i%2].clear(); - } - - if (i == 0) - { - gGL.getTexUnit(0)->bind(&mGlow[2]); - } - else - { - gGL.getTexUnit(0)->bind(&mGlow[(i-1)%2]); - } + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1, 3); - if (i%2 == 0) - { - gGlowProgram.uniform2f(LLShaderMgr::GLOW_DELTA, delta, 0); - } - else - { - gGlowProgram.uniform2f(LLShaderMgr::GLOW_DELTA, 0, delta); - } + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3, -1); - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); - - mGlow[i%2].flush(); - } + gGL.end(); - gGlowProgram.unbind(); + gGL.getTexUnit(0)->unbind(mScreen.getUsage()); - /*if (LLRenderTarget::sUseFBO) - { - LL_RECORD_BLOCK_TIME(FTM_RENDER_BLOOM_FBO); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - }*/ + mGlow[2].flush(); - gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; - gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; - gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); - gGLViewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight(); - glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); + tc1.setVec(0, 0); + tc2.setVec(2, 2); + } - tc2.setVec((F32) mScreen.getWidth(), - (F32) mScreen.getHeight()); + // power of two between 1 and 1024 + U32 glowResPow = RenderGlowResolutionPow; + const U32 glow_res = llmax(1, llmin(1024, 1 << glowResPow)); - gGL.flush(); - - LLVertexBuffer::unbind(); + S32 kernel = RenderGlowIterations * 2; + F32 delta = RenderGlowWidth / glow_res; + // Use half the glow width if we have the res set to less than 9 so that it looks + // almost the same in either case. + if (glowResPow < 9) + { + delta *= 0.5f; + } + F32 strength = RenderGlowStrength; - if (LLPipeline::sRenderDeferred) - { + gGlowProgram.bind(); + gGlowProgram.uniform1f(LLShaderMgr::GLOW_STRENGTH, strength); - bool dof_enabled = !LLViewerCamera::getInstance()->cameraUnderWater() && - (RenderDepthOfFieldInEditMode || !LLToolMgr::getInstance()->inBuildMode()) && - RenderDepthOfField; + for (S32 i = 0; i < kernel; i++) + { + { + LL_RECORD_BLOCK_TIME(FTM_RENDER_BLOOM_FBO); + mGlow[i % 2].bindTarget(); + mGlow[i % 2].clear(); + } + if (i == 0) + { + gGL.getTexUnit(0)->bind(&mGlow[2]); + } + else + { + gGL.getTexUnit(0)->bind(&mGlow[(i - 1) % 2]); + } - bool multisample = RenderFSAASamples > 1 && mFXAABuffer.isComplete(); + if (i % 2 == 0) + { + gGlowProgram.uniform2f(LLShaderMgr::GLOW_DELTA, delta, 0); + } + else + { + gGlowProgram.uniform2f(LLShaderMgr::GLOW_DELTA, 0, delta); + } - gViewerWindow->setup3DViewport(); - - if (dof_enabled) - { - LLGLSLShader* shader = &gDeferredPostProgram; - LLGLDisable blend(GL_BLEND); + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1, -1); - //depth of field focal plane calculations - static F32 current_distance = 16.f; - static F32 start_distance = 16.f; - static F32 transition_time = 1.f; + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1, 3); - LLVector3 focus_point; + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3, -1); - LLViewerObject* obj = LLViewerMediaFocus::getInstance()->getFocusedObject(); - if (obj && obj->mDrawable && obj->isSelected()) - { //focus on selected media object - S32 face_idx = LLViewerMediaFocus::getInstance()->getFocusedFace(); - if (obj && obj->mDrawable) - { - LLFace* face = obj->mDrawable->getFace(face_idx); - if (face) - { - focus_point = face->getPositionAgent(); - } - } - } - - if (focus_point.isExactlyZero()) - { - if (LLViewerJoystick::getInstance()->getOverrideCamera()) - { //focus on point under cursor - focus_point.set(gDebugRaycastIntersection.getF32ptr()); - } - else if (gAgentCamera.cameraMouselook()) - { //focus on point under mouselook crosshairs - LLVector4a result; - result.clear(); + gGL.end(); - gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, - NULL, - &result); + mGlow[i % 2].flush(); + } - focus_point.set(result.getF32ptr()); - } - else - { - //focus on alt-zoom target - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - focus_point = LLVector3(gAgentCamera.getFocusGlobal()-region->getOriginGlobal()); - } - } - } + gGlowProgram.unbind(); + } + else // !sRenderGlow, skip the glow ping-pong and just clear the result target + { + mGlow[1].bindTarget(); + mGlow[1].clear(); + mGlow[1].flush(); + } - LLVector3 eye = LLViewerCamera::getInstance()->getOrigin(); - F32 target_distance = 16.f; - if (!focus_point.isExactlyZero()) - { - target_distance = LLViewerCamera::getInstance()->getAtAxis() * (focus_point-eye); - } + gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; + gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; + gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); + gGLViewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight(); + glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); - if (transition_time >= 1.f && - fabsf(current_distance-target_distance)/current_distance > 0.01f) - { //large shift happened, interpolate smoothly to new target distance - transition_time = 0.f; - start_distance = current_distance; - } - else if (transition_time < 1.f) - { //currently in a transition, continue interpolating - transition_time += 1.f/CameraFocusTransitionTime*gFrameIntervalSeconds.value(); - transition_time = llmin(transition_time, 1.f); + tc2.setVec((F32) mScreen.getWidth(), (F32) mScreen.getHeight()); - F32 t = cosf(transition_time*F_PI+F_PI)*0.5f+0.5f; - current_distance = start_distance + (target_distance-start_distance)*t; - } - else - { //small or no change, just snap to target distance - current_distance = target_distance; - } + gGL.flush(); - //convert to mm - F32 subject_distance = current_distance*1000.f; - F32 fnumber = CameraFNumber; - F32 default_focal_length = CameraFocalLength; + LLVertexBuffer::unbind(); - F32 fov = LLViewerCamera::getInstance()->getView(); - - const F32 default_fov = CameraFieldOfView * F_PI/180.f; - - //F32 aspect_ratio = (F32) mScreen.getWidth()/(F32)mScreen.getHeight(); - - F32 dv = 2.f*default_focal_length * tanf(default_fov/2.f); + if (LLPipeline::sRenderDeferred) + { - F32 focal_length = dv/(2*tanf(fov/2.f)); - - //F32 tan_pixel_angle = tanf(LLDrawable::sCurPixelAngle); - - // from wikipedia -- c = |s2-s1|/s2 * f^2/(N(S1-f)) - // where N = fnumber - // s2 = dot distance - // s1 = subject distance - // f = focal length - // + bool dof_enabled = !LLViewerCamera::getInstance()->cameraUnderWater() && + (RenderDepthOfFieldInEditMode || !LLToolMgr::getInstance()->inBuildMode()) && + RenderDepthOfField; - F32 blur_constant = focal_length*focal_length/(fnumber*(subject_distance-focal_length)); - blur_constant /= 1000.f; //convert to meters for shader - F32 magnification = focal_length/(subject_distance-focal_length); + bool multisample = RenderFSAASamples > 1 && mFXAABuffer.isComplete(); - { //build diffuse+bloom+CoF - mDeferredLight.bindTarget(); - shader = &gDeferredCoFProgram; + gViewerWindow->setup3DViewport(); - bindDeferredShader(*shader); + if (dof_enabled) + { + LLGLSLShader *shader = &gDeferredPostProgram; + LLGLDisable blend(GL_BLEND); - S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mScreen.getUsage()); - if (channel > -1) - { - mScreen.bindTexture(0, channel); - } + // depth of field focal plane calculations + static F32 current_distance = 16.f; + static F32 start_distance = 16.f; + static F32 transition_time = 1.f; - shader->uniform1f(LLShaderMgr::DOF_FOCAL_DISTANCE, -subject_distance/1000.f); - shader->uniform1f(LLShaderMgr::DOF_BLUR_CONSTANT, blur_constant); - shader->uniform1f(LLShaderMgr::DOF_TAN_PIXEL_ANGLE, tanf(1.f/LLDrawable::sCurPixelAngle)); - shader->uniform1f(LLShaderMgr::DOF_MAGNIFICATION, magnification); - shader->uniform1f(LLShaderMgr::DOF_MAX_COF, CameraMaxCoF); - shader->uniform1f(LLShaderMgr::DOF_RES_SCALE, CameraDoFResScale); + LLVector3 focus_point; - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); + LLViewerObject *obj = LLViewerMediaFocus::getInstance()->getFocusedObject(); + if (obj && obj->mDrawable && obj->isSelected()) + { // focus on selected media object + S32 face_idx = LLViewerMediaFocus::getInstance()->getFocusedFace(); + if (obj && obj->mDrawable) + { + LLFace *face = obj->mDrawable->getFace(face_idx); + if (face) + { + focus_point = face->getPositionAgent(); + } + } + } - unbindDeferredShader(*shader); - mDeferredLight.flush(); - } + if (focus_point.isExactlyZero()) + { + if (LLViewerJoystick::getInstance()->getOverrideCamera()) + { // focus on point under cursor + focus_point.set(gDebugRaycastIntersection.getF32ptr()); + } + else if (gAgentCamera.cameraMouselook()) + { // focus on point under mouselook crosshairs + LLVector4a result; + result.clear(); - U32 dof_width = (U32) (mScreen.getWidth()*CameraDoFResScale); - U32 dof_height = (U32) (mScreen.getHeight()*CameraDoFResScale); - - { //perform DoF sampling at half-res (preserve alpha channel) - mScreen.bindTarget(); - glViewport(0,0, dof_width, dof_height); - gGL.setColorMask(true, false); + gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, NULL, &result); - shader = &gDeferredPostProgram; - bindDeferredShader(*shader); - S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mDeferredLight.getUsage()); - if (channel > -1) - { - mDeferredLight.bindTexture(0, channel); - } + focus_point.set(result.getF32ptr()); + } + else + { + // focus on alt-zoom target + LLViewerRegion *region = gAgent.getRegion(); + if (region) + { + focus_point = LLVector3(gAgentCamera.getFocusGlobal() - region->getOriginGlobal()); + } + } + } - shader->uniform1f(LLShaderMgr::DOF_MAX_COF, CameraMaxCoF); - shader->uniform1f(LLShaderMgr::DOF_RES_SCALE, CameraDoFResScale); - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); + LLVector3 eye = LLViewerCamera::getInstance()->getOrigin(); + F32 target_distance = 16.f; + if (!focus_point.isExactlyZero()) + { + target_distance = LLViewerCamera::getInstance()->getAtAxis() * (focus_point - eye); + } - unbindDeferredShader(*shader); - mScreen.flush(); - gGL.setColorMask(true, true); - } - - { //combine result based on alpha - if (multisample) - { - mDeferredLight.bindTarget(); - glViewport(0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight()); - } - else - { - gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; - gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; - gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); - gGLViewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight(); - glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); - } + if (transition_time >= 1.f && fabsf(current_distance - target_distance) / current_distance > 0.01f) + { // large shift happened, interpolate smoothly to new target distance + transition_time = 0.f; + start_distance = current_distance; + } + else if (transition_time < 1.f) + { // currently in a transition, continue interpolating + transition_time += 1.f / CameraFocusTransitionTime * gFrameIntervalSeconds.value(); + transition_time = llmin(transition_time, 1.f); - shader = &gDeferredDoFCombineProgram; - bindDeferredShader(*shader); - - S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mScreen.getUsage()); - if (channel > -1) - { - mScreen.bindTexture(0, channel); - } + F32 t = cosf(transition_time * F_PI + F_PI) * 0.5f + 0.5f; + current_distance = start_distance + (target_distance - start_distance) * t; + } + else + { // small or no change, just snap to target distance + current_distance = target_distance; + } - shader->uniform1f(LLShaderMgr::DOF_MAX_COF, CameraMaxCoF); - shader->uniform1f(LLShaderMgr::DOF_RES_SCALE, CameraDoFResScale); - shader->uniform1f(LLShaderMgr::DOF_WIDTH, dof_width-1); - shader->uniform1f(LLShaderMgr::DOF_HEIGHT, dof_height-1); + // convert to mm + F32 subject_distance = current_distance * 1000.f; + F32 fnumber = CameraFNumber; + F32 default_focal_length = CameraFocalLength; - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); + F32 fov = LLViewerCamera::getInstance()->getView(); - unbindDeferredShader(*shader); + const F32 default_fov = CameraFieldOfView * F_PI / 180.f; - if (multisample) - { - mDeferredLight.flush(); - } - } - } - else - { - if (multisample) - { - mDeferredLight.bindTarget(); - } - LLGLSLShader* shader = &gDeferredPostNoDoFProgram; - - bindDeferredShader(*shader); - - S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mScreen.getUsage()); - if (channel > -1) - { - mScreen.bindTexture(0, channel); - } + // F32 aspect_ratio = (F32) mScreen.getWidth()/(F32)mScreen.getHeight(); - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); + F32 dv = 2.f * default_focal_length * tanf(default_fov / 2.f); - unbindDeferredShader(*shader); + F32 focal_length = dv / (2 * tanf(fov / 2.f)); - if (multisample) - { - mDeferredLight.flush(); - } - } + // F32 tan_pixel_angle = tanf(LLDrawable::sCurPixelAngle); - if (multisample) - { - //bake out texture2D with RGBL for FXAA shader - mFXAABuffer.bindTarget(); - - S32 width = mScreen.getWidth(); - S32 height = mScreen.getHeight(); - glViewport(0, 0, width, height); + // from wikipedia -- c = |s2-s1|/s2 * f^2/(N(S1-f)) + // where N = fnumber + // s2 = dot distance + // s1 = subject distance + // f = focal length + // - LLGLSLShader* shader = &gGlowCombineFXAAProgram; + F32 blur_constant = focal_length * focal_length / (fnumber * (subject_distance - focal_length)); + blur_constant /= 1000.f; // convert to meters for shader + F32 magnification = focal_length / (subject_distance - focal_length); - shader->bind(); - shader->uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, width, height); + { // build diffuse+bloom+CoF + mDeferredLight.bindTarget(); + shader = &gDeferredCoFProgram; - S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mDeferredLight.getUsage()); - if (channel > -1) - { - mDeferredLight.bindTexture(0, channel); - } - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.vertex2f(-1,-1); - gGL.vertex2f(-1,3); - gGL.vertex2f(3,-1); - gGL.end(); + bindDeferredShader(*shader); - gGL.flush(); + S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mScreen.getUsage()); + if (channel > -1) + { + mScreen.bindTexture(0, channel); + } - shader->disableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mDeferredLight.getUsage()); - shader->unbind(); - - mFXAABuffer.flush(); + shader->uniform1f(LLShaderMgr::DOF_FOCAL_DISTANCE, -subject_distance / 1000.f); + shader->uniform1f(LLShaderMgr::DOF_BLUR_CONSTANT, blur_constant); + shader->uniform1f(LLShaderMgr::DOF_TAN_PIXEL_ANGLE, tanf(1.f / LLDrawable::sCurPixelAngle)); + shader->uniform1f(LLShaderMgr::DOF_MAGNIFICATION, magnification); + shader->uniform1f(LLShaderMgr::DOF_MAX_COF, CameraMaxCoF); + shader->uniform1f(LLShaderMgr::DOF_RES_SCALE, CameraDoFResScale); - shader = &gFXAAProgram; - shader->bind(); + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1, -1); - channel = shader->enableTexture(LLShaderMgr::DIFFUSE_MAP, mFXAABuffer.getUsage()); - if (channel > -1) - { - mFXAABuffer.bindTexture(0, channel, LLTexUnit::TFO_BILINEAR); - } - - gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; - gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; - gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); - gGLViewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight(); - glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); - - F32 scale_x = (F32) width/mFXAABuffer.getWidth(); - F32 scale_y = (F32) height/mFXAABuffer.getHeight(); - shader->uniform2f(LLShaderMgr::FXAA_TC_SCALE, scale_x, scale_y); - shader->uniform2f(LLShaderMgr::FXAA_RCP_SCREEN_RES, 1.f/width*scale_x, 1.f/height*scale_y); - shader->uniform4f(LLShaderMgr::FXAA_RCP_FRAME_OPT, -0.5f/width*scale_x, -0.5f/height*scale_y, 0.5f/width*scale_x, 0.5f/height*scale_y); - shader->uniform4f(LLShaderMgr::FXAA_RCP_FRAME_OPT2, -2.f/width*scale_x, -2.f/height*scale_y, 2.f/width*scale_x, 2.f/height*scale_y); - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.vertex2f(-1,-1); - gGL.vertex2f(-1,3); - gGL.vertex2f(3,-1); - gGL.end(); + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1, 3); - gGL.flush(); - shader->unbind(); - } - } - else - { - U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1; - LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(mask, 0); - buff->allocateBuffer(3,0,TRUE); + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3, -1); - LLStrider<LLVector3> v; - LLStrider<LLVector2> uv1; - LLStrider<LLVector2> uv2; + gGL.end(); - buff->getVertexStrider(v); - buff->getTexCoord0Strider(uv1); - buff->getTexCoord1Strider(uv2); - - uv1[0] = LLVector2(0, 0); - uv1[1] = LLVector2(0, 2); - uv1[2] = LLVector2(2, 0); - - uv2[0] = LLVector2(0, 0); - uv2[1] = LLVector2(0, tc2.mV[1]*2.f); - uv2[2] = LLVector2(tc2.mV[0]*2.f, 0); - - v[0] = LLVector3(-1,-1,0); - v[1] = LLVector3(-1,3,0); - v[2] = LLVector3(3,-1,0); - - buff->flush(); + unbindDeferredShader(*shader); + mDeferredLight.flush(); + } - LLGLDisable blend(GL_BLEND); + U32 dof_width = (U32)(mScreen.getWidth() * CameraDoFResScale); + U32 dof_height = (U32)(mScreen.getHeight() * CameraDoFResScale); - if (LLGLSLShader::sNoFixedFunction) - { - gGlowCombineProgram.bind(); - } - else - { - //tex unit 0 - gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_COLOR); - //tex unit 1 - gGL.getTexUnit(1)->setTextureColorBlend(LLTexUnit::TBO_ADD, LLTexUnit::TBS_TEX_COLOR, LLTexUnit::TBS_PREV_COLOR); - } - - gGL.getTexUnit(0)->bind(&mGlow[1]); - gGL.getTexUnit(1)->bind(&mScreen); - - LLGLEnable multisample(RenderFSAASamples > 0 ? GL_MULTISAMPLE_ARB : 0); - - buff->setBuffer(mask); - buff->drawArrays(LLRender::TRIANGLE_STRIP, 0, 3); - - if (LLGLSLShader::sNoFixedFunction) - { - gGlowCombineProgram.unbind(); - } - else - { - gGL.getTexUnit(1)->disable(); - gGL.getTexUnit(1)->setTextureBlendType(LLTexUnit::TB_MULT); + { // perform DoF sampling at half-res (preserve alpha channel) + mScreen.bindTarget(); + glViewport(0, 0, dof_width, dof_height); + gGL.setColorMask(true, false); - gGL.getTexUnit(0)->activate(); - gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); - } - - } + shader = &gDeferredPostProgram; + bindDeferredShader(*shader); + S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mDeferredLight.getUsage()); + if (channel > -1) + { + mDeferredLight.bindTexture(0, channel); + } - gGL.setSceneBlendType(LLRender::BT_ALPHA); + shader->uniform1f(LLShaderMgr::DOF_MAX_COF, CameraMaxCoF); + shader->uniform1f(LLShaderMgr::DOF_RES_SCALE, CameraDoFResScale); - if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PHYSICS_SHAPES)) - { - if (LLGLSLShader::sNoFixedFunction) - { - gSplatTextureRectProgram.bind(); - } + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1, -1); - gGL.setColorMask(true, false); + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1, 3); - LLVector2 tc1(0,0); - LLVector2 tc2((F32) gViewerWindow->getWorldViewWidthRaw()*2, - (F32) gViewerWindow->getWorldViewHeightRaw()*2); + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3, -1); - LLGLEnable blend(GL_BLEND); - gGL.color4f(1,1,1,0.75f); + gGL.end(); - gGL.getTexUnit(0)->bind(&mPhysicsDisplay); + unbindDeferredShader(*shader); + mScreen.flush(); + gGL.setColorMask(true, true); + } - gGL.begin(LLRender::TRIANGLES); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); - gGL.flush(); + { // combine result based on alpha + if (multisample) + { + mDeferredLight.bindTarget(); + glViewport(0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight()); + } + else + { + gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; + gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; + gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); + gGLViewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight(); + glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); + } - if (LLGLSLShader::sNoFixedFunction) - { - gSplatTextureRectProgram.unbind(); - } - } + shader = &gDeferredDoFCombineProgram; + bindDeferredShader(*shader); - - if (LLRenderTarget::sUseFBO) - { //copy depth buffer from mScreen to framebuffer - LLRenderTarget::copyContentsToFramebuffer(mScreen, 0, 0, mScreen.getWidth(), mScreen.getHeight(), - 0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); - } - + S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mScreen.getUsage()); + if (channel > -1) + { + mScreen.bindTexture(0, channel); + } - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.popMatrix(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); + shader->uniform1f(LLShaderMgr::DOF_MAX_COF, CameraMaxCoF); + shader->uniform1f(LLShaderMgr::DOF_RES_SCALE, CameraDoFResScale); + shader->uniform1f(LLShaderMgr::DOF_WIDTH, dof_width - 1); + shader->uniform1f(LLShaderMgr::DOF_HEIGHT, dof_height - 1); - LLVertexBuffer::unbind(); + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1, -1); - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1, 3); + + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3, -1); + + gGL.end(); + + unbindDeferredShader(*shader); + + if (multisample) + { + mDeferredLight.flush(); + } + } + } + else + { + if (multisample) + { + mDeferredLight.bindTarget(); + } + LLGLSLShader *shader = &gDeferredPostNoDoFProgram; + + bindDeferredShader(*shader); + + S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mScreen.getUsage()); + if (channel > -1) + { + mScreen.bindTexture(0, channel); + } + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1, -1); + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1, 3); + + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3, -1); + + gGL.end(); + + unbindDeferredShader(*shader); + + if (multisample) + { + mDeferredLight.flush(); + } + } + + if (multisample) + { + // bake out texture2D with RGBL for FXAA shader + mFXAABuffer.bindTarget(); + + S32 width = mScreen.getWidth(); + S32 height = mScreen.getHeight(); + glViewport(0, 0, width, height); + + LLGLSLShader *shader = &gGlowCombineFXAAProgram; + + shader->bind(); + shader->uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, width, height); + + S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mDeferredLight.getUsage()); + if (channel > -1) + { + mDeferredLight.bindTexture(0, channel); + } + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.vertex2f(-1, -1); + gGL.vertex2f(-1, 3); + gGL.vertex2f(3, -1); + gGL.end(); + + gGL.flush(); + + shader->disableTexture(LLShaderMgr::DEFERRED_DIFFUSE, mDeferredLight.getUsage()); + shader->unbind(); + + mFXAABuffer.flush(); + + shader = &gFXAAProgram; + shader->bind(); + + channel = shader->enableTexture(LLShaderMgr::DIFFUSE_MAP, mFXAABuffer.getUsage()); + if (channel > -1) + { + mFXAABuffer.bindTexture(0, channel, LLTexUnit::TFO_BILINEAR); + } + + gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; + gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; + gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); + gGLViewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight(); + glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); + + F32 scale_x = (F32) width / mFXAABuffer.getWidth(); + F32 scale_y = (F32) height / mFXAABuffer.getHeight(); + shader->uniform2f(LLShaderMgr::FXAA_TC_SCALE, scale_x, scale_y); + shader->uniform2f(LLShaderMgr::FXAA_RCP_SCREEN_RES, 1.f / width * scale_x, 1.f / height * scale_y); + shader->uniform4f(LLShaderMgr::FXAA_RCP_FRAME_OPT, -0.5f / width * scale_x, -0.5f / height * scale_y, + 0.5f / width * scale_x, 0.5f / height * scale_y); + shader->uniform4f(LLShaderMgr::FXAA_RCP_FRAME_OPT2, -2.f / width * scale_x, -2.f / height * scale_y, + 2.f / width * scale_x, 2.f / height * scale_y); + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.vertex2f(-1, -1); + gGL.vertex2f(-1, 3); + gGL.vertex2f(3, -1); + gGL.end(); + + gGL.flush(); + shader->unbind(); + } + } + else // not deferred + { + U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1; + LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(mask, 0); + buff->allocateBuffer(3, 0, TRUE); + + LLStrider<LLVector3> v; + LLStrider<LLVector2> uv1; + LLStrider<LLVector2> uv2; + + buff->getVertexStrider(v); + buff->getTexCoord0Strider(uv1); + buff->getTexCoord1Strider(uv2); + + uv1[0] = LLVector2(0, 0); + uv1[1] = LLVector2(0, 2); + uv1[2] = LLVector2(2, 0); + + uv2[0] = LLVector2(0, 0); + uv2[1] = LLVector2(0, tc2.mV[1] * 2.f); + uv2[2] = LLVector2(tc2.mV[0] * 2.f, 0); + + v[0] = LLVector3(-1, -1, 0); + v[1] = LLVector3(-1, 3, 0); + v[2] = LLVector3(3, -1, 0); + + buff->flush(); + + LLGLDisable blend(GL_BLEND); + + if (LLGLSLShader::sNoFixedFunction) + { + gGlowCombineProgram.bind(); + } + else + { + // tex unit 0 + gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_COLOR); + // tex unit 1 + gGL.getTexUnit(1)->setTextureColorBlend(LLTexUnit::TBO_ADD, LLTexUnit::TBS_TEX_COLOR, + LLTexUnit::TBS_PREV_COLOR); + } + + gGL.getTexUnit(0)->bind(&mGlow[1]); + gGL.getTexUnit(1)->bind(&mScreen); + + LLGLEnable multisample(RenderFSAASamples > 0 ? GL_MULTISAMPLE_ARB : 0); + + buff->setBuffer(mask); + buff->drawArrays(LLRender::TRIANGLE_STRIP, 0, 3); + + if (LLGLSLShader::sNoFixedFunction) + { + gGlowCombineProgram.unbind(); + } + else + { + gGL.getTexUnit(1)->disable(); + gGL.getTexUnit(1)->setTextureBlendType(LLTexUnit::TB_MULT); + + gGL.getTexUnit(0)->activate(); + gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); + } + } + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PHYSICS_SHAPES)) + { + if (LLGLSLShader::sNoFixedFunction) + { + gSplatTextureRectProgram.bind(); + } + + gGL.setColorMask(true, false); + + LLVector2 tc1(0, 0); + LLVector2 tc2((F32) gViewerWindow->getWorldViewWidthRaw() * 2, + (F32) gViewerWindow->getWorldViewHeightRaw() * 2); + + LLGLEnable blend(GL_BLEND); + gGL.color4f(1, 1, 1, 0.75f); + + gGL.getTexUnit(0)->bind(&mPhysicsDisplay); + + gGL.begin(LLRender::TRIANGLES); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1, -1); + + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1, 3); + + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3, -1); + + gGL.end(); + gGL.flush(); + + if (LLGLSLShader::sNoFixedFunction) + { + gSplatTextureRectProgram.unbind(); + } + } + + if (LLRenderTarget::sUseFBO) + { // copy depth buffer from mScreen to framebuffer + LLRenderTarget::copyContentsToFramebuffer(mScreen, 0, 0, mScreen.getWidth(), mScreen.getHeight(), 0, 0, + mScreen.getWidth(), mScreen.getHeight(), + GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); + } + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + + LLVertexBuffer::unbind(); + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); } static LLTrace::BlockTimerStatHandle FTM_BIND_DEFERRED("Bind Deferred"); @@ -8453,628 +8457,622 @@ static LLTrace::BlockTimerStatHandle FTM_FULLSCREEN_LIGHTS("Fullscreen Lights"); static LLTrace::BlockTimerStatHandle FTM_PROJECTORS("Projectors"); static LLTrace::BlockTimerStatHandle FTM_POST("Post"); - -void LLPipeline::renderDeferredLighting(LLRenderTarget* screen_target) +void LLPipeline::renderDeferredLighting(LLRenderTarget *screen_target) { - if (!sCull) - { - return; - } + if (!sCull) + { + return; + } - LLRenderTarget* deferred_target = &mDeferredScreen; - LLRenderTarget* deferred_depth_target = &mDeferredDepth; - LLRenderTarget* deferred_light_target = &mDeferredLight; + LLRenderTarget *deferred_target = &mDeferredScreen; + LLRenderTarget *deferred_depth_target = &mDeferredDepth; + LLRenderTarget *deferred_light_target = &mDeferredLight; - { - LL_RECORD_BLOCK_TIME(FTM_RENDER_DEFERRED); - LLViewerCamera* camera = LLViewerCamera::getInstance(); - { - LLGLDepthTest depth(GL_TRUE); - deferred_depth_target->copyContents(*deferred_target, 0, 0, deferred_target->getWidth(), deferred_target->getHeight(), - 0, 0, deferred_depth_target->getWidth(), deferred_depth_target->getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); - } + { + LL_RECORD_BLOCK_TIME(FTM_RENDER_DEFERRED); + LLViewerCamera *camera = LLViewerCamera::getInstance(); + { + LLGLDepthTest depth(GL_TRUE); + deferred_depth_target->copyContents(*deferred_target, + 0, + 0, + deferred_target->getWidth(), + deferred_target->getHeight(), + 0, + 0, + deferred_depth_target->getWidth(), + deferred_depth_target->getHeight(), + GL_DEPTH_BUFFER_BIT, + GL_NEAREST); + } - LLGLEnable multisample(RenderFSAASamples > 0 ? GL_MULTISAMPLE_ARB : 0); + LLGLEnable multisample(RenderFSAASamples > 0 ? GL_MULTISAMPLE_ARB : 0); - if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) - { - gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD); - } + if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) + { + gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD); + } - //ati doesn't seem to love actually using the stencil buffer on FBO's - LLGLDisable stencil(GL_STENCIL_TEST); - //glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); - //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + // ati doesn't seem to love actually using the stencil buffer on FBO's + LLGLDisable stencil(GL_STENCIL_TEST); + // glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); + // glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - gGL.setColorMask(true, true); - - //draw a cube around every light - LLVertexBuffer::unbind(); + gGL.setColorMask(true, true); - LLGLEnable cull(GL_CULL_FACE); - LLGLEnable blend(GL_BLEND); + // draw a cube around every light + LLVertexBuffer::unbind(); + + LLGLEnable cull(GL_CULL_FACE); + LLGLEnable blend(GL_BLEND); glh::matrix4f mat = copy_matrix(gGLModelView); - LLStrider<LLVector3> vert; - mDeferredVB->getVertexStrider(vert); - - vert[0].set(-1,1,0); - vert[1].set(-1,-3,0); - vert[2].set(3,1,0); - - setupHWLights(NULL); //to set mSun/MoonDir; + LLStrider<LLVector3> vert; + mDeferredVB->getVertexStrider(vert); + + vert[0].set(-1, 1, 0); + vert[1].set(-1, -3, 0); + vert[2].set(3, 1, 0); + + setupHWLights(NULL); // to set mSun/MoonDir; glh::vec4f tc(mSunDir.mV); - mat.mult_matrix_vec(tc); - mTransformedSunDir.set(tc.v); + mat.mult_matrix_vec(tc); + mTransformedSunDir.set(tc.v); glh::vec4f tc_moon(mMoonDir.mV); mat.mult_matrix_vec(tc_moon); mTransformedMoonDir.set(tc_moon.v); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); - if (RenderDeferredSSAO || RenderShadowDetail > 0) - { + if (RenderDeferredSSAO || RenderShadowDetail > 0) + { deferred_light_target->bindTarget(); - { //paint shadow/SSAO light map (direct lighting lightmap) - LL_RECORD_BLOCK_TIME(FTM_SUN_SHADOW); + { // paint shadow/SSAO light map (direct lighting lightmap) + LL_RECORD_BLOCK_TIME(FTM_SUN_SHADOW); bindDeferredShader(gDeferredSunProgram, deferred_light_target); - mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - glClearColor(1,1,1,1); + mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); + glClearColor(1, 1, 1, 1); deferred_light_target->clear(GL_COLOR_BUFFER_BIT); - glClearColor(0,0,0,0); + glClearColor(0, 0, 0, 0); glh::matrix4f inv_trans = get_current_modelview().inverse().transpose(); - const U32 slice = 32; - F32 offset[slice*3]; - for (U32 i = 0; i < 4; i++) - { - for (U32 j = 0; j < 8; j++) - { - glh::vec3f v; - v.set_value(sinf(6.284f/8*j), cosf(6.284f/8*j), -(F32) i); - v.normalize(); - inv_trans.mult_matrix_vec(v); - v.normalize(); - offset[(i*8+j)*3+0] = v.v[0]; - offset[(i*8+j)*3+1] = v.v[2]; - offset[(i*8+j)*3+2] = v.v[1]; - } - } + const U32 slice = 32; + F32 offset[slice * 3]; + for (U32 i = 0; i < 4; i++) + { + for (U32 j = 0; j < 8; j++) + { + glh::vec3f v; + v.set_value(sinf(6.284f / 8 * j), cosf(6.284f / 8 * j), -(F32) i); + v.normalize(); + inv_trans.mult_matrix_vec(v); + v.normalize(); + offset[(i * 8 + j) * 3 + 0] = v.v[0]; + offset[(i * 8 + j) * 3 + 1] = v.v[2]; + offset[(i * 8 + j) * 3 + 2] = v.v[1]; + } + } - gDeferredSunProgram.uniform3fv(sOffset, slice, offset); - gDeferredSunProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, deferred_light_target->getWidth(), deferred_light_target->getHeight()); - - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - stop_glerror(); - mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); - stop_glerror(); - } - - unbindDeferredShader(gDeferredSunProgram); - } + gDeferredSunProgram.uniform3fv(sOffset, slice, offset); + gDeferredSunProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, + deferred_light_target->getWidth(), + deferred_light_target->getHeight()); + + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + stop_glerror(); + mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); + stop_glerror(); + } + + unbindDeferredShader(gDeferredSunProgram); + } deferred_light_target->flush(); - } - - if (RenderDeferredSSAO) - { //soften direct lighting lightmap - LL_RECORD_BLOCK_TIME(FTM_SOFTEN_SHADOW); - //blur lightmap + } + + if (RenderDeferredSSAO) + { // soften direct lighting lightmap + LL_RECORD_BLOCK_TIME(FTM_SOFTEN_SHADOW); + // blur lightmap screen_target->bindTarget(); - glClearColor(1,1,1,1); + glClearColor(1, 1, 1, 1); screen_target->clear(GL_COLOR_BUFFER_BIT); - glClearColor(0,0,0,0); - - bindDeferredShader(gDeferredBlurLightProgram); - mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - LLVector3 go = RenderShadowGaussian; - const U32 kern_length = 4; - F32 blur_size = RenderShadowBlurSize; - F32 dist_factor = RenderShadowBlurDistFactor; + glClearColor(0, 0, 0, 0); - // sample symmetrically with the middle sample falling exactly on 0.0 - F32 x = 0.f; + bindDeferredShader(gDeferredBlurLightProgram); + mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); + LLVector3 go = RenderShadowGaussian; + const U32 kern_length = 4; + F32 blur_size = RenderShadowBlurSize; + F32 dist_factor = RenderShadowBlurDistFactor; - LLVector3 gauss[32]; // xweight, yweight, offset + // sample symmetrically with the middle sample falling exactly on 0.0 + F32 x = 0.f; - for (U32 i = 0; i < kern_length; i++) - { - gauss[i].mV[0] = llgaussian(x, go.mV[0]); - gauss[i].mV[1] = llgaussian(x, go.mV[1]); - gauss[i].mV[2] = x; - x += 1.f; - } + LLVector3 gauss[32]; // xweight, yweight, offset + + for (U32 i = 0; i < kern_length; i++) + { + gauss[i].mV[0] = llgaussian(x, go.mV[0]); + gauss[i].mV[1] = llgaussian(x, go.mV[1]); + gauss[i].mV[2] = x; + x += 1.f; + } + + gDeferredBlurLightProgram.uniform2f(sDelta, 1.f, 0.f); + gDeferredBlurLightProgram.uniform1f(sDistFactor, dist_factor); + gDeferredBlurLightProgram.uniform3fv(sKern, kern_length, gauss[0].mV); + gDeferredBlurLightProgram.uniform1f(sKernScale, blur_size * (kern_length / 2.f - 0.5f)); + + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + stop_glerror(); + mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); + stop_glerror(); + } - gDeferredBlurLightProgram.uniform2f(sDelta, 1.f, 0.f); - gDeferredBlurLightProgram.uniform1f(sDistFactor, dist_factor); - gDeferredBlurLightProgram.uniform3fv(sKern, kern_length, gauss[0].mV); - gDeferredBlurLightProgram.uniform1f(sKernScale, blur_size * (kern_length/2.f - 0.5f)); - - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - stop_glerror(); - mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); - stop_glerror(); - } - screen_target->flush(); - unbindDeferredShader(gDeferredBlurLightProgram); + unbindDeferredShader(gDeferredBlurLightProgram); bindDeferredShader(gDeferredBlurLightProgram, screen_target); - mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); + mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); deferred_light_target->bindTarget(); - gDeferredBlurLightProgram.uniform2f(sDelta, 0.f, 1.f); + gDeferredBlurLightProgram.uniform2f(sDelta, 0.f, 1.f); - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - stop_glerror(); - mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); - stop_glerror(); - } + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + stop_glerror(); + mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); + stop_glerror(); + } deferred_light_target->flush(); - unbindDeferredShader(gDeferredBlurLightProgram); - } + unbindDeferredShader(gDeferredBlurLightProgram); + } - stop_glerror(); - gGL.popMatrix(); - stop_glerror(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - stop_glerror(); - gGL.popMatrix(); - stop_glerror(); + stop_glerror(); + gGL.popMatrix(); + stop_glerror(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + stop_glerror(); + gGL.popMatrix(); + stop_glerror(); screen_target->bindTarget(); - // clear color buffer here - zeroing alpha (glow) is important or it will accumulate against sky - glClearColor(0,0,0,0); + // clear color buffer here - zeroing alpha (glow) is important or it will accumulate against sky + glClearColor(0, 0, 0, 0); screen_target->clear(GL_COLOR_BUFFER_BIT); - - if (RenderDeferredAtmospheric) - { //apply sunlight contribution - LLGLSLShader& soften_shader = LLPipeline::sUnderWaterRender ? gDeferredSoftenWaterProgram : gDeferredSoftenProgram; - LL_RECORD_BLOCK_TIME(FTM_ATMOSPHERICS); - bindDeferredShader(soften_shader); + if (RenderDeferredAtmospheric) + { // apply sunlight contribution + LLGLSLShader &soften_shader = LLPipeline::sUnderWaterRender ? gDeferredSoftenWaterProgram : gDeferredSoftenProgram; + + LL_RECORD_BLOCK_TIME(FTM_ATMOSPHERICS); + bindDeferredShader(soften_shader); - LLEnvironment& environment = LLEnvironment::instance(); + LLEnvironment &environment = LLEnvironment::instance(); soften_shader.uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0); soften_shader.uniform4fv(LLShaderMgr::LIGHTNORM, 1, environment.getClampedLightNorm().mV); - { - LLGLDepthTest depth(GL_FALSE); - LLGLDisable blend(GL_BLEND); - LLGLDisable test(GL_ALPHA_TEST); + { + LLGLDepthTest depth(GL_FALSE); + LLGLDisable blend(GL_BLEND); + LLGLDisable test(GL_ALPHA_TEST); - //full screen blit - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); + // full screen blit + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); - mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - - mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); + mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - gGL.popMatrix(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); - } + mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); - unbindDeferredShader(LLPipeline::sUnderWaterRender ? gDeferredSoftenWaterProgram : gDeferredSoftenProgram); - } + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + } - { //render non-deferred geometry (fullbright, alpha, etc) - LLGLDisable blend(GL_BLEND); - LLGLDisable stencil(GL_STENCIL_TEST); - gGL.setSceneBlendType(LLRender::BT_ALPHA); + unbindDeferredShader(LLPipeline::sUnderWaterRender ? gDeferredSoftenWaterProgram : gDeferredSoftenProgram); + } - gPipeline.pushRenderTypeMask(); - - gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, - LLPipeline::RENDER_TYPE_CLOUDS, - LLPipeline::RENDER_TYPE_WL_SKY, - LLPipeline::END_RENDER_TYPES); - - - renderGeomPostDeferred(*LLViewerCamera::getInstance(), false); - gPipeline.popRenderTypeMask(); - } + { // render non-deferred geometry (fullbright, alpha, etc) + LLGLDisable blend(GL_BLEND); + LLGLDisable stencil(GL_STENCIL_TEST); + gGL.setSceneBlendType(LLRender::BT_ALPHA); - bool render_local = RenderLocalLights; - - if (render_local) - { - gGL.setSceneBlendType(LLRender::BT_ADD); - std::list<LLVector4> fullscreen_lights; - LLDrawable::drawable_list_t spot_lights; - LLDrawable::drawable_list_t fullscreen_spot_lights; + gPipeline.pushRenderTypeMask(); - for (U32 i = 0; i < 2; i++) - { - mTargetShadowSpotLight[i] = NULL; - } + gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, + LLPipeline::RENDER_TYPE_CLOUDS, + LLPipeline::RENDER_TYPE_WL_SKY, + LLPipeline::END_RENDER_TYPES); - std::list<LLVector4> light_colors; + renderGeomPostDeferred(*LLViewerCamera::getInstance(), false); + gPipeline.popRenderTypeMask(); + } - LLVertexBuffer::unbind(); + bool render_local = RenderLocalLights; - { - bindDeferredShader(gDeferredLightProgram); - - if (mCubeVB.isNull()) - { - mCubeVB = ll_create_cube_vb(LLVertexBuffer::MAP_VERTEX, GL_STATIC_DRAW_ARB); - } + if (render_local) + { + gGL.setSceneBlendType(LLRender::BT_ADD); + std::list<LLVector4> fullscreen_lights; + LLDrawable::drawable_list_t spot_lights; + LLDrawable::drawable_list_t fullscreen_spot_lights; - mCubeVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); iter != mLights.end(); ++iter) - { - LLDrawable* drawablep = *iter; - - LLVOVolume* volume = drawablep->getVOVolume(); - if (!volume) - { - continue; - } + for (U32 i = 0; i < 2; i++) + { + mTargetShadowSpotLight[i] = NULL; + } - if (volume->isAttachment()) - { - if (!sRenderAttachedLights) - { - continue; - } - } + std::list<LLVector4> light_colors; + + LLVertexBuffer::unbind(); + + { + bindDeferredShader(gDeferredLightProgram); + + if (mCubeVB.isNull()) + { + mCubeVB = ll_create_cube_vb(LLVertexBuffer::MAP_VERTEX, GL_STATIC_DRAW_ARB); + } + + mCubeVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - const LLViewerObject *vobj = drawablep->getVObj(); - if (vobj) + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + // mNearbyLights already includes distance calculation and excludes muted avatars. + // It is calculated from mLights + // mNearbyLights also provides fade value to gracefully fade-out out of range lights + for (light_set_t::iterator iter = mNearbyLights.begin(); iter != mNearbyLights.end(); ++iter) + { + LLDrawable * drawablep = iter->drawable; + LLVOVolume * volume = drawablep->getVOVolume(); + if (!volume) + { + continue; + } + + if (volume->isAttachment()) { - LLVOAvatar *av = vobj->getAvatar(); - if (av && (av->isTooComplex() || av->isInMuteList())) + if (!sRenderAttachedLights) { continue; } } - const LLVector3 position = drawablep->getPositionAgent(); - if (dist_vec(position, LLViewerCamera::getInstance()->getOrigin()) > RenderFarClip + volume->getLightRadius()) + LLVector4a center; + center.load3(drawablep->getPositionAgent().mV); + const F32 *c = center.getF32ptr(); + F32 s = volume->getLightRadius() * 1.5f; + + // send light color to shader in linear space + LLColor3 col = volume->getLightLinearColor(); + + if (col.magVecSquared() < 0.001f) { continue; } - LLVector4a center; - center.load3(position.mV); - const F32* c = center.getF32ptr(); - F32 s = volume->getLightRadius()*1.5f; - - //send light color to shader in linear space - LLColor3 col = volume->getLightLinearColor(); - - if (col.magVecSquared() < 0.001f) - { - continue; - } + if (s <= 0.001f) + { + continue; + } - if (s <= 0.001f) - { - continue; - } + LLVector4a sa; + sa.splat(s); + if (camera->AABBInFrustumNoFarClip(center, sa) == 0) + { + continue; + } - LLVector4a sa; - sa.splat(s); - if (camera->AABBInFrustumNoFarClip(center, sa) == 0) - { - continue; - } + sVisibleLightCount++; - sVisibleLightCount++; - - if (camera->getOrigin().mV[0] > c[0] + s + 0.2f || - camera->getOrigin().mV[0] < c[0] - s - 0.2f || - camera->getOrigin().mV[1] > c[1] + s + 0.2f || - camera->getOrigin().mV[1] < c[1] - s - 0.2f || - camera->getOrigin().mV[2] > c[2] + s + 0.2f || - camera->getOrigin().mV[2] < c[2] - s - 0.2f) - { //draw box if camera is outside box - if (render_local) - { - if (volume->isLightSpotlight()) - { - drawablep->getVOVolume()->updateSpotLightPriority(); - spot_lights.push_back(drawablep); - continue; - } - - LL_RECORD_BLOCK_TIME(FTM_LOCAL_LIGHTS); - gDeferredLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, c); - gDeferredLightProgram.uniform1f(LLShaderMgr::LIGHT_SIZE, s); - gDeferredLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV); + if (camera->getOrigin().mV[0] > c[0] + s + 0.2f || camera->getOrigin().mV[0] < c[0] - s - 0.2f || + camera->getOrigin().mV[1] > c[1] + s + 0.2f || camera->getOrigin().mV[1] < c[1] - s - 0.2f || + camera->getOrigin().mV[2] > c[2] + s + 0.2f || camera->getOrigin().mV[2] < c[2] - s - 0.2f) + { // draw box if camera is outside box + if (render_local) + { + if (volume->isLightSpotlight()) + { + drawablep->getVOVolume()->updateSpotLightPriority(); + spot_lights.push_back(drawablep); + continue; + } + + LL_RECORD_BLOCK_TIME(FTM_LOCAL_LIGHTS); + gDeferredLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, c); + gDeferredLightProgram.uniform1f(LLShaderMgr::LIGHT_SIZE, s); + gDeferredLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV); gDeferredLightProgram.uniform1f(LLShaderMgr::LIGHT_FALLOFF, volume->getLightFalloff(DEFERRED_LIGHT_FALLOFF)); - gGL.syncMatrices(); - - mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, center)); - stop_glerror(); - } - } - else - { - if (volume->isLightSpotlight()) - { - drawablep->getVOVolume()->updateSpotLightPriority(); - fullscreen_spot_lights.push_back(drawablep); - continue; - } + gGL.syncMatrices(); - glh::vec3f tc(c); - mat.mult_matrix_vec(tc); - - fullscreen_lights.push_back(LLVector4(tc.v[0], tc.v[1], tc.v[2], s)); + mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, center)); + stop_glerror(); + } + } + else + { + if (volume->isLightSpotlight()) + { + drawablep->getVOVolume()->updateSpotLightPriority(); + fullscreen_spot_lights.push_back(drawablep); + continue; + } + + glh::vec3f tc(c); + mat.mult_matrix_vec(tc); + + fullscreen_lights.push_back(LLVector4(tc.v[0], tc.v[1], tc.v[2], s)); light_colors.push_back(LLVector4(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff(DEFERRED_LIGHT_FALLOFF))); - } - } - unbindDeferredShader(gDeferredLightProgram); - } + } + } - if (!spot_lights.empty()) - { - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - bindDeferredShader(gDeferredSpotLightProgram); + // Bookmark comment to allow searching for mSpecialRenderMode == 3 (avatar edit mode), + // prev site of appended deferred character light, removed by SL-13522 09/20 - mCubeVB->setBuffer(LLVertexBuffer::MAP_VERTEX); + unbindDeferredShader(gDeferredLightProgram); + } - gDeferredSpotLightProgram.enableTexture(LLShaderMgr::DEFERRED_PROJECTION); + if (!spot_lights.empty()) + { + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + bindDeferredShader(gDeferredSpotLightProgram); - for (LLDrawable::drawable_list_t::iterator iter = spot_lights.begin(); iter != spot_lights.end(); ++iter) - { - LL_RECORD_BLOCK_TIME(FTM_PROJECTORS); - LLDrawable* drawablep = *iter; + mCubeVB->setBuffer(LLVertexBuffer::MAP_VERTEX); + + gDeferredSpotLightProgram.enableTexture(LLShaderMgr::DEFERRED_PROJECTION); - LLVOVolume* volume = drawablep->getVOVolume(); + for (LLDrawable::drawable_list_t::iterator iter = spot_lights.begin(); iter != spot_lights.end(); ++iter) + { + LL_RECORD_BLOCK_TIME(FTM_PROJECTORS); + LLDrawable *drawablep = *iter; - LLVector4a center; - center.load3(drawablep->getPositionAgent().mV); - const F32* c = center.getF32ptr(); - F32 s = volume->getLightRadius()*1.5f; + LLVOVolume *volume = drawablep->getVOVolume(); - sVisibleLightCount++; + LLVector4a center; + center.load3(drawablep->getPositionAgent().mV); + const F32 *c = center.getF32ptr(); + F32 s = volume->getLightRadius() * 1.5f; - setupSpotLight(gDeferredSpotLightProgram, drawablep); - - //send light color to shader in linear space + sVisibleLightCount++; + + setupSpotLight(gDeferredSpotLightProgram, drawablep); + + // send light color to shader in linear space LLColor3 col = volume->getLightLinearColor(); - - gDeferredSpotLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, c); - gDeferredSpotLightProgram.uniform1f(LLShaderMgr::LIGHT_SIZE, s); - gDeferredSpotLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV); + + gDeferredSpotLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, c); + gDeferredSpotLightProgram.uniform1f(LLShaderMgr::LIGHT_SIZE, s); + gDeferredSpotLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV); gDeferredSpotLightProgram.uniform1f(LLShaderMgr::LIGHT_FALLOFF, volume->getLightFalloff(DEFERRED_LIGHT_FALLOFF)); - gGL.syncMatrices(); - - mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, center)); - } - gDeferredSpotLightProgram.disableTexture(LLShaderMgr::DEFERRED_PROJECTION); - unbindDeferredShader(gDeferredSpotLightProgram); - } + gGL.syncMatrices(); + + mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, center)); + } + gDeferredSpotLightProgram.disableTexture(LLShaderMgr::DEFERRED_PROJECTION); + unbindDeferredShader(gDeferredSpotLightProgram); + } - //reset mDeferredVB to fullscreen triangle - mDeferredVB->getVertexStrider(vert); - vert[0].set(-1,1,0); - vert[1].set(-1,-3,0); - vert[2].set(3,1,0); + // reset mDeferredVB to fullscreen triangle + mDeferredVB->getVertexStrider(vert); + vert[0].set(-1, 1, 0); + vert[1].set(-1, -3, 0); + vert[2].set(3, 1, 0); - { - LLGLDepthTest depth(GL_FALSE); + { + LLGLDepthTest depth(GL_FALSE); - //full screen blit - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); + // full screen blit + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); - U32 count = 0; + U32 count = 0; - const U32 max_count = LL_DEFERRED_MULTI_LIGHT_COUNT; - LLVector4 light[max_count]; - LLVector4 col[max_count]; + const U32 max_count = LL_DEFERRED_MULTI_LIGHT_COUNT; + LLVector4 light[max_count]; + LLVector4 col[max_count]; - F32 far_z = 0.f; + F32 far_z = 0.f; - while (!fullscreen_lights.empty()) - { - LL_RECORD_BLOCK_TIME(FTM_FULLSCREEN_LIGHTS); - light[count] = fullscreen_lights.front(); - fullscreen_lights.pop_front(); - col[count] = light_colors.front(); - light_colors.pop_front(); - - far_z = llmin(light[count].mV[2]-light[count].mV[3], far_z); - count++; - if (count == max_count || fullscreen_lights.empty()) - { - U32 idx = count-1; - bindDeferredShader(gDeferredMultiLightProgram[idx]); - gDeferredMultiLightProgram[idx].uniform1i(LLShaderMgr::MULTI_LIGHT_COUNT, count); - gDeferredMultiLightProgram[idx].uniform4fv(LLShaderMgr::MULTI_LIGHT, count, (GLfloat*) light); - gDeferredMultiLightProgram[idx].uniform4fv(LLShaderMgr::MULTI_LIGHT_COL, count, (GLfloat*) col); - gDeferredMultiLightProgram[idx].uniform1f(LLShaderMgr::MULTI_LIGHT_FAR_Z, far_z); - far_z = 0.f; - count = 0; - mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); - unbindDeferredShader(gDeferredMultiLightProgram[idx]); - } - } - - bindDeferredShader(gDeferredMultiSpotLightProgram); + while (!fullscreen_lights.empty()) + { + LL_RECORD_BLOCK_TIME(FTM_FULLSCREEN_LIGHTS); + light[count] = fullscreen_lights.front(); + fullscreen_lights.pop_front(); + col[count] = light_colors.front(); + light_colors.pop_front(); + + far_z = llmin(light[count].mV[2] - light[count].mV[3], far_z); + count++; + if (count == max_count || fullscreen_lights.empty()) + { + U32 idx = count - 1; + bindDeferredShader(gDeferredMultiLightProgram[idx]); + gDeferredMultiLightProgram[idx].uniform1i(LLShaderMgr::MULTI_LIGHT_COUNT, count); + gDeferredMultiLightProgram[idx].uniform4fv(LLShaderMgr::MULTI_LIGHT, count, (GLfloat *) light); + gDeferredMultiLightProgram[idx].uniform4fv(LLShaderMgr::MULTI_LIGHT_COL, count, (GLfloat *) col); + gDeferredMultiLightProgram[idx].uniform1f(LLShaderMgr::MULTI_LIGHT_FAR_Z, far_z); + far_z = 0.f; + count = 0; + mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); + mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); + unbindDeferredShader(gDeferredMultiLightProgram[idx]); + } + } - gDeferredMultiSpotLightProgram.enableTexture(LLShaderMgr::DEFERRED_PROJECTION); + bindDeferredShader(gDeferredMultiSpotLightProgram); - mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); + gDeferredMultiSpotLightProgram.enableTexture(LLShaderMgr::DEFERRED_PROJECTION); - for (LLDrawable::drawable_list_t::iterator iter = fullscreen_spot_lights.begin(); iter != fullscreen_spot_lights.end(); ++iter) - { - LL_RECORD_BLOCK_TIME(FTM_PROJECTORS); - LLDrawable* drawablep = *iter; - - LLVOVolume* volume = drawablep->getVOVolume(); + mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - LLVector3 center = drawablep->getPositionAgent(); - F32* c = center.mV; - F32 light_size_final = volume->getLightRadius()*1.5f; - F32 light_falloff_final = volume->getLightFalloff(DEFERRED_LIGHT_FALLOFF); + for (LLDrawable::drawable_list_t::iterator iter = fullscreen_spot_lights.begin(); iter != fullscreen_spot_lights.end(); ++iter) + { + LL_RECORD_BLOCK_TIME(FTM_PROJECTORS); + LLDrawable *drawablep = *iter; + LLVOVolume *volume = drawablep->getVOVolume(); + LLVector3 center = drawablep->getPositionAgent(); + F32 * c = center.mV; + F32 light_size_final = volume->getLightRadius() * 1.5f; + F32 light_falloff_final = volume->getLightFalloff(DEFERRED_LIGHT_FALLOFF); - sVisibleLightCount++; + sVisibleLightCount++; - glh::vec3f tc(c); - mat.mult_matrix_vec(tc); - - setupSpotLight(gDeferredMultiSpotLightProgram, drawablep); + glh::vec3f tc(c); + mat.mult_matrix_vec(tc); - //send light color to shader in linear space + setupSpotLight(gDeferredMultiSpotLightProgram, drawablep); + + // send light color to shader in linear space LLColor3 col = volume->getLightLinearColor(); - - gDeferredMultiSpotLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, tc.v); + + gDeferredMultiSpotLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, tc.v); gDeferredMultiSpotLightProgram.uniform1f(LLShaderMgr::LIGHT_SIZE, light_size_final); - gDeferredMultiSpotLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV); + gDeferredMultiSpotLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV); gDeferredMultiSpotLightProgram.uniform1f(LLShaderMgr::LIGHT_FALLOFF, light_falloff_final); - mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); - } + mDeferredVB->drawArrays(LLRender::TRIANGLES, 0, 3); + } - gDeferredMultiSpotLightProgram.disableTexture(LLShaderMgr::DEFERRED_PROJECTION); - unbindDeferredShader(gDeferredMultiSpotLightProgram); + gDeferredMultiSpotLightProgram.disableTexture(LLShaderMgr::DEFERRED_PROJECTION); + unbindDeferredShader(gDeferredMultiSpotLightProgram); - gGL.popMatrix(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); - } - } + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); + } + } - gGL.setColorMask(true, true); - } + gGL.setColorMask(true, true); + } screen_target->flush(); - //gamma correct lighting - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.pushMatrix(); - gGL.loadIdentity(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.pushMatrix(); - gGL.loadIdentity(); + // gamma correct lighting + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); - { - LLGLDepthTest depth(GL_FALSE, GL_FALSE); + { + LLGLDepthTest depth(GL_FALSE, GL_FALSE); - LLVector2 tc1(0,0); - LLVector2 tc2((F32) screen_target->getWidth()*2, - (F32) screen_target->getHeight()*2); + LLVector2 tc1(0, 0); + LLVector2 tc2((F32) screen_target->getWidth() * 2, (F32) screen_target->getHeight() * 2); screen_target->bindTarget(); - // Apply gamma correction to the frame here. - gDeferredPostGammaCorrectProgram.bind(); - //mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - S32 channel = 0; - channel = gDeferredPostGammaCorrectProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, screen_target->getUsage()); - if (channel > -1) - { + // Apply gamma correction to the frame here. + gDeferredPostGammaCorrectProgram.bind(); + // mDeferredVB->setBuffer(LLVertexBuffer::MAP_VERTEX); + S32 channel = 0; + channel = gDeferredPostGammaCorrectProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, screen_target->getUsage()); + if (channel > -1) + { screen_target->bindTexture(0, channel, LLTexUnit::TFO_POINT); - } - + } + gDeferredPostGammaCorrectProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, screen_target->getWidth(), screen_target->getHeight()); - - F32 gamma = gSavedSettings.getF32("RenderDeferredDisplayGamma"); - gDeferredPostGammaCorrectProgram.uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f)); - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); - + F32 gamma = gSavedSettings.getF32("RenderDeferredDisplayGamma"); + + gDeferredPostGammaCorrectProgram.uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f / 2.2f)); + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1, -1); + + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1, 3); + + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3, -1); + + gGL.end(); + gGL.getTexUnit(channel)->unbind(screen_target->getUsage()); - gDeferredPostGammaCorrectProgram.unbind(); + gDeferredPostGammaCorrectProgram.unbind(); screen_target->flush(); - } + } - gGL.matrixMode(LLRender::MM_PROJECTION); - gGL.popMatrix(); - gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.popMatrix(); screen_target->bindTarget(); - { //render non-deferred geometry (alpha, fullbright, glow) - LLGLDisable blend(GL_BLEND); - LLGLDisable stencil(GL_STENCIL_TEST); - - pushRenderTypeMask(); - andRenderTypeMask(LLPipeline::RENDER_TYPE_ALPHA, - LLPipeline::RENDER_TYPE_FULLBRIGHT, - LLPipeline::RENDER_TYPE_VOLUME, - LLPipeline::RENDER_TYPE_GLOW, - LLPipeline::RENDER_TYPE_BUMP, - LLPipeline::RENDER_TYPE_PASS_SIMPLE, - LLPipeline::RENDER_TYPE_PASS_ALPHA, - LLPipeline::RENDER_TYPE_PASS_ALPHA_MASK, - LLPipeline::RENDER_TYPE_PASS_BUMP, - LLPipeline::RENDER_TYPE_PASS_POST_BUMP, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY, - LLPipeline::RENDER_TYPE_PASS_GLOW, - LLPipeline::RENDER_TYPE_PASS_GRASS, - LLPipeline::RENDER_TYPE_PASS_SHINY, - LLPipeline::RENDER_TYPE_PASS_INVISIBLE, - LLPipeline::RENDER_TYPE_PASS_INVISI_SHINY, - LLPipeline::RENDER_TYPE_AVATAR, - LLPipeline::RENDER_TYPE_ALPHA_MASK, - LLPipeline::RENDER_TYPE_FULLBRIGHT_ALPHA_MASK, - END_RENDER_TYPES); - - renderGeomPostDeferred(*LLViewerCamera::getInstance()); - popRenderTypeMask(); - } + { // render non-deferred geometry (alpha, fullbright, glow) + LLGLDisable blend(GL_BLEND); + LLGLDisable stencil(GL_STENCIL_TEST); + + pushRenderTypeMask(); + andRenderTypeMask(LLPipeline::RENDER_TYPE_ALPHA, + LLPipeline::RENDER_TYPE_FULLBRIGHT, + LLPipeline::RENDER_TYPE_VOLUME, + LLPipeline::RENDER_TYPE_GLOW, + LLPipeline::RENDER_TYPE_BUMP, + LLPipeline::RENDER_TYPE_PASS_SIMPLE, + LLPipeline::RENDER_TYPE_PASS_ALPHA, + LLPipeline::RENDER_TYPE_PASS_ALPHA_MASK, + LLPipeline::RENDER_TYPE_PASS_BUMP, + LLPipeline::RENDER_TYPE_PASS_POST_BUMP, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY, + LLPipeline::RENDER_TYPE_PASS_GLOW, + LLPipeline::RENDER_TYPE_PASS_GRASS, + LLPipeline::RENDER_TYPE_PASS_SHINY, + LLPipeline::RENDER_TYPE_PASS_INVISIBLE, + LLPipeline::RENDER_TYPE_PASS_INVISI_SHINY, + LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_CONTROL_AV, + LLPipeline::RENDER_TYPE_ALPHA_MASK, + LLPipeline::RENDER_TYPE_FULLBRIGHT_ALPHA_MASK, + END_RENDER_TYPES); + + renderGeomPostDeferred(*LLViewerCamera::getInstance()); + popRenderTypeMask(); + } - { - //render highlights, etc. - renderHighlights(); - mHighlightFaces.clear(); + { + // render highlights, etc. + renderHighlights(); + mHighlightFaces.clear(); - renderDebug(); + renderDebug(); - LLVertexBuffer::unbind(); + LLVertexBuffer::unbind(); - if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - // Render debugging beacons. - gObjectList.renderObjectBeacons(); - gObjectList.resetObjectBeacons(); + if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + // Render debugging beacons. + gObjectList.renderObjectBeacons(); + gObjectList.resetObjectBeacons(); gSky.addSunMoonBeacons(); - } - } + } + } - screen_target->flush(); + screen_target->flush(); } void LLPipeline::setupSpotLight(LLGLSLShader& shader, LLDrawable* drawablep) @@ -9291,7 +9289,8 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) gPipeline.pushRenderTypeMask(); - glh::matrix4f projection = get_current_projection(); + glh::matrix4f saved_modelview = get_current_modelview(); + glh::matrix4f saved_projection = get_current_projection(); glh::matrix4f mat; S32 detail = RenderReflectionDetail; @@ -9330,8 +9329,6 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) //disable occlusion culling for reflection map for now LLPipeline::sUseOcclusion = 0; - glh::matrix4f current = get_current_modelview(); - if (!camera_is_underwater) { //generate planar reflection map @@ -9340,15 +9337,10 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.pushMatrix(); - glh::matrix4f mat; - camera.getOpenGLTransform(mat.m); - - glh::matrix4f scal; - scal.set_scale(glh::vec3f(1, 1, -1)); - mat = scal * mat; + mat.set_scale(glh::vec3f(1, 1, -1)); + mat.set_translate(glh::vec3f(0,0,water_height*2.f)); + mat = saved_modelview * mat; - // convert from CFR to OGL coord sys... - mat = glh::matrix4f((GLfloat*) OGL_TO_CFR_ROTATION) * mat; mReflectionModelView = mat; @@ -9357,6 +9349,12 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) LLViewerCamera::updateFrustumPlanes(camera, FALSE, TRUE); + glh::vec3f origin(0, 0, 0); + glh::matrix4f inv_mat = mat.inverse(); + inv_mat.mult_matrix_vec(origin); + + camera.setOrigin(origin.v); + glCullFace(GL_FRONT); if (LLDrawPoolWater::sNeedsReflectionUpdate) @@ -9401,7 +9399,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) clearRenderTypeMask(LLPipeline::RENDER_TYPE_PARTICLES, END_RENDER_TYPES); if (detail < 3) { - clearRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES); + clearRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, LLPipeline::RENDER_TYPE_CONTROL_AV, END_RENDER_TYPES); if (detail < 2) { clearRenderTypeMask(LLPipeline::RENDER_TYPE_VOLUME, END_RENDER_TYPES); @@ -9409,7 +9407,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) } } - LLGLUserClipPlane clip_plane(plane, mReflectionModelView, projection); + LLGLUserClipPlane clip_plane(plane, mReflectionModelView, saved_projection); LLGLDisable cull(GL_CULL_FACE); updateCull(camera, mReflectedObjects, -water_clip, &plane); stateSort(camera, mReflectedObjects); @@ -9423,7 +9421,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.popMatrix(); - set_current_modelview(current); + set_current_modelview(saved_modelview); } //LLPipeline::sUseOcclusion = occlusion; @@ -9479,7 +9477,7 @@ void LLPipeline::generateWaterReflection(LLCamera& camera_in) //clip out geometry on the same side of water as the camera w/ enough margin to not include the water geo itself, // but not so much as to clip out parts of avatars that should be seen under the water in the distortion map LLPlane plane(-pnorm, water_dist); - LLGLUserClipPlane clip_plane(plane, current, projection); + LLGLUserClipPlane clip_plane(plane, saved_modelview, saved_projection); gGL.setColorMask(true, true); mWaterDis.clear(); @@ -10116,6 +10114,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera) LLPipeline::RENDER_TYPE_BUMP, LLPipeline::RENDER_TYPE_VOLUME, LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_CONTROL_AV, LLPipeline::RENDER_TYPE_TREE, LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::RENDER_TYPE_WATER, @@ -10909,7 +10908,9 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar) if (visually_muted || too_complex) { - andRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES); + andRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_CONTROL_AV, + END_RENDER_TYPES); } else { @@ -10932,6 +10933,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar) LLPipeline::RENDER_TYPE_PASS_INVISIBLE, LLPipeline::RENDER_TYPE_PASS_INVISI_SHINY, LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_CONTROL_AV, LLPipeline::RENDER_TYPE_ALPHA_MASK, LLPipeline::RENDER_TYPE_FULLBRIGHT_ALPHA_MASK, LLPipeline::RENDER_TYPE_INVISIBLE, @@ -10952,7 +10954,6 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar) { LL_RECORD_BLOCK_TIME(FTM_IMPOSTOR_MARK_VISIBLE); markVisible(avatar->mDrawable, *viewer_camera); - LLVOAvatar::sUseImpostors = false; // @TODO ??? LLVOAvatar::attachment_map_t::iterator iter; for (iter = avatar->mAttachmentPoints.begin(); @@ -11192,7 +11193,6 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar) avatar->setImpostorDim(tdim); - LLVOAvatar::sUseImpostors = (0 != LLVOAvatar::sMaxNonImpostors); sUseOcclusion = occlusion; sReflectionRender = false; sImpostorRender = false; diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 68ce3fe88d..52fd51cd80 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -139,7 +139,7 @@ public: void resetVertexBuffers(LLDrawable* drawable); void generateImpostor(LLVOAvatar* avatar); void bindScreenToTexture(); - void renderBloom(bool for_snapshot, F32 zoom_factor = 1.f, int subfield = 0); + void renderFinalize(); void init(); void cleanup(); @@ -409,6 +409,7 @@ public: static bool getRenderHighlights(); static void setRenderHighlightTextureChannel(LLRender::eTexIndex channel); // sets which UV setup to display in highlight overlay + static void updateRenderTransparentWater(); static void updateRenderBump(); static void updateRenderDeferred(); static void refreshCachedSettings(); @@ -453,6 +454,7 @@ public: RENDER_TYPE_BUMP = LLDrawPool::POOL_BUMP, RENDER_TYPE_MATERIALS = LLDrawPool::POOL_MATERIALS, RENDER_TYPE_AVATAR = LLDrawPool::POOL_AVATAR, + RENDER_TYPE_CONTROL_AV = LLDrawPool::POOL_CONTROL_AV, // Animesh RENDER_TYPE_TREE = LLDrawPool::POOL_TREE, RENDER_TYPE_INVISIBLE = LLDrawPool::POOL_INVISIBLE, RENDER_TYPE_VOIDWATER = LLDrawPool::POOL_VOIDWATER, @@ -577,6 +579,7 @@ public: static bool sAutoMaskAlphaDeferred; static bool sAutoMaskAlphaNonDeferred; static bool sDisableShaders; // if true, rendering will be done without shaders + static bool sRenderTransparentWater; static bool sRenderBump; static bool sBakeSunlight; static bool sNoAlpha; diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc index 8587243791..ff2d8b4943 100755 --- a/indra/newview/res/viewerRes.rc +++ b/indra/newview/res/viewerRes.rc @@ -155,7 +155,7 @@ BEGIN VALUE "FileDescription", "Second Life" VALUE "FileVersion", "${VIEWER_VERSION_MAJOR}.${VIEWER_VERSION_MINOR}.${VIEWER_VERSION_PATCH}.${VIEWER_VERSION_REVISION}" VALUE "InternalName", "Second Life" - VALUE "LegalCopyright", "Copyright � 2001, Linden Research, Inc." + VALUE "LegalCopyright", "Copyright (c) 2020, Linden Research, Inc." VALUE "OriginalFilename", "SecondLife.exe" VALUE "ProductName", "Second Life" VALUE "ProductVersion", "${VIEWER_VERSION_MAJOR}.${VIEWER_VERSION_MINOR}.${VIEWER_VERSION_PATCH}.${VIEWER_VERSION_REVISION}" diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index e0da7f5d9e..efe8102f83 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -42,6 +42,9 @@ name="Gray" value="0.5 0.5 0.5 1" /> <color + name="DkGray0" + value="0.27 0.27 0.27 1" /> + <color name="DkGray" value="0.125 0.125 0.125 1" /> <color @@ -334,6 +337,9 @@ name="ContextSilhouetteColor" reference="EmphasisColor" /> <color + name="ConversationFriendColor" + value="0.42 0.85 0.71 1" /> + <color name="DefaultHighlightDark" reference="White_10" /> <color diff --git a/indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.png b/indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.png Binary files differnew file mode 100644 index 0000000000..fd13bb699d --- /dev/null +++ b/indra/newview/skins/default/textures/containers/TabTop_Right_Flashing.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 7325d836d2..a875c4e848 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -634,6 +634,7 @@ with the same filename but different name <texture name="TabTop_Right_Off" file_name="containers/TabTop_Right_Off.png" preload="false" scale.left="8" scale.top="8" scale.right="62" scale.bottom="9" /> <texture name="TabTop_Right_Selected" file_name="containers/TabTop_Right_Selected.png" preload="false" scale.left="8" scale.top="8" scale.right="62" scale.bottom="9" /> + <texture name="TabTop_Right_Flashing" file_name="containers/TabTop_Right_Flashing.png" preload="false" scale.left="8" scale.top="8" scale.right="62" scale.bottom="9" /> <texture name="TabTop_Middle_Off" file_name="containers/TabTop_Middle_Off.png" preload="false" scale.left="8" scale.top="8" scale.right="120" scale.bottom="9" /> <texture name="TabTop_Middle_Selected" file_name="containers/TabTop_Middle_Selected.png" preload="false" scale.left="8" scale.top="8" scale.right="96" scale.bottom="9" /> <texture name="TabTop_Left_Off" file_name="containers/TabTop_Left_Off.png" preload="false" scale.left="8" scale.top="8" scale.right="120" scale.bottom="9" /> diff --git a/indra/newview/skins/default/xui/da/floater_buy_currency.xml b/indra/newview/skins/default/xui/da/floater_buy_currency.xml index 3c0428b2b0..b7ac181dd4 100644 --- a/indra/newview/skins/default/xui/da/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/da/floater_buy_currency.xml @@ -60,8 +60,7 @@ objektet. </text> <button label="Køb nu" name="buy_btn"/> <button label="Annullér" name="cancel_btn"/> - <text name="info_cannot_buy"> + <floater.string name="info_cannot_buy"> Kan ikke købe - </text> - <button label="Fortsæt til web" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/da/menu_cof_attachment.xml b/indra/newview/skins/default/xui/da/menu_cof_attachment.xml index 9d7fc0f223..37e9351ab5 100644 --- a/indra/newview/skins/default/xui/da/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/da/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="Berør" name="touch_attach" /> + <menu_item_call label="Redigér" name="edit_item" /> <menu_item_call label="Tag af" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/da/menu_inventory.xml b/indra/newview/skins/default/xui/da/menu_inventory.xml index f9bdf36f1f..ba96d6dca1 100644 --- a/indra/newview/skins/default/xui/da/menu_inventory.xml +++ b/indra/newview/skins/default/xui/da/menu_inventory.xml @@ -78,6 +78,7 @@ <menu_item_call label="Tag på" name="Wearable And Object Wear"/> <menu label="Vedhæft" name="Attach To"/> <menu label="Vedhæft til HUD" name="Attach To HUD"/> + <menu_item_call label="Berør" name="Attachment Touch" /> <menu_item_call label="Redigér" name="Wearable Edit"/> <menu_item_call label="Tilføj" name="Wearable Add"/> <menu_item_call label="Tag af" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/da/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/da/menu_wearable_list_item.xml index 63f4b0b388..67adf74bcd 100644 --- a/indra/newview/skins/default/xui/da/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/da/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Erstat" name="wear_replace"/> <menu_item_call label="Tag på" name="wear_wear"/> <menu_item_call label="Tilføj" name="wear_add"/> + <menu_item_call label="Berør" name="touch" /> <menu_item_call label="Tag af" name="take_off_or_detach"/> <menu_item_call label="Tag af" name="detach"/> <context_menu label="Vedhæft til" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/da/menu_wearing_gear.xml b/indra/newview/skins/default/xui/da/menu_wearing_gear.xml index 515a15b287..ff58cd68b2 100644 --- a/indra/newview/skins/default/xui/da/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/da/menu_wearing_gear.xml @@ -1,5 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <menu name="Gear Wearing"> - <menu_item_call label="Redigér sæt" name="edit"/> + <menu_item_call label="Berør" name="touch"/> + <menu_item_call label="Redigér" name="edit_item"/> + <menu_item_call label="Redigér sæt" name="edit_outfit"/> <menu_item_call label="Tag af" name="takeoff"/> </menu> diff --git a/indra/newview/skins/default/xui/da/menu_wearing_tab.xml b/indra/newview/skins/default/xui/da/menu_wearing_tab.xml index c0db7b6842..adaac35f68 100644 --- a/indra/newview/skins/default/xui/da/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/da/menu_wearing_tab.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="Berør" name="touch_attach"/> + <menu_item_call label="Redigér" name="edit_item"/> <menu_item_call label="Tag af" name="take_off"/> <menu_item_call label="Tag af" name="detach"/> - <menu_item_call label="Redigér sæt" name="edit"/> + <menu_item_call label="Redigér sæt" name="edit_outfit"/> </context_menu> diff --git a/indra/newview/skins/default/xui/de/floater_buy_currency.xml b/indra/newview/skins/default/xui/de/floater_buy_currency.xml index 65926c088c..eb94df1cad 100644 --- a/indra/newview/skins/default/xui/de/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/de/floater_buy_currency.xml @@ -59,8 +59,7 @@ </text> <button label="Jetzt kaufen" name="buy_btn"/> <button label="Abbrechen" name="cancel_btn"/> - <text name="info_cannot_buy"> + <floater.string name="info_cannot_buy"> Kaufabbruch - </text> - <button label="Weiter zur Kontoseite" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/de/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/de/floater_texture_ctrl.xml index b818a7d480..81b093b8c2 100644 --- a/indra/newview/skins/default/xui/de/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/de/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> Mehrere Texturen </text> - <radio_group name="mode_selection"> - <radio_item label="Inventar" name="inventory" value="0"/> - <radio_item label="Lokal" name="local" value="1"/> - <radio_item label="Backen" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - Größe: [DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="Inventar" name="inventory" value="0"/> + <combo_box.item label="Lokal" name="local" value="1"/> + </combo_box> <button label="Standard" label_selected="Standard" name="Default"/> <button label="Leer" label_selected="Leer" name="Blank"/> <button label="Keine" label_selected="Keine" name="None"/> diff --git a/indra/newview/skins/default/xui/de/menu_cof_attachment.xml b/indra/newview/skins/default/xui/de/menu_cof_attachment.xml index 05d3dfca9d..7c7da58866 100644 --- a/indra/newview/skins/default/xui/de/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/de/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="Berühren" name="touch_attach" /> + <menu_item_call label="Bearbeiten" name="edit_item" /> <menu_item_call label="Abnehmen" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/de/menu_inventory.xml b/indra/newview/skins/default/xui/de/menu_inventory.xml index 84cded77ce..743b8b2073 100644 --- a/indra/newview/skins/default/xui/de/menu_inventory.xml +++ b/indra/newview/skins/default/xui/de/menu_inventory.xml @@ -105,6 +105,7 @@ <menu_item_call label="Anziehen" name="Wearable And Object Wear"/> <menu label="Anhängen an" name="Attach To"/> <menu label="An HUD hängen" name="Attach To HUD"/> + <menu_item_call label="Berühren" name="Attachment Touch" /> <menu_item_call label="Bearbeiten" name="Wearable Edit"/> <menu_item_call label="Hinzufügen" name="Wearable Add"/> <menu_item_call label="Ausziehen" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/de/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/de/menu_wearable_list_item.xml index 283e454a06..e4c2c88f11 100644 --- a/indra/newview/skins/default/xui/de/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/de/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Ersetzen" name="wear_replace"/> <menu_item_call label="Anziehen" name="wear_wear"/> <menu_item_call label="Hinzufügen" name="wear_add"/> + <menu_item_call label="Berühren" name="touch" /> <menu_item_call label="Ausziehen / Abnehmen" name="take_off_or_detach"/> <menu_item_call label="Abnehmen" name="detach"/> <context_menu label="Anhängen an" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/de/menu_wearing_gear.xml b/indra/newview/skins/default/xui/de/menu_wearing_gear.xml index dacf898b6a..6cb0d095e7 100644 --- a/indra/newview/skins/default/xui/de/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/de/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="Outfit bearbeiten" name="edit"/> + <menu_item_call label="Berühren" name="touch"/> + <menu_item_call label="Bearbeiten" name="edit_item"/> + <menu_item_call label="Outfit bearbeiten" name="edit_outfit"/> <menu_item_call label="Ausziehen" name="takeoff"/> <menu_item_call label="Outfitliste in Zwischenablage kopieren" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/de/menu_wearing_tab.xml b/indra/newview/skins/default/xui/de/menu_wearing_tab.xml index 61002b3dad..c729ef6b00 100644 --- a/indra/newview/skins/default/xui/de/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/de/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="Berühren" name="touch_attach"/> <menu_item_call label="Ausziehen" name="take_off"/> <menu_item_call label="Abnehmen" name="detach"/> - <menu_item_call label="Outfit bearbeiten" name="edit"/> + <menu_item_call label="Outfit bearbeiten" name="edit_outfit"/> <menu_item_call label="Bearbeiten" name="edit_item"/> <menu_item_call label="Original anzeigen" name="show_original"/> </context_menu> diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index d16815c0f4..359a835630 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -266,6 +266,10 @@ Möchten Sie den ausgewählten Einwohnern Änderungsrechte gewähren? Möchten Sie den ausgewählten Einwohnern die Änderungsrechte entziehen? <usetemplate name="okcancelbuttons" notext="Nein" yestext="Ja"/> </notification> + <notification name="GroupNameLengthWarning"> + Ein Gruppenname muss zwischen [MIN_LEN] und [MAX_LEN] Zeichen lang sein. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UnableToCreateGroup"> Gruppe konnte nicht erstellt werden. [MESSAGE] @@ -366,7 +370,7 @@ Fortfahren? Sie haben nicht genug L$, um dieser Gruppe beizutreten. </notification> <notification name="CreateGroupCost"> - Die Gründung dieser Gruppe kostet 100 L$. + Die Erstellung dieser Gruppe kostet L$[COST]. Gruppen müssen mehr als ein Mitglied haben oder sie werden gelöscht. Bitte laden Sie innerhalb von 48 Stunden Mitglieder in Ihre Gruppe ein. <usetemplate canceltext="Abbrechen" name="okcancelbuttons" notext="Abbrechen" yestext="Gruppe für 100 L$ erstellen"/> @@ -508,6 +512,9 @@ Um Medien nur auf einer Fläche einzufügen, wählen Sie „Oberfläche auswähl <notification name="ErrorEncodingSnapshot"> Fehler beim Erstellen des Fotos! </notification> + <notification name="ErrorCannotAffordUpload"> + Du brauchst L$[COST], um diesen Artikel hochzuladen. + </notification> <notification name="ErrorPhotoCannotAfford"> Es kostet L$[COST], um ein Foto in Ihrem Inventar zu speichern. Sie können entweder L$ kaufen oder das Foto auf Ihrem Computer speichern. </notification> @@ -1753,11 +1760,14 @@ Diese Gruppe verlassen? <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="GroupLimitInfo"> - Die Gruppenbegrenzung für Basiskonten ist [MAX_BASIC]; für -[https://secondlife.com/premium/ Premium-]Konten ist sie [MAX_PREMIUM]. -Wenn Sie ein Downgrade Ihres Kontos durchgeführt haben, müssen Sie das Gruppenlimit unter [MAX_BASIC] bringen, bevor sich weitere Personen registrieren können. - -[https://secondlife.com/my/account/membership.php Noch heute upgraden!] + Einwohner mit Basic-Mitgliedschaft können bis zu [MAX_BASIC] Gruppen beitreten. +Premium-Mitgliedschaften erlauben bis zu [MAX_PREMIUM]. [https://secondlife.com/my/account/membership.php? Mehr Informationen oder Upgrade] + <usetemplate name="okbutton" yestext="Schließen"/> + </notification> + <notification name="GroupLimitInfoPlus"> + Einwohner mit Basic-Mitgliedschaft können bis zu [MAX_BASIC] Gruppen beitreten. +Premium-Mitgliedschaften erlauben bis zu [MAX_PREMIUM]. Premium-Plus-Mitgliedschaften +erlauben bis zu [MAX_PREMIUM_PLUS]. [https://secondlife.com/my/account/membership.php? Mehr Informationen oder Upgrade] <usetemplate name="okbutton" yestext="Schließen"/> </notification> <notification name="KickUser"> @@ -3320,6 +3330,22 @@ Diese werden für ein paar Sekunden sicherheitshalber gesperrt. Sie wurden vom Moderator stummgeschaltet. <usetemplate name="okbutton" yestext="OK"/> </notification> + <notification name="FailedToGetBenefits"> + Leider konnten wir für diese Sitzung keine Informationen zu den Leistungen erhalten. Dies sollte in einer normalen Produktionsumgebung nicht passieren. Kontaktiere bitte den Support. Diese Sitzung wird nicht normal laufen, und wir empfehlen, die Sitzung neu zu starten. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadCostConfirmation"> + Dadurch werden [COUNT] Artikel zu einem Gesamtpreis von L$[COST] hochgeladen. Möchtest du mit dem Hochladen fortfahren? + <usetemplate name="okcancelbuttons" notext="Abbrechen" yestext="Hochladen"/> + </notification> + <notification name="BulkUploadNoCompatibleFiles"> + Ausgewählte Dateien können nicht per Bulk-Upload hochgeladen werden. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadIncompatibleFiles"> + Einige der ausgewählten Dateien können nicht per Bulk-Upload hochgeladen werden. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UploadCostConfirmation"> Das Hochladen kostet [PRICE] L$. Möchten Sie fortfahren? <usetemplate name="okcancelbuttons" notext="Abbrechen" yestext="Hochladen"/> diff --git a/indra/newview/skins/default/xui/de/panel_people.xml b/indra/newview/skins/default/xui/de/panel_people.xml index 1eb3d4d1b9..81de679429 100644 --- a/indra/newview/skins/default/xui/de/panel_people.xml +++ b/indra/newview/skins/default/xui/de/panel_people.xml @@ -18,7 +18,7 @@ Sie suchen nach Leuten? Verwenden Sie die [secondlife:///app/worldmap Karte]. <string name="no_groups_msg" value="Suchen Sie nach Gruppen? Versuchen Sie es mit der [secondlife:///app/search/groups Suche]."/> <string name="MiniMapToolTipMsg" value="[REGION](Doppelklicken, um Karte zu öffnen; Umschalttaste gedrückt halten und ziehen, um zu schwenken)"/> <string name="AltMiniMapToolTipMsg" value="[REGION](Doppelklicken, um zu teleportieren; Umschalttaste gedrückt halten und ziehen, um zu schwenken)"/> - <string name="GroupCountWithInfo" value="Sie gehören [COUNT] Gruppen an und können [REMAINING] weiteren beitreten. [secondlife:/// Möchten Sie noch mehr?]"/> + <string name="GroupCountWithInfo" value="Du gehörst zu [COUNT] Gruppen, und kannst [REMAINING] weiteren beitreten. [secondlife:/// Erhöhe dein Limit]"/> <tab_container name="tabs"> <panel label="IN DER NÄHE" name="nearby_panel"> <panel label="bottom_panel" name="nearby_buttons_panel"> @@ -51,7 +51,7 @@ Sie suchen nach Leuten? Verwenden Sie die [secondlife:///app/worldmap Karte]. <dnd_button name="minus_btn" tool_tip="Ausgewählte Gruppe verlassen"/> </panel> <text name="groupcount"> - Sie gehören [COUNT] Gruppen an und können [REMAINING] weiteren beitreten. + Du gehörst zu [COUNT] Gruppen, und kannst [REMAINING] weiteren beitreten. </text> </panel> <panel label="AKTUELL" name="recent_panel"> diff --git a/indra/newview/skins/default/xui/de/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/de/panel_preferences_graphics1.xml index e0aa9fe4a9..16fdb69509 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_graphics1.xml @@ -32,10 +32,10 @@ </text> <check_box initial_value="true" label="Freunde immer darstellen" name="AlwaysRenderFriends"/> <button label="Ausnahmen..." name="RenderExceptionsButton"/> - <button label="Einstellungen als Voreinstellung speichern..." name="PrefSaveButton"/> - <button label="Voreinstellung laden..." name="PrefLoadButton"/> + <button label="Einstellungen als Voreinstellung speichern" name="PrefSaveButton" width="235" left="5"/> + <button label="Voreinstellung laden" name="PrefLoadButton" width="120"/> min_val="0.125" - <button label="Voreinstellung löschen..." name="PrefDeleteButton"/> - <button label="Auf empfohlene Einstellungen zurücksetzen" name="Defaults"/> + <button label="Voreinstellung löschen" name="PrefDeleteButton" width="130"/> + <button label="Auf empfohlene Einstellungen zurücksetzen" name="Defaults" width="248"/> <button label="Erweiterte Einstellungen..." name="AdvancedSettings"/> </panel> diff --git a/indra/newview/skins/default/xui/de/panel_preferences_setup.xml b/indra/newview/skins/default/xui/de/panel_preferences_setup.xml index 1435b7f87d..a8509cabac 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_setup.xml @@ -35,5 +35,5 @@ <text name="Proxy Settings:"> Proxy-Einstellungen: </text> - <button label="Proxy-Einstellungen ändern" label_selected="Durchsuchen" name="set_proxy"/> + <button label="Proxy-Einstellungen ändern" label_selected="Durchsuchen" name="set_proxy" width="160"/> </panel> diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 7db76ec552..43327c132d 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -1645,11 +1645,14 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich unter http://suppo <string name="MarketplaceUpdating"> Aktualisierung läuft... </string> + <string name="UploadFeeInfo"> + Die Gebühr richtet sich nach deiner Abonnementstufe. Bei höheren Stufen werden niedrigere Gebühren erhoben. [https://secondlife.com/my/account/membership.php? Mehr erfahren] + </string> <string name="Open landmarks"> - Landmarken öffnen + Wegweiser öffnen </string> <string name="Unconstrained"> - Variabel + Unbegrenzt </string> <string name="no_transfer" value=" (kein Transferieren)"/> <string name="no_modify" value=" (kein Bearbeiten)"/> @@ -5096,6 +5099,15 @@ Bitte überprüfen Sie http://status.secondlifegrid.net, um herauszufinden, ob e <string name="Chat" value=" Chat:"> Chat </string> + <string name="BaseMembership"> + Basis + </string> + <string name="PremiumMembership"> + Premium + </string> + <string name="Premium PlusMembership"> + Premium Plus + </string> <string name="DeleteItems"> Ausgewählte Objekte löschen? </string> diff --git a/indra/newview/skins/default/xui/en/control_table_contents_camera.xml b/indra/newview/skins/default/xui/en/control_table_contents_camera.xml new file mode 100644 index 0000000000..24cbb2b885 --- /dev/null +++ b/indra/newview/skins/default/xui/en/control_table_contents_camera.xml @@ -0,0 +1,240 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<contents> + <rows + enabled="false" + name="camera_actions" + value=""> + <columns + type="icontext" + column="lst_action" + font="SansSerif" + halign="left" + label="Camera" + name="lst_action" + value="Cam_FreeCam_Off" /> + </rows> + <rows + name="look_up" + value="look_up"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Look Up" /> + </rows> + <rows + name="look_down" + value="look_down"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Look Down" /> + </rows> + <rows + name="move_forward" + value="move_forward"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Forward" /> + </rows> + <rows + name="move_backward" + value="move_backward"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Backward" /> + </rows> + <rows + name="move_forward_fast" + value="move_forward_fast"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Forward Fast" /> + </rows> + <rows + name="move_backward_fast" + value="move_backward_fast"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Backward Fast" /> + </rows> + <rows + name="spin_over" + value="spin_over"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Spin Over" /> + </rows> + <rows + name="spin_under" + value="spin_under"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Spin Under" /> + </rows> + <rows + name="pan_up" + value="pan_up"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Pan Up" /> + </rows> + <rows + name="pan_down" + value="pan_down"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Pan Down" /> + </rows> + <rows + name="pan_left" + value="pan_left"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Pan Left" /> + </rows> + <rows + name="pan_right" + value="pan_right"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Pan Right" /> + </rows> + <rows + name="pan_in" + value="pan_in"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Pan In" /> + </rows> + <rows + name="pan_out" + value="pan_out"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Pan Out" /> + </rows> + <rows + name="spin_around_ccw" + value="spin_around_ccw"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Camera spin around counterclockwise" + value="Counterclockwise" /> + </rows> + <rows + name="spin_around_cw" + value="spin_around_cw"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Camera spin around clockwise" + value="Clockwise" /> + </rows> + <rows + name="move_forward_sitting" + value="move_forward_sitting"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Forward Sitting" /> + </rows> + <rows + name="move_backward_sitting" + value="move_backward_sitting"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Backward Sitting" /> + </rows> + <rows + name="spin_over_sitting" + value="spin_over_sitting"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Spin Over Sitting" /> + </rows> + <rows + name="spin_under_sitting" + value="spin_under_sitting"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Spin Under Sitting" /> + </rows> + <rows + name="spin_around_ccw_sitting" + value="spin_around_ccw_sitting"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Camera spin around counterclockwise sitting" + value="Counterclockwise Sitting" /> + </rows> + <rows + name="spin_around_cw_sitting" + value="spin_around_cw_sitting"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Camera spin around clockwise sitting" + value="Clockwise Sitting" /> + </rows> +</contents> diff --git a/indra/newview/skins/default/xui/en/control_table_contents_columns_basic.xml b/indra/newview/skins/default/xui/en/control_table_contents_columns_basic.xml new file mode 100644 index 0000000000..e707aaf22c --- /dev/null +++ b/indra/newview/skins/default/xui/en/control_table_contents_columns_basic.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<contents> + <columns + relative_width="0.34" + label="Action" + name="lst_action" /> + <columns + relative_width="0.22" + label="Primary Control" + name="lst_ctrl1" /> + <columns + relative_width="0.22" + label="Alternate 1" + name="lst_ctrl2" /> + <columns + relative_width="0.22" + label="Alternate 2" + name="lst_ctrl3" /> +</contents> diff --git a/indra/newview/skins/default/xui/en/control_table_contents_editing.xml b/indra/newview/skins/default/xui/en/control_table_contents_editing.xml new file mode 100644 index 0000000000..2a3314840a --- /dev/null +++ b/indra/newview/skins/default/xui/en/control_table_contents_editing.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<contents> + <rows + enabled="false" + name="editing_actions" + value=""> + <columns + type="icontext" + column="lst_action" + font="SansSerif" + halign="left" + label="Editing" + name="lst_action" + value="Tool_Dozer" /> + </rows> + <rows + name="edit_avatar_spin_ccw" + value="edit_avatar_spin_ccw"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Camera spin around avatar counterclockwise" + value="Counterclockwise" /> + </rows> + <rows + name="edit_avatar_spin_cw" + value="edit_avatar_spin_cw"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Camera spin around avatar clockwise" + value="Clockwise" /> + </rows> + <rows + name="edit_avatar_spin_over" + value="edit_avatar_spin_over"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Camera spin over avatar" + value="Camera Spin Over" /> + </rows> + <rows + name="edit_avatar_spin_under" + value="edit_avatar_spin_under"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Camera spin under avatar" + value="Camera Spin Under" /> + </rows> + <rows + name="edit_avatar_move_forward" + value="edit_avatar_move_forward"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Forward" /> + </rows> + <rows + name="edit_avatar_move_backward" + value="edit_avatar_move_backward"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Camera Backward" /> + </rows> +</contents> diff --git a/indra/newview/skins/default/xui/en/control_table_contents_media.xml b/indra/newview/skins/default/xui/en/control_table_contents_media.xml new file mode 100644 index 0000000000..ce5d3556b6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/control_table_contents_media.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<contents> + <rows + enabled="false" + name="media_actions" + value=""> + <columns + type="icontext" + column="lst_action" + font="SansSerif" + halign="left" + label="Sound and Media" + name="lst_action" + value="Audio_Press" /> + </rows> + <rows + name="toggle_pause_media" + value="toggle_pause_media"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Play/Pause Media" /> + </rows> + <rows + name="toggle_enable_media" + value="toggle_enable_media"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Play/Stop All Media" /> + </rows> + <rows + name="voice_follow_key" + value="voice_follow_key"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Voice" /> + </rows> + <rows + name="toggle_voice" + value="toggle_voice"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Toggle Voice" /> + </rows> + <rows + name="start_chat" + value="start_chat"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Start Chat" /> + </rows> + <rows + name="start_gesture" + value="start_gesture"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Start Gesture" /> + </rows> +</contents> diff --git a/indra/newview/skins/default/xui/en/control_table_contents_movement.xml b/indra/newview/skins/default/xui/en/control_table_contents_movement.xml new file mode 100644 index 0000000000..b410d2dc1c --- /dev/null +++ b/indra/newview/skins/default/xui/en/control_table_contents_movement.xml @@ -0,0 +1,198 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<contents> + <rows + enabled="false" + name="move_actions" + value=""> + <columns + type="icontext" + column="lst_action" + font="SansSerif" + halign="left" + label="Move Actions" + name="lst_action" + value="Move_Walk_Off" /> + </rows> + <rows + name="walk_to" + value="walk_to"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Walk to location mouse cursor points to" + value="Walk to" /> + </rows> + <rows + name="teleport_to" + value="teleport_to"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + tool_tip="Teleport to location mouse cursor points to, but not all locations allow direct teleportation so you might be teleported closer to destination instead" + value="Teleport to" /> + </rows> + <rows + name="push_forward" + value="push_forward"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Move Forward" /> + </rows> + <rows + name="push_backward" + value="push_backward"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Move Backward" /> + </rows> + <rows + name="turn_left" + value="turn_left"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Left" /> + </rows> + <rows + name="turn_right" + value="turn_right"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Right" /> + </rows> + <rows + name="slide_left" + value="slide_left"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Strafe left" /> + </rows> + <rows + name="slide_right" + value="slide_right"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Strafe right" /> + </rows> + <rows + name="jump" + value="jump"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Jump/Up" /> + </rows> + <rows + name="push_down" + value="push_down"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Down" /> + </rows> + <rows + name="run_forward" + value="run_forward"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Run Forward" /> + </rows> + <rows + name="run_backward" + value="run_backward"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Run Backward" /> + </rows> + <rows + name="run_left" + value="run_left"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Run Left" /> + </rows> + <rows + name="run_right" + value="run_right"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Run Right" /> + </rows> + <rows + name="toggle_run" + value="toggle_run"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Toggle Run" /> + </rows> + <rows + name="toggle_fly" + value="toggle_fly"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Fly/Stop flying" /> + </rows> + <rows + name="toggle_sit" + value="toggle_sit"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Sit/Stand" /> + </rows> + <rows + name="stop_moving" + value="stop_moving"> + <columns + column="lst_action" + font="SansSerif" + halign="left" + name="lst_action" + value="Stop Moving" /> + </rows> +</contents> 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 a1dd179765..b2d9e53039 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -123,8 +123,8 @@ No parcel selected. </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> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] + </panel.string> <text type="string" length="1" diff --git a/indra/newview/skins/default/xui/en/floater_buy_currency.xml b/indra/newview/skins/default/xui/en/floater_buy_currency.xml index 553c5d51d0..061af1b67c 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_currency.xml @@ -13,6 +13,10 @@ name="buy_currency"> Buy L$ [LINDENS] for approx. [LOCALAMOUNT] </floater.string> + <floater.string + name="info_cannot_buy"> + Unable to Buy + </floater.string> <icon height="215" image_name="Linden_Dollar_Background" @@ -286,42 +290,4 @@ Re-enter amount to see the latest exchange rate. left_pad="10" name="cancel_btn" width="90"/> - <icon - height="215" - image_name="Linden_Dollar_Alert" - layout="topleft" - left="0" - name="error_background" - top="15" - use_draw_context_alpha="false" - width="350"/> - <text - type="string" - font="SansSerifHuge" - left="165" - width="360" - height="25" - top="25" - name="info_cannot_buy"> - Unable to Buy - </text> - <text - type="string" - width="176" - height="125" - top="60" - left="165" - word_wrap="true" - follows="bottom|right" - name="cannot_buy_message"> - </text> - <button - follows="bottom|left" - height="20" - label="Continue to the Web" - layout="topleft" - left="170" - name="error_web" - top="200" - width="160"/> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_buy_object.xml b/indra/newview/skins/default/xui/en/floater_buy_object.xml index 49be4290c7..1f7d52dbf5 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_object.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_object.xml @@ -32,6 +32,10 @@ name="no_transfer_text"> (no transfer) </floater.string> + <floater.string + name="mupliple_selected"> + Mupliple selection + </floater.string> <scroll_list background_visible="true" draw_border="false" diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index c64ee5565a..15f02ab9c3 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -267,6 +267,36 @@ right="-1"> <layout_panel name="input_editor_layout_panel"> + <avatar_icon + follows="left|bottom" + name="avatar_icon" + height="20" + default_icon_name="Generic_Person" + layout="topleft" + left="3" + bottom="-9" + visible="false" + width="20" /> + <group_icon + follows="left|bottom" + name="group_chat_icon" + height="20" + default_icon_name="Generic_Group" + layout="topleft" + left="3" + bottom="-9" + visible="false" + width="20" /> + <icon + follows="left|bottom" + height="20" + image_name="Nearby_chat_icon" + layout="topleft" + left="3" + bottom="-9" + name="nearby_chat_icon" + visible="false" + width="20"/> <chat_editor layout="topleft" expand_lines_count="5" @@ -280,7 +310,7 @@ spellcheck="true" tab_group="3" bottom="-8" - left="5" + left_pad="5" right="-5" wrap="true" /> </layout_panel> diff --git a/indra/newview/skins/default/xui/en/floater_image_preview.xml b/indra/newview/skins/default/xui/en/floater_image_preview.xml index 3daff1a132..773d9aafc9 100644 --- a/indra/newview/skins/default/xui/en/floater_image_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_image_preview.xml @@ -120,8 +120,8 @@ Try saving image as 24 bit Targa (.tga). </text> <check_box - control_name="LosslessJ2CUpload" enabled="false" + initial_value="false" follows="bottom|left" height="16" label="Use lossless compression" diff --git a/indra/newview/skins/default/xui/en/floater_inspect.xml b/indra/newview/skins/default/xui/en/floater_inspect.xml index 63334e2b24..802a6649c8 100644 --- a/indra/newview/skins/default/xui/en/floater_inspect.xml +++ b/indra/newview/skins/default/xui/en/floater_inspect.xml @@ -13,7 +13,7 @@ width="400"> <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,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </floater.string> <scroll_list bottom="268" diff --git a/indra/newview/skins/default/xui/en/floater_joystick.xml b/indra/newview/skins/default/xui/en/floater_joystick.xml index 3dfdf8e1a5..7d2cea1fe5 100644 --- a/indra/newview/skins/default/xui/en/floater_joystick.xml +++ b/indra/newview/skins/default/xui/en/floater_joystick.xml @@ -8,27 +8,32 @@ title="JOYSTICK CONFIGURATION" width="569"> <floater.string - name="NoDevice"> - no device detected + name="JoystickDisabled"> + None </floater.string> - <check_box - bottom="38" - height="10" - control_name="JoystickEnabled" - halign="left" - label="Enable Joystick:" + <text + type="string" layout="topleft" + follows="left|top" + halign="left" + height="12" + top="22" left="14" - name="enable_joystick" - width="60" /> - <text - bottom="32" + width="50" + mouse_opaque="false" + name="joystick_lbl"> + Joystick: + </text> + <combo_box + allow_text_entry="false" + follows="left|top" layout="topleft" - left="120" - name="joystick_type" - width="380" /> + name="joystick_combo" + top="19" + left_pad="4" + width="300"/> <spinner - bottom="48" + bottom="56" height="10" control_name="JoystickAxis1" decimal_digits="0" @@ -42,7 +47,7 @@ name="JoystickAxis1" width="140" /> <spinner - bottom="48" + bottom_delta="0" height="10" control_name="JoystickAxis2" decimal_digits="0" @@ -56,7 +61,7 @@ name="JoystickAxis2" width="140" /> <spinner - bottom="48" + bottom_delta="0" height="10" control_name="JoystickAxis0" decimal_digits="0" @@ -70,7 +75,7 @@ name="JoystickAxis0" width="140" /> <spinner - bottom="68" + bottom="76" height="10" control_name="JoystickAxis4" decimal_digits="0" @@ -84,7 +89,7 @@ name="JoystickAxis4" width="140" /> <spinner - bottom="68" + bottom_delta="0" height="10" control_name="JoystickAxis5" decimal_digits="0" @@ -98,7 +103,7 @@ name="JoystickAxis5" width="140" /> <spinner - bottom="68" + bottom_delta="0" height="10" control_name="JoystickAxis3" decimal_digits="0" @@ -112,7 +117,7 @@ name="JoystickAxis3" width="140" /> <spinner - bottom="88" + bottom="96" height="10" control_name="JoystickAxis6" decimal_digits="0" @@ -162,12 +167,12 @@ left="37" mouse_opaque="false" name="Control Modes:" - top="110" + top="118" width="102"> Control Modes: </text> <check_box - bottom="127" + bottom="134" height="10" control_name="JoystickAvatarEnabled" halign="center" @@ -177,7 +182,7 @@ name="JoystickAvatarEnabled" width="60" /> <check_box - bottom="127" + bottom_delta="0" height="10" control_name="JoystickBuildEnabled" halign="center" @@ -187,7 +192,7 @@ name="JoystickBuildEnabled" width="60" /> <check_box - bottom="127" + bottom_delta="0" height="10" control_name="JoystickFlycamEnabled" halign="center" @@ -203,7 +208,7 @@ left="359" name="axis_view" show_label="true" - top="135" + top="143" width="200"> <stat_bar bar_max="2" @@ -266,7 +271,7 @@ <text type="string" length="1" - bottom="144" + bottom="152" halign="right" layout="topleft" left="3" @@ -275,7 +280,7 @@ X Scale </text> <spinner - bottom="144" + bottom_delta="0" height="10" control_name="AvatarAxisScale1" decimal_digits="2" @@ -287,7 +292,7 @@ name="AvatarAxisScale1" width="56" /> <spinner - bottom="144" + bottom_delta="0" height="10" control_name="BuildAxisScale1" decimal_digits="2" @@ -299,7 +304,7 @@ name="BuildAxisScale1" width="56" /> <spinner - bottom="144" + bottom_delta="0" height="10" control_name="FlycamAxisScale1" decimal_digits="2" @@ -313,7 +318,7 @@ <text type="string" length="1" - bottom="164" + bottom="172" halign="right" layout="topleft" left="3" @@ -322,7 +327,7 @@ Y Scale </text> <spinner - bottom="164" + bottom_delta="0" height="10" control_name="AvatarAxisScale2" decimal_digits="2" @@ -334,7 +339,7 @@ name="AvatarAxisScale2" width="56" /> <spinner - bottom="164" + bottom_delta="0" height="10" control_name="BuildAxisScale2" decimal_digits="2" @@ -346,7 +351,7 @@ name="BuildAxisScale2" width="56" /> <spinner - bottom="164" + bottom_delta="0" height="10" control_name="FlycamAxisScale2" decimal_digits="2" @@ -360,7 +365,7 @@ <text type="string" length="1" - bottom="184" + bottom="192" halign="right" layout="topleft" left="3" @@ -369,7 +374,7 @@ Z Scale </text> <spinner - bottom="184" + bottom_delta="0" height="10" control_name="AvatarAxisScale0" decimal_digits="2" @@ -381,7 +386,7 @@ name="AvatarAxisScale0" width="56" /> <spinner - bottom="184" + bottom_delta="0" height="10" control_name="BuildAxisScale0" decimal_digits="2" @@ -393,7 +398,7 @@ name="BuildAxisScale0" width="56" /> <spinner - bottom="184" + bottom_delta="0" height="10" control_name="FlycamAxisScale0" decimal_digits="2" @@ -407,7 +412,7 @@ <text type="string" length="1" - bottom="204" + bottom="212" halign="right" layout="topleft" left="3" @@ -416,7 +421,7 @@ Pitch Scale </text> <spinner - bottom="204" + bottom_delta="0" height="10" control_name="AvatarAxisScale4" decimal_digits="2" @@ -428,7 +433,7 @@ name="AvatarAxisScale4" width="56" /> <spinner - bottom="204" + bottom_delta="0" height="10" control_name="BuildAxisScale4" decimal_digits="2" @@ -440,7 +445,7 @@ name="BuildAxisScale4" width="56" /> <spinner - bottom="204" + bottom_delta="0" height="10" control_name="FlycamAxisScale4" decimal_digits="2" @@ -454,7 +459,7 @@ <text type="string" length="1" - bottom="224" + bottom="232" halign="right" layout="topleft" left="3" @@ -463,7 +468,7 @@ Yaw Scale </text> <spinner - bottom="224" + bottom_delta="0" height="10" control_name="AvatarAxisScale5" decimal_digits="2" @@ -475,7 +480,7 @@ name="AvatarAxisScale5" width="56" /> <spinner - bottom="224" + bottom_delta="0" height="10" control_name="BuildAxisScale5" decimal_digits="2" @@ -487,7 +492,7 @@ name="BuildAxisScale5" width="56" /> <spinner - bottom="224" + bottom_delta="0" height="10" control_name="FlycamAxisScale5" decimal_digits="2" @@ -501,7 +506,7 @@ <text type="string" length="1" - bottom="244" + bottom="252" halign="right" layout="topleft" left="3" @@ -510,7 +515,7 @@ Roll Scale </text> <spinner - bottom="244" + bottom_delta="0" height="10" control_name="BuildAxisScale3" decimal_digits="2" @@ -522,7 +527,7 @@ name="BuildAxisScale3" width="56" /> <spinner - bottom="244" + bottom_delta="0" height="10" control_name="FlycamAxisScale3" decimal_digits="2" @@ -536,7 +541,7 @@ <text type="string" length="1" - bottom="274" + bottom="282" halign="right" layout="topleft" left="3" @@ -545,7 +550,7 @@ X Dead Zone </text> <spinner - bottom="274" + bottom_delta="0" height="10" control_name="AvatarAxisDeadZone1" decimal_digits="2" @@ -556,7 +561,7 @@ name="AvatarAxisDeadZone1" width="56" /> <spinner - bottom="274" + bottom_delta="0" height="10" control_name="BuildAxisDeadZone1" decimal_digits="2" @@ -567,7 +572,7 @@ name="BuildAxisDeadZone1" width="56" /> <spinner - bottom="274" + bottom_delta="0" height="10" control_name="FlycamAxisDeadZone1" decimal_digits="2" @@ -580,7 +585,7 @@ <text type="string" length="1" - bottom="294" + bottom="302" halign="right" layout="topleft" left="3" @@ -589,7 +594,7 @@ Y Dead Zone </text> <spinner - bottom="294" + bottom_delta="0" height="10" control_name="AvatarAxisDeadZone2" decimal_digits="2" @@ -600,7 +605,7 @@ name="AvatarAxisDeadZone2" width="56" /> <spinner - bottom="294" + bottom_delta="0" height="10" control_name="BuildAxisDeadZone2" decimal_digits="2" @@ -611,7 +616,7 @@ name="BuildAxisDeadZone2" width="56" /> <spinner - bottom="294" + bottom_delta="0" height="10" control_name="FlycamAxisDeadZone2" decimal_digits="2" @@ -624,7 +629,7 @@ <text type="string" length="1" - bottom="314" + bottom="322" halign="right" layout="topleft" left="3" @@ -633,7 +638,7 @@ Z Dead Zone </text> <spinner - bottom="314" + bottom_delta="0" height="10" control_name="AvatarAxisDeadZone0" decimal_digits="2" @@ -644,7 +649,7 @@ name="AvatarAxisDeadZone0" width="56" /> <spinner - bottom="314" + bottom_delta="0" height="10" control_name="BuildAxisDeadZone0" decimal_digits="2" @@ -655,7 +660,7 @@ name="BuildAxisDeadZone0" width="56" /> <spinner - bottom="314" + bottom_delta="0" height="10" control_name="FlycamAxisDeadZone0" decimal_digits="2" @@ -668,7 +673,7 @@ <text type="string" length="1" - bottom="334" + bottom="342" halign="right" layout="topleft" left="2" @@ -677,7 +682,7 @@ Pitch Dead Zone </text> <spinner - bottom="334" + bottom_delta="0" height="10" control_name="AvatarAxisDeadZone4" decimal_digits="2" @@ -688,7 +693,7 @@ name="AvatarAxisDeadZone4" width="56" /> <spinner - bottom="334" + bottom_delta="0" height="10" control_name="BuildAxisDeadZone4" decimal_digits="2" @@ -699,7 +704,7 @@ name="BuildAxisDeadZone4" width="56" /> <spinner - bottom="334" + bottom_delta="0" height="10" control_name="FlycamAxisDeadZone4" decimal_digits="2" @@ -712,7 +717,7 @@ <text type="string" length="1" - bottom="354" + bottom="362" halign="right" layout="topleft" left="3" @@ -721,7 +726,7 @@ Yaw Dead Zone </text> <spinner - bottom="354" + bottom_delta="0" height="10" control_name="AvatarAxisDeadZone5" decimal_digits="2" @@ -732,7 +737,7 @@ name="AvatarAxisDeadZone5" width="56" /> <spinner - bottom="354" + bottom_delta="0" height="10" control_name="BuildAxisDeadZone5" decimal_digits="2" @@ -743,7 +748,7 @@ name="BuildAxisDeadZone5" width="56" /> <spinner - bottom="354" + bottom_delta="0" height="10" control_name="FlycamAxisDeadZone5" decimal_digits="2" @@ -756,7 +761,7 @@ <text type="string" length="1" - bottom="374" + bottom="382" halign="right" layout="topleft" left="3" @@ -765,7 +770,7 @@ Roll Dead Zone </text> <spinner - bottom="374" + bottom_delta="0" height="10" control_name="BuildAxisDeadZone3" decimal_digits="2" @@ -776,7 +781,7 @@ name="BuildAxisDeadZone3" width="56" /> <spinner - bottom="374" + bottom_delta="0" height="10" control_name="FlycamAxisDeadZone3" decimal_digits="2" @@ -789,7 +794,7 @@ <text type="string" length="1" - bottom="402" + bottom="410" halign="right" layout="topleft" left="3" @@ -810,7 +815,7 @@ min_val="1" name="AvatarFeathering" show_text="false" - top="402" + top="410" width="73" /> <slider control_name="BuildFeathering" @@ -845,7 +850,7 @@ <text type="string" length="1" - bottom="430" + bottom="438" halign="right" layout="topleft" left="3" @@ -854,7 +859,7 @@ Zoom Scale </text> <spinner - bottom="430" + bottom_delta="0" height="10" control_name="FlycamAxisScale6" decimal_digits="2" @@ -868,7 +873,7 @@ <text type="string" length="1" - bottom="450" + bottom="458" halign="right" layout="topleft" left="3" @@ -877,7 +882,7 @@ Zoom Dead Zone </text> <spinner - bottom="450" + bottom_delta="0" height="10" control_name="FlycamAxisDeadZone6" decimal_digits="2" @@ -894,7 +899,7 @@ layout="topleft" left="359" name="SpaceNavigatorDefaults" - top="429" + top="437" width="200" /> <button follows="right|bottom" diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml index 5a86eb06fb..02a21764ce 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -2,15 +2,16 @@ <floater can_close="true" can_drag_on_left="false" - can_minimize="false" - can_resize="false" - height="480" - min_height="480" + can_minimize="true" + can_resize="true" + height="625" + min_height="625" width="980" min_width="980" name="Model Preview" title="UPLOAD MODEL" - help_topic="upload_model" > + help_topic="upload_model" + legacy_header_height="25"> <string name="status_idle"></string> <string name="status_parse_error">Error: Dae parsing issue - see log for details.</string> @@ -33,19 +34,27 @@ <string name="mesh_status_missing_lod">Missing required level of detail.</string> <string name="mesh_status_invalid_material_list">LOD materials are not a subset of reference model.</string> <string name="phys_status_vertex_limit_exceeded">Some physical hulls exceed vertex limitations.</string> + <string name="phys_status_degenerate_triangles">The physics mesh too dense remove the small thin triangles (see preview)</string> <string name="layer_all">All</string> <!-- Text to display in physics layer combo box for "all layers" --> <string name="decomposing">Analyzing...</string> <string name="simplifying">Simplifying...</string> <string name="tbd">TBD</string> + + <!-- Warnings and info from model loader--> + <string name="TooManyJoint">Skinning disabled due to too many joints: [JOINTS], maximum: [MAX]</string> + <string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string> + <string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string> + <string name="ModelLoaded">Model [MODEL_NAME] loaded</string> + <string name="IncompleteTC">Texture coordinates data is not complete.</string> -<panel - follows="top|left" - height="455" - layout="topleft" - left="3" - name="left_panel" - top_pad="10" - width="630"> + <panel + follows="top|left" + height="595" + layout="topleft" + left="3" + name="left_panel" + top_pad="25" + width="635"> <panel follows="all" height="50" @@ -76,12 +85,16 @@ </panel> <tab_container follows="top|left" - top_pad="15" + top_pad="10" left="0" - height="300" + height="330" width="635" name="import_tab" - tab_position="top"> + tab_position="top" + enable_tabs_flashing="true" + tabs_flashing_color="MenuItemFlashBgColor"> + <last_tab + tab_top_image_flash="TabTop_Right_Flashing"/> <!-- for log tab --> <!-- LOD PANEL --> <panel help_topic="upload_model_lod" @@ -92,12 +105,12 @@ <view_border bevel_style="none" follows="top|left" - height="275" + height="306" layout="topleft" left="3" name="lod_tab_border" top_pad="0" - width="629" /> + width="628" /> <text follows="left|top" height="18" @@ -688,7 +701,7 @@ left="10" name="lod_tab_border" top_pad="20" - width="605" /> + width="614" /> <check_box follows="top|left" height="15" @@ -730,12 +743,12 @@ <view_border bevel_style="none" follows="top|left" - height="275" + height="306" layout="topleft" left="3" name="physics_tab_border" top_pad="0" - width="619"/> + width="628"/> <panel bg_alpha_color="0 0 0 0" bg_opaque_color="0 0 0 0.3" @@ -755,8 +768,9 @@ name="first_step_name" text_color="White" top_pad="0" - width="210"> - Step 1: Level of Detail + width="210" + valign="center"> + Step 1: Pick a physics model : </text> <combo_box follows="left|top" @@ -798,7 +812,7 @@ layout="topleft" left="18" name="physics_tab_border" - top_pad="15" + top_pad="10" width="589"/> <panel bg_alpha_color="0 0 0 0" @@ -807,7 +821,7 @@ follows="top|left" left="18" name="physics analysis" - top_pad="15" + top_pad="10" visible="true" width="589"> <text @@ -819,7 +833,7 @@ name="method_label" text_color="White" top_pad="0"> - Step 2: Analyze + Step 2: Convert to hulls (optional) </text> <text follows="top|left" @@ -905,7 +919,7 @@ layout="topleft" left="18" name="physics_tab_border" - top_pad="15" + top_pad="10" width="589"/> <panel bg_alpha_color="0 0 0 0" @@ -914,7 +928,7 @@ height="66" left="18" name="physics simplification" - top_pad="15" + top_pad="10" width="589"> <text text_color="White" @@ -1013,7 +1027,7 @@ layout="topleft" left="18" name="physics_tab_border" - top_pad="15" + top_pad="10" width="589"/> <panel bg_alpha_color="0 0 0 0" @@ -1075,10 +1089,9 @@ follows="left|top" height="19" layout="topleft" - left_pad="5" - top_delta="0" + top_pad="5" name="physics message" - width="270"> + width="589"> <icon follows="left|top" height="16" @@ -1093,7 +1106,7 @@ layout="topleft" left_pad="2" name="physics_status_message_text" - width="252" + width="573" top_delta="3"/> </panel> </panel> @@ -1105,12 +1118,12 @@ <view_border bevel_style="none" follows="top|left" - height="275" + height="306" layout="topleft" left="3" name="border" top_pad="0" - width="619"/> + width="628"/> <text follows="top|left" height="16" @@ -1157,75 +1170,211 @@ label_text.text_color="White" left="20" top_pad="20"/> - <view_border - bevel_style="none" - follows="top|left" - height="0" - layout="topleft" - name="border" - top_pad="20" - width="579"/> - <text - follows="top|left" - height="15" - left="20" - name="include_label" - text_color="White" - top_pad="20" - width="150"> - For avatar models only: - </text> - <check_box - follows="top|left" - height="15" - label="Include skin weight" - label_text.text_color="White" - name="upload_skin" - top_pad="15"/> - <check_box - follows="top|left" - height="15" - label="Include joint positions" - label_text.text_color="White" - name="upload_joints" - top_pad="15"/> - <check_box - follows="top|left" - height="15" - label="Lock scale if joint position defined" - label_text.text_color="White" - name="lock_scale_if_joint_position" - top_pad="15"/> - <text - follows="top|left" - height="15" - layout="topleft" - left="220" - name="pelvis_offset_label" - text_color="White" - top="134" - width="200"> - Z offset (raise or lower avatar): - </text> - <spinner - follows="top|left" - height="20" - min_val="-3.00" - max_val="3.0" - name="pelvis_offset" - top_pad="10" - value="0.0" - width="80"/> </panel> + <panel + label="Overrides" + layout="topleft" + name="rigging_panel" + title="Rigging"> + <view_border + bevel_style="none" + follows="top|left" + height="306" + layout="topleft" + left="3" + name="avatar_tab_border" + top_pad="0" + width="628" /> + <check_box + follows="top|left" + height="15" + label="Include skin weight" + label_text.text_color="White" + name="upload_skin" + top="8" + left="20"/> + <check_box + follows="top|left" + height="15" + label="Include joint positions" + label_text.text_color="White" + name="upload_joints" + left_delta="0" + top_pad="7"/> + <check_box + follows="top|left" + height="15" + label="Lock scale if joint position defined" + label_text.text_color="White" + name="lock_scale_if_joint_position" + top_pad="7"/> + <text + follows="top|left" + height="15" + layout="topleft" + left="220" + name="pelvis_offset_label" + text_color="White" + top="8" + width="200"> + Z offset (raise or lower avatar): + </text> + <spinner + follows="top|left" + height="20" + min_val="-3.00" + max_val="3.0" + name="pelvis_offset" + top_pad="10" + value="0.0" + width="80"/> + <text + follows="top|left" + height="17" + left="425" + name="skin_too_many_joints" + text_color="Orange" + top="7" + width="195" + word_wrap="true"> + Too many skinned joints + </text> + <text + follows="top|left" + height="32" + left="425" + name="skin_unknown_joint" + text_color="Orange" + top="8" + width="195" + word_wrap="true"> + Model has an unknown joint(s) + </text> + <text + layout="topleft" + follows="top|left" + height="15" + left="20" + name="joints_descr" + top="73" + width="150"> + Joints: + </text> + <scroll_list + layout="topleft" + follows="top|left" + name="joints_list" + column_padding="0" + draw_heading="false" + draw_stripes="false" + commit_on_selection_change="true" + heading_height="23" + height="199" + left_delta="0" + top_pad="0" + width="200"/> + <text + layout="topleft" + follows="top|left" + height="15" + left_delta="0" + name="conflicts_description" + top_pad="2" + width="200"> + [CONFLICTS] conflicts in [JOINTS_COUNT] joints + </text> + <text + layout="topleft" + follows="top|left" + height="15" + left_pad="5" + name="pos_overrides_descr" + top="73" + width="300"> + Position overrides for joint '[JOINT]': + </text> + <scroll_list + layout="topleft" + follows="top|left" + name="pos_overrides_list" + column_padding="0" + draw_heading="true" + draw_stripes="false" + heading_height="23" + height="100" + left_delta="0" + top_pad="0" + width="385"> + <scroll_list.columns + label="Model" + name="model_name" + relative_width="0.49" /> + <scroll_list.columns + label="X" + name="axis_x" + relative_width="0.17" /> + <scroll_list.columns + label="Y" + name="axis_y" + relative_width="0.17" /> + <scroll_list.columns + label="Z" + name="axis_z" + relative_width="0.17" /> + </scroll_list> + </panel> + <panel + label="Log" + layout="topleft" + name="logs_panel" + title="Log"> + <view_border + bevel_style="none" + follows="top|left" + height="289" + layout="topleft" + left="3" + name="log_tab_border" + top_pad="0" + width="628" /> + <text_editor + type="string" + length="1" + embedded_items="false" + follows="top|left" + font="SansSerif" + ignore_tab="false" + layout="topleft" + height="289" + left="4" + top="0" + right="-1" + max_length="65536" + name="log_text" + parse_urls="true" + spellcheck="false" + read_only="true" + word_wrap="true"> + </text_editor> + <check_box + control_name="ImporterDebug" + follows="top|left" + top_pad="9" + left="6" + width="70" + label="Enable detailed logging" + name="verbose_logging"/> + </panel> </tab_container> <panel - follows="top|left" - height="80" - layout="top|left" - left="0" + follows="top|left|bottom" + layout="topleft" + height="195" + left="4" + border="true" name="weights_and_warning_panel" top_pad="3" - width="625"> + width="629"> <button follows="top|left" label="Calculate weights & fee" @@ -1265,10 +1414,10 @@ label_color="White" layout="topleft" name="reset_btn" - right="-2" + right="-5" top="3" height="20" - width="275"/> + width="265"/> <!-- ========== WEIGHTS ==========--> <text follows="top|left" @@ -1287,7 +1436,7 @@ left_pad="0" name="prim_weight" top_delta="0" - width="120" + width="130" word_wrap="true"> Land impact: [EQ] </text> @@ -1297,7 +1446,7 @@ left_pad="0" name="download_weight" top_delta="0" - width="100" + width="130" word_wrap="true"> Download: [ST] </text> @@ -1307,7 +1456,7 @@ layout="topleft" left_pad="0" name="physics_weight" - width="90" + width="130" word_wrap="true"> Physics: [PH] </text> @@ -1317,19 +1466,150 @@ layout="topleft" left_pad="0" name="server_weight" - width="83" + width="130" word_wrap="true"> Server: [SIM] </text> - <!-- ========== NOTE MESSAGE ========== --> + <!-- =========== Cost breakdown ======== --> + <panel + border="true" + top_pad="5" + layout="topleft" + left="6" + name="price_breakdown_panel" + width="120" + height="100"> + <text + layout="topleft" + left="3"> + Price Breakdown + </text> + <view_border + bevel_style="none" + follows="top|left" + height="0" + layout="topleft" + left="3" + name="price_breakdown_border" + top_pad="5" + width="110"/> + <text + height="80" + top_pad="5" + layout="topleft" + left="3" + name="price_breakdown_labels" + width="70" + word_wrap="false"> +Download: +Physics: +Instances: +Textures: +Model: + </text> + <text + height="80" + top_delta="0" + layout="topleft" + halign="right" + left_pad="0" + name="price_breakdown" + width="40" + word_wrap="false"> +[STREAMING] +[PHYSICS] +[INSTANCES] +[TEXTURES] +[MODEL] + </text> + </panel> + <!-- + Streaming breakdown numbers are available but not fully understood + uncommenting the following sections will display the numbers for debugging purposes + <text + height="80" + top_delta="0" + layout="topleft" + left="130" + name="streaming_breakdown_labels" + width="65" + word_wrap="true"> +Streaming/Download: +High: +Medium: +Low: +Lowest: + </text> <text + height="80" + top_delta="0" + layout="topleft" + left_pad="0" + name="streaming_breakdown" + width="95" + word_wrap="true"> +[STR_TOTAL] +[STR_HIGH] +[STR_MED] +[STR_LOW] +[STR_LOWEST] + </text>--> + <panel + border="true" + layout="topleft" + left_pad="265" + name="physics_costs_panel" + width="120" + height="100"> + <text + layout="topleft" + left="3"> + Physics Costs + </text> + <view_border + bevel_style="none" + follows="top|left" + height="0" + layout="topleft" + left="3" + name="price_breakdown_border" + top_pad="5" + width="110"/> + <text + height="80" + top_pad="5" + layout="topleft" + left="5" + name="physics_breakdown_labels" + width="65"> +Base Hull: +Mesh: +Analysed: + </text> + <text + height="80" + top_delta="0" + layout="topleft" + left_pad="0" + name="physics_breakdown" + width="40" + halign="right" + word_wrap="false" + visible="true"> +[PCH] +[PM] +[PHU] + </text>--> + </panel> + <!-- ========== NOTE MESSAGE ========== --> + <text font="SansSerif" layout="topleft" left="6" name="warning_title" - top_pad="10" + top_pad="5" text_color="DrYellow" - visible="false" + visible="true" width="40"> NOTE: </text> @@ -1340,44 +1620,51 @@ left_pad="1" name="warning_message" parse_urls="true" - top_delta="2" + top_delta="1" wrap="true" width="462" - visible="false"> + visible="true"> You dont have rights to upload mesh models. [[VURL] Find out how] to get certified. + </text> + <text + text_color="Yellow" + layout="topleft" + top_pad="-2" + left="6" + name="status"> +[STATUS] </text> - <text text_color="Yellow" layout="topleft" top_delta="20" left="6" name="status">[STATUS]</text> - </panel> -</panel> - -<text - follows="left|top" - layout="topleft" - left="640" - name="lod_label" - text_color="White" - top="13" - height="15" - width="290"> - Preview: - </text> -<panel - border="true" - bevel_style="none" - follows="top|left" - name="preview_panel" - top_pad="4" - width="290" - height="290"/> - -<panel - follows="all" - height="130" - layout="topleft" - name="right_panel" - top_pad="5" - width="340"> + </panel> + + <text + follows="left|top" + layout="topleft" + left="640" + name="lod_label" + text_color="White" + top="29" + height="15" + width="290"> + Preview: + </text> + <panel + follows="all" + layout="topleft" + border="true" + bevel_style="none" + name="preview_panel" + top_pad="4" + width="325" + height="408"/> + <panel + follows="right|bottom" + can_resize="false" + height="140" + layout="topleft" + name="right_panel" + top_pad="5" + width="340"> <combo_box top_pad="3" follows="left|top" @@ -1386,10 +1673,10 @@ name="preview_lod_combo" width="150" tool_tip="LOD to view in preview render"> - <combo_item name="high"> High </combo_item> - <combo_item name="medium"> Medium </combo_item> - <combo_item name="low"> Low </combo_item> - <combo_item name="lowest"> Lowest </combo_item> + <combo_item name="high"> High </combo_item> + <combo_item name="medium"> Medium </combo_item> + <combo_item name="low"> Low </combo_item> + <combo_item name="lowest"> Lowest </combo_item> </combo_box> <text follows="top|left" @@ -1436,11 +1723,21 @@ </check_box> <check_box follows="top|left" + label="Joint position overrides" + label_text.text_color="White" + word_wrap="down" + width="130" + layout="topleft" + name="show_joint_overrides" + top_pad="8"> + </check_box> + <check_box + follows="top|left" label="Joints" label_text.text_color="White" layout="topleft" name="show_joint_positions" - top_pad="8"> + top_pad="17"> </check_box> <text follows="top|left" @@ -1460,5 +1757,5 @@ max_val="3.0" height="20" width="150"/> -</panel> + </panel> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index 0e62d50072..ee730dcb01 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -169,6 +169,13 @@ https://accounts.secondlife.com/change_email/ layout="topleft" help_topic="preferences_uploads_tab" name="uploads" /> + <panel + class="panel_preference_controls" + filename="panel_preferences_controls.xml" + label="Controls" + layout="topleft" + help_topic="preferences_controls_tab" + name="controls" /> </tab_container> </floater> 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 e93568a87e..e282f1b179 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 @@ -596,7 +596,10 @@ left="420" name="TransparentWater" top_delta="16" - width="300" /> + width="300"> + <check_box.commit_callback + function="Pref.RenderOptionUpdate" /> + </check_box> <check_box control_name="RenderObjectBump" diff --git a/indra/newview/skins/default/xui/en/floater_publish_classified.xml b/indra/newview/skins/default/xui/en/floater_publish_classified.xml index 322e34272c..84e0b489d0 100644 --- a/indra/newview/skins/default/xui/en/floater_publish_classified.xml +++ b/indra/newview/skins/default/xui/en/floater_publish_classified.xml @@ -6,6 +6,7 @@ layout="topleft" name="publish_classified" title="Publishing Classified" + help_topic="profile_edit_classified" width="320"> <text top="20" diff --git a/indra/newview/skins/default/xui/en/floater_script_debug.xml b/indra/newview/skins/default/xui/en/floater_script_debug.xml index cd88048d6b..6c49cfa1a8 100644 --- a/indra/newview/skins/default/xui/en/floater_script_debug.xml +++ b/indra/newview/skins/default/xui/en/floater_script_debug.xml @@ -17,5 +17,6 @@ name="Preview Tabs" tab_position="bottom" top="16" - width="448" /> + width="448" + enable_tabs_flashing="true"/> </multi_floater> diff --git a/indra/newview/skins/default/xui/en/floater_select_key.xml b/indra/newview/skins/default/xui/en/floater_select_key.xml index 4e89df5a73..48d9eee4cd 100644 --- a/indra/newview/skins/default/xui/en/floater_select_key.xml +++ b/indra/newview/skins/default/xui/en/floater_select_key.xml @@ -4,10 +4,27 @@ border="false" can_close="false" can_minimize="false" - height="90" + height="116" layout="topleft" name="modal container" - width="240"> + width="272"> + <floater.string + name="keyboard"> + Keyboard + </floater.string> + <floater.string + name="mouse"> + Mouse Buttons + </floater.string> + <floater.string + name="basic_description"> +Press a key to set your trigger. +Allowed input: [INPUT]. + </floater.string> + <floater.string + name="reserved_by_menu"> +Combination [KEYSTR] is reserved by menu. + </floater.string> <text type="string" halign="center" @@ -16,19 +33,47 @@ height="30" layout="topleft" left="30" - name="Save item as:" + name="descritption" top="25" word_wrap="true" - width="180"> - Press a key to set your Speak button trigger. + width="212"> +Press a key to set your trigger. +Allowed input: [INPUT]. </text> + <check_box + follows="top|left" + height="20" + initial_value="false" + label="Apply to all" + layout="topleft" + left="90" + name="apply_all" + tool_tip="Viewer uses different control combinations depending on what you are doing in world, setting this will apply your change to all combinations" + top_pad="8" + width="160" /> + + <button + height="23" + label="Set Empty" + layout="topleft" + left="8" + name="SetEmpty" + top_pad="6" + width="80" /> + <button + height="23" + label="Default" + layout="topleft" + left_pad="8" + name="Default" + top_delta="0" + width="80" /> <button height="23" label="Cancel" - label_selected="Cancel" layout="topleft" - right="-10" + left_pad="8" name="Cancel" - top_pad="8" - width="100" /> + top_delta="0" + width="80" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml index a52d0a95d6..3a66911389 100644 --- a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml @@ -48,42 +48,28 @@ </text> <!-- mode selector --> - <radio_group + <combo_box control_name="mode_selection" height="20" layout="topleft" - left="0" - top_pad="80" + left="6" + top_pad="77" name="mode_selection" - follows="left|top"> - <radio_item + follows="left|top" + width="120"> + <combo_box.item label="Inventory" name="inventory" - top_delta="20" - layout="topleft" - height="16" - left="0" - value="0" - width="70" /> - <radio_item + value="0" /> + <combo_box.item label="Local" - left_pad="0" - layout="topleft" - top_delta="0" - height="16" name="local" - value="1" - width="50" /> - <radio_item + value="1" /> + <combo_box.item label="Bake" - left_pad="0" - layout="topleft" - top_delta="0" - height="16" name="bake" - value="2" - width="50" /> - </radio_group> + value="2" /> + </combo_box> <!-- --> <text @@ -92,21 +78,9 @@ follows="left|top" height="14" layout="topleft" - left_delta="12" + left="8" name="size_lbl" top_pad="4"> - Size: - </text> - - <text - type="string" - length="1" - follows="left|top" - height="14" - layout="topleft" - left_delta="0" - name="unknown" - top_pad="4"> [DIMENSIONS] </text> <!-- middle: inventory mode --> @@ -149,8 +123,8 @@ image_selected="eye_button_active.tga" image_unselected="eye_button_inactive.tga" layout="topleft" - left_delta="-80" - top_delta="-10" + left="18" + top_delta="-23" name="Pipette" width="28" /> <text @@ -329,7 +303,7 @@ layout="topleft" label="OK" label_selected="OK" layout="topleft" - left="95" + left="176" top="-30" name="Select" width="100" /> @@ -352,5 +326,5 @@ layout="topleft" left="6" name="apply_immediate_check" top_delta="0" - width="120" /> + width="150" /> </floater> diff --git a/indra/newview/skins/default/xui/en/fonts.xml b/indra/newview/skins/default/xui/en/fonts.xml index 76df0abdfd..d88c267a95 100644 --- a/indra/newview/skins/default/xui/en/fonts.xml +++ b/indra/newview/skins/default/xui/en/fonts.xml @@ -12,6 +12,7 @@ <file>msyh.ttc</file> <file load_collection="true">Cambria.ttc</file> <file>malgun.ttf</file> + <file>micross.ttf</file> </os> <os name="Mac"> <file>ヒラギノ角ゴシック W3.ttc</file> @@ -25,6 +26,7 @@ <file>华文细黑.ttf</file> <file>PingFang.ttc</file> <file>STIXGeneral.otf</file> + <file>Thonburi.ttc</file> </os> </font> diff --git a/indra/newview/skins/default/xui/en/menu_cof_attachment.xml b/indra/newview/skins/default/xui/en/menu_cof_attachment.xml index c402100fb1..3f545c936d 100644 --- a/indra/newview/skins/default/xui/en/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/en/menu_cof_attachment.xml @@ -3,6 +3,26 @@ layout="topleft" name="COF Attachment"> <menu_item_call + label="Touch" + layout="topleft" + name="touch_attach"> + <on_click + function="Attachment.Touch" /> + <on_enable + function="Attachment.OnEnable" + parameter="touch" /> + </menu_item_call> + <menu_item_call + label="Edit" + layout="topleft" + name="edit_item"> + <on_click + function="Attachment.Edit" /> + <on_enable + function="Attachment.OnEnable" + parameter="edit" /> + </menu_item_call> + <menu_item_call label="Detach" layout="topleft" name="detach"> diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 9aa84c1bac..eda9739976 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -810,14 +810,6 @@ <menu_item_separator layout="topleft" name="Wearable And Object Separator"/> - <menu_item_call - label="Detach From Yourself" - layout="topleft" - name="Detach From Yourself"> - <menu_item_call.on_click - function="Inventory.DoToSelected" - parameter="detach" /> - </menu_item_call> <!-- COMMENTED OUT for DEV-32347 --> <!-- <menu_item_call @@ -846,6 +838,14 @@ layout="topleft" name="Attach To HUD" /> <menu_item_call + label="Touch" + layout="topleft" + name="Attachment Touch"> + <menu_item_call.on_click + function="Inventory.DoToSelected" + parameter="touch" /> + </menu_item_call> + <menu_item_call label="Edit" layout="topleft" name="Wearable Edit"> @@ -862,6 +862,14 @@ parameter="wear_add" /> </menu_item_call> <menu_item_call + label="Detach From Yourself" + layout="topleft" + name="Detach From Yourself"> + <menu_item_call.on_click + function="Inventory.DoToSelected" + parameter="detach" /> + </menu_item_call> + <menu_item_call label="Take Off" layout="topleft" name="Take Off"> diff --git a/indra/newview/skins/default/xui/en/menu_script_chiclet.xml b/indra/newview/skins/default/xui/en/menu_script_chiclet.xml index db29d9cebc..49e52ebb8d 100644 --- a/indra/newview/skins/default/xui/en/menu_script_chiclet.xml +++ b/indra/newview/skins/default/xui/en/menu_script_chiclet.xml @@ -16,4 +16,12 @@ function="ScriptChiclet.Action" parameter="end" /> </menu_item_call> + <menu_item_call + label="Close All Dialogs" + layout="topleft" + name="Close All"> + <menu_item_call.on_click + function="ScriptChiclet.Action" + parameter="close all" /> + </menu_item_call> </menu> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 873b95926b..0a50ff089f 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -56,10 +56,10 @@ </menu_item_call> <menu_item_call label="Places..." - name="Places"> + name="Places" + shortcut="control|L"> <menu_item_call.on_click - function="Floater.ToggleOrBringToFront" - parameter="places" /> + function="Tools.Link"/> </menu_item_call> <menu_item_call label="Picks..." @@ -85,11 +85,24 @@ <menu_item_separator/> <menu_item_call label="Camera Controls..." - name="Camera Controls"> + name="Camera Controls" + shortcut="control|K"> <menu_item_call.on_click function="Floater.ToggleOrBringToFront" parameter="camera" /> </menu_item_call> + <menu_item_call + label="Hover Height" + name="HoverHeight" + shortcut="alt|control|H" + use_mac_ctrl="true" + visible="false"> + <menu_item_call.on_click + function="Floater.ToggleOrBringToFront" + parameter="edit_hover_height"/> + <menu_item_call.on_enable + function="Edit.EnableHoverHeight"/> + </menu_item_call> <menu create_jump_keys="true" label="Movement" @@ -150,7 +163,9 @@ </menu_item_check> <menu_item_call label="Stop Animating Me" - name="Stop Animating My Avatar"> + name="Stop Animating My Avatar" + allow_key_repeat="true" + shortcut="alt|shift|A"> <menu_item_call.on_click function="Tools.StopAllAnimations" /> </menu_item_call> @@ -458,7 +473,8 @@ </menu_item_check> <menu_item_call label="Events" - name="Events"> + name="Events" + shortcut="control|E"> <menu_item_call.on_click function="Advanced.ShowURL" parameter="https://secondlife.com/my/community/events"/> @@ -606,8 +622,7 @@ <menu_item_separator /> <menu_item_check label="Advanced Menu" - name="Show Advanced Menu" - shortcut="control|alt|shift|D"> + name="Show Advanced Menu"> <on_check function="CheckControl" parameter="UseDebugMenus" /> @@ -647,7 +662,8 @@ tear_off="true"> <menu_item_check label="Sunrise" - name="Sunrise"> + name="Sunrise" + shortcut="control|shift|O"> <menu_item_check.on_click function="World.EnvSettings" parameter="sunrise" /> @@ -679,7 +695,8 @@ </menu_item_check> <menu_item_check label="Midnight" - name="Midnight"> + name="Midnight" + shortcut="control|shift|Z"> <menu_item_check.on_click function="World.EnvSettings" parameter="midnight" /> @@ -1317,7 +1334,8 @@ function="World.EnvPreset" <menu_item_call label="Model..." layout="topleft" - name="Upload Model"> + name="Upload Model" + shortcut="alt|control|U"> <menu_item_call.on_click function="File.UploadModel" parameter="" /> @@ -1733,6 +1751,16 @@ function="World.EnvPreset" parameter="character" /> </menu_item_check> <menu_item_check + label="Animeshes" + name="Rendering Type Control Avatar"> + <menu_item_check.on_check + function="Advanced.CheckRenderType" + parameter="controlAV" /> + <menu_item_check.on_click + function="Advanced.ToggleRenderType" + parameter="controlAV" /> + </menu_item_check> + <menu_item_check label="Surface Patch" name="Rendering Type Surface Patch" shortcut="control|alt|shift|5"> @@ -1972,20 +2000,6 @@ function="World.EnvPreset" name="Shortcuts" tear_off="true" visible="false"> - <!-- This second, alternative shortcut for Show Advanced Menu is for backward compatibility. The main shortcut has been changed so it's Linux-friendly, where the old shortcut is typically eaten by the window manager. --> - <menu_item_check - label="Show Advanced Menu - legacy shortcut" - name="Show Advanced Menu - legacy shortcut" - shortcut="control|alt|D"> - <on_check - function="CheckControl" - parameter="UseDebugMenus" /> - <on_click - function="ToggleControl" - parameter="UseDebugMenus" /> - </menu_item_check> - - <menu_item_separator/> <menu_item_call label="Close Window" @@ -3611,7 +3625,7 @@ function="World.EnvPreset" <menu_item_call label="Debug Avatar Textures" name="Debug Avatar Textures" - shortcut="control|alt|shift|A"> + shortcut="control|alt|shift|K"> <menu_item_call.on_click function="Advanced.DebugAvatarTextures" /> </menu_item_call> diff --git a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml index aa56b4ba63..20c81c983b 100644 --- a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml @@ -4,6 +4,7 @@ <menu_item_call label="Replace" layout="topleft" + visible="false" name="wear_replace"> <on_click function="Wearable.Wear" /> @@ -11,6 +12,7 @@ <menu_item_call label="Wear" layout="topleft" + visible="false" name="wear_wear"> <on_click function="Wearable.Wear" /> @@ -18,13 +20,29 @@ <menu_item_call label="Add" layout="topleft" + visible="false" name="wear_add"> <on_click function="Wearable.Add" /> </menu_item_call> <menu_item_call + label="Touch" + layout="topleft" + name="touch" + visible="false" + on_click.function="Attachment.Touch" + /> + <menu_item_call + label="Edit" + layout="topleft" + name="edit" + visible="false" + on_click.function="Wearable.Edit" + /> + <menu_item_call label="Take Off / Detach" layout="topleft" + visible="false" name="take_off_or_detach"> <on_click function="Wearable.TakeOffDetach" /> @@ -32,6 +50,7 @@ <menu_item_call label="Detach" layout="topleft" + visible="false" name="detach"> <on_click function="Attachment.Detach" /> @@ -47,20 +66,15 @@ <menu_item_call label="Take Off" layout="topleft" + visible="false" name="take_off"> <on_click function="Clothing.TakeOff" /> </menu_item_call> <menu_item_call - label="Edit" - layout="topleft" - name="edit"> - <on_click - function="Wearable.Edit" /> - </menu_item_call> - <menu_item_call label="Item Profile" layout="topleft" + visible="false" name="object_profile"> <on_click function="Attachment.Profile" /> @@ -68,6 +82,7 @@ <menu_item_call label="Show Original" layout="topleft" + visible="false" name="show_original"> <on_click function="Wearable.ShowOriginal" /> @@ -75,6 +90,7 @@ <menu_item_call label="Create New" layout="topleft" + visible="false" name="create_new" translate="false"> <on_click @@ -83,6 +99,7 @@ <menu_item_call label="--no options--" layout="topleft" + visible="false" name="--no options--" translate="false"> </menu_item_call> diff --git a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml index 0e858ccf10..57b20dfda9 100644 --- a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml @@ -4,11 +4,24 @@ visible="false" name="Gear Wearing"> <menu_item_call - label="Edit Outfit" + label="Touch" + layout="topleft" + name="touch"> + <on_click + function="Gear.TouchAttach" /> + <on_enable + function="Gear.OnEnable" + parameter="touch_attach" /> + </menu_item_call> + <menu_item_call + label="Edit" layout="topleft" - name="edit"> + name="edit_item"> <on_click - function="Gear.Edit" /> + function="Gear.EditItem" /> + <on_enable + function="Gear.OnEnable" + parameter="edit_item" /> </menu_item_call> <menu_item_call label="Take Off" @@ -20,6 +33,14 @@ function="Gear.OnEnable" parameter="take_off" /> </menu_item_call> + <menu_item_separator /> + <menu_item_call + label="Edit Outfit" + layout="topleft" + name="edit_outfit"> + <on_click + function="Gear.EditOutfit" /> + </menu_item_call> <menu_item_call label="Copy outfit list to clipboard" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/menu_wearing_tab.xml b/indra/newview/skins/default/xui/en/menu_wearing_tab.xml index 75c1de24aa..b8e2b44884 100644 --- a/indra/newview/skins/default/xui/en/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/en/menu_wearing_tab.xml @@ -3,6 +3,20 @@ layout="topleft" name="Wearing"> <menu_item_call + label="Touch" + layout="topleft" + name="touch_attach"> + <on_click + function="Wearing.TouchAttach" /> + </menu_item_call> + <menu_item_call + label="Edit" + layout="topleft" + name="edit_item"> + <on_click + function="Wearing.EditItem" /> + </menu_item_call> + <menu_item_call label="Take Off" layout="topleft" name="take_off"> @@ -23,16 +37,9 @@ <menu_item_call label="Edit Outfit" layout="topleft" - name="edit"> + name="edit_outfit"> <on_click - function="Wearing.Edit" /> - </menu_item_call> - <menu_item_call - label="Edit" - layout="topleft" - name="edit_item"> - <on_click - function="Wearing.EditItem" /> + function="Wearing.EditOutfit" /> </menu_item_call> <menu_item_call label="Show Original" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 32ae56e3af..a1c73d12d0 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1343,6 +1343,18 @@ You must agree to the Second Life Terms and Conditions, Privacy Policy, and Term <notification icon="alertmodal.tga" + name="CouldNotBuyCurrency" + type="alertmodal"> +[TITLE] +[MESSAGE] + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" name="CouldNotPutOnOutfit" type="alertmodal"> Could not put on outfit. @@ -3174,7 +3186,7 @@ Do you want to remove multiple friends from your Friends list? type="alertmodal"> Are you sure you want to delete all scripted objects owned by ** [AVATAR_NAME] ** -on all others land in this sim? +on all others land in this region? <tag>confirm</tag> <usetemplate name="okcancelbuttons" @@ -3188,7 +3200,7 @@ on all others land in this sim? type="alertmodal"> Are you sure you want to DELETE ALL scripted objects owned by ** [AVATAR_NAME] ** -on ALL LAND in this sim? +on ALL LAND in this region? <tag>confirm</tag> <usetemplate name="okcancelbuttons" @@ -3202,7 +3214,7 @@ on ALL LAND in this sim? type="alertmodal"> Are you sure you want to DELETE ALL objects (scripted or not) owned by ** [AVATAR_NAME] ** -on ALL LAND in this sim? +on ALL LAND in this region? <tag>confirm</tag> <usetemplate name="okcancelbuttons" @@ -3680,6 +3692,17 @@ Could not teleport to [SLURL] as it's on a different grid ([GRID]) than the curr </notification> <notification icon="alertmodal.tga" + name="GeneralCertificateErrorShort" + type="alertmodal"> +Could not connect to the server. +[REASON] + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification icon="alertmodal.tga" name="GeneralCertificateError" type="alertmodal"> Could not connect to the server. @@ -4016,6 +4039,8 @@ Finished download of raw terrain file to: [DOWNLOAD_PATH]. </notification> + <!-- RequiredUpdate does not display release notes URL because we don't get + that from login.cgi's login failure message. --> <notification icon="alertmodal.tga" name="RequiredUpdate" @@ -4033,6 +4058,8 @@ Please download from https://secondlife.com/support/downloads/ name="PauseForUpdate" type="alertmodal"> Version [VERSION] is required for login. +Release notes: [URL] + Click OK to download and install. <tag>confirm</tag> <usetemplate @@ -4045,6 +4072,8 @@ Click OK to download and install. name="OptionalUpdateReady" type="alertmodal"> Version [VERSION] has been downloaded and is ready to install. +Release notes: [URL] + Click OK to install. <tag>confirm</tag> <usetemplate @@ -4057,6 +4086,8 @@ Click OK to install. name="PromptOptionalUpdate" type="alertmodal"> Version [VERSION] has been downloaded and is ready to install. +Release notes: [URL] + Proceed? <tag>confirm</tag> <usetemplate @@ -9545,6 +9576,12 @@ Do you wish to continue? yestext="OK"/> </notification> + <global name="UnsupportedShaderRequirements"> +You do not appear to meet the hardware requirements for [APP_NAME]. [APP_NAME] requires OpenGL 2.0 or later shader support. If this is the case, you may want to make sure that you have the latest drivers for your graphics card, and service packs and patches for your operating system. + +If you continue to have problems, please visit the [SUPPORT_SITE]. + </global> + <global name="UnsupportedGLRequirements"> You do not appear to have the proper hardware requirements for [APP_NAME]. [APP_NAME] requires an OpenGL graphics card that has multitexture support. If this is the case, you may want to make sure that you have the latest drivers for your graphics card, and service packs and patches for your operating system. @@ -9566,6 +9603,10 @@ If you continue to have problems, please visit the [SUPPORT_SITE]. <global name="UnsupportedRAM"> - Your system memory does not meet the minimum requirements. </global> + + <global name="LLLeapUpdaterFailure"> +Failed to launch updater service [UPDATER_APP]. Please verify the viewer is installed correctly and has the necessary permissions to run. If you continue to experience issues, please visit the [SUPPORT_SITE]. + </global> <!-- these are alert strings from server. the name needs to match entire the server string, and needs to be changed whenever the server string changes --> @@ -10267,7 +10308,7 @@ Unable to add script! name="AssetServerTimeoutObjReturn" type="notify"> <tag>fail</tag> -Asset server didn't respond in a timely fashion. Object returned to sim. +Asset server didn't respond in a timely fashion. Object returned to the region. </notification> <notification @@ -11408,6 +11449,19 @@ Cannot create large prims that intersect other residents. Please re-try when ot notext="Cancel" yestext="OK"/> </notification> + + <notification + icon="alertmodal.tga" + name="PreferenceControlsDefaults" + type="alertmodal"> + Do you want to restore default values for controls? + <tag>confirm</tag> + <usetemplate + canceltext="Cancel" + name="yesnocancelbuttons" + notext="Current mode" + yestext="All modes"/> + </notification> <notification icon="alertmodal.tga" diff --git a/indra/newview/skins/default/xui/en/panel_group_notices.xml b/indra/newview/skins/default/xui/en/panel_group_notices.xml index 47aceb2c2e..a5aca5c72b 100644 --- a/indra/newview/skins/default/xui/en/panel_group_notices.xml +++ b/indra/newview/skins/default/xui/en/panel_group_notices.xml @@ -59,7 +59,8 @@ Maximum 200 per group daily <scroll_list.columns label="Date" name="date" - width="60" /> + sort_column="sort" + width="100" /> <scroll_list.columns name="sort" width="-1" /> 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 13986c4030..7935d66aee 100644 --- a/indra/newview/skins/default/xui/en/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/en/panel_landmark_info.xml @@ -41,7 +41,7 @@ </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,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </string> <!-- Texture names for rating icons --> <string @@ -95,7 +95,7 @@ <panel bg_alpha_color="DkGray2" follows="left|top|right" - height="630" + height="654" layout="topleft" left="0" min_height="300" @@ -112,35 +112,56 @@ name="logo" top="10" width="290" /> + <!-- texture picker has an empty label section, compensate for it with negative top_pad--> <text follows="left|top|right" font="SansSerifLarge" height="14" layout="topleft" left="10" - name="region_title" + top_pad="-10" + width="280" + name="parcel_title" text_color="white" - top_pad="10" use_ellipses="true" - value="SampleRegion" - width="280" /> + value="SampleParcel, Name Long" /> <text follows="left|top|right" height="14" layout="topleft" left="10" - name="parcel_title" - top_pad="10" + top_pad="9" + width="280" + name="region_title" + use_ellipses="true"> + Region: [REGIONAMEPOS] + </text> + <text + follows="left|top" + height="15" + layout="topleft" + left="10" + name="parcel_owner_label" + top_pad="7" + value="Owner:" + width="80" /> + <text + follows="left|top|right" + height="15" + layout="topleft" + left_pad="0" + name="parcel_owner" + top_delta="0" use_ellipses="true" - value="SampleParcel, Name Long (145, 228, 26)" - width="280" /> + value="TempOwner" + width="215" /> <expandable_text follows="left|top|right" height="50" layout="topleft" left="10" name="description" - top_pad="10" + top_pad="7" value="Du waltz die spritz" width="280" /> <icon @@ -163,19 +184,38 @@ width="268" /> <panel follows="left|top|right" - height="55" + height="81" layout="topleft" left="10" name="landmark_info_panel" top_pad="10" width="290"> + <view_border + bevel_style="none" + follows="top|left" + height="0" + layout="topleft" + left="0" + name="lod_tab_border" + top_pad="5" + width="290" /> + <text + follows="left|top" + height="15" + layout="topleft" + left="0" + name="this_landmark" + top_pad="8" + width="90"> + This landmark: + </text> <text follows="left|top" height="15" layout="topleft" left="0" name="owner_label" - top_pad="10" + top_pad="8" value="Owner:" width="90" /> <text @@ -237,9 +277,13 @@ top_pad="10" value="Title:" width="290" /> - <text + <line_editor + text_readonly_color="white" + enabled="false" + use_bg_color="true" + bg_color="DkGray0" parse_urls="false" - follows="left|top" + follows="left|top|right" height="22" layout="topleft" left="0" @@ -269,7 +313,7 @@ value="My notes:" width="290" /> <text_editor - bg_readonly_color="DkGray2" + bg_readonly_color="DkGray0" follows="all" height="75" layout="topleft" @@ -300,6 +344,17 @@ name="folder_combo" top_pad="5" width="200" /> + <button + follows="bottom|left|right" + height="23" + label="Edit" + layout="topleft" + left="0" + mouse_opaque="false" + name="edit_btn" + tool_tip="Edit landmark information" + top_pad="-42" + width="100" /> </panel> </panel> </scroll_container> diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index afce9f6eb5..3f7444dec3 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -321,6 +321,7 @@ It is calculated as border_size + 2*UIResizeBarOverlap name="folder_view" top_pad="0" width="313" + preinitialize_views="false" visible="false"/> <panel name="filtered_wearables_panel" background_opaque="true" diff --git a/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml b/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml index eeb930485e..6c8cc9d39a 100644 --- a/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_outfits_inventory.xml @@ -30,7 +30,6 @@ tab_height="30" tab_position="top" halign="center" - hide_scroll_arrows="true" top="8" width="315"> <panel 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 0dd75b1b55..36b7b0501b 100644 --- a/indra/newview/skins/default/xui/en/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml @@ -200,7 +200,7 @@ <panel bg_alpha_color="DkGray2" follows="left|top|right|bottom" - height="580" + height="597" layout="topleft" left="0" min_height="300" @@ -277,32 +277,25 @@ height="14" layout="topleft" left="10" - name="region_title" - text_color="white" top_pad="5" + width="290" + name="parcel_title" + text_color="white" use_ellipses="true" - value="SampleRegion" - width="290" /> + value="SampleParcel" /> <text parse_urls="false" follows="left|top|right" height="14" layout="topleft" left="10" - name="parcel_title" - top_pad="4" - use_ellipses="true" - value="SampleParcel, Name Long (145, 228, 26)" - width="285" /> - <expandable_text - follows="left|top" - height="50" - layout="topleft" - left="5" - name="description" - top_pad="10" - value="Du waltz die spritz" - width="285" /> + top_pad="5" + width="285" + name="region_title" + text_color="White" + use_ellipses="true"> + Region: [REGIONAMEPOS] + </text> <text follows="left|top" height="14" @@ -310,20 +303,28 @@ left="10" name="owner_label" text_color="White" - top_pad="0" + top_pad="2" value="Owner:" - width="90" /> - <!--TODO: HOOK THIS NAME UP WITH AN INSPECTOR --> + width="80" /> <text follows="left|top|right" height="14" layout="topleft" - left_pad="1" - name="owner_value" + left_pad="0" + name="parcel_owner" top_delta="0" value="Alex Superduperlongenamenton" use_ellipses="true" width="200" /> + <expandable_text + follows="left|top" + height="50" + layout="topleft" + left="5" + name="description" + top_pad="10" + value="Du waltz die spritz" + width="285" /> <icon follows="top|left" height="16" @@ -331,7 +332,7 @@ layout="topleft" left="10" name="maturity_icon" - top_delta="0" + top_pad="0" width="18" /> <text follows="left|top|right" diff --git a/indra/newview/skins/default/xui/en/panel_places.xml b/indra/newview/skins/default/xui/en/panel_places.xml index 7d171490e8..1f32ae53ba 100644 --- a/indra/newview/skins/default/xui/en/panel_places.xml +++ b/indra/newview/skins/default/xui/en/panel_places.xml @@ -82,6 +82,7 @@ background_visible="true" layout="topleft" mouse_opaque="false" name="bottom_bar_ls0" + animate="false" left="4" orientation="horizontal" top="0" @@ -150,8 +151,32 @@ background_visible="true" width="85" /> </layout_panel> </layout_stack> - </layout_panel> - + </layout_panel> + <!--*********************** Options button ***********************--> + <layout_panel + follows="bottom|right" + height="23" + layout="bottomleft" + left_pad="0" + mouse_opaque="false" + visible="false" + name="lp_options" + auto_resize="false" + width="23"> + <menu_button + follows="bottom|right" + height="23" + image_disabled="ComboButton_UpOff" + image_unselected="ComboButton_UpOff" + image_selected="ComboButton_UpSelected" + layout="topleft" + mouse_opaque="false" + name="overflow_btn" + tool_tip="Show additional options" + top="0" + left="0" + width="23" /> + </layout_panel> <layout_panel follows="bottom|left|right" height="23" @@ -159,69 +184,9 @@ background_visible="true" left_pad="0" mouse_opaque="false" name="lp2" - auto_resize="true" + auto_resize="true" width="116"> - - <!--*********************** Edit, Options buttons ***********************--> - - <layout_stack - follows="bottom|left|right" - height="23" - layout="topleft" - mouse_opaque="false" - name="bottom_bar_ls3" - left="0" - orientation="horizontal" - top="0" - width="113"> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="0" - mouse_opaque="false" - name="edit_btn_lp" - auto_resize="true" - width="84"> - <button - follows="bottom|left|right" - height="23" - label="Edit" - layout="topleft" - left="1" - mouse_opaque="false" - name="edit_btn" - tool_tip="Edit landmark information" - top="0" - width="83" /> - </layout_panel> - - <layout_panel - follows="bottom|right" - height="23" - layout="bottomleft" - left_pad="0" - mouse_opaque="false" - name="overflow_btn_lp" - auto_resize="true" - width="24"> - <menu_button - follows="bottom|right" - height="23" - image_disabled="ComboButton_UpOff" - image_unselected="ComboButton_UpOff" - image_selected="ComboButton_UpSelected" - layout="topleft" - mouse_opaque="false" - name="overflow_btn" - tool_tip="Show additional options" - top="0" - left="1" - width="23" /> - </layout_panel> - </layout_stack> - + <!--*********************** Profile button ***********************--> <layout_stack diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index ece6c95080..c023cb036e 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -52,7 +52,7 @@ </check_box> <check_box - control_name="VoiceCallsFriendsOnly" + enabled="false" height="16" label="Only friends and groups can call or IM me" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_controls.xml b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml new file mode 100644 index 0000000000..9dab7d34e6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<panel + border="true" + follows="all" + height="408" + label="Controls" + layout="topleft" + left="102" + name="controls" + top="1" + width="517"> + <combo_box + follows="top|left" + layout="topleft" + top="6" + left="10" + height="23" + width="232" + name="key_mode"> + <combo_box.item + label="When in third person" + name="third_person" + value="1"/> + <combo_box.item + label="When in first person mode (Mouselook)" + name="first_person" + value="0"/> + <combo_box.item + label="When editing avatar" + name="edit_avatar" + value="2"/> + <combo_box.item + label="When sitting" + name="sitting" + value="3"/> + </combo_box> + + <button + follows="top|left" + layout="topleft" + top="6" + right="-10" + height="23" + width="140" + label="Restore Defaults" + tool_tip="Restores default values for all control modes." + name="restore_defaults"/> + + <scroll_list + draw_heading="true" + follows="all" + layout="topleft" + column_padding="0" + selection_type="header" + top="31" + left="3" + bottom="-3" + right="-3" + can_sort="false" + multi_select="false" + name="controls_list" + fg_disable_color="ScrollUnselectedColor"/> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml index c2defdd772..2ea20570b1 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml @@ -463,63 +463,24 @@ enabled_control="EnableVoiceChat" control_name="PushToTalkToggle" height="15" - label="Toggle speak on/off when I press:" + label="Toggle speak on/off when I press button in toolbar" layout="topleft" left="44" name="push_to_talk_toggle_check" width="237" tool_tip="When in toggle mode, press and release the trigger key ONCE to switch your microphone on or off. When not in toggle mode, the microphone broadcasts your voice only while the trigger is being held down." top_pad="3"/> - <line_editor - follows="top|left" - control_name="PushToTalkButton" - enabled="false" - enabled_control="EnableVoiceChat" - height="23" - left="80" - max_length_bytes="200" - name="modifier_combo" - label="Push-to-Speak trigger" - top_pad="3" - width="200" /> - <button - layout="topleft" - follows="top|left" - enabled_control="EnableVoiceChat" - height="23" - label="Set Key" - left_pad="5" - name="set_voice_hotkey_button" - width="100"> - <button.commit_callback - function="Pref.VoiceSetKey" /> - </button> - <button - enabled_control="EnableVoiceChat" - follows="top|left" - halign="center" - height="23" - image_overlay="Refresh_Off" - layout="topleft" - tool_tip="Reset to Middle Mouse Button" - mouse_opaque="true" - name="set_voice_middlemouse_button" - left_pad="5" - width="25"> - <button.commit_callback - function="Pref.VoiceSetMiddleMouse" /> - </button> <button control_name="ShowDeviceSettings" follows="left|top" height="23" is_toggle="true" - label="Input/Output devices" + label="Voice Input/Output devices" layout="topleft" left="20" top_pad="6" name="device_settings_btn" - width="190"> + width="230"> </button> <panel layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index ed37e9e2cc..545c01935b 100644 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -145,10 +145,6 @@ name="Help" width="112"> <menu_item_call - label="Help..." - layout="topleft" - name="Help..." /> - <menu_item_call label="Keyword Help..." layout="topleft" name="Keyword Help..." /> diff --git a/indra/newview/skins/default/xui/en/panel_settings_sky_atmos.xml b/indra/newview/skins/default/xui/en/panel_settings_sky_atmos.xml index bec793bbee..6f82a0efa1 100644 --- a/indra/newview/skins/default/xui/en/panel_settings_sky_atmos.xml +++ b/indra/newview/skins/default/xui/en/panel_settings_sky_atmos.xml @@ -203,10 +203,10 @@ Ice Level: </text> <slider - decimal_digits="1" + decimal_digits="3" follows="left|top" height="14" - increment="0.1" + increment="0.001" initial_value="0" left_delta="5" top_delta="20" diff --git a/indra/newview/skins/default/xui/en/sidepanel_appearance.xml b/indra/newview/skins/default/xui/en/sidepanel_appearance.xml index 2a1eb425ed..1777a0db05 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_appearance.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_appearance.xml @@ -32,7 +32,7 @@ width="333"> top="5" follows="left|top|right" layout="topleft" - width="303" + width="307" height="33" name="panel_currentlook" > @@ -118,14 +118,14 @@ width="333"> name="Filter" search_button_visible="true" top_pad="10" - width="303" /> + width="307" /> <panel class="panel_outfits_inventory" filename="panel_outfits_inventory.xml" name="panel_outfits_inventory" height="493" min_height="410" - width="320" + width="325" visible="false" left="0" tab_group="1" 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 acb6f5b42a..9a68479d05 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -33,7 +33,7 @@ </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,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </panel.string> <panel.string name="origin_inventory"> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 1bfac6aeb7..440e8b140f 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -28,7 +28,7 @@ <string name="BuildConfig">Build Configuration [BUILD_CONFIG]</string> <string name="AboutPosition"> -You are at [POSITION_LOCAL_0,number,1], [POSITION_LOCAL_1,number,1], [POSITION_LOCAL_2,number,1] in [REGION] located at <nolink>[HOSTNAME]</nolink> ([HOSTIP]) +You are at [POSITION_LOCAL_0,number,1], [POSITION_LOCAL_1,number,1], [POSITION_LOCAL_2,number,1] in [REGION] located at <nolink>[HOSTNAME]</nolink> SLURL: <nolink>[SLURL]</nolink> (global coordinates [POSITION_0,number,1], [POSITION_1,number,1], [POSITION_2,number,1]) [SERVER_VERSION] @@ -107,7 +107,9 @@ Voice Server Version: [VOICE_VERSION] <string name="CertExpired">The certificate returned by the Grid appears to be expired. Please check your system clock, or contact your Grid administrator.</string> <string name="CertKeyUsage">The certificate returned by the server could not be used for SSL. Please contact your Grid administrator.</string> <string name="CertBasicConstraints">Too many certificates were in the servers Certificate chain. Please contact your Grid administrator.</string> + <string name="CertInvalid">Could not load certificate. Please contact your Grid administrator.</string> <string name="CertInvalidSignature">The certificate signature returned by the Grid server could not be verified. Please contact your Grid administrator.</string> + <string name="CertAllocationFailure">Failed to allocate openssl memory for certificate.</string> <string name="LoginFailedNoNetwork">Network error: Could not establish connection, please check your network connection.</string> <string name="LoginFailed">Login failed.</string> @@ -2297,6 +2299,7 @@ For AI Character: Get the closest navigable point to the point provided. <string name="InventoryNoMatchingRecentItems">Didn't find what you're looking for? Try [secondlife:///app/inventory/filters Show filters].</string> <string name="PlacesNoMatchingItems">Didn't find what you're looking for? Try [secondlife:///app/search/places/[SEARCH_TERM] Search].</string> <string name="FavoritesNoMatchingItems">Drag a landmark here to add it to your favorites.</string> + <string name="MarketplaceNoListing">You have no listings yet.</string> <string name="MarketplaceNoMatchingItems">No items found. Check the spelling of your search string and try again.</string> <string name="InventoryNoTexture">You do not have a copy of this texture in your inventory</string> <string name="InventoryInboxNoItems">Your Marketplace purchases will appear here. You may then drag them into your inventory to use them.</string> @@ -3691,10 +3694,10 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. Also there are some other places where "generic" is used. So, let add string with name="generic" with the same value as "generic_request_error" --> <string name="generic"> - Error making request, please try again later. + Please close and reopen the conversation, or relog and try again. </string> <string name="generic_request_error"> - Error making request, please try again later. + Please close and reopen the conversation, or relog and try again. </string> <string name="insufficient_perms_error"> You do not have sufficient permissions. @@ -4088,6 +4091,15 @@ Try enclosing path to the editor with double quotes. <string name="Z">Z</string> <!-- Key names end --> + <!-- Mouse button names (short) begin --> + <string name="LMB">LMB</string> + <string name="MMB">MMB</string> + <string name="RMB">RMB</string> + <string name="MB4">MB4</string> + <string name="MB5">MB5</string> + <string name="Double LMB">Double LMB</string> + <!-- Mouse button names end --> + <!-- llviewerwindow --> <string name="BeaconParticle">Viewing particle beacons (blue)</string> <string name="BeaconPhysical">Viewing physical object beacons (green)</string> diff --git a/indra/newview/skins/default/xui/es/floater_buy_currency.xml b/indra/newview/skins/default/xui/es/floater_buy_currency.xml index dbff3fcf0e..086150dd57 100644 --- a/indra/newview/skins/default/xui/es/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/es/floater_buy_currency.xml @@ -60,8 +60,7 @@ no el objeto. </text> <button label="Comprar ahora" name="buy_btn"/> <button label="Cancelar" name="cancel_btn"/> - <text name="info_cannot_buy" left="150" font="SansSerifBig"> + <floater.string name="info_cannot_buy"> No se pudo hacer la compra - </text> - <button label="Ir a la web" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/es/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/es/floater_texture_ctrl.xml index 2e409ef69c..a77dd99af0 100644 --- a/indra/newview/skins/default/xui/es/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/es/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> Texturas múltiples </text> - <radio_group name="mode_selection"> - <radio_item label="Inventario" name="inventory" value="0"/> - <radio_item label="Local" name="local" value="1"/> - <radio_item label="Hornear" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - Tamaño: [DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="Inventario" name="inventory" value="0"/> + <combo_box.item label="Local" name="local" value="1"/> + </combo_box> <button label="Por defecto" label_selected="Por defecto" name="Default" width="84"/> <button label="Blanca" label_selected="Blanca" name="Blank"/> <button label="Ninguna" label_selected="Ninguna" left="90" name="None"/> diff --git a/indra/newview/skins/default/xui/es/menu_cof_attachment.xml b/indra/newview/skins/default/xui/es/menu_cof_attachment.xml index 7541530601..65e31c0654 100644 --- a/indra/newview/skins/default/xui/es/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/es/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="Tocar" name="touch_attach" /> + <menu_item_call label="Editar" name="edit_item" /> <menu_item_call label="Quitar" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/es/menu_inventory.xml b/indra/newview/skins/default/xui/es/menu_inventory.xml index c426158d3e..d855d6f04f 100644 --- a/indra/newview/skins/default/xui/es/menu_inventory.xml +++ b/indra/newview/skins/default/xui/es/menu_inventory.xml @@ -104,6 +104,7 @@ <menu_item_call label="Ponerme" name="Wearable And Object Wear"/> <menu label="Anexar a" name="Attach To"/> <menu label="Anexar como HUD" name="Attach To HUD"/> + <menu_item_call label="Tocar" name="Attachment Touch" /> <menu_item_call label="Editar" name="Wearable Edit"/> <menu_item_call label="Añadir" name="Wearable Add"/> <menu_item_call label="Quitarse" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/es/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/es/menu_wearable_list_item.xml index 4bffa689e7..cb68ad39a4 100644 --- a/indra/newview/skins/default/xui/es/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/es/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Reemplazar" name="wear_replace"/> <menu_item_call label="Ponerme" name="wear_wear"/> <menu_item_call label="Añadir" name="wear_add"/> + <menu_item_call label="Tocar" name="touch" /> <menu_item_call label="Quitarme / Quitar" name="take_off_or_detach"/> <menu_item_call label="Quitar" name="detach"/> <context_menu label="Anexar a" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/es/menu_wearing_gear.xml b/indra/newview/skins/default/xui/es/menu_wearing_gear.xml index ec13f99a01..01d1b16b58 100644 --- a/indra/newview/skins/default/xui/es/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/es/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="Editar el vestuario" name="edit"/> + <menu_item_call label="Tocar" name="touch"/> + <menu_item_call label="Editar" name="edit_item"/> + <menu_item_call label="Editar el vestuario" name="edit_outfit"/> <menu_item_call label="Quitarme" name="takeoff"/> <menu_item_call label="Copiar la lista del vestuario al portapapeles" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/es/menu_wearing_tab.xml b/indra/newview/skins/default/xui/es/menu_wearing_tab.xml index 637a14cf5b..54a7d9b92c 100644 --- a/indra/newview/skins/default/xui/es/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/es/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="Tocar" name="touch_attach"/> <menu_item_call label="Quitarme" name="take_off"/> <menu_item_call label="Quitar" name="detach"/> - <menu_item_call label="Editar el vestuario" name="edit"/> + <menu_item_call label="Editar el vestuario" name="edit_outfit"/> <menu_item_call label="Editar" name="edit_item"/> <menu_item_call label="Mostrar original" name="show_original"/> </context_menu> diff --git a/indra/newview/skins/default/xui/es/notifications.xml b/indra/newview/skins/default/xui/es/notifications.xml index 86e3b5d38b..c7750320d5 100644 --- a/indra/newview/skins/default/xui/es/notifications.xml +++ b/indra/newview/skins/default/xui/es/notifications.xml @@ -265,6 +265,10 @@ La inicialización del mercado ha fallado por un error del sistema o de la red. ¿Quieres revocar los derechos de modificación a los residentes seleccionados? <usetemplate name="okcancelbuttons" notext="No" yestext="Sí"/> </notification> + <notification name="GroupNameLengthWarning"> + El nombre de un grupo debe contener entre [MIN_LEN] y [MAX_LEN] caracteres. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UnableToCreateGroup"> No se ha podido crear el grupo. [MESSAGE] @@ -356,7 +360,7 @@ Si no quieres que este rol siga teniendo dichas capacidades, deshabilítalas inm No tienes dinero suficiente para entrar. </notification> <notification name="CreateGroupCost"> - Crear este grupo te costará 100 L$. + Crear este grupo costará L$[COST]. Los grupos necesitan más de un miembro. Si no, son borrados permanentemente. Por favor, invita a miembros en las próximas 48 horas. <usetemplate canceltext="Cancelar" name="okcancelbuttons" notext="Cancelar" yestext="Crear un grupo por 100 L$"/> @@ -498,6 +502,9 @@ debes estar dentro de ella. <notification name="ErrorEncodingSnapshot"> Error al codificar la foto. </notification> + <notification name="ErrorCannotAffordUpload"> + Necesitas L$[COST] para subir este objeto. + </notification> <notification name="ErrorPhotoCannotAfford"> Necesitas [COST] L$ para guardar una foto en el inventario. Puedes comprar L$ o bien guardar la foto en tu equipo. </notification> @@ -1745,11 +1752,14 @@ Haz clic en OK para instalar. <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="GroupLimitInfo"> - El límite de grupos para las cuentas básicas es de [MAX_BASIC], y para -las cuentas [https://secondlife.com/premium/ Premium] es de [MAX_PREMIUM]. -Si has bajado la categoría de tu cuenta, tendrás que estar por debajo del límite de [MAX_BASIC] grupos para poder apuntarte a más grupos. - -[https://secondlife.com/my/account/membership.php Cámbiate hoy a Premium] + Los residentes con membresías Básicas pueden unirse a hasta [MAX_BASIC] grupos. +Las membresías Premium permiten hasta [MAX_PREMIUM]. [https://secondlife.com/my/account/membership.php? Aprende más al respecto o mejora tu membresía] + <usetemplate name="okbutton" yestext="Cerrar"/> + </notification> + <notification name="GroupLimitInfoPlus"> + Los residentes con membresías Básicas pueden unirse a hasta [MAX_BASIC] cinco grupos. +Las membresías Premium permiten hasta [MAX_PREMIUM]. Las membresías Premium Plus permiten +hasta [MAX_PREMIUM_PLUS]. [https://secondlife.com/my/account/membership.php? Aprende más al respecto o mejora tu membresía] <usetemplate name="okbutton" yestext="Cerrar"/> </notification> <notification name="KickUser"> @@ -3304,6 +3314,22 @@ Por tu seguridad, serán bloqueadas durante unos segundos. Un moderador ha silenciado tu voz. <usetemplate name="okbutton" yestext="OK"/> </notification> + <notification name="FailedToGetBenefits"> + Desafortunadamente no fuimos capaces de obtener información sobre los beneficios para esta sesión. Esto no debería suceder en un espacio de producción normal. Por favor contacte con soporte. Esta sesión no funcionara normalmente y recomendamos reiniciar. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadCostConfirmation"> + Esto subirá [COUNT] objetos por un costo total de L$[COST]. ¿Deseas continuar con la subida? + <usetemplate name="okcancelbuttons" notext="Cancelar" yestext="Subir"/> + </notification> + <notification name="BulkUploadNoCompatibleFiles"> + Los archivos seleccionados no pueden ser subidos en grupo. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadIncompatibleFiles"> + Algunos de los archivos seleccionados no pueden ser subidos en grupo. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UploadCostConfirmation"> Esta carga te costará [PRECIO] L$. ¿Deseas continuar? <usetemplate name="okcancelbuttons" notext="Cancelar" yestext="Subir"/> diff --git a/indra/newview/skins/default/xui/es/panel_people.xml b/indra/newview/skins/default/xui/es/panel_people.xml index 909743c325..73b9af3665 100644 --- a/indra/newview/skins/default/xui/es/panel_people.xml +++ b/indra/newview/skins/default/xui/es/panel_people.xml @@ -18,7 +18,7 @@ <string name="no_groups_msg" value="¿Buscas grupos en que participar? Prueba la [secondlife:///app/search/groups Búsqueda]."/> <string name="MiniMapToolTipMsg" value="[REGION](Pulsa dos veces para abrir el mapa, pulsa mayús y arrastra para obtener una panorámica)"/> <string name="AltMiniMapToolTipMsg" value="[REGION](Pulsa dos veces para teleportarte, pulsa mayús y arrastra para obtener una panorámica)"/> - <string name="GroupCountWithInfo" value="Perteneces a [COUNT] grupos y puedes unirte a [REMAINING] más. [secondlife:/// ¿Quieres más?]"/> + <string name="GroupCountWithInfo" value="Perteneces a [COUNT] grupos y puedes unirte a [REMAINING] más. [secondlife:/// Incrementa tu límite]"/> <tab_container name="tabs"> <panel label="CERCANA" name="nearby_panel"> <panel label="bottom_panel" name="nearby_buttons_panel"> @@ -51,7 +51,7 @@ <dnd_button name="minus_btn" tool_tip="Dejar el grupo seleccionado"/> </panel> <text name="groupcount"> - Formas parte de [COUNT] grupos y puedes unirte a [REMAINING] más. + Perteneces a [COUNT] grupos y puedes unirte a [REMAINING] más. </text> </panel> <panel label="RECIENTE" name="recent_panel"> diff --git a/indra/newview/skins/default/xui/es/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/es/panel_preferences_advanced.xml index 007101b8fe..0ba676898f 100644 --- a/indra/newview/skins/default/xui/es/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/es/panel_preferences_advanced.xml @@ -28,5 +28,5 @@ <check_box label="Mostrar la selección de cuadrícula al iniciar sesión" name="show_grid_selection_check"/> <check_box label="Mostrar el menú Avanzado" name="show_advanced_menu_check"/> <check_box label="Mostrar el menú Desarrollar" name="show_develop_menu_check"/> - <button label="Permisos de creación predeterminados" name="default_creation_permissions"/> + <button label="Permisos de creación predeterminados" name="default_creation_permissions" width="235"/> </panel> diff --git a/indra/newview/skins/default/xui/es/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/es/panel_preferences_graphics1.xml index 816c698548..47815d0296 100644 --- a/indra/newview/skins/default/xui/es/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/es/panel_preferences_graphics1.xml @@ -32,10 +32,10 @@ </text> <check_box initial_value="true" label="Renderizar siempre los amigos" name="AlwaysRenderFriends"/> <button label="Excepciones..." name="RenderExceptionsButton"/> - <button label="Guardar configuración como valor predefinido..." name="PrefSaveButton"/> - <button label="Cargar predefinido..." name="PrefLoadButton"/> + <button label="Guardar configuración como valor predefinido" name="PrefSaveButton" width="260" left="5"/> + <button label="Cargar predefinido" name="PrefLoadButton" left_pad="7"/> min_val="0.125" - <button label="Eliminar predefinido..." name="PrefDeleteButton"/> - <button label="Restablecer la configuración recomendada" name="Defaults"/> + <button label="Eliminar predefinido" name="PrefDeleteButton" width="117" left_pad="7"/> + <button label="Restablecer la configuración recomendada" name="Defaults" width="248"/> <button label="Configuración avanzada..." name="AdvancedSettings"/> </panel> diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index af66907f8d..f5e7d0bf4e 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -1628,11 +1628,14 @@ Si sigues recibiendo el mismo mensaje, solicita ayuda al personal de asistencia <string name="MarketplaceUpdating"> actualizando... </string> + <string name="UploadFeeInfo"> + Las tasas se basan en tu nivel de suscripción. Niveles más altos tienen tasas más bajas. [https://secondlife.com/my/account/membership.php? Aprende más al respecto] + </string> <string name="Open landmarks"> - Abrir hitos + Abrir puntos destacados </string> <string name="Unconstrained"> - Sin restricciones + Sin Restricciones </string> <string name="no_transfer" value="(no transferible)"/> <string name="no_modify" value="(no modificable)"/> diff --git a/indra/newview/skins/default/xui/fr/floater_buy_currency.xml b/indra/newview/skins/default/xui/fr/floater_buy_currency.xml index c295172abf..55b0d1825a 100644 --- a/indra/newview/skins/default/xui/fr/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/fr/floater_buy_currency.xml @@ -60,8 +60,7 @@ le Lindex... </text> <button label="Acheter" name="buy_btn"/> <button label="Annuler" name="cancel_btn"/> - <text name="info_cannot_buy" left="160" width="200"> + <floater.string name="info_cannot_buy" left="160" width="200"> Achat impossible - </text> - <button label="Accéder au Web" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/fr/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/fr/floater_texture_ctrl.xml index 2925727b48..a4de7954c5 100644 --- a/indra/newview/skins/default/xui/fr/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/fr/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> Textures multiples </text> - <radio_group name="mode_selection"> - <radio_item label="Inventaire" name="inventory" value="0"/> - <radio_item label="Local" name="local" value="1"/> - <radio_item label="Figer" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - Taille : [DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="Inventaire" name="inventory" value="0"/> + <combo_box.item label="Local" name="local" value="1"/> + </combo_box> <button label="Défaut" label_selected="Défaut" name="Default" width="60"/> <button label="Vierge" label_selected="Vierge" name="Blank" width="60"/> <button label="Aucune" label_selected="Aucune" left="68" name="None" width="60"/> diff --git a/indra/newview/skins/default/xui/fr/menu_cof_attachment.xml b/indra/newview/skins/default/xui/fr/menu_cof_attachment.xml index a4ead48b6b..32bc564d72 100644 --- a/indra/newview/skins/default/xui/fr/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/fr/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="Toucher" name="touch_attach" /> + <menu_item_call label="Modifier" name="edit_item" /> <menu_item_call label="Détacher" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/fr/menu_inventory.xml b/indra/newview/skins/default/xui/fr/menu_inventory.xml index f81723e6cf..5d66d0998b 100644 --- a/indra/newview/skins/default/xui/fr/menu_inventory.xml +++ b/indra/newview/skins/default/xui/fr/menu_inventory.xml @@ -105,6 +105,7 @@ <menu_item_call label="Porter" name="Wearable And Object Wear"/> <menu label="Attacher à" name="Attach To"/> <menu label="Attacher au HUD " name="Attach To HUD"/> + <menu_item_call label="Toucher" name="Attachment Touch" /> <menu_item_call label="Modifier" name="Wearable Edit"/> <menu_item_call label="Ajouter" name="Wearable Add"/> <menu_item_call label="Enlever" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/fr/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/fr/menu_wearable_list_item.xml index 187cb4bcd2..a222de298f 100644 --- a/indra/newview/skins/default/xui/fr/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/fr/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Remplacer" name="wear_replace"/> <menu_item_call label="Porter" name="wear_wear"/> <menu_item_call label="Ajouter" name="wear_add"/> + <menu_item_call label="Toucher" name="touch" /> <menu_item_call label="Enlever / Détacher" name="take_off_or_detach"/> <menu_item_call label="Détacher" name="detach"/> <context_menu label="Attacher à" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/fr/menu_wearing_gear.xml b/indra/newview/skins/default/xui/fr/menu_wearing_gear.xml index c3d9d908b0..90b5ece574 100644 --- a/indra/newview/skins/default/xui/fr/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/fr/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="Modifier la tenue" name="edit"/> + <menu_item_call label="Toucher" name="touch"/> + <menu_item_call label="Modifier" name="edit_item"/> + <menu_item_call label="Modifier la tenue" name="edit_outfit"/> <menu_item_call label="Enlever" name="takeoff"/> <menu_item_call label="Copier la liste de la tenue dans le presse-papiers" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/fr/menu_wearing_tab.xml b/indra/newview/skins/default/xui/fr/menu_wearing_tab.xml index 5a7193a7cc..af115b956e 100644 --- a/indra/newview/skins/default/xui/fr/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/fr/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="Toucher" name="touch_attach"/> <menu_item_call label="Enlever" name="take_off"/> <menu_item_call label="Détacher" name="detach"/> - <menu_item_call label="Modifier la tenue" name="edit"/> + <menu_item_call label="Modifier la tenue" name="edit_outfit"/> <menu_item_call label="Modifier" name="edit_item"/> <menu_item_call label="Afficher l’original" name="show_original"/> </context_menu> diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml index 41e4ad13f9..e84de375d8 100644 --- a/indra/newview/skins/default/xui/fr/notifications.xml +++ b/indra/newview/skins/default/xui/fr/notifications.xml @@ -266,6 +266,10 @@ Souhaitez-vous accorder des droits d'édition aux résidents sélectionnés Souhaitez-vous retirer les droits d'édition aux résidents selectionnés ? <usetemplate name="okcancelbuttons" notext="Non" yestext="Oui"/> </notification> + <notification name="GroupNameLengthWarning"> + Un nom de groupe doit être compris entre [MIN_LEN] et [MAX_LEN] caractères. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UnableToCreateGroup"> Impossible de créer le groupe. [MESSAGE] @@ -359,7 +363,7 @@ Souhaitez-vous continuer ? Vous n'avez pas suffisamment de L$ pour rejoindre ce groupe. </notification> <notification name="CreateGroupCost"> - La création de ce groupe coûte 100 L$. + La création de ce groupe coûtera L$[COST]. Les groupes doivent comporter plus d'un membre, sinon ils sont supprimés. Veuillez inviter des membres d'ici 48 heures. <usetemplate canceltext="Annuler" name="okcancelbuttons" notext="Annuler" yestext="Créer un groupe pour 100 L$"/> @@ -500,6 +504,9 @@ Pour ne placer le média que sur une seule face, choisissez Sélectionner une fa <notification name="ErrorEncodingSnapshot"> Erreur d'encodage de la photo. </notification> + <notification name="ErrorCannotAffordUpload"> + Vous avez besoin de L$[COST] pour charger cet élément. + </notification> <notification name="ErrorPhotoCannotAfford"> Il vous faut [COST] L$ pour enregistrer une photo dans votre inventaire. Vous pouvez acheter des L$ ou enregistrer la photo sur votre ordinateur. </notification> @@ -1736,11 +1743,14 @@ Quitter le groupe ? <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="GroupLimitInfo"> - Le nombre de groupes maximum est [MAX_BASIC] pour les comptes basiques et -[MAX_PREMIUM] pour les comptes [https://secondlife.com/premium/ premium]. -Si vous avez rétrogradé votre compte, vous devez réduire votre nombre de groupes pour passer sous le nombre de groupes maximum ([MAX_BASIC]) avant de pouvoir en rejoindre d’autres. - -[https://secondlife.com/my/account/membership.php Mettez à niveau dès aujourd’hui !] + Les résidents ayant une adhésion de base peuvent s'inscrire à [MAX_BASIC] groupes maximum. +Les adhésions premium permettent jusqu'à [MAX_PREMIUM]. [https://secondlife.com/my/account/membership.php? En savoir plus ou actualiser] + <usetemplate name="okbutton" yestext="Fermer"/> + </notification> + <notification name="GroupLimitInfoPlus"> + Les résidents ayant une adhésion de base peuvent s'inscrire à [MAX_BASIC] groupes maximum. +Les adhésions premium permettent jusqu'à [MAX_PREMIUM]. Les adhésions à Premium Plus permettent +jusqu'à [MAX_PREMIUM_PLUS]. [https://secondlife.com/my/account/membership.php? En savoir plus ou actualiser] <usetemplate name="okbutton" yestext="Fermer"/> </notification> <notification name="KickUser"> @@ -3304,6 +3314,22 @@ Elles vont être bloquées pendant quelques secondes pour votre sécurité. Le modérateur ignore vos paroles. <usetemplate name="okbutton" yestext="OK"/> </notification> + <notification name="FailedToGetBenefits"> + Malheureusement, nous n'avons pas pu obtenir d'informations sur les avantages pour cette session. Cela ne devrait pas se produire dans un environnement de production normal. Veuillez contacter le service d'assistance. Cette session ne fonctionnera pas normalement et nous vous recommandons de recommencer. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadCostConfirmation"> + Ceci permettra de charger [COUNT] éléments pour un coût total de L$[COST]. Souhaitez-vous poursuivre le téléchargement ? + <usetemplate name="okcancelbuttons" notext="Annuler" yestext="Charger"/> + </notification> + <notification name="BulkUploadNoCompatibleFiles"> + Les fichiers sélectionnés ne peuvent pas être téléchargés en masse. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadIncompatibleFiles"> + Certains des fichiers sélectionnés ne peuvent pas être téléchargés en masse. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UploadCostConfirmation"> Ce chargement coûtera [PRICE] L$. Continuer ? <usetemplate name="okcancelbuttons" notext="Annuler" yestext="Charger"/> diff --git a/indra/newview/skins/default/xui/fr/panel_people.xml b/indra/newview/skins/default/xui/fr/panel_people.xml index 95cd13eb94..3be6bae52a 100644 --- a/indra/newview/skins/default/xui/fr/panel_people.xml +++ b/indra/newview/skins/default/xui/fr/panel_people.xml @@ -18,7 +18,7 @@ Pour rechercher des résidents avec qui passer du temps, utilisez [secondlife:// <string name="no_groups_msg" value="Vous souhaitez trouver des groupes à rejoindre ? Utilisez [secondlife:///app/search/groups Rechercher]."/> <string name="MiniMapToolTipMsg" value="[REGION](Carte : double-clic ; Panoramique : Maj + faire glisser)"/> <string name="AltMiniMapToolTipMsg" value="[REGION](Téléportation : double-clic ; Panoramique : Maj + faire glisser)"/> - <string name="GroupCountWithInfo" value="Vous appartenez à [COUNT] groupes, et pouvez en rejoindre [REMAINING] autres. [secondlife:/// Vous en voulez plus ?]"/> + <string name="GroupCountWithInfo" value="Vous appartenez à [COUNT] groupes et pouvez rejoindre [REMAINING] autres groupes. [secondlife:/// Augmentez votre limite]"/> <tab_container name="tabs"> <panel label="PRÈS DE VOUS" name="nearby_panel"> <panel label="bottom_panel" name="nearby_buttons_panel"> @@ -51,7 +51,7 @@ Pour rechercher des résidents avec qui passer du temps, utilisez [secondlife:// <dnd_button name="minus_btn" tool_tip="Quitter le groupe sélectionné"/> </panel> <text name="groupcount"> - Vous appartenez à [COUNT] groupes, et pouvez en rejoindre [REMAINING] autres. + Vous appartenez à [COUNT] groupes et pouvez rejoindre [REMAINING] autres groupes. </text> </panel> <panel label="RÉCENT" name="recent_panel"> diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/fr/panel_preferences_graphics1.xml index 7117ace7e1..46d6305290 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_graphics1.xml @@ -32,10 +32,10 @@ </text> <check_box initial_value="true" label="Toujours effectuer le rendu des amis" name="AlwaysRenderFriends"/> <button label="Exceptions..." name="RenderExceptionsButton"/> - <button label="Enregistrer les paramètres comme préréglage..." name="PrefSaveButton"/> - <button label="Charger un préréglage..." name="PrefLoadButton"/> + <button label="Enregistrer les paramètres comme préréglage" name="PrefSaveButton" width="260" /> + <button label="Charger un préréglage" name="PrefLoadButton" width="132"/> min_val="0,125" - <button label="Supprimer un préréglage..." name="PrefDeleteButton"/> - <button label="Réinitialiser les paramètres recommandés" name="Defaults"/> + <button label="Supprimer un préréglage" name="PrefDeleteButton" width="148" top_delta="30" left_pad="-220"/> + <button label="Réinitialiser les paramètres recommandés" name="Defaults" width="255" top_delta="35"/> <button label="Paramètres avancés" name="AdvancedSettings"/> </panel> diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 6d40ab4bc9..f26eac545a 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -1646,11 +1646,14 @@ Si vous continuez de recevoir ce message, contactez l’assistance Second Life <string name="MarketplaceUpdating"> mise à jour... </string> + <string name="UploadFeeInfo"> + Les frais dépendent de votre niveau d'abonnement. Les niveaux supérieurs sont soumis à des frais moins élevés. [https://secondlife.com/my/account/membership.php? En savoir plus] + </string> <string name="Open landmarks"> - Ouvrir les repères + Points de repère ouverts </string> <string name="Unconstrained"> - Sans contraintes + Sans contrainte </string> <string name="no_transfer" value=" (pas de transfert)"/> <string name="no_modify" value=" (pas de modification)"/> @@ -5097,6 +5100,15 @@ Veuillez vous reporter à http://status.secondlifegrid.net afin de déterminer s <string name="Chat" value=" Chat :"> Chat </string> + <string name="BaseMembership"> + Base + </string> + <string name="PremiumMembership"> + Premium + </string> + <string name="Premium PlusMembership"> + Premium Plus + </string> <string name="DeleteItems"> Supprimer les articles sélectionnés ? </string> diff --git a/indra/newview/skins/default/xui/it/floater_buy_currency.xml b/indra/newview/skins/default/xui/it/floater_buy_currency.xml index 53a2057455..522d26373e 100644 --- a/indra/newview/skins/default/xui/it/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/it/floater_buy_currency.xml @@ -60,8 +60,7 @@ l'oggetto. </text> <button label="Acquista" name="buy_btn"/> <button label="Annulla" name="cancel_btn"/> - <text name="info_cannot_buy" left="160" font="SansSerifBig"> + <floater.string name="info_cannot_buy" left="160" font="SansSerifBig"> Non in grado di acquistare - </text> - <button label="Continua sul Web" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/it/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/it/floater_texture_ctrl.xml index 2e4644cef3..f857bfe49f 100644 --- a/indra/newview/skins/default/xui/it/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/it/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> Texture multiple </text> - <radio_group name="mode_selection"> - <radio_item label="Inventario" name="inventory" value="0"/> - <radio_item label="Locale" name="local" value="1"/> - <radio_item label="Effettua il bake" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - Dimensioni: [DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="Inventario" name="inventory" value="0"/> + <combo_box.item label="Locale" name="local" value="1"/> + </combo_box> <button label="Default" label_selected="Default" name="Default"/> <button label="Vuoto" label_selected="Vuoto" name="Blank"/> <button label="Niente" label_selected="Niente" name="None"/> diff --git a/indra/newview/skins/default/xui/it/menu_cof_attachment.xml b/indra/newview/skins/default/xui/it/menu_cof_attachment.xml index 699490c8f1..8861cc726f 100644 --- a/indra/newview/skins/default/xui/it/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/it/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="Tocca" name="touch_attach" /> + <menu_item_call label="Modifica" name="edit_item" /> <menu_item_call label="Stacca" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/it/menu_inventory.xml b/indra/newview/skins/default/xui/it/menu_inventory.xml index 84ec7c4bd4..d2fbcafe97 100644 --- a/indra/newview/skins/default/xui/it/menu_inventory.xml +++ b/indra/newview/skins/default/xui/it/menu_inventory.xml @@ -105,6 +105,7 @@ <menu_item_call label="Indossa" name="Wearable And Object Wear"/> <menu label="Attacca a" name="Attach To"/> <menu label="Attacca all'HUD" name="Attach To HUD"/> + <menu_item_call label="Tocca" name="Attachment Touch" /> <menu_item_call label="Modifica" name="Wearable Edit"/> <menu_item_call label="Aggiungi" name="Wearable Add"/> <menu_item_call label="Togli" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/it/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/it/menu_wearable_list_item.xml index c9a02d8a86..23abdd7bf2 100644 --- a/indra/newview/skins/default/xui/it/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/it/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Sostituisci" name="wear_replace"/> <menu_item_call label="Indossa" name="wear_wear"/> <menu_item_call label="Aggiungi" name="wear_add"/> + <menu_item_call label="Tocca" name="touch" /> <menu_item_call label="Togli / Stacca" name="take_off_or_detach"/> <menu_item_call label="Stacca" name="detach"/> <context_menu label="Attacca a" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/it/menu_wearing_gear.xml b/indra/newview/skins/default/xui/it/menu_wearing_gear.xml index de25f88aca..2f3a2aea43 100644 --- a/indra/newview/skins/default/xui/it/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/it/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="Modifica vestiario" name="edit"/> + <menu_item_call label="Tocca" name="touch"/> + <menu_item_call label="Modifica" name="edit_item"/> + <menu_item_call label="Modifica vestiario" name="edit_outfit"/> <menu_item_call label="Togli" name="takeoff"/> <menu_item_call label="Copia gruppo vestiti negli Appunti" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/it/menu_wearing_tab.xml b/indra/newview/skins/default/xui/it/menu_wearing_tab.xml index ec375e5240..08b56888a3 100644 --- a/indra/newview/skins/default/xui/it/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/it/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="Tocca" name="touch_attach"/> <menu_item_call label="Togli" name="take_off"/> <menu_item_call label="Stacca" name="detach"/> - <menu_item_call label="Modifica vestiario" name="edit"/> + <menu_item_call label="Modifica vestiario" name="edit_outfit"/> <menu_item_call label="Modifica" name="edit_item"/> <menu_item_call label="Mostra originale" name="show_original"/> </context_menu> diff --git a/indra/newview/skins/default/xui/it/notifications.xml b/indra/newview/skins/default/xui/it/notifications.xml index a63b027349..1c43013255 100644 --- a/indra/newview/skins/default/xui/it/notifications.xml +++ b/indra/newview/skins/default/xui/it/notifications.xml @@ -266,6 +266,10 @@ Vuoi concedere i diritti di modifica ai residenti selezionati? Vuoi revocare i permessi di modifica dati ai residenti selezionati? <usetemplate name="okcancelbuttons" notext="No" yestext="Si"/> </notification> + <notification name="GroupNameLengthWarning"> + Il nome di un gruppo deve essere compreso tra [MIN_LEN] e [MAX_LEN] caratteri. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UnableToCreateGroup"> Non è possibile creare il gruppo. [MESSAGE] @@ -360,7 +364,7 @@ Vuoi continuare? Non hai abbastanza L$ per iscriverti a questo gruppo. </notification> <notification name="CreateGroupCost"> - La creazione di questo gruppo costerà L$ 100. + La creazione di questo gruppo ti costerà [COST]L$. I gruppi devono avere più di un partecipante, o saranno eliminati definitivamente. Invita altri partecipanti entro le prossime 48 ore. <usetemplate canceltext="Annulla" name="okcancelbuttons" notext="Annulla" yestext="Crea un gruppo per L$ 100"/> @@ -501,6 +505,9 @@ Per collocare il media su una sola faccia, scegli Seleziona faccia, clicca su un <notification name="ErrorEncodingSnapshot"> Errore nella codifica della fotografia. </notification> + <notification name="ErrorCannotAffordUpload"> + Ti serviranno [COST]L$ per caricare questo articolo. + </notification> <notification name="ErrorPhotoCannotAfford"> Hai bisogno di L$ [COST] per salvare una foto nel tuo inventario. Puoi acquistare L$ o salvare la foto sul tuo computer. </notification> @@ -1740,11 +1747,14 @@ Vuoi cancellare quell'elemento? <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="GroupLimitInfo"> - Il numero massimo di gruppi per gli account Basic è [MAX_BASIC] e -per gli account [https://secondlife.com/premium/ Premium] è [MAX_PREMIUM]. -Se hai ridotto il livello del tuo account, dovrai essere iscritto a meno di [MAX_BASIC] gruppi prima di poter iscriverti a un nuovo gruppo. - -[https://secondlife.com/my/account/membership.php Passa a un livello superiore oggi stesso!] + I residenti con un'iscrizione Base possono aderire fino a [MAX_BASIC] gruppi. +Le iscrizioni Premium consentono fino a [MAX_PREMIUM]. [https://secondlife.com/my/account/membership.php? Per saperne di più o per l'aggiornamento] + <usetemplate name="okbutton" yestext="Chiudi"/> + </notification> + <notification name="GroupLimitInfoPlus"> + I residenti con un'iscrizione Base possono aderire fino a [MAX_BASIC] gruppi. +Le iscrizioni Premium consentono fino a [MAX_PREMIUM]. Le iscrizioni Premium Plus +consentono fino a [MAX_PREMIUM_PLUS]. [https://secondlife.com/my/account/membership.php? Per saperne di più o per l'aggiornamento] <usetemplate name="okbutton" yestext="Chiudi"/> </notification> <notification name="KickUser"> @@ -3306,6 +3316,22 @@ Per sicurezza, verranno bloccati per alcuni secondi. La tua voce è stata interrotta dal moderatore. <usetemplate name="okbutton" yestext="OK"/> </notification> + <notification name="FailedToGetBenefits"> + Purtroppo non siamo stati in grado di ottenere informazioni utili per questa sessione. Questo non dovrebbe accadere in un normale ambiente di produzione. Si prega di contattare il supporto. Questa sessione non funzionerà correttamente, si consiglia di riavviare. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadCostConfirmation"> + Questo caricherà [COUNT] oggetti a un costo totale di [COST]L$. Vuoi continuare con il caricamento? + <usetemplate name="okcancelbuttons" notext="Annulla" yestext="Carica"/> + </notification> + <notification name="BulkUploadNoCompatibleFiles"> + I file selezionati non possono essere caricati. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadIncompatibleFiles"> + Alcuni dei file selezionati non possono essere caricati. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UploadCostConfirmation"> Questo caricamento costerà L$[PRICE]. Continuare con il caricamento? <usetemplate name="okcancelbuttons" notext="Annulla" yestext="Carica"/> diff --git a/indra/newview/skins/default/xui/it/panel_people.xml b/indra/newview/skins/default/xui/it/panel_people.xml index 38a03fb4d2..3df2368ae0 100644 --- a/indra/newview/skins/default/xui/it/panel_people.xml +++ b/indra/newview/skins/default/xui/it/panel_people.xml @@ -18,7 +18,7 @@ Stai cercando persone da frequentare? Prova la [secondlife:///app/worldmap Mappa <string name="no_groups_msg" value="Stai cercando gruppi di cui far parte? Prova [secondlife:///app/search/groups Cerca]."/> <string name="MiniMapToolTipMsg" value="[REGION](Fai doppio clic per aprire la Mappa, premi il tasto Maiusc e trascina per la panoramica)"/> <string name="AltMiniMapToolTipMsg" value="[REGION](Fai doppio clic per teleportarti, premi il tasto Maiusc e trascina per la panoramica)"/> - <string name="GroupCountWithInfo" value="Fai parte di [COUNT] gruppi e puoi iscriverti a [REMAINING] altri. [secondlife:/// Ne vuoi altri?]"/> + <string name="GroupCountWithInfo" value="Fai parte di [COUNT] gruppi e puoi ancora unirti a [REMAINING] gruppi. [secondlife:/// Aumenta il tuo limite]"/> <tab_container name="tabs"> <panel label="NELLE VICINANZE" name="nearby_panel"> <panel label="bottom_panel" name="nearby_buttons_panel"> @@ -51,7 +51,7 @@ Stai cercando persone da frequentare? Prova la [secondlife:///app/worldmap Mappa <dnd_button name="minus_btn" tool_tip="Lascia il gruppo selezionato"/> </panel> <text name="groupcount"> - Fai parte di [COUNT] gruppi e puoi iscriverti a [REMAINING] altri. + Fai parte di [COUNT] gruppi e puoi ancora unirti a [REMAINING] gruppi. </text> </panel> <panel label="RECENTE" name="recent_panel"> diff --git a/indra/newview/skins/default/xui/it/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/it/panel_preferences_graphics1.xml index f3ca9fafb3..c7739b8472 100644 --- a/indra/newview/skins/default/xui/it/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/it/panel_preferences_graphics1.xml @@ -32,10 +32,10 @@ </text> <check_box initial_value="true" label="Esegui sempre il rendering degli amici" name="AlwaysRenderFriends"/> <button label="Eccezioni..." name="RenderExceptionsButton"/> - <button label="Salva impostazioni come valori predefiniti..." name="PrefSaveButton"/> - <button label="Carica valore predefinito..." name="PrefLoadButton"/> + <button label="Salva impostazioni come valori predefiniti" name="PrefSaveButton" width="240" left="4"/> + <button label="Carica valore predefinito" name="PrefLoadButton" width="145" left_pad="7"/> min_val="0.125" - <button label="Elimina valore predefinito..." name="PrefDeleteButton"/> + <button label="Elimina valore predefinito" name="PrefDeleteButton" width="148" left_pad="7"/> <button label="Ripristina impostazioni consigliate" name="Defaults"/> <button label="Impostazioni avanzate..." name="AdvancedSettings"/> </panel> diff --git a/indra/newview/skins/default/xui/it/panel_preferences_setup.xml b/indra/newview/skins/default/xui/it/panel_preferences_setup.xml index aa3ff53f4a..c9d90539e1 100644 --- a/indra/newview/skins/default/xui/it/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/it/panel_preferences_setup.xml @@ -35,5 +35,5 @@ <text name="Proxy Settings:"> Impostazioni proxy: </text> - <button label="Regola impostazioni proxy" label_selected="Sfoglia" name="set_proxy"/> + <button label="Regola impostazioni proxy" label_selected="Sfoglia" name="set_proxy" width="160"/> </panel> diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index ca486f832e..f0466cea81 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -1636,11 +1636,14 @@ Se continui a ricevere questo messaggio, contatta l'assistenza Second Life <string name="MarketplaceUpdating"> in aggiornamento... </string> + <string name="UploadFeeInfo"> + Il costo si basa sul tuo livello di iscrizione. Più alto è il livello, più basso è il costo. [https://secondlife.com/my/account/membership.php? Scopri di più] + </string> <string name="Open landmarks"> - Apri luoghi di riferimento + Luoghi aperti </string> <string name="Unconstrained"> - Libero + Senza limitazioni </string> <string name="no_transfer" value="(nessun trasferimento)"/> <string name="no_modify" value="(nessuna modifica)"/> @@ -5012,6 +5015,15 @@ Consulta la pagina http://status.secondlifegrid.net per determinare se il proble <string name="Chat" value="Chat :"> Chat </string> + <string name="BaseMembership"> + Base + </string> + <string name="PremiumMembership"> + Premium + </string> + <string name="Premium PlusMembership"> + Premium Plus + </string> <string name="DeleteItems"> Cancellare gli elementi selezionati? </string> diff --git a/indra/newview/skins/default/xui/ja/floater_buy_currency.xml b/indra/newview/skins/default/xui/ja/floater_buy_currency.xml index a472f163e3..ac2db917cc 100644 --- a/indra/newview/skins/default/xui/ja/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/ja/floater_buy_currency.xml @@ -59,8 +59,7 @@ </text> <button label="購入する" name="buy_btn"/> <button label="取り消し" name="cancel_btn"/> - <text name="info_cannot_buy"> + <floater.string name="info_cannot_buy"> 購入できません - </text> - <button label="Web サイトに移動" name="error_web" width="140"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/ja/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/ja/floater_texture_ctrl.xml index 05e7bfefd2..1221702e9b 100644 --- a/indra/newview/skins/default/xui/ja/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/ja/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> 複数のテクスチャ </text> - <radio_group name="mode_selection"> - <radio_item label="インベントリ" name="inventory" value="0"/> - <radio_item label="ローカル" name="local" value="1"/> - <radio_item label="構築(ベーク)" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - サイズ: [DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="インベントリ" name="inventory" value="0"/> + <combo_box.item label="ローカル" name="local" value="1"/> + </combo_box> <button label="デフォルト" label_selected="デフォルト" name="Default"/> <button label="ブランク" label_selected="ブランク" name="Blank"/> <button label="なし" label_selected="なし" name="None"/> diff --git a/indra/newview/skins/default/xui/ja/menu_cof_attachment.xml b/indra/newview/skins/default/xui/ja/menu_cof_attachment.xml index e786d02e40..aa07b9476e 100644 --- a/indra/newview/skins/default/xui/ja/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/ja/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="触る" name="touch_attach" /> + <menu_item_call label="編集" name="edit_item" /> <menu_item_call label="取り外す" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/ja/menu_inventory.xml b/indra/newview/skins/default/xui/ja/menu_inventory.xml index ec16f1cf17..7f68c18e0b 100644 --- a/indra/newview/skins/default/xui/ja/menu_inventory.xml +++ b/indra/newview/skins/default/xui/ja/menu_inventory.xml @@ -105,6 +105,7 @@ <menu_item_call label="装着" name="Wearable And Object Wear"/> <menu label="装着先" name="Attach To"/> <menu label="HUD 装着先" name="Attach To HUD"/> + <menu_item_call label="触る" name="Attachment Touch" /> <menu_item_call label="編集" name="Wearable Edit"/> <menu_item_call label="追加" name="Wearable Add"/> <menu_item_call label="取り外す" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/ja/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/ja/menu_wearable_list_item.xml index c402fa0b6d..02029230ba 100644 --- a/indra/newview/skins/default/xui/ja/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/ja/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="交換" name="wear_replace"/> <menu_item_call label="装着" name="wear_wear"/> <menu_item_call label="追加" name="wear_add"/> + <menu_item_call label="触る" name="touch" /> <menu_item_call label="取り外す" name="take_off_or_detach"/> <menu_item_call label="取り外す" name="detach"/> <context_menu label="装着:" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/ja/menu_wearing_gear.xml b/indra/newview/skins/default/xui/ja/menu_wearing_gear.xml index 5334042dc9..48aac2ed05 100644 --- a/indra/newview/skins/default/xui/ja/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/ja/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="アウトフットの編集" name="edit"/> + <menu_item_call label="触る" name="touch"/> + <menu_item_call label="編集" name="edit_item"/> + <menu_item_call label="アウトフットの編集" name="edit_outfit"/> <menu_item_call label="取り外す" name="takeoff"/> <menu_item_call label="アウトフィットのリストをクリップボードにコピー" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/ja/menu_wearing_tab.xml b/indra/newview/skins/default/xui/ja/menu_wearing_tab.xml index bf8e72e457..8c331bc008 100644 --- a/indra/newview/skins/default/xui/ja/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/ja/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="触る" name="touch_attach"/> <menu_item_call label="取り外す" name="take_off"/> <menu_item_call label="取り外す" name="detach"/> - <menu_item_call label="アウトフットの編集" name="edit"/> + <menu_item_call label="アウトフットの編集" name="edit_outfit"/> <menu_item_call label="編集" name="edit_item"/> <menu_item_call label="オリジナルを表示" name="show_original"/> </context_menu> diff --git a/indra/newview/skins/default/xui/ja/notifications.xml b/indra/newview/skins/default/xui/ja/notifications.xml index 96a5cb741a..a66552d3fe 100644 --- a/indra/newview/skins/default/xui/ja/notifications.xml +++ b/indra/newview/skins/default/xui/ja/notifications.xml @@ -266,6 +266,10 @@ 選択した住人から変更権限を取り下げますか? <usetemplate name="okcancelbuttons" notext="いいえ" yestext="はい"/> </notification> + <notification name="GroupNameLengthWarning"> + グループ名は [MIN_LEN] ~ [MAX_LEN] 文字である必要があります。 + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UnableToCreateGroup"> グループを作成できません。 [MESSAGE] @@ -367,7 +371,7 @@ L$ が不足しているのでこのグループに参加することができません。 </notification> <notification name="CreateGroupCost"> - このグループを作るには L$ 100 かかります。 + このグループ作成にかかる費用:L$[COST] 一人ではグループにならないので、永久に削除されてしまいます。 48 時間以内にメンバーを勧誘し、入会してもらってください。 <usetemplate canceltext="キャンセル" name="okcancelbuttons" notext="キャンセル" yestext="L$100 でグループを作成"/> @@ -518,6 +522,9 @@ L$ が不足しているのでこのグループに参加することができ <notification name="ErrorEncodingSnapshot"> スナップショットのエンコード化でエラーが出ました! </notification> + <notification name="ErrorCannotAffordUpload"> + このアイテムをアップロードするためには L$[COST] が必要です。 + </notification> <notification name="ErrorPhotoCannotAfford"> インベントリに写真を保存するには L$[COST] が必要です。L$ を購入するか、代わりに写真をっコンピュータに保存できます。 </notification> @@ -1773,11 +1780,14 @@ https://secondlife.com/support/downloads/ からダウンロードしてくだ <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="GroupLimitInfo"> - ベースアカウントのグループ制限は [MAX_BASIC]、[https://secondlife.com/premium/ プレミアム] アカウントの -グループ制限は [MAX_PREMIUM] です。 -アカウントをダウングレードした場合、さらにグループに参加する前に、下の [MAX_BASIC] グループ制限を取得する必要があります。 - -[https://secondlife.com/my/account/membership.php 今すぐアップグレード!] + ベーシック会員の住民は、最大 [MAX_BASIC] グループまで参加することができます。 +プレミアム会員は、最大 [MAX_PREMIUM] まで可能です。[https://secondlife.com/my/account/membership.php? 詳細、またはアップグレード] + <usetemplate name="okbutton" yestext="閉じる"/> + </notification> + <notification name="GroupLimitInfoPlus"> + ベーシック会員の住民は、最大 [MAX_BASIC] グループまで参加することができます。 +プレミアム会員は、最大 [MAX_PREMIUM] まで可能です。プレミアムプラス会員は、最大 [MAX_PREMIUM_PLUS] まで可能です。 +[https://secondlife.com/my/account/membership.php? 詳細、またはアップグレード] <usetemplate name="okbutton" yestext="閉じる"/> </notification> <notification name="KickUser"> @@ -3349,6 +3359,22 @@ M キーを押して変更します。 モデレーターがあなたのボイスをミュートしました。 <usetemplate name="okbutton" yestext="OK"/> </notification> + <notification name="FailedToGetBenefits"> + 残念ながら、このセッションのベネフィット情報を得ることができませんでした。通常のプロダクション環境で起こることではありません。サポートまでご連絡ください。このセッションは通常通りに作動しませんので、再スタートをお薦めします + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadCostConfirmation"> + 合計 L$[COST] で [COUNT] アイテムがアップロードされます。 アップロードを続けますか? + <usetemplate name="okcancelbuttons" notext="取り消し" yestext="アップロード"/> + </notification> + <notification name="BulkUploadNoCompatibleFiles"> + 選択したファイルは、まとめてアップロードできません。 + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadIncompatibleFiles"> + 選択したファイルのいくつかは、まとめてアップロードできません。 + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UploadCostConfirmation"> このアップロードは L$[PRICE] のコストがかかります。アップロードを続けますか? <usetemplate name="okcancelbuttons" notext="取り消し" yestext="アップロード"/> diff --git a/indra/newview/skins/default/xui/ja/panel_people.xml b/indra/newview/skins/default/xui/ja/panel_people.xml index 5fc4b57a08..0a295855d0 100644 --- a/indra/newview/skins/default/xui/ja/panel_people.xml +++ b/indra/newview/skins/default/xui/ja/panel_people.xml @@ -18,7 +18,7 @@ <string name="no_groups_msg" value="グループをお探しですか? [secondlife:///app/search/groups 検索] をお試しください。"/> <string name="MiniMapToolTipMsg" value="[地域](ダブルクリックで地図を開く。Shift‐ドラッグで水平・垂直移動)"/> <string name="AltMiniMapToolTipMsg" value="[地域](ダブルクリックでテレポート。Shift‐ドラッグで水平・垂直移動)"/> - <string name="GroupCountWithInfo" value="あなたは [COUNT] グループに属しているので、まだ [REMAINING] 参加できます。[secondlife:/// 詳細]"/> + <string name="GroupCountWithInfo" value="あなたは現在、[COUNT] グループに属しています。あと [REMAINING] グループに参加することができます。[secondlife:/// 上限を増やす]"/> <tab_container name="tabs"> <panel label="近く" name="nearby_panel"> <panel label="bottom_panel" name="nearby_buttons_panel"> @@ -51,7 +51,7 @@ <dnd_button name="minus_btn" tool_tip="選択したグループから脱退"/> </panel> <text name="groupcount"> - あなたは[COUNT]グループに属しているので、まだ[REMAINING]参加できます。 + あなたは現在、[COUNT] グループに属しています。あと [REMAINING] グループに参加することができます。 </text> </panel> <panel label="最新" name="recent_panel"> diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 3f46376595..52d6fb0c2b 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -1644,11 +1644,14 @@ support@secondlife.com にお問い合わせください。 <string name="MarketplaceUpdating"> アップデート中... </string> + <string name="UploadFeeInfo"> + 料金はサブスクリプションのレベルにより異なります。レベルが高いほど、料金が下がります。[https://secondlife.com/my/account/membership.php? 詳細] + </string> <string name="Open landmarks"> - ランドマークを開く + オープン ランドマーク </string> <string name="Unconstrained"> - 非拘束 + アンコンストレインド(制約なし) </string> <string name="no_transfer" value=" (再販・プレゼント不可)"/> <string name="no_modify" value=" (編集不可)"/> @@ -5095,6 +5098,15 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ <string name="Chat" value=" チャット:"> チャット </string> + <string name="BaseMembership"> + ベース + </string> + <string name="PremiumMembership"> + プレミアム + </string> + <string name="Premium PlusMembership"> + プレミアムプラス + </string> <string name="DeleteItems"> 選択したアイテムを削除しますか </string> diff --git a/indra/newview/skins/default/xui/pl/floater_buy_currency.xml b/indra/newview/skins/default/xui/pl/floater_buy_currency.xml index 72167e0d3c..a1d703a15a 100644 --- a/indra/newview/skins/default/xui/pl/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/pl/floater_buy_currency.xml @@ -50,8 +50,7 @@ </text> <button label="Kup teraz" name="buy_btn" /> <button label="Anuluj" name="cancel_btn" /> - <text name="info_cannot_buy"> + <floater.string name="info_cannot_buy"> Nie można kupić - </text> - <button label="Odwiedź stronę WWW" name="error_web" /> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/pl/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/pl/floater_texture_ctrl.xml index 8ac158b462..2425213160 100644 --- a/indra/newview/skins/default/xui/pl/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/pl/floater_texture_ctrl.xml @@ -9,13 +9,10 @@ <text name="Multiple"> Wiele tekstur </text> - <radio_group name="mode_selection"> - <radio_item label="Szafa" name="inventory" /> - <radio_item label="Lokalna" name="local" /> - </radio_group> - <text name="unknown"> - Rozm.: [DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="Szafa" name="inventory" /> + <combo_box.item label="Lokalna" name="local" /> + </combo_box> <button label="Domyślna" label_selected="Domyślna" name="Default" /> <button label="Pusta" label_selected="Pusta" name="Blank" /> <button label="Przezrocz." label_selected="Przezrocz." name="None" /> diff --git a/indra/newview/skins/default/xui/pl/menu_cof_attachment.xml b/indra/newview/skins/default/xui/pl/menu_cof_attachment.xml index add2d59998..f5fbf5c0c1 100644 --- a/indra/newview/skins/default/xui/pl/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/pl/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <context_menu name="COF Attachment"> + <menu_item_call label="Dotknij" name="touch_attach" /> + <menu_item_call label="Edytuj" name="edit_item" /> <menu_item_call label="Odłącz" name="detach" /> </context_menu> diff --git a/indra/newview/skins/default/xui/pl/menu_inventory.xml b/indra/newview/skins/default/xui/pl/menu_inventory.xml index 0edb680b16..1aeffc58b7 100644 --- a/indra/newview/skins/default/xui/pl/menu_inventory.xml +++ b/indra/newview/skins/default/xui/pl/menu_inventory.xml @@ -85,6 +85,7 @@ <menu_item_call label="Załóż" name="Wearable And Object Wear" /> <menu label="Dołącz do" name="Attach To" /> <menu label="Dołącz do HUD-a" name="Attach To HUD" /> + <menu_item_call label="Dotknij" name="Attachment Touch" /> <menu_item_call label="Edytuj" name="Wearable Edit" /> <menu_item_call label="Dodaj/dołącz" name="Wearable Add" /> <menu_item_call label="Zdejmij" name="Take Off" /> diff --git a/indra/newview/skins/default/xui/pl/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/pl/menu_wearable_list_item.xml index 260b86cb07..b0ef8e4393 100644 --- a/indra/newview/skins/default/xui/pl/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/pl/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Zastąp" name="wear_replace" /> <menu_item_call label="Załóż" name="wear_wear" /> <menu_item_call label="Dodaj" name="wear_add" /> + <menu_item_call label="Dotknij" name="touch" /> <menu_item_call label="Zdejmij/Odłącz" name="take_off_or_detach" /> <menu_item_call label="Odłącz" name="detach" /> <context_menu label="Dołącz do" name="wearable_attach_to" /> diff --git a/indra/newview/skins/default/xui/pl/menu_wearing_gear.xml b/indra/newview/skins/default/xui/pl/menu_wearing_gear.xml index 73138b2cf7..c129448adc 100644 --- a/indra/newview/skins/default/xui/pl/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/pl/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="Edytuj strój" name="edit" /> + <menu_item_call label="Dotknij" name="touch"/> + <menu_item_call label="Edytuj" name="edit_item"/> + <menu_item_call label="Edytuj strój" name="edit_outfit" /> <menu_item_call label="Zdejmij" name="takeoff" /> <menu_item_call label="Kopiuj listę przedmiotów stroju do schowka" name="copy" /> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/pl/menu_wearing_tab.xml b/indra/newview/skins/default/xui/pl/menu_wearing_tab.xml index 09c82da427..188f77a3bb 100644 --- a/indra/newview/skins/default/xui/pl/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/pl/menu_wearing_tab.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <context_menu name="Wearing"> + <menu_item_call label="Dotknij" name="touch_attach"/> + <menu_item_call label="Edytuj" name="edit_item"/> <menu_item_call label="Zdejmij" name="take_off" /> <menu_item_call label="Odłącz" name="detach" /> - <menu_item_call label="Edytuj strój" name="edit" /> + <menu_item_call label="Edytuj strój" name="edit_outfit" /> </context_menu> diff --git a/indra/newview/skins/default/xui/pt/floater_buy_currency.xml b/indra/newview/skins/default/xui/pt/floater_buy_currency.xml index 513400954b..c740b90472 100644 --- a/indra/newview/skins/default/xui/pt/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/pt/floater_buy_currency.xml @@ -59,8 +59,7 @@ </text> <button label="Comprar já!" name="buy_btn"/> <button label="Fechar" name="cancel_btn"/> - <text name="info_cannot_buy" font="SansSerifBig"> + <floater.string name="info_cannot_buy"> Transação incompleta - </text> - <button label="Prosseguir para a web" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/pt/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/pt/floater_texture_ctrl.xml index d06c16d8c8..d3eec114e2 100644 --- a/indra/newview/skins/default/xui/pt/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/pt/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> Multiplas texturas </text> - <radio_group name="mode_selection"> - <radio_item label="Inventário" name="inventory" value="0"/> - <radio_item label="Local" name="local" value="1"/> - <radio_item label="Assar" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - Tamanho: [DIMENSÕES] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="Inventário" name="inventory" value="0"/> + <combo_box.item label="Local" name="local" value="1"/> + </combo_box> <button label="Padrão" label_selected="Padrão" name="Default"/> <button label="Branco" label_selected="Branco" name="Blank"/> <button label="Nenhum" label_selected="Nenhum" name="None"/> diff --git a/indra/newview/skins/default/xui/pt/menu_cof_attachment.xml b/indra/newview/skins/default/xui/pt/menu_cof_attachment.xml index 527e3af3c9..5072b54f06 100644 --- a/indra/newview/skins/default/xui/pt/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/pt/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="Tocar" name="touch_attach" /> + <menu_item_call label="Editar" name="edit_item" /> <menu_item_call label="Separar" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/pt/menu_inventory.xml b/indra/newview/skins/default/xui/pt/menu_inventory.xml index 78a0482dee..363ed43986 100644 --- a/indra/newview/skins/default/xui/pt/menu_inventory.xml +++ b/indra/newview/skins/default/xui/pt/menu_inventory.xml @@ -105,6 +105,7 @@ <menu_item_call label="Vestir" name="Wearable And Object Wear"/> <menu label="Anexar a" name="Attach To"/> <menu label="Anexar ao HUD" name="Attach To HUD"/> + <menu_item_call label="Tocar" name="Attachment Touch" /> <menu_item_call label="Editar" name="Wearable Edit"/> <menu_item_call label="Adicionar" name="Wearable Add"/> <menu_item_call label="Tirar" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/pt/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/pt/menu_wearable_list_item.xml index 2487f6779f..2d16736277 100644 --- a/indra/newview/skins/default/xui/pt/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/pt/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Trocar" name="wear_replace"/> <menu_item_call label="Vestir" name="wear_wear"/> <menu_item_call label="Adicionar" name="wear_add"/> + <menu_item_call label="Tocar" name="touch" /> <menu_item_call label="Tirar / Separar" name="take_off_or_detach"/> <menu_item_call label="Separar" name="detach"/> <context_menu label="Colocar em" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/pt/menu_wearing_gear.xml b/indra/newview/skins/default/xui/pt/menu_wearing_gear.xml index 75dca703cb..bc24ffcdd9 100644 --- a/indra/newview/skins/default/xui/pt/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/pt/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="Editar look" name="edit"/> + <menu_item_call label="Tocar" name="touch"/> + <menu_item_call label="Editar" name="edit_item"/> + <menu_item_call label="Editar look" name="edit_outfit"/> <menu_item_call label="Tirar" name="takeoff"/> <menu_item_call label="Copiar lista do look para a área de transferência" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/pt/menu_wearing_tab.xml b/indra/newview/skins/default/xui/pt/menu_wearing_tab.xml index 42aa386215..d00fd9ae28 100644 --- a/indra/newview/skins/default/xui/pt/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/pt/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="Tocar" name="touch_attach"/> <menu_item_call label="Tirar" name="take_off"/> <menu_item_call label="Tirar" name="detach"/> - <menu_item_call label="Editar look" name="edit"/> + <menu_item_call label="Editar look" name="edit_outfit"/> <menu_item_call label="Editar" name="edit_item"/> <menu_item_call label="Mostrar original" name="show_original"/> </context_menu> diff --git a/indra/newview/skins/default/xui/pt/notifications.xml b/indra/newview/skins/default/xui/pt/notifications.xml index 35f65a59bc..bd1185bdd2 100644 --- a/indra/newview/skins/default/xui/pt/notifications.xml +++ b/indra/newview/skins/default/xui/pt/notifications.xml @@ -265,6 +265,10 @@ Deseja conceder direitos de modificação para os residentes selecionados? Você quer revogar os direitos de edição para os residentes selecionados? <usetemplate name="okcancelbuttons" notext="Não" yestext="Sim"/> </notification> + <notification name="GroupNameLengthWarning"> + O nome do grupo deve conter entre [MIN_LEN] e [MAX_LEN] caracteres. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UnableToCreateGroup"> Não foi possível criar um grupo. [MESSAGE] @@ -359,7 +363,7 @@ Deseja continuar? Você não tem L$ suficientes para associar-se a este grupo. </notification> <notification name="CreateGroupCost"> - Criar este grupo custa L$100. + Criar este grupo custará L$[COST]. Grupos ser formados por mais de um membro, caso contrário serão definitivamente excluídos. Convite outros membros dentro de 48 horas. <usetemplate canceltext="Cancelar" name="okcancelbuttons" notext="Cancelar" yestext="Criar grupo por L$100"/> @@ -498,6 +502,9 @@ Para colocar a mídia em só uma face, selecione Selecionar face e clique na fac <notification name="ErrorEncodingSnapshot"> Erro ao codificar a foto. </notification> + <notification name="ErrorCannotAffordUpload"> + Você precisa de L$[COST] para fazer o upload deste item. + </notification> <notification name="ErrorPhotoCannotAfford"> Você precisa de L$ [COST] para salvar uma foto em seu inventário. Você pode comprar L$ ou salvar a foto em seu computador. </notification> @@ -1729,13 +1736,16 @@ Deseja prosseguir? <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="GroupLimitInfo"> - O limite de grupos para as contas básicas é [MAX_BASIC] e para as contas [https://secondlife.com/premium/ premium], -é [MAX_PREMIUM]. -Se você fizer downgrade de sua conta, precisará ficar abaixo do limite de grupos [MAX_BASIC] antes de entrar em mais. - -[https://secondlife.com/my/account/membership.php Faça o upgrade hoje!] + Residentes com o plano Básico podem participar de até [MAX_BASIC] grupos. +O plano Premium permite até [MAX_PREMIUM]. [https://secondlife.com/my/account/membership.php? Saiba mais ou faça um upgrade] <usetemplate name="okbutton" yestext="Fechar"/> </notification> + <notification name="GroupLimitInfoPlus"> + Residentes com o plano Básico podem participar de até [MAX_BASIC] grupos. +O plano Premium permite até [MAX_PREMIUM]. O plano Premium Plus permite até [MAX_PREMIUM_PLUS]. +[https://secondlife.com/my/account/membership.php? Saiba mais ou faça um upgrade] + <usetemplate name="okbutton" yestext="Fechar"/> + </notification> <notification name="KickUser"> Chutar este residente com qual mensagem? <form name="form"> @@ -3292,6 +3302,22 @@ Para sua segurança, os SLurls serão bloqueados por alguns instantes. Sua voz foi silenciada pelo moderador. <usetemplate name="okbutton" yestext="OK"/> </notification> + <notification name="FailedToGetBenefits"> + Infelizmente, não conseguimos obter as informações de benefícios para esta sessão. Isto não deveria ocorrer em um ambiente de produção normal. Por favor, contate o suporte. Esta sessão não funcionará normalmente e recomendamos que você reinicie. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadCostConfirmation"> + Será feito o upload de [COUNT] itens, com um custo total de L$[COST]. Você deseja prosseguir com o upload? + <usetemplate name="okcancelbuttons" notext="Cancelar" yestext="Carregar"/> + </notification> + <notification name="BulkUploadNoCompatibleFiles"> + Não é possível fazer o upload dos arquivos selecionados de uma vez só. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadIncompatibleFiles"> + Não é possível fazer o upload de alguns dos arquivos selecionados de uma vez só. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UploadCostConfirmation"> O carregamento custa L$[PRICE]. Deseja prosseguir? <usetemplate name="okcancelbuttons" notext="Cancelar" yestext="Carregar"/> diff --git a/indra/newview/skins/default/xui/pt/panel_people.xml b/indra/newview/skins/default/xui/pt/panel_people.xml index fce170110e..2ef01841c5 100644 --- a/indra/newview/skins/default/xui/pt/panel_people.xml +++ b/indra/newview/skins/default/xui/pt/panel_people.xml @@ -18,7 +18,7 @@ Em busca de alguém para conversar? Procure no [secondlife:///app/worldmap Mapa- <string name="no_groups_msg" value="À procura de grupos interessantes? Tente fazer uma [secondlife:///app/search/groups Busca]."/> <string name="MiniMapToolTipMsg" value="[REGION](Clique duas vezes para abrir o mapa, shift+arraste para a visão pan)"/> <string name="AltMiniMapToolTipMsg" value="[REGION](Clique duas vezes para teletransportar, shift+arraste para a visão pan)"/> - <string name="GroupCountWithInfo" value="Você pertence a [COUNT] grupos e pode entrar em mais [REMAINING]. [secondlife:/// Quer mais?]"/> + <string name="GroupCountWithInfo" value="Você faz parte de [COUNT] grupos e pode participar de mais [REMAINING]. [secondlife:/// Aumente o seu limite]"/> <tab_container name="tabs"> <panel label="PROXIMIDADE" name="nearby_panel"> <panel label="bottom_panel" name="nearby_buttons_panel"> @@ -51,7 +51,7 @@ Em busca de alguém para conversar? Procure no [secondlife:///app/worldmap Mapa- <dnd_button name="minus_btn" tool_tip="Sair do grupo selecionado"/> </panel> <text name="groupcount"> - Você pertence a [COUNT] grupos e pode entrar em mais [REMAINING]. + Você faz parte de [COUNT] grupos e pode participar de mais [REMAINING]. </text> </panel> <panel label="RECENTE" name="recent_panel"> diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/pt/panel_preferences_graphics1.xml index a0f4ea4ed5..d387c4c869 100644 --- a/indra/newview/skins/default/xui/pt/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/pt/panel_preferences_graphics1.xml @@ -33,10 +33,10 @@ rápido </text> <check_box initial_value="true" label="Sempre renderizar amigos" name="AlwaysRenderFriends"/> <button label="Exceções..." name="RenderExceptionsButton"/> - <button label="Salvar configurações como predefinição..." name="PrefSaveButton"/> - <button label="Carregar predefinição..." name="PrefLoadButton"/> + <button label="Salvar configurações como predefinição" name="PrefSaveButton" width="235" left="5"/> + <button label="Carregar predefinição" name="PrefLoadButton" width="130" left_pad="8"/> min_val="0.125" - <button label="Excluir predefinição..." name="PrefDeleteButton"/> - <button label="Redefinir para configurações recomendadas" left="110" name="Defaults"/> + <button label="Excluir predefinição" name="PrefDeleteButton" width="122" left_pad="8"/> + <button label="Redefinir para configurações recomendadas" name="Defaults" width="255"/> <button label="Configurações avançadas..." name="AdvancedSettings"/> </panel> diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_setup.xml b/indra/newview/skins/default/xui/pt/panel_preferences_setup.xml index 03536f28c3..56fc225bc0 100644 --- a/indra/newview/skins/default/xui/pt/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/pt/panel_preferences_setup.xml @@ -35,5 +35,5 @@ <text name="Proxy Settings:"> Configurações de proxy: </text> - <button label="Ajustar configurações de proxy" label_selected="Procurar" name="set_proxy"/> + <button label="Ajustar configurações de proxy" label_selected="Procurar" name="set_proxy" width="180"/> </panel> diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 6b86c4330c..ee982b5b22 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -1596,11 +1596,14 @@ Se você continuar a receber essa mensagem, entre em contato com o suporte do Se <string name="MarketplaceUpdating"> atualizando... </string> + <string name="UploadFeeInfo"> + A taxa é baseada em seu nível de inscrição. Níveis mais altos possuem taxas mais baixas. [https://secondlife.com/my/account/membership.php? Saiba mais] + </string> <string name="Open landmarks"> - Marcos abertos + Marcos em aberto </string> <string name="Unconstrained"> - Sem limites + Ilimitado </string> <string name="no_transfer" value="(não transferível)"/> <string name="no_modify" value="(não modificável)"/> @@ -4971,6 +4974,9 @@ Visite http://status.secondlifegrid.net para saber se foi detectado um problema <string name="Chat" value="Bate papo"> Bate-papo </string> + <string name="BaseMembership"> + Básico + </string> <string name="DeleteItems"> Excluir itens selecionados? </string> diff --git a/indra/newview/skins/default/xui/ru/floater_buy_currency.xml b/indra/newview/skins/default/xui/ru/floater_buy_currency.xml index 87e8bd524e..ef55ce58d4 100644 --- a/indra/newview/skins/default/xui/ru/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/ru/floater_buy_currency.xml @@ -59,8 +59,7 @@ </text> <button label="Приобрести" name="buy_btn"/> <button label="Отмена" name="cancel_btn"/> - <text name="info_cannot_buy"> + <floater.string name="info_cannot_buy"> Нельзя купить - </text> - <button label="Продолжить в Интернете" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/ru/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/ru/floater_texture_ctrl.xml index c56657b86b..d4323e5c34 100644 --- a/indra/newview/skins/default/xui/ru/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/ru/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> Несколько текстур </text> - <radio_group name="mode_selection"> - <radio_item label="Инвентарь" name="inventory" value="0"/> - <radio_item label="Локально" name="local" value="1"/> - <radio_item label="Зафиксировать" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - Размер: [DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="Инвентарь" name="inventory" value="0"/> + <combo_box.item label="Локально" name="local" value="1"/> + </combo_box> <button label="По умолчанию" label_selected="По умолчанию" name="Default"/> <button label="Очистить" label_selected="Очистить" name="Blank"/> <button label="Нет" label_selected="Нет" name="None"/> diff --git a/indra/newview/skins/default/xui/ru/menu_cof_attachment.xml b/indra/newview/skins/default/xui/ru/menu_cof_attachment.xml index 72d1bc52b5..bc60188e2d 100644 --- a/indra/newview/skins/default/xui/ru/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/ru/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="Коснуться" name="touch_attach" /> + <menu_item_call label="Изменить" name="edit_item" /> <menu_item_call label="Отсоединить" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/ru/menu_inventory.xml b/indra/newview/skins/default/xui/ru/menu_inventory.xml index 05cccebafc..0f63324e90 100644 --- a/indra/newview/skins/default/xui/ru/menu_inventory.xml +++ b/indra/newview/skins/default/xui/ru/menu_inventory.xml @@ -105,6 +105,7 @@ <menu_item_call label="Надеть" name="Wearable And Object Wear"/> <menu label="Присоединить к" name="Attach To"/> <menu label="Присоединить к данным в игре" name="Attach To HUD"/> + <menu_item_call label="Коснуться" name="Attachment Touch" /> <menu_item_call label="Изменить" name="Wearable Edit"/> <menu_item_call label="Добавить" name="Wearable Add"/> <menu_item_call label="Снять" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/ru/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/ru/menu_wearable_list_item.xml index 2832e17b7d..caef7db458 100644 --- a/indra/newview/skins/default/xui/ru/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/ru/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Заменить" name="wear_replace"/> <menu_item_call label="Надеть" name="wear_wear"/> <menu_item_call label="Добавить" name="wear_add"/> + <menu_item_call label="Коснуться" name="touch" /> <menu_item_call label="Снять / отсоединить" name="take_off_or_detach"/> <menu_item_call label="Отсоединить" name="detach"/> <context_menu label="Присоединить" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/ru/menu_wearing_gear.xml b/indra/newview/skins/default/xui/ru/menu_wearing_gear.xml index c2351fbfff..01491b9694 100644 --- a/indra/newview/skins/default/xui/ru/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/ru/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="Изменить костюм" name="edit"/> + <menu_item_call label="Коснуться" name="touch"/> + <menu_item_call label="Изменить" name="edit_item"/> + <menu_item_call label="Изменить костюм" name="edit_outfit"/> <menu_item_call label="Снять" name="takeoff"/> <menu_item_call label="Копировать список костюмов в буфер обмена" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/ru/menu_wearing_tab.xml b/indra/newview/skins/default/xui/ru/menu_wearing_tab.xml index 1e32090c2a..e68b67bb33 100644 --- a/indra/newview/skins/default/xui/ru/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/ru/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="Коснуться" name="touch_attach"/> <menu_item_call label="Снять" name="take_off"/> <menu_item_call label="Отсоединить" name="detach"/> - <menu_item_call label="Изменить костюм" name="edit"/> + <menu_item_call label="Изменить костюм" name="edit_outfit"/> <menu_item_call label="Изменить" name="edit_item"/> <menu_item_call label="Показать оригинал" name="show_original"/> </context_menu> diff --git a/indra/newview/skins/default/xui/ru/notifications.xml b/indra/newview/skins/default/xui/ru/notifications.xml index 517c4db278..bfcda798be 100644 --- a/indra/newview/skins/default/xui/ru/notifications.xml +++ b/indra/newview/skins/default/xui/ru/notifications.xml @@ -266,6 +266,10 @@ Отобрать у выбранных жителей права на изменение? <usetemplate name="okcancelbuttons" notext="Нет" yestext="Да"/> </notification> + <notification name="GroupNameLengthWarning"> + Название группы может содержать от [MIN_LEN] до [MAX_LEN] символов. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UnableToCreateGroup"> Невозможно создать группу. [MESSAGE] @@ -360,7 +364,7 @@ У вас не хватает L$ для вступления. </notification> <notification name="CreateGroupCost"> - Создание этой группы стоит L$100. + Создание этой группы стоит L$[COST]. В группе должно быть более одного участника, иначе она будет удалена. Пригласите участников в ближайшие 48 часов. <usetemplate canceltext="Отмена" name="okcancelbuttons" notext="Отмена" yestext="Создать группу за L$100"/> @@ -500,6 +504,9 @@ <notification name="ErrorEncodingSnapshot"> Ошибка при кодировке снимка. </notification> + <notification name="ErrorCannotAffordUpload"> + Чтобы загрузить этот предмет, вам нужно L$[COST]. + </notification> <notification name="ErrorPhotoCannotAfford"> Требуется L$[COST] для сохранения фото в вашем инвентаре. Купите L$ или сохраните фото на компьютере. </notification> @@ -1612,22 +1619,22 @@ [DOWNLOAD_PATH]. </notification> <notification name="RequiredUpdate"> - Для входа необходима версия \[VERSION]. + Для входа необходима версия [VERSION]. Скачайте обновление с веб-сайта https://secondlife.com/support/downloads/ <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="PauseForUpdate"> - Для входа необходима версия \[VERSION]. + Для входа необходима версия [VERSION]. Нажмите OK для загрузки и установки. <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="OptionalUpdateReady"> - Версия \[VERSION] загружена и готова к установке. + Версия [VERSION] загружена и готова к установке. Нажмите OK для установки. <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="PromptOptionalUpdate"> - Версия \[VERSION] загружена и готова к установке. + Версия [VERSION] загружена и готова к установке. Продолжить? <usetemplate canceltext="Не сейчас" name="yesnocancelbuttons" notext="Пропустить" yestext="Установить"/> </notification> @@ -1738,11 +1745,14 @@ <usetemplate name="okbutton" yestext="OK"/> </notification> <notification name="GroupLimitInfo"> - Максимальное число групп для пользователя базового аккаунта составляет [MAX_BASIC], -а для [https://secondlife.com/premium/ премиум]-аккаунта – [MAX_PREMIUM]. -Чтобы вступать в новые группы после возврата к базовому аккаунту, вам придется выйти из части групп, чтобы их общее число было меньше [MAX_BASIC]. - -[https://secondlife.com/my/account/membership.php Перейдите на премиум-членство!] + Резиденты с Базовым аккаунтом могут присоединиться к [MAX_BASIC] группам. +Премиум аккаунт разрешает до [MAX_PREMIUM]. [https://secondlife.com/my/account/membership.php? Узнайте больше или расширьте свой аккаунт] + <usetemplate name="okbutton" yestext="Закрыть"/> + </notification> + <notification name="GroupLimitInfoPlus"> + Резиденты с Базовым аккаунтом могут присоединиться к [MAX_BASIC] группам. +Премиум аккаунт разрешает до [MAX_PREMIUM]. Премиум Плюс аккаунт разрешает до [MAX_PREMIUM_PLUS]. +[https://secondlife.com/my/account/membership.php? Узнайте больше или расширьте свой аккаунт] <usetemplate name="okbutton" yestext="Закрыть"/> </notification> <notification name="KickUser"> @@ -3303,6 +3313,22 @@ Ваш голос заглушен модератором. <usetemplate name="okbutton" yestext="OK"/> </notification> + <notification name="FailedToGetBenefits"> + К сожалению, в этой сессии мы не смогли получить информацию о преимуществах аккаунта. Такое не должно происходить в нормально работающей среде. Пожалуйста, свяжитесь со службой поддержки. Эта сессия работает некорректно, поэтому мы рекомендуем вам перезапустить программу. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadCostConfirmation"> + Этим действием загружается [COUNT] предметов на общую стоимость L$[COST]. Вы хотите продолжить загрузку? + <usetemplate name="okcancelbuttons" notext="Отмена" yestext="Загрузить"/> + </notification> + <notification name="BulkUploadNoCompatibleFiles"> + Выбранные файлы не могут быть загружены группой. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="BulkUploadIncompatibleFiles"> + Некоторые из выбранных файлов не могут быть загружены группой. + <usetemplate name="okbutton" yestext="OK"/> + </notification> <notification name="UploadCostConfirmation"> Эта передача будет стоить L$[PRICE]. Продолжить передачу? <usetemplate name="okcancelbuttons" notext="Отмена" yestext="Передать"/> diff --git a/indra/newview/skins/default/xui/ru/panel_people.xml b/indra/newview/skins/default/xui/ru/panel_people.xml index 0fdc06fb32..0812eb7433 100644 --- a/indra/newview/skins/default/xui/ru/panel_people.xml +++ b/indra/newview/skins/default/xui/ru/panel_people.xml @@ -18,7 +18,7 @@ <string name="no_groups_msg" value="Ищете группу, чтобы присоединиться к ней? Воспользуйтесь [secondlife:///app/search/groups поиском]."/> <string name="MiniMapToolTipMsg" value="[REGION](Двойной щелчок открывает карту, shift+перетягивание – обзор)"/> <string name="AltMiniMapToolTipMsg" value="[REGION](Двойной щелчок – телепортация, shift+перетягивание – обзор)"/> - <string name="GroupCountWithInfo" value="Вы входите в [COUNT] групп и можете присоединиться еще к [REMAINING]. [secondlife:/// Хотите еще?]"/> + <string name="GroupCountWithInfo" value="Вы состоите в [COUNT] группах, и можете присоединиться еще к [REMAINING]. [secondlife:/// Расширить свои лимиты]"/> <tab_container name="tabs"> <panel label="РЯДОМ" name="nearby_panel"> <panel label="bottom_panel" name="nearby_buttons_panel"> @@ -51,7 +51,7 @@ <dnd_button name="minus_btn" tool_tip="Покинуть выбранную группу"/> </panel> <text name="groupcount"> - Вы входите в [COUNT] групп и можете присоединиться еще к [REMAINING]. + Вы состоите в [COUNT] группах, и можете присоединиться еще к [REMAINING]. </text> </panel> <panel label="НЕДАВНИЕ" name="recent_panel"> diff --git a/indra/newview/skins/default/xui/ru/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/ru/panel_preferences_advanced.xml index dd0cf8e172..79d5cb7960 100644 --- a/indra/newview/skins/default/xui/ru/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/ru/panel_preferences_advanced.xml @@ -28,5 +28,5 @@ <check_box label="Выбор сетки при входе" name="show_grid_selection_check"/> <check_box label="Показывать расширенное меню" name="show_advanced_menu_check"/> <check_box label="Показать меню разработчика" name="show_develop_menu_check"/> - <button label="Стандартные разрешения на создание" name="default_creation_permissions"/> + <button label="Стандартные разрешения на создание" name="default_creation_permissions" width="235"/> </panel> diff --git a/indra/newview/skins/default/xui/ru/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/ru/panel_preferences_graphics1.xml index 4524fb4d43..f392a1f0b7 100644 --- a/indra/newview/skins/default/xui/ru/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/ru/panel_preferences_graphics1.xml @@ -32,7 +32,7 @@ </text> <check_box initial_value="true" label="Всегда рисовать друзей" name="AlwaysRenderFriends"/> <button label="Исключения..." name="RenderExceptionsButton"/> - <button label="Сохранить настройки как пресет..." name="PrefSaveButton"/> + <button label="Сохранить настройки как пресет..." name="PrefSaveButton" width="210"/> <button label="Загрузить пресет..." name="PrefLoadButton"/> min_val="0,125" <button label="Удалить пресет..." name="PrefDeleteButton"/> diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index edcf9d3e00..e9592a0476 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -1643,11 +1643,14 @@ support@secondlife.com. <string name="MarketplaceUpdating"> обновление... </string> + <string name="UploadFeeInfo"> + Тариф зависит от типа вашей подписки. Тарифы для владельцев расширенных пакетов меньше. [https://secondlife.com/my/account/membership.php? Узнать больше] + </string> <string name="Open landmarks"> - Открыть закладки + Открыть сохраненные локации </string> <string name="Unconstrained"> - Без ограничения + Без ограничений </string> <string name="no_transfer" value="(не передается)"/> <string name="no_modify" value="(не изменяется)"/> @@ -5091,6 +5094,15 @@ support@secondlife.com. <string name="Chat"> Чат </string> + <string name="BaseMembership"> + Базовый + </string> + <string name="PremiumMembership"> + Премиум + </string> + <string name="Premium PlusMembership"> + Премиум Плюс + </string> <string name="DeleteItems"> Удалить выбранные объекты? </string> diff --git a/indra/newview/skins/default/xui/tr/floater_buy_currency.xml b/indra/newview/skins/default/xui/tr/floater_buy_currency.xml index d90985dcff..33c4b2287f 100644 --- a/indra/newview/skins/default/xui/tr/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/tr/floater_buy_currency.xml @@ -59,8 +59,7 @@ </text> <button label="Şimdi Satın Al" name="buy_btn"/> <button label="İptal" name="cancel_btn"/> - <text name="info_cannot_buy"> + <floater.string name="info_cannot_buy"> Satın Alınamıyor - </text> - <button label="Web'e devam et" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/tr/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/tr/floater_texture_ctrl.xml index 1a16709127..0389cdcbe5 100644 --- a/indra/newview/skins/default/xui/tr/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/tr/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> Birden çok doku </text> - <radio_group name="mode_selection"> - <radio_item label="Envanter" name="inventory" value="0"/> - <radio_item label="Yerel" name="local" value="1"/> - <radio_item label="Kurut" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - Büyüklük: [DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="Envanter" name="inventory" value="0"/> + <combo_box.item label="Yerel" name="local" value="1"/> + </combo_box> <button label="Varsayılan" label_selected="Varsayılan" name="Default"/> <button label="Boş" label_selected="Boş" name="Blank"/> <button label="Hiçbiri" label_selected="Hiçbiri" name="None"/> diff --git a/indra/newview/skins/default/xui/tr/menu_cof_attachment.xml b/indra/newview/skins/default/xui/tr/menu_cof_attachment.xml index d57c43f6c1..82c9b286bb 100644 --- a/indra/newview/skins/default/xui/tr/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/tr/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="Dokun" name="touch_attach" /> + <menu_item_call label="Düzenle" name="edit_item" /> <menu_item_call label="Ayır" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/tr/menu_inventory.xml b/indra/newview/skins/default/xui/tr/menu_inventory.xml index d5e4113feb..ce85c437cb 100644 --- a/indra/newview/skins/default/xui/tr/menu_inventory.xml +++ b/indra/newview/skins/default/xui/tr/menu_inventory.xml @@ -105,6 +105,7 @@ <menu_item_call label="Giy" name="Wearable And Object Wear"/> <menu label="Şuna Ekle:" name="Attach To"/> <menu label="BÜG'e Ekle" name="Attach To HUD"/> + <menu_item_call label="Dokun" name="Attachment Touch" /> <menu_item_call label="Düzenle" name="Wearable Edit"/> <menu_item_call label="Ekle" name="Wearable Add"/> <menu_item_call label="Çıkar" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/tr/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/tr/menu_wearable_list_item.xml index 448202df87..486f76a29a 100644 --- a/indra/newview/skins/default/xui/tr/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/tr/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="Değiştir" name="wear_replace"/> <menu_item_call label="Giy" name="wear_wear"/> <menu_item_call label="Ekle" name="wear_add"/> + <menu_item_call label="Dokun" name="touch" /> <menu_item_call label="Çıkar / Ayır" name="take_off_or_detach"/> <menu_item_call label="Ayır" name="detach"/> <context_menu label="Şuna ekle" name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/tr/menu_wearing_gear.xml b/indra/newview/skins/default/xui/tr/menu_wearing_gear.xml index 438e580cd3..1594cf3afb 100644 --- a/indra/newview/skins/default/xui/tr/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/tr/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="Dış Görünümü Düzenle" name="edit"/> + <menu_item_call label="Dokun" name="touch"/> + <menu_item_call label="Düzenle" name="edit_item"/> + <menu_item_call label="Dış Görünümü Düzenle" name="edit_outfit"/> <menu_item_call label="Çıkar" name="takeoff"/> <menu_item_call label="Dış görünüm listesini panoya kopyala" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/tr/menu_wearing_tab.xml b/indra/newview/skins/default/xui/tr/menu_wearing_tab.xml index 1db95c17e1..56397c8628 100644 --- a/indra/newview/skins/default/xui/tr/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/tr/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="Dokun" name="touch_attach"/> <menu_item_call label="Çıkar" name="take_off"/> <menu_item_call label="Ayır" name="detach"/> - <menu_item_call label="Dış Görünümü Düzenle" name="edit"/> + <menu_item_call label="Dış Görünümü Düzenle" name="edit_outfit"/> <menu_item_call label="Düzenle" name="edit_item"/> <menu_item_call label="Orijinali Göster" name="show_original"/> </context_menu> diff --git a/indra/newview/skins/default/xui/tr/notifications.xml b/indra/newview/skins/default/xui/tr/notifications.xml index b7593322e3..5403a78f22 100644 --- a/indra/newview/skins/default/xui/tr/notifications.xml +++ b/indra/newview/skins/default/xui/tr/notifications.xml @@ -266,6 +266,10 @@ Seçili Sakinlere değişiklik yapma hakkı vermek istiyor musunuz? Seçili Sakinlerin değişiklik yapma hakkını iptal etmek istiyor musunuz? <usetemplate name="okcancelbuttons" notext="Hayır" yestext="Evet"/> </notification> + <notification name="GroupNameLengthWarning"> + Bir grup adı [MIN_LEN] ile [MAX_LEN] karakter olmalıdır. + <usetemplate name="okbutton" yestext="Tamam"/> + </notification> <notification name="UnableToCreateGroup"> Grup oluşturulamıyor. [MESSAGE] @@ -360,7 +364,7 @@ Devam etmek istiyor musunuz? Bu gruba katılmak için yeterli L$'na sahip değilsiniz. </notification> <notification name="CreateGroupCost"> - Bu grubu oluşturmanın maliyeti: L$ 100. + Bu grubu oluşturmak L$[COST]'dır. Grupların birden fazla üyeye sahip olması gereklidir, aksi takdirde grup kalıcı olarak silinir. Lütfen 48 saat içinde diğer üyeleri davet edin. <usetemplate canceltext="İptal" name="okcancelbuttons" notext="İptal" yestext="L$ 100 ödeyerek grubu oluştur"/> @@ -501,6 +505,9 @@ Ortamı sadece bir yüze yerleştirmek için, Yüz Seç'i seçin ve ardınd <notification name="ErrorEncodingSnapshot"> Anlık görüntü kodlanırken hata oluştu. </notification> + <notification name="ErrorCannotAffordUpload"> + Bu nesneyi yüklemek için L$[COST] ' a ihtiyacınız var. + </notification> <notification name="ErrorPhotoCannotAfford"> Envanterinize bir fotoğraf kaydedebilmek için [COST] L$ paraya ihtiyacınız var. L$ satın alabilir veya bunun yerine fotoğrafı bilgisayarınıza kaydedebilirsiniz. </notification> @@ -1739,11 +1746,14 @@ Gruptan ayrılmak istiyor musunuz? <usetemplate name="okbutton" yestext="Tamam"/> </notification> <notification name="GroupLimitInfo"> - Temel hesaplar için grup limiti [MAX_BASIC], [https://secondlife.com/premium/ özel] hesaplar -içinse [MAX_PREMIUM] olarak belirlenmiştir. -Hesabınızı indirgediyseniz, daha fazla gruba katılmak için önce grup sayınızı [MAX_BASIC] grubun altına düşürmelisiniz. - -[https://secondlife.com/my/account/membership.php Şimdi yükselt!] + Temel üyelikler [MAX_BASIC]'a kadar katılım sağlayabilir. +Premium üyelikler [MAX_PREMIUM] 'a kadar izinlidir. [https://secondlife.com/my/account/membership.php? Daha fazla bilgi alın ya da üyeiliğinizi yükseltin] + <usetemplate name="okbutton" yestext="Kapat"/> + </notification> + <notification name="GroupLimitInfoPlus"> + Temel üyelik sahibi yerleşimciler [MAX_BASIC]'a kadar olan gruplara katılabilir. +Premium üyeler [MAX_PREMIUM] 'a kadar izinlidir. Premium Plus üyeler [MAX_PREMIUM_PLUS] 'a kadar izinlidir. +[https://secondlife.com/my/account/membership.php? Daha fazla bilgi alın ya da üyeiliğinizi yükseltin] <usetemplate name="okbutton" yestext="Kapat"/> </notification> <notification name="KickUser"> @@ -3303,6 +3313,22 @@ Güvenliğiniz için birkaç saniye engellenecek. Sesli sohbetiniz moderatör tarafından engellendi. <usetemplate name="okbutton" yestext="Tamam"/> </notification> + <notification name="FailedToGetBenefits"> + Maalesef, bu oturumun avantajlarıyla ilgili bilgilere ulaşamıyoruz. Normal prodüksiyon ortamında yaşanmaması gereken bir durumdur. Lütfen destek ekibiyle iletişime geçin. Bu oturum normal bir şekilde çalışmayacaktır, yeniden başlatmanızı öneririz. + <usetemplate name="okbutton" yestext="Tamam"/> + </notification> + <notification name="BulkUploadCostConfirmation"> + Bu, toplam tutarı L$[COST] olan [COUNT] nesne yükleyecektir. Bu yüklemeye devam etmek istiyor musunuz? + <usetemplate name="okcancelbuttons" notext="İptal" yestext="Karşıya Yükle"/> + </notification> + <notification name="BulkUploadNoCompatibleFiles"> + Seçili dosyalar aynı anda yüklenemez. + <usetemplate name="okbutton" yestext="Tamam"/> + </notification> + <notification name="BulkUploadIncompatibleFiles"> + Seçili bazı dosyalar aynı anda yüklenemez. + <usetemplate name="okbutton" yestext="Tamam"/> + </notification> <notification name="UploadCostConfirmation"> Bu karşıya yükleme işleminin maliyeti L$[PRICE] olacak, karşıya yüklemeye devam etmek istiyor musunuz? <usetemplate name="okcancelbuttons" notext="İptal" yestext="Karşıya Yükle"/> diff --git a/indra/newview/skins/default/xui/tr/panel_people.xml b/indra/newview/skins/default/xui/tr/panel_people.xml index 29ca4772fd..25d29fcbb5 100644 --- a/indra/newview/skins/default/xui/tr/panel_people.xml +++ b/indra/newview/skins/default/xui/tr/panel_people.xml @@ -18,7 +18,7 @@ Birlikte takılacak kişiler mi arıyorsunuz? [secondlife:///app/worldmap Dünya <string name="no_groups_msg" value="Katılacak Gruplar mı arıyorsunuz? [secondlife:///app/search/groups Ara] deneyin."/> <string name="MiniMapToolTipMsg" value="[REGION](Haritayı açmak için çift tıkla, yatay hareket için shift çek)"/> <string name="AltMiniMapToolTipMsg" value="[REGION](Işınlamak için çift tıkla, yatay hareket için shift çek)"/> - <string name="GroupCountWithInfo" value="[COUNT] gruba üyesiniz, daha [REMAINING] gruba üye olabilirsiniz. [secondlife:/// Daha fazlasını mı istiyorsunuz?]"/> + <string name="GroupCountWithInfo" value="[COUNT] gruba üyesin ve [REMAINING] daha gruba üye olabilirsin. [secondlife:/// Limitini arttır]"/> <tab_container name="tabs"> <panel label="YAKIN" name="nearby_panel"> <panel label="bottom_panel" name="nearby_buttons_panel"> @@ -51,7 +51,7 @@ Birlikte takılacak kişiler mi arıyorsunuz? [secondlife:///app/worldmap Dünya <dnd_button name="minus_btn" tool_tip="Seçilen gruptan ayrıl"/> </panel> <text name="groupcount"> - [COUNT] gruba üyesiniz, daha [REMAINING] gruba üye olabilirsiniz. + [COUNT] gruba üyesin ve [REMAINING] daha gruba üye olabilirsin. </text> </panel> <panel label="SON" name="recent_panel"> diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index 3fd466d71c..56fad978f5 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -1643,11 +1643,14 @@ Bu mesaj size gelmeye devam ederse lütfen http://support.secondlife.com adresin <string name="MarketplaceUpdating"> güncelleniyor... </string> + <string name="UploadFeeInfo"> + Ücret, üyelik seviyene göre belirlenir. Yüksek seviyelere daha düşük ücretler uygulanır. [https://secondlife.com/my/account/membership.php? Daha fazla bilgi al] + </string> <string name="Open landmarks"> - Açık yer imleri + Açık alanlar </string> <string name="Unconstrained"> - Kısıtsız + Serbest </string> <string name="no_transfer" value="(aktarım yok)"/> <string name="no_modify" value="(değiştirme yok)"/> @@ -5092,6 +5095,9 @@ Hizmetle ilişkili bilinen bir sorun olup olmadığını görmek için lütfen h <string name="Chat"> Sohbet </string> + <string name="BaseMembership"> + Temel + </string> <string name="DeleteItems"> Seçili öğeler silinsin mi? </string> diff --git a/indra/newview/skins/default/xui/zh/floater_buy_currency.xml b/indra/newview/skins/default/xui/zh/floater_buy_currency.xml index fcf2800728..41c8c26ccc 100644 --- a/indra/newview/skins/default/xui/zh/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/zh/floater_buy_currency.xml @@ -59,8 +59,7 @@ </text> <button label="立即購買" name="buy_btn"/> <button label="取消" name="cancel_btn"/> - <text name="info_cannot_buy"> + <floater.string name="info_cannot_buy"> 無法購買 - </text> - <button label="繼續到網頁" name="error_web"/> + </floater.string> </floater> diff --git a/indra/newview/skins/default/xui/zh/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/zh/floater_texture_ctrl.xml index 881ca40338..6fc3e1129b 100644 --- a/indra/newview/skins/default/xui/zh/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/zh/floater_texture_ctrl.xml @@ -9,14 +9,10 @@ <text name="Multiple"> 多重材質 </text> - <radio_group name="mode_selection"> - <radio_item label="收納區" name="inventory" value="0"/> - <radio_item label="本地" name="local" value="1"/> - <radio_item label="確定產出" name="bake" value="2"/> - </radio_group> - <text name="unknown"> - 尺寸:[DIMENSIONS] - </text> + <combo_box name="mode_selection"> + <combo_box.item label="收納區" name="inventory" value="0"/> + <combo_box.item label="本地" name="local" value="1"/> + </combo_box> <button label="預設" label_selected="預設" name="Default"/> <button label="空白" label_selected="空白" name="Blank"/> <button label="無" label_selected="無" name="None"/> diff --git a/indra/newview/skins/default/xui/zh/menu_cof_attachment.xml b/indra/newview/skins/default/xui/zh/menu_cof_attachment.xml index 876fef16df..6ab2220ca7 100644 --- a/indra/newview/skins/default/xui/zh/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/zh/menu_cof_attachment.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="COF Attachment"> + <menu_item_call label="觸碰" name="touch_attach" /> + <menu_item_call label="編輯" name="edit_item" /> <menu_item_call label="卸下" name="detach"/> </context_menu> diff --git a/indra/newview/skins/default/xui/zh/menu_inventory.xml b/indra/newview/skins/default/xui/zh/menu_inventory.xml index 8b65b5c8de..10b170d4a4 100644 --- a/indra/newview/skins/default/xui/zh/menu_inventory.xml +++ b/indra/newview/skins/default/xui/zh/menu_inventory.xml @@ -105,6 +105,7 @@ <menu_item_call label="穿上" name="Wearable And Object Wear"/> <menu label="附著到..." name="Attach To"/> <menu label="附著到擡頭顯示" name="Attach To HUD"/> + <menu_item_call label="觸碰" name="Attachment Touch" /> <menu_item_call label="編輯" name="Wearable Edit"/> <menu_item_call label="添加" name="Wearable Add"/> <menu_item_call label="脫下" name="Take Off"/> diff --git a/indra/newview/skins/default/xui/zh/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/zh/menu_wearable_list_item.xml index 576f7f3b73..0093eb68f1 100644 --- a/indra/newview/skins/default/xui/zh/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/zh/menu_wearable_list_item.xml @@ -3,6 +3,7 @@ <menu_item_call label="取代" name="wear_replace"/> <menu_item_call label="穿上" name="wear_wear"/> <menu_item_call label="添加" name="wear_add"/> + <menu_item_call label="觸碰" name="touch" /> <menu_item_call label="脫下裝扮 / 卸除附件" name="take_off_or_detach"/> <menu_item_call label="卸下" name="detach"/> <context_menu label="附著到..." name="wearable_attach_to"/> diff --git a/indra/newview/skins/default/xui/zh/menu_wearing_gear.xml b/indra/newview/skins/default/xui/zh/menu_wearing_gear.xml index 6184f956d1..f09d4cfba9 100644 --- a/indra/newview/skins/default/xui/zh/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/zh/menu_wearing_gear.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <toggleable_menu name="Gear Wearing"> - <menu_item_call label="編輯裝扮" name="edit"/> + <menu_item_call label="觸碰" name="touch"/> + <menu_item_call label="編輯" name="edit_item"/> + <menu_item_call label="編輯裝扮" name="edit_outfit"/> <menu_item_call label="脫下" name="takeoff"/> <menu_item_call label="複製裝扮清單到剪貼簿" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/zh/menu_wearing_tab.xml b/indra/newview/skins/default/xui/zh/menu_wearing_tab.xml index dc9adcbd25..945297885e 100644 --- a/indra/newview/skins/default/xui/zh/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/zh/menu_wearing_tab.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Wearing"> + <menu_item_call label="觸碰" name="touch_attach"/> <menu_item_call label="脫下" name="take_off"/> <menu_item_call label="卸下" name="detach"/> - <menu_item_call label="編輯裝扮" name="edit"/> + <menu_item_call label="編輯裝扮" name="edit_outfit"/> <menu_item_call label="編輯" name="edit_item"/> <menu_item_call label="顯示原件" name="show_original"/> </context_menu> diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 57f2d31eab..f9abc8b25d 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -41,6 +41,7 @@ #include "../test/lltut.h" #include "llevents.h" #include "llnotificationsutil.h" +#include "lltrans.h" #if defined(LL_WINDOWS) #pragma warning(disable: 4355) // using 'this' in base-class ctor initializer expr @@ -77,6 +78,11 @@ void LLViewerWindow::setShowProgress(BOOL show) {} LLProgressView * LLViewerWindow::getProgressView(void) const { return 0; } LLViewerWindow* gViewerWindow; + +std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args, bool def_string) +{ + return std::string("test_trans"); +} class LLLogin::Impl { diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index 63967fae37..e5d226a2a4 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -124,6 +124,14 @@ S32 LLMachineID::getUniqueID(unsigned char *unique_id, size_t len) S32 LLMachineID::init() { return 1; } +LLCertException::LLCertException(const LLSD& cert_data, const std::string& msg) + : LLException(msg), + mCertData(cert_data) +{ + LL_WARNS("SECAPI") << "Certificate Error: " << msg << LL_ENDL; +} + + // ------------------------------------------------------------------------------------------- // TUT // ------------------------------------------------------------------------------------------- diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 6161a8b413..9f9821b4be 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -260,6 +260,9 @@ class ViewerManifest(LLManifest): app_suffix=self.channel_variant() return CHANNEL_VENDOR_BASE + ' ' + app_suffix + def exec_name(self): + return "SecondLifeViewer" + def app_name_oneword(self): return ''.join(self.app_name().split()) @@ -419,10 +422,9 @@ class WindowsManifest(ViewerManifest): build_data_json_platform = 'win' def final_exe(self): - return self.app_name_oneword()+".exe" + return self.exec_name()+".exe" def finish_build_data_dict(self, build_data_dict): - #MAINT-7294: Windows exe names depend on channel name, so write that in also build_data_dict['Executable'] = self.final_exe() build_data_dict['AppName'] = self.app_name() return build_data_dict @@ -523,9 +525,17 @@ class WindowsManifest(ViewerManifest): else: self.path("fmod.dll") + if self.args['openal'] == 'ON': + # Get openal dll + self.path("OpenAL32.dll") + self.path("alut.dll") + # For textures self.path("openjpeg.dll") + # Uriparser + self.path("uriparser.dll") + # These need to be installed as a SxS assembly, currently a 'private' assembly. # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx self.path("msvcp140.dll") @@ -1029,6 +1039,7 @@ class DarwinManifest(ViewerManifest): # libnghttp2.major.dylib, which is a symlink to # libnghttp2.version.dylib. Get all of them. "libnghttp2.*dylib", + "liburiparser.*dylib", ): dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) @@ -1514,7 +1525,6 @@ class Linux_i686_Manifest(LinuxManifest): print "Skipping libfmod.so - not found" pass - # Vivox runtimes with self.prefix(src=relpkgdir, dst="bin"): self.path("SLVoice") @@ -1544,10 +1554,12 @@ if __name__ == "__main__": print('%s \\\n%s' % (sys.executable, ' '.join((("'%s'" % arg) if ' ' in arg else arg) for arg in sys.argv))) + # fmodstudio and openal can be used simultaneously and controled by environment extra_arguments = [ dict(name='bugsplat', description="""BugSplat database to which to post crashes, if BugSplat crash reporting is desired""", default=''), dict(name='fmodstudio', description="""Indication if fmod studio libraries are needed""", default='OFF'), + dict(name='openal', description="""Indication openal libraries are needed""", default='OFF'), ] try: main(extra=extra_arguments) diff --git a/indra/test/llpermissions_tut.cpp b/indra/test/llpermissions_tut.cpp index fa4b085fd3..e6ccd5ecb7 100644 --- a/indra/test/llpermissions_tut.cpp +++ b/indra/test/llpermissions_tut.cpp @@ -406,43 +406,6 @@ namespace tut template<> template<> void permission_object_t::test<20>() { - LLFILE* fp = LLFile::fopen("linden_file.dat","w+"); - if(!fp) - { - LL_ERRS() << "file couldn't be opened\n" << LL_ENDL; - return; - } - LLPermissions perm,perm1; - LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); - LLUUID owner("68edcf47-ccd7-45b8-9f90-1649d7f12806"); - LLUUID lastOwner("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); - LLUUID group("9c8eca51-53d5-42a7-bb58-cef070395db8"); - perm.init(creator,owner,lastOwner,group); - - U32 base = PERM_TRANSFER | PERM_COPY; - U32 ownerp = PERM_TRANSFER; - U32 groupp = PERM_TRANSFER; - U32 everyone = PERM_TRANSFER; - U32 next = PERM_NONE; - - perm.initMasks(base, ownerp, everyone, groupp, next); - - ensure("Permissions export failed", perm.exportFile(fp)); - fclose(fp); - fp = LLFile::fopen("linden_file.dat","r+"); - if(!fp) - { - LL_ERRS() << "file couldn't be opened\n" << LL_ENDL; - return; - } - ensure("Permissions import failed", perm1.importFile(fp)); - fclose(fp); - ensure_equals("exportFile()/importFile():failed to export and import the data ", perm1, perm); -} - - template<> template<> - void permission_object_t::test<21>() - { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); LLUUID owner("68edcf47-ccd7-45b8-9f90-1649d7f12806"); @@ -467,14 +430,7 @@ namespace tut } template<> template<> - void permission_object_t::test<22>() - { - // Deleted LLPermissions::exportFileXML() and LLPermissions::importXML() - // because I can't find any non-test code references to it. 2009-05-04 JC - } - - template<> template<> - void permission_object_t::test<23>() + void permission_object_t::test<21>() { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -490,7 +446,7 @@ namespace tut } template<> template<> - void permission_object_t::test<24>() + void permission_object_t::test<22>() { LLPermissions perm,perm1; LLUUID creator("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); @@ -513,7 +469,7 @@ namespace tut } template<> template<> - void permission_object_t::test<25>() + void permission_object_t::test<23>() { LLAggregatePermissions AggrPermission; LLAggregatePermissions AggrPermission1; diff --git a/indra/test/llsaleinfo_tut.cpp b/indra/test/llsaleinfo_tut.cpp index 5f4d9186a8..b7b207610f 100644 --- a/indra/test/llsaleinfo_tut.cpp +++ b/indra/test/llsaleinfo_tut.cpp @@ -107,41 +107,6 @@ namespace tut template<> template<> void llsaleinfo_test_t::test<2>() { - - LLFILE* fp = LLFile::fopen("linden_file.dat","w+"); - if(!fp) - { - LL_ERRS() << "file could not be opened\n" << LL_ENDL; - return; - } - - S32 sale_price = 43500; - LLSaleInfo llsaleinfo(LLSaleInfo::FS_COPY, sale_price); - - llsaleinfo.exportFile(fp); - fclose(fp); - - LLSaleInfo llsaleinfo1; - U32 perm_mask; - BOOL has_perm_mask; - fp = LLFile::fopen("linden_file.dat","r"); - - if(!fp) - { - LL_ERRS() << "file coudnt be opened\n" << LL_ENDL; - return; - } - - llsaleinfo1.importFile(fp, has_perm_mask, perm_mask); - fclose(fp); - - ensure("importFile() fn failed ", llsaleinfo.getSaleType() == llsaleinfo1.getSaleType() && - llsaleinfo.getSalePrice() == llsaleinfo1.getSalePrice()); - } - - template<> template<> - void llsaleinfo_test_t::test<3>() - { S32 sale_price = 525452; LLSaleInfo llsaleinfo(LLSaleInfo::FS_ORIGINAL, sale_price); @@ -160,14 +125,7 @@ namespace tut } template<> template<> - void llsaleinfo_test_t::test<4>() - { - // Deleted LLSaleInfo::exportFileXML() and LLSaleInfo::importXML() - // because I can't find any non-test code references to it. 2009-05-04 JC - } - - template<> template<> - void llsaleinfo_test_t::test<5>() + void llsaleinfo_test_t::test<3>() { S32 sale_price = 99000; LLSaleInfo saleinfo(LLSaleInfo::FS_ORIGINAL, sale_price); @@ -186,7 +144,7 @@ namespace tut //static EForSale lookup(const char* name) fn test template<> template<> - void llsaleinfo_test_t::test<6>() + void llsaleinfo_test_t::test<4>() { S32 sale_price = 233223; LLSaleInfo::EForSale ret_type = LLSaleInfo::lookup("orig"); @@ -200,7 +158,7 @@ namespace tut //void LLSaleInfo::accumulate(const LLSaleInfo& sale_info) fn test template<> template<> - void llsaleinfo_test_t::test<7>() + void llsaleinfo_test_t::test<5>() { S32 sale_price = 20; LLSaleInfo saleinfo(LLSaleInfo::FS_COPY, sale_price); @@ -213,7 +171,7 @@ namespace tut // test cases of bool operator==(const LLSaleInfo &rhs) fn // test case of bool operator!=(const LLSaleInfo &rhs) fn template<> template<> - void llsaleinfo_test_t::test<8>() + void llsaleinfo_test_t::test<6>() { S32 sale_price = 55000; LLSaleInfo saleinfo(LLSaleInfo::FS_ORIGINAL, sale_price); @@ -225,7 +183,7 @@ namespace tut } template<> template<> - void llsaleinfo_test_t::test<9>() + void llsaleinfo_test_t::test<7>() { //TBD: void LLSaleInfo::packMessage(LLMessageSystem* msg) const diff --git a/scripts/code_tools/modified-strings.sh b/scripts/code_tools/modified-strings.sh new file mode 100644 index 0000000000..435dda3f5d --- /dev/null +++ b/scripts/code_tools/modified-strings.sh @@ -0,0 +1,199 @@ +#!/usr/bin/env bash +# $LicenseInfo:firstyear=2014&license=viewerlgpl$ +# Second Life Viewer Source Code +# Copyright (C) 2011, Linden Research, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; +# version 2.1 of the License only. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +# +### +### Extract strings modified between some version and the current version +### + +Action=DEFAULT +Rev=master +DefaultXuiDir="indra/newview/skins/default/xui" +Verbose=false +ExitStatus=0 + +while [ $# -ne 0 ] +do + case ${1} in + ## + ## Show usage + ## + -h|--help) + Action=USAGE + ;; + + -v|--verbose) + Verbose=true + ;; + + ## + ## Select the revision to compare against + ## + -r) + if [ $# -lt 2 ] + then + echo "Must specify <revision> with ${1}" 1>&2 + Action=USAGE + ExitStatus=1 + break + else + Rev=${2} + shift # consume the switch ( for n values, consume n-1 ) + fi + ;; + + ## + ## handle an unknown switch + ## + -*) + Action=USAGE + ExitStatus=1 + break + ;; + + *) + if [ -z "${XuiDir}" ] + then + XuiDir=${1} + else + echo "Too many arguments supplied: $@" 1>&2 + Action=USAGE + ExitStatus=1 + break + fi + ;; + esac + + shift # always consume 1 +done + +progress() +{ + if $Verbose + then + echo $* 1>&2 + fi +} + +if [[ $ExitStatus -eq 0 && "${Action}" = "DEFAULT" ]] +then + if [[ ! -d "${XuiDir:=$DefaultXuiDir}" ]] + then + echo "No XUI directory found in '$XuiDir'" 1>&2 + Action=USAGE + ExitStatus=1 + fi +fi + +if [ "${Action}" = "USAGE" ] +then + cat <<USAGE + +Usage: + + modified-strings.sh [ { -v | --verbose } ] [-r <revision>] [<path-to-xui>] + + where + --verbose shows progress messages on stderr (the command takes a while, so this is reassuring) + + -r <revision> specifies a git revision (branch, tag, commit, or relative specifier) + defaults to 'master' so that comparison is against the HEAD of the released viewer branch + + <path-to-xui> is the path to the root directory for XUI files + defaults to '$DefaultXuiDir' + + Emits a tab-separated file with these columns: + filename + the path of a file that has a string change (columns 2 and 3 are empty for lines with a filename) + name + the name attribute of a string or label whose value changed + English value + the current value of the string or label whose value changed + for strings, newlines are changed to '\n' and tab characters are changed to '\t' + + There is also a column for each of the language directories following the English. + +USAGE + exit $ExitStatus +fi + +stringval() # reads stdin and prints the escaped value of a string for the requested tag +{ + local tag=$1 + xmllint --xpath "string(/strings/string[@name=\"$tag\"])" - | perl -p -e 'chomp; s/\n/\\n/g; s/\t/\\t/g;' +} + +columns="file\tname\tEN" +for lang in $(ls -1 ${XuiDir}) +do + if [[ "$lang" != "en" && -d "${XuiDir}" && -f "${XuiDir}/$lang/strings.xml" ]] + then + columns+="\t$lang" + fi +done +echo -e "$columns" + +EnglishStrings="${XuiDir}/en/strings.xml" +progress -n "scanning $EnglishStrings " +echo -e "$EnglishStrings" +# loop over all tags in the current version of the strings file +cat "$EnglishStrings" | xmllint --xpath '/strings/string/@name' - | sed 's/ name="//; s/"$//;' \ +| while read name +do + progress -n "." + # fetch the $Rev and current values for each tag + old_stringval=$(git show "$Rev:$EnglishStrings" 2> /dev/null | stringval "$name") + new_stringval=$(cat "$EnglishStrings" | stringval "$name") + + if [[ "$old_stringval" != "$new_stringval" ]] + then + # the value is different, so print the tag and it's current value separated by a tab + echo -e "\t$name\t$new_stringval" + fi +done +progress "" + +# loop over all XUI files other than strings.xml finding labels +grep -rlw 'label' "${XuiDir}/en" | grep -v '/strings.xml' \ +| while read xuipath +do + progress -n "scanning $xuipath " + listed_file=false + # loop over all elements for which there is a label attribute, getting the name attribute value + xmllint --xpath '//*[@label]/@name' "$xuipath" 2> /dev/null | sed 's/ name="//; s/"$//;' \ + | while read name + do + progress -n "." + # get the old and new label attribute values for each name + old_label=$(git show "$Rev:$xuipath" 2> /dev/null | xmllint --xpath "string(//*[@name=\"${name}\"]/@label)" - 2> /dev/null) + new_label=$(cat "$xuipath" | xmllint --xpath "string(//*[@name=\"${name}\"]/@label)" - 2> /dev/null) + if [[ "$old_label" != "$new_label" ]] + then + if ! $listed_file + then + echo -e "$xuipath" + listed_file=true + fi + echo -e "\t$name\t$new_label" + fi + done + progress "" +done + diff --git a/scripts/code_tools/modified_strings.py b/scripts/code_tools/modified_strings.py new file mode 100644 index 0000000000..6a763b6ec5 --- /dev/null +++ b/scripts/code_tools/modified_strings.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python +"""\ + +This script scans the SL codebase for translation-related strings. + +$LicenseInfo:firstyear=2020&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2020, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" + +from __future__ import print_function + +import xml.etree.ElementTree as ET +import argparse +import os +import sys +from git import Repo, Git # requires the gitpython package +import pandas as pd +import re +from datetime import datetime + +usage_msg="""%(prog)s [options] + +Analyze the XUI configuration files to find text that may need to +be translated. Works by comparing two specified revisions, one +specified by --rev (default HEAD) and one specified by --rev_base +(default master). The script works by comparing xui contents of the +two revisions, and outputs a spreadsheet listing any areas of +difference. The target language must be specified using the --lang +option. Output is an excel file, which can be used as-is or imported +into google sheets. + +If the --rev revision already contains a translation for the text, it +will be included in the spreadsheet for reference. + +Normally you would want --rev_base to be the last revision to have +translations added, and --rev to be the tip of the current +project. You can find the last commit with translation work using "git log --grep INTL- | head" + +The --missing argument can be used to find all text with missing +translations, regardless of when it was added. If translations are being kept +reasonably current, you will normally not need this argument. +""" + +translate_attribs = [ + "title", + "short_title", + "value", + "label", + "label_selected", + "tool_tip", + "ignoretext", + "yestext", + "notext", + "canceltext", + "description", + "longdescription" +] + +def codify_for_print(val): + if isinstance(val, unicode): + return val.encode("utf-8") + else: + return unicode(val, 'utf-8').encode("utf-8") + +# Returns a dict of { name => xml_node } +def read_xml_elements(blob): + try: + contents = blob.data_stream.read() + except: + # default - pretend we read a file with no elements of interest. + # Parser will complain if it gets no elements at all. + contents = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?><strings></strings>' + xml = ET.fromstring(contents) + elts = {} + for child in xml.iter(): + if "name" in child.attrib: + name = child.attrib['name'] + elts[name] = child + return elts + +def failure(*msg): + print(*msg) + sys.exit(1) + +# return True iff any element of lis is "in" thing +def has_any(thing,lis): + for l in lis: + if l in thing: + return True + return False + +def should_translate(filename, elt, field, val): + if val is None: + return False + # Should translate apply recursively? + if "translate" in elt.attrib and elt.attrib["translate"] == "false": + return False + if has_any(filename,["floater_test","floater_aaa","floater_ui_preview"]): + return False + if "TestString PleaseIgnore" in val: + return False + val = re.sub(r"\[.*?\]","",val) + if len(val) == 0: + return False + if val.isspace(): + return False + val = val.strip() + if val.isdigit(): + return False + if not re.search('\w+', val): + return False + if re.match(r"^\s*\d*\s*x\s*\d*\s*$", val): + #print(val, "matches resolution string, will ignore") + return False + # "value" attribute is a hairball, mostly used to encode non-display info but a few exceptions + if field == "value": + if elt.text is not None and len(elt.text) > 0: + #print("value has text, ignoring", ET.tostring(elt)) + return False + if has_any(elt.attrib,["label"]): + return False + if elt.tag in ["string","text"]: + return True + #print("including value attribute", val, "tag", elt.tag,"in", ET.tostring(elt)) + return True + return True + +def make_translation_table(mod_tree, base_tree, lang, args): + + xui_path = "{}/{}".format(xui_base, args.base_lang) + try: + mod_xui_tree = mod_tree[xui_path] + except: + failure("xui tree not found for base language", args.base_lang,"or target lang", lang) + + if args.rev == args.rev_base: + failure("Revs are the same, nothing to compare") + + + data = [] + # For all files to be checked for translations + all_en_strings = set() + for mod_blob in mod_xui_tree.traverse(): + filename = mod_blob.path + if mod_blob.type == "tree": # directory, skip + continue + + if args.verbose: + print(filename) + + try: + base_blob = base_tree[filename] + except: + if args.verbose: + print("No matching base file found for", filename) + base_blob = None + + try: + transl_filename = filename.replace("/xui/{}/".format(args.base_lang), "/xui/{}/".format(lang)) + transl_blob = mod_tree[transl_filename] + except: + if args.verbose: + print("No matching translation file found at", transl_filename) + transl_blob = None + + mod_dict = read_xml_elements(mod_blob) + base_dict = read_xml_elements(base_blob) + transl_dict = read_xml_elements(transl_blob) + + rows = 0 + for name in mod_dict.keys(): + if not name in base_dict or mod_dict[name].text != base_dict[name].text or (args.missing and not name in transl_dict): + elt = mod_dict[name] + val = elt.text + field = "text" + if should_translate(filename, elt, field, val): + transl_val = "--" + if name in transl_dict: + transl_val = transl_dict[name].text + if val in all_en_strings: + new_val = "(DUPLICATE)" + else: + new_val = "" + data.append([val, transl_val, new_val, "", "", filename, name, field]) + all_en_strings.add(val) + rows += 1 + for attr in translate_attribs: + if attr in mod_dict[name].attrib: + if name not in base_dict \ + or attr not in base_dict[name].attrib \ + or mod_dict[name].attrib[attr] != base_dict[name].attrib[attr] \ + or (args.missing and (not name in transl_dict or not attr in transl_dict[name].attrib)): + elt = mod_dict[name] + val = elt.attrib[attr] + if should_translate(filename, elt, attr, val): + transl_val = "--" + if name in transl_dict and attr in transl_dict[name].attrib: + transl_val = transl_dict[name].attrib[attr] + if val in all_en_strings: + new_val = "(DUPLICATE)" + else: + new_val = "" + #attr = attr + ":" + ET.tostring(elt) + data.append([val, transl_val, new_val, "", "", filename, name, attr]) + all_en_strings.add(val) + rows += 1 + + return data + +def find_deletions(mod_tree, base_tree, lang, args, f): + + transl_xui_path = "{}/{}".format(xui_base, lang) + try: + transl_xui_tree = mod_tree[transl_xui_path] + except: + failure("xui tree not found for base language", args.base_lang,"or target lang", lang) + + for transl_blob in transl_xui_tree.traverse(): + if transl_blob.type == "tree": # directory, skip + continue + transl_filename = transl_blob.path + mod_filename = transl_filename.replace("/xui/{}/".format(lang), "/xui/{}/".format(args.base_lang)) + #print("checking",transl_filename,"against",mod_filename) + try: + mod_blob = mod_tree[mod_filename] + except: + print(" delete file", transl_filename, file=f) + continue + mod_dict = read_xml_elements(mod_blob) + if len(mod_dict) == 0: + print(" delete file", transl_filename, file=f) + continue + transl_dict = read_xml_elements(transl_blob) + #print("mod vs transl", len(mod_dict), len(transl_dict)) + lines = 0 + for elt_key in transl_dict: + if not elt_key in mod_dict: + if lines == 0: + print(" in file", transl_filename, file=f) + lines += 1 + print(" delete element", elt_key, file=f) + else: + transl_elt = transl_dict[elt_key] + mod_elt = mod_dict[elt_key] + for a in transl_elt.attrib: + if not a in mod_elt.attrib: + if lines == 0: + print(" in file", transl_filename, file=f) + lines += 1 + print(" delete attribute", a, "from", elt_key, file=f) + if transl_elt.text and (not mod_elt.text): + if lines == 0: + print(" in file", transl_filename, file=f) + lines += 1 + print(" delete text from", elt_key, file=f) + +def save_translation_file(per_lang_data, aux_data, outfile): + + langs = sorted(per_lang_data.keys()) + print("Saving languages", ",".join(langs),"as",outfile) + + writer = pd.ExcelWriter(outfile, engine='xlsxwriter') + + workbook = writer.book + wrap_format = workbook.add_format({'text_wrap': True}) + bold_wrap_format = workbook.add_format({'text_wrap': True, 'bold': True}) + wrap_unlocked_format = workbook.add_format({'text_wrap': True, 'locked': False}) + + for lang in langs: + data = per_lang_data[lang] + num_translations = len(data) + cols = ["EN", "Previous Translation ({})".format(lang.upper()), "ENTER NEW TRANSLATION ({})".format(lang.upper()), "Translator Questions", "Notes", "File", "Element", "Field"] + df = pd.DataFrame(data, columns=cols) + df.to_excel(writer, index=False, sheet_name = lang.upper()) + + worksheet = writer.sheets[lang.upper()] + + # Translators primarily care about columns A-C, and should write + # only in column C. Hide the others. Set widths. + worksheet.protect() + worksheet.set_column('A:B', 60, wrap_format) + worksheet.set_column('C:C', 60, wrap_unlocked_format) + worksheet.set_column('D:E', 40, wrap_unlocked_format) + worksheet.set_column('F:F', 50, wrap_format, {'hidden': True}) + worksheet.set_column('G:H', 30, wrap_format, {'hidden': True}) + + # Lock the top row (column headers) in place while scrolling + worksheet.freeze_panes(1, 0) + print("Added", num_translations, "rows for language", lang) + + # Reference info, not for translation + for aux, data in aux_data.items(): + df = pd.DataFrame(data, columns = ["Key", "Value"]) + df.to_excel(writer, index=False, sheet_name=aux) + worksheet = writer.sheets[aux] + worksheet.set_column('A:A', 50, bold_wrap_format) + worksheet.set_column('B:B', 80, wrap_format) + + print("Writing", outfile) + writer.save() + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description="analyze viewer xui files for needed translations", usage=usage_msg) + parser.add_argument("-v","--verbose", action="store_true", help="verbose flag") + parser.add_argument("--missing", action="store_true", default = False, help="include all fields for which a translation does not exist") + parser.add_argument("--deleted", action="store_true", default = False, help="show all translated entities which don't exist in english") + parser.add_argument("--skip_spreadsheet", action="store_true", default = False, help="skip creating the translation spreadsheet") + parser.add_argument("--rev", help="revision with modified strings, default HEAD", default="HEAD") + parser.add_argument("--rev_base", help="previous revision to compare against, default master", default="master") + parser.add_argument("--base_lang", help="base language, default en (normally leave unchanged - other values are only useful for testing)", default="en") + parser.add_argument("--lang", help="target languages, or 'all_valid' or 'supported'; default is 'supported'", nargs="+", default = ["supported"]) + args = parser.parse_args() + + cwd = os.getcwd() + rootdir = Git(cwd).rev_parse("--show-toplevel") + repo = Repo(rootdir) + try: + mod_commit = repo.commit(args.rev) + except: + failure(args.rev,"is not a valid commit") + try: + base_commit = repo.commit(args.rev_base) + except: + failure(args.rev_base,"is not a valid commit") + + print("Will identify changes in", args.rev, "not present in", args.rev_base) + if args.missing: + print("Will also include any text for which no corresponding translation exists, regardless of when it was added") + sys.stdout.flush() + + mod_tree = mod_commit.tree + base_tree = base_commit.tree + + xui_base = "indra/newview/skins/default/xui" + xui_base_tree = mod_tree[xui_base] + + # Find target languages + # all languages present in the codebase + valid_langs = [tree.name.lower() for tree in xui_base_tree if tree.name.lower() != args.base_lang.lower()] + # offically supported languages + supported_langs = ["fr", "es", "it", "pt", "ja", "de"] + langs = [l.lower() for l in args.lang] + if "supported" in args.lang: + langs = supported_langs + if "all_valid" in args.lang: + langs = valid_langs + langs = sorted(langs) + for lang in langs: + if not lang in valid_langs: + failure("Unknown target language {}. Valid values are {}".format(lang,", ".join(sorted(valid_langs) + ["all_valid","supported"]))) + print("Target language(s) are", ",".join(sorted(langs))) + sys.stdout.flush() + + outfile = "SL_Translations.xlsx" + try: + f = open(outfile,"a+") + f.close() + except: + failure("Can't write to output file",outfile,". Is it already open?") + + aux_data = { "REFERENCE": [["Command", " ".join(sys.argv)], + ["Date", str(datetime.now())], + ["Mod Commit", mod_commit.hexsha], + ["Base Commit", base_commit.hexsha], + ] } + + if not args.skip_spreadsheet: + per_lang_data = {} + for lang in langs: + print("Creating spreadsheet for language", lang) + sys.stdout.flush() + + per_lang_data[lang] = make_translation_table(mod_tree, base_tree, lang, args) + + print("Saving output file", outfile) + save_translation_file(per_lang_data, aux_data, outfile) + + if args.deleted: + deletion_file = "Translate_deletions.txt" + print("Saving deletion info to", deletion_file) + with open(deletion_file,"w") as f: + for lang in langs: + find_deletions(mod_tree, base_tree, lang, args, f) + diff --git a/scripts/content_tools/arche_tool.py b/scripts/content_tools/arche_tool.py index 23c96fc64e..f99d7be39a 100644 --- a/scripts/content_tools/arche_tool.py +++ b/scripts/content_tools/arche_tool.py @@ -79,15 +79,32 @@ def compare_trees(file_trees): print "Summary:" print summary - +def dump_appearance_params(tree): + vals = [] + for e in tree.getroot().iter(): + if e.tag == "param": + g = int(e.get("group")) + if g in [0,3]: + vals.append("{" + e.get("id") + "," +e.get("u8") + "}") + #print e.get("id"), e.get("name"), e.get("group"), e.get("u8") + if len(vals)==253: + print ", ".join(vals) + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="compare avatar XML archetype files") parser.add_argument("--verbose", help="verbose flag", action="store_true") + parser.add_argument("--compare", help="compare flag", action="store_true") + parser.add_argument("--appearance_params", help="compare flag", action="store_true") parser.add_argument("files", nargs="+", help="name of one or more archtype files") args = parser.parse_args() print "files",args.files file_trees = [etree.parse(filename) for filename in args.files] - compare_trees(file_trees) + print args + if args.compare: + compare_trees(file_trees) + if args.appearance_params: + dump_appearance_params(file_trees[0]) |