diff options
| author | Andrey Kleshchev <andreykproductengine@lindenlab.com> | 2025-08-06 16:05:19 +0300 | 
|---|---|---|
| committer | Andrey Kleshchev <andreykproductengine@lindenlab.com> | 2025-08-06 16:05:19 +0300 | 
| commit | ce9d66cdd18c58c3b26fbebde633ae00732d7f9f (patch) | |
| tree | fdbc5a913c62265f95e610819637e096f0f85339 /indra/newview | |
| parent | 514b658fde13bb0c0ec862b081eeebf47bace70d (diff) | |
| parent | 0f68bcd46ca3f2babf94303b80e2be006e4693ae (diff) | |
Merge branch develop into 2025.06
# Conflicts:
#	indra/newview/llvoavatar.cpp
Diffstat (limited to 'indra/newview')
129 files changed, 5108 insertions, 1760 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index f06ee36ad4..5274986ff0 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -15,6 +15,9 @@ include(CMakeCopyIfDifferent)  include(CubemapToEquirectangularJS)  include(DBusGlib)  include(DragDrop) +if (USE_DISCORD) +  include(Discord) +endif ()  include(EXPAT)  include(Hunspell)  include(JPEGEncoderBasic) @@ -76,6 +79,7 @@ set(viewer_SOURCE_FILES      gltf/accessor.cpp      gltf/primitive.cpp      gltf/animation.cpp +    gltf/llgltfloader.cpp      groupchatlistener.cpp      llaccountingcostmanager.cpp      llaisapi.cpp @@ -178,11 +182,11 @@ set(viewer_SOURCE_FILES      llflexibleobject.cpp      llfloater360capture.cpp      llfloaterabout.cpp +    llfloateravatarwelcomepack.cpp      llfloaterbvhpreview.cpp      llfloateraddpaymentmethod.cpp      llfloaterauction.cpp      llfloaterautoreplacesettings.cpp -    llfloateravatar.cpp      llfloateravatarpicker.cpp      llfloateravatarrendersettings.cpp      llfloateravatartextures.cpp @@ -748,6 +752,7 @@ set(viewer_HEADER_FILES      gltf/buffer_util.h      gltf/primitive.h      gltf/animation.h +    gltf/llgltfloader.h      llaccountingcost.h      llaccountingcostmanager.h      llaisapi.h @@ -851,11 +856,11 @@ set(viewer_HEADER_FILES      llflexibleobject.h      llfloater360capture.h      llfloaterabout.h +    llfloateravatarwelcomepack.h      llfloaterbvhpreview.h      llfloateraddpaymentmethod.h      llfloaterauction.h      llfloaterautoreplacesettings.h -    llfloateravatar.h      llfloateravatarpicker.h      llfloateravatarrendersettings.h      llfloateravatartextures.h @@ -1570,6 +1575,7 @@ if (WINDOWS)          res-sdl/ll_icon.BMP          res/ll_icon.BMP          res/ll_icon.ico +        res/ll_icon_small.ico          res/resource.h          res/toolpickobject.cur          res/toolpickobject2.cur @@ -1785,6 +1791,12 @@ if (WINDOWS)                 )      endif (ADDRESS_SIZE EQUAL 64) +    if (TARGET ll::discord_sdk) +        list(APPEND COPY_INPUT_DEPENDENCIES +             ${SHARED_LIB_STAGING_DIR}/discord_partner_sdk.dll +             ) +    endif () +      if (TARGET ll::openal)        list(APPEND COPY_INPUT_DEPENDENCIES             ${SHARED_LIB_STAGING_DIR}/OpenAL32.dll @@ -1801,6 +1813,7 @@ if (WINDOWS)          --arch=${ARCH}          --artwork=${ARTWORK_DIR}          "--bugsplat=${BUGSPLAT_DB}" +        "--discord=${USE_DISCORD}"          "--openal=${USE_OPENAL}"          "--tracy=${USE_TRACY}"          --build=${CMAKE_CURRENT_BINARY_DIR} @@ -1839,6 +1852,7 @@ if (WINDOWS)              --arch=${ARCH}              --artwork=${ARTWORK_DIR}              "--bugsplat=${BUGSPLAT_DB}" +            "--discord=${USE_DISCORD}"              "--openal=${USE_OPENAL}"              "--tracy=${USE_TRACY}"              --build=${CMAKE_CURRENT_BINARY_DIR} @@ -1903,6 +1917,7 @@ if (WINDOWS)                --arch=${ARCH}                --artwork=${ARTWORK_DIR}                "--bugsplat=${BUGSPLAT_DB}" +              "--discord=${USE_DISCORD}"                "--openal=${USE_OPENAL}"                "--tracy=${USE_TRACY}"                --build=${CMAKE_CURRENT_BINARY_DIR} @@ -1998,6 +2013,10 @@ target_link_libraries(${VIEWER_BINARY_NAME}          ll::openxr          ) +if (USE_DISCORD) +   target_link_libraries(${VIEWER_BINARY_NAME} ll::discord_sdk ) +endif () +  if( TARGET ll::intel_memops )     target_link_libraries(${VIEWER_BINARY_NAME} ll::intel_memops )  endif() @@ -2054,6 +2073,7 @@ if (LINUX)          --arch=${ARCH}          --artwork=${ARTWORK_DIR}          "--bugsplat=${BUGSPLAT_DB}" +        "--discord=${USE_DISCORD}"          "--openal=${USE_OPENAL}"          "--tracy=${USE_TRACY}"          --build=${CMAKE_CURRENT_BINARY_DIR} @@ -2082,6 +2102,7 @@ if (LINUX)        --arch=${ARCH}        --artwork=${ARTWORK_DIR}        "--bugsplat=${BUGSPLAT_DB}" +      "--discord=${USE_DISCORD}"        "--openal=${USE_OPENAL}"        "--tracy=${USE_TRACY}"        --build=${CMAKE_CURRENT_BINARY_DIR} @@ -2158,6 +2179,7 @@ if (DARWIN)        --arch=${ARCH}        --artwork=${ARTWORK_DIR}        "--bugsplat=${BUGSPLAT_DB}" +      "--discord=${USE_DISCORD}"        "--openal=${USE_OPENAL}"        "--tracy=${USE_TRACY}"        --build=${CMAKE_CURRENT_BINARY_DIR} @@ -2193,6 +2215,7 @@ if (DARWIN)                --arch=${ARCH}                --artwork=${ARTWORK_DIR}                "--bugsplat=${BUGSPLAT_DB}" +              "--discord=${USE_DISCORD}"                "--openal=${USE_OPENAL}"                "--tracy=${USE_TRACY}"                --build=${CMAKE_CURRENT_BINARY_DIR} diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 6329380f96..0ee843cc60 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -7.1.15 +7.2.0 diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 4a3dfffde1..7bcfecf9fa 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -26,9 +26,9 @@             label_ref="Command_Avatar_Label"             tooltip_ref="Command_Avatar_Tooltip"             execute_function="Floater.ToggleOrBringToFront" -           execute_parameters="avatar" +           execute_parameters="avatar_welcome_pack"             is_running_function="Floater.IsOpen" -           is_running_parameters="avatar" +           is_running_parameters="avatar_welcome_pack"             />    <command name="build"             available_in_toybox="true" diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8a8b107b76..f4158188ad 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2,7 +2,7 @@  <llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:noNamespaceSchemaLocation="llsd.xsd">  <map> -  <key>ImporterDebug</key> +  <key>ImporterDebugVerboseLogging</key>    <map>      <key>Comment</key>      <string>Enable debug output to more precisely identify sources of import errors. Warning: the output can slow down import on many machines.</string> @@ -27,7 +27,7 @@    <key>ImporterModelLimit</key>    <map>      <key>Comment</key> -    <string>Limits amount of importer generated models for dae files</string> +    <string>Limits amount of importer generated (when over 8 faces) models for dae and gltf files</string>      <key>Persist</key>      <integer>1</integer>      <key>Type</key> @@ -35,6 +35,17 @@      <key>Value</key>      <integer>768</integer>    </map> +  <key>ImporterDebugMode</key> +  <map> +    <key>Comment</key> +    <string>At 0 does nothing, at 1 dumps skinning data near orifinal file, at 2 dumps skining data and positions/weights of first 5 models, at 3 dumps skinning data and models as llsd</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>U32</string> +    <key>Value</key> +    <integer>0</integer> +  </map>    <key>ImporterPreprocessDAE</key>    <map>      <key>Comment</key> @@ -621,16 +632,16 @@        <key>Value</key>        <real>16.0</real>      </map> -    <key>AvatarPickerURL</key> +    <key>AvatarWelcomePack</key>      <map> -      <key>Comment</key> -      <string>Avatar picker contents</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>String</string> -      <key>Value</key> -      <string>http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/[GRID_LOWERCASE]/avatars.html</string> +        <key>Comment</key> +        <string>Avatar Welcome Pack contents</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>String</string> +        <key>Value</key> +        <string>http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/[GRID_LOWERCASE]/vawp/index.html</string>      </map>      <!--AvatarBakedTextureUploadTimeout is in use by QA-->      <key>AvatarBakedTextureUploadTimeout</key> @@ -1139,6 +1150,39 @@        <key>Value</key>        <integer>1</integer>      </map> +    <key>EnableDiscord</key> +    <map> +      <key>Comment</key> +      <string>When set, connect to Discord to enable Rich Presence</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map> +    <key>ShowDiscordActivityDetails</key> +    <map> +      <key>Comment</key> +      <string>When set, show avatar name on Discord Rich Presence</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map> +    <key>ShowDiscordActivityState</key> +    <map> +      <key>Comment</key> +      <string>When set, show location on Discord Rich Presence</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map>      <key>EnableDiskCacheDebugInfo</key>      <map>        <key>Comment</key> @@ -1153,13 +1197,13 @@      <key>DiskCachePercentOfTotal</key>      <map>        <key>Comment</key> -      <string>The percent of total cache size (defined by CacheSize) to use for the disk cache</string> +      <string>The percent of total cache size (defined by CacheSize) to use for the disk cache (ex: asset storage, excludes textures)</string>        <key>Persist</key>        <integer>1</integer>        <key>Type</key>        <string>F32</string>        <key>Value</key> -      <real>40.0</real> +      <real>35.0</real>      </map>      <key>DiskCacheDirName</key>      <map> @@ -1203,7 +1247,7 @@        <key>Type</key>        <string>U32</string>        <key>Value</key> -      <integer>4096</integer> +      <integer>6144</integer>      </map>      <key>CacheValidateCounter</key>      <map> @@ -9606,6 +9650,17 @@        <key>Value</key>        <integer>1</integer>      </map> +    <key>ObscureBalanceInStatusBar</key> +    <map> +        <key>Comment</key> +        <string>If true, balance will be shows as '*'</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>Boolean</string> +        <key>Value</key> +        <integer>0</integer> +    </map>      <key>RenderUIBuffer</key>      <map>        <key>Comment</key> @@ -11499,6 +11554,28 @@        <key>Value</key>        <string>fss.txt</string>      </map> +    <key>StatsFrametimeSampleSeconds</key> +    <map> +        <key>Comment</key> +        <string>The number of seconds to sample extended frametime data (percentiles, stddev).</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>S32</string> +        <key>Value</key> +        <integer>5</integer> +    </map> +    <key>StatsFrametimeEventThreshold</key> +    <map> +        <key>Comment</key> +        <string>The percentage that the frametime difference must exceed in order to register a frametime event. 0.1 = 10%, 0.25 = 25%, etc.</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>F32</string> +        <key>Value</key> +        <real>0.1</real> +    </map>      <key>SystemLanguage</key>      <map>        <key>Comment</key> @@ -13731,7 +13808,7 @@      <key>FullScreen</key>      <map>        <key>Comment</key> -      <string>run a fullscreen session</string> +      <string>Run a fullscreen session. MacOS not supported</string>        <key>Persist</key>        <integer>1</integer>        <key>Type</key> @@ -16267,5 +16344,71 @@          <key>Value</key>          <integer>1</integer>      </map> +    <key>MediaAutoPlayHuds</key> +    <map> +        <key>Comment</key> +        <string>Automatically play HUD media</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>Boolean</string> +        <key>Value</key> +        <integer>1</integer> +    </map> +    <key>MediaFirstClickInteract</key> +    <map> +      <key>Comment</key> +      <string>This setting controls which media (once loaded) does not require a first click to focus before interaction can begin. This allows clicks to be passed directly to media bypassing the focus click requirement. This setting is a bitfield, precomputed values are as follows: Disabled=0; Worn HUDs only=1; Owned objects=2; Friend objects=4; Group objects=8; Landowner objects=16; Any object=32767; All MOAP=32768. For complete details see lltoolpie.h enum MediaFirstClickTypes.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>S32</string> +      <key>Value</key> +      <integer>31</integer> +    </map> +    <key>EnableSelectionHints</key> +    <map> +      <key>Comment</key> +      <string>Whether or not to send editing hints to animate the arm when editing an object.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>1</integer> +    </map> +    <key>EnableLookAtTarget</key> +    <map> +      <key>Comment</key> +      <string>Whether or not to animate the avatar head and send look at targets when moving the cursor or focusing on objects</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>1</integer> +    </map> +    <key>LimitLookAtTarget</key> +    <map> +      <key>Comment</key> +      <string>Whether or not to clamp the look at targets around the avatar head before sending</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map> +    <key>LimitLookAtTargetDistance</key> +    <map> +      <key>Comment</key> +      <string>Distance to limit look at target to</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>F32</string> +      <key>Value</key> +      <integer>2</integer> +    </map>  </map>  </llsd> diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp index d1845605d4..03f7331893 100644 --- a/indra/newview/gltf/accessor.cpp +++ b/indra/newview/gltf/accessor.cpp @@ -159,7 +159,7 @@ bool Buffer::prep(Asset& asset)          std::string dir = gDirUtilp->getDirName(asset.mFilename);          std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri; -        std::ifstream file(bin_file, std::ios::binary); +        llifstream file(bin_file.c_str(), std::ios::binary);          if (!file.is_open())          {              LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL; diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index c210b9c61d..e24aea4a28 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -50,6 +50,10 @@ namespace LL              "KHR_texture_transform"          }; +        static std::unordered_set<std::string> ExtensionsIgnored = { +            "KHR_materials_pbrSpecularGlossiness" +        }; +          Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)          {              if (alpha_mode == "OPAQUE") @@ -472,11 +476,14 @@ void Asset::update()              for (auto& image : mImages)              { -                if (image.mTexture.notNull()) -                { // HACK - force texture to be loaded full rez -                    // TODO: calculate actual vsize -                    image.mTexture->addTextureStats(2048.f * 2048.f); -                    image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH); +                if (image.mLoadIntoTexturePipe) +                { +                    if (image.mTexture.notNull()) +                    { // HACK - force texture to be loaded full rez +                        // TODO: calculate actual vsize +                        image.mTexture->addTextureStats(2048.f * 2048.f); +                        image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH); +                    }                  }              }          } @@ -486,18 +493,23 @@ void Asset::update()  bool Asset::prep()  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; -    // check required extensions and fail if not supported -    bool unsupported = false; +    // check required extensions      for (auto& extension : mExtensionsRequired)      {          if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())          { -            LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL; -            unsupported = true; +            if (ExtensionsIgnored.find(extension) == ExtensionsIgnored.end()) +            { +                LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL; +                mUnsupportedExtensions.push_back(extension); +            } +            else +            { +                mIgnoredExtensions.push_back(extension); +            }          }      } - -    if (unsupported) +    if (mUnsupportedExtensions.size() > 0)      {          return false;      } @@ -513,7 +525,7 @@ bool Asset::prep()      for (auto& image : mImages)      { -        if (!image.prep(*this)) +        if (!image.prep(*this, mLoadIntoVRAM))          {              return false;          } @@ -542,102 +554,106 @@ bool Asset::prep()              return false;          }      } +    if (mLoadIntoVRAM) +    { +        // prepare vertex buffers -    // prepare vertex buffers - -    // material count is number of materials + 1 for default material -    U32 mat_count = (U32) mMaterials.size() + 1; - -    if (LLGLSLShader::sCurBoundShaderPtr == nullptr) -    { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer -        gDebugProgram.bind(); -    } +        // material count is number of materials + 1 for default material +        U32 mat_count = (U32) mMaterials.size() + 1; -    for (S32 double_sided = 0; double_sided < 2; ++double_sided) -    { -        RenderData& rd = mRenderData[double_sided]; -        for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i) -        { -            rd.mBatches[i].resize(mat_count); +        if (LLGLSLShader::sCurBoundShaderPtr == nullptr) +        { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer +            gDebugProgram.bind();          } -        // for each material -        for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id) +        for (S32 double_sided = 0; double_sided < 2; ++double_sided)          { -            // for each shader variant -            U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 }; -            U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 }; - -            S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided; -            if (ds_mat != double_sided) +            RenderData& rd = mRenderData[double_sided]; +            for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)              { -                continue; +                rd.mBatches[i].resize(mat_count);              } -            for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant) +            // for each material +            for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)              { -                U32 attribute_mask = 0; -                // for each mesh -                for (auto& mesh : mMeshes) +                // for each shader variant +                U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 }; +                U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 }; + +                S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided; +                if (ds_mat != double_sided)                  { -                    // for each primitive -                    for (auto& primitive : mesh.mPrimitives) +                    continue; +                } + +                for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant) +                { +                    U32 attribute_mask = 0; +                    // for each mesh +                    for (auto& mesh : mMeshes)                      { -                        if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) +                        // for each primitive +                        for (auto& primitive : mesh.mPrimitives)                          { -                            // accumulate vertex and index counts -                            primitive.mVertexOffset = vertex_count[variant]; -                            primitive.mIndexOffset = index_count[variant]; +                            if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) +                            { +                                // accumulate vertex and index counts +                                primitive.mVertexOffset = vertex_count[variant]; +                                primitive.mIndexOffset = index_count[variant]; -                            vertex_count[variant] += primitive.getVertexCount(); -                            index_count[variant] += primitive.getIndexCount(); +                                vertex_count[variant] += primitive.getVertexCount(); +                                index_count[variant] += primitive.getIndexCount(); -                            // all primitives of a given variant and material should all have the same attribute mask -                            llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask); -                            attribute_mask |= primitive.mAttributeMask; +                                // all primitives of a given variant and material should all have the same attribute mask +                                llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask); +                                attribute_mask |= primitive.mAttributeMask; +                            }                          }                      } -                } -                // allocate vertex buffer and pack it -                if (vertex_count[variant] > 0) -                { -                    U32 mat_idx = mat_id + 1; -                    LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask); +                    // allocate vertex buffer and pack it +                    if (vertex_count[variant] > 0) +                    { +                        U32 mat_idx = mat_id + 1; +                        #if 0 +                        LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask); -                    rd.mBatches[variant][mat_idx].mVertexBuffer = vb; -                    vb->allocateBuffer(vertex_count[variant], -                        index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used -                    vb->setBuffer(); +                        rd.mBatches[variant][mat_idx].mVertexBuffer = vb; +                        vb->allocateBuffer(vertex_count[variant], +                            index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used +                        vb->setBuffer(); -                    for (auto& mesh : mMeshes) -                    { -                        for (auto& primitive : mesh.mPrimitives) +                        for (auto& mesh : mMeshes)                          { -                            if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) +                            for (auto& primitive : mesh.mPrimitives)                              { -                                primitive.upload(vb); +                                if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) +                                { +                                    primitive.upload(vb); +                                }                              }                          } -                    } -                    vb->unmapBuffer(); +                        vb->unmapBuffer(); -                    vb->unbind(); +                        vb->unbind(); +                        #endif +                    }                  }              }          } -    } -    // sanity check that all primitives have a vertex buffer -    for (auto& mesh : mMeshes) -    { -        for (auto& primitive : mesh.mPrimitives) +        // sanity check that all primitives have a vertex buffer +        for (auto& mesh : mMeshes)          { -            llassert(primitive.mVertexBuffer.notNull()); +            for (auto& primitive : mesh.mPrimitives) +            { +                //llassert(primitive.mVertexBuffer.notNull()); +            }          }      } - +    #if 0      // build render batches      for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)      { @@ -664,6 +680,7 @@ bool Asset::prep()              }          }      } +    #endif      return true;  } @@ -672,13 +689,14 @@ Asset::Asset(const Value& src)      *this = src;  } -bool Asset::load(std::string_view filename) +bool Asset::load(std::string_view filename, bool loadIntoVRAM)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; +    mLoadIntoVRAM = loadIntoVRAM;      mFilename = filename;      std::string ext = gDirUtilp->getExtension(mFilename); -    std::ifstream file(filename.data(), std::ios::binary); +    llifstream file(filename.data(), std::ios::binary);      if (file.is_open())      {          std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); @@ -692,7 +710,7 @@ bool Asset::load(std::string_view filename)          }          else if (ext == "glb")          { -            return loadBinary(str); +            return loadBinary(str, mLoadIntoVRAM);          }          else          { @@ -709,8 +727,9 @@ bool Asset::load(std::string_view filename)      return false;  } -bool Asset::loadBinary(const std::string& data) +bool Asset::loadBinary(const std::string& data, bool loadIntoVRAM)  { +    mLoadIntoVRAM = loadIntoVRAM;      // load from binary gltf      const U8* ptr = (const U8*)data.data();      const U8* end = ptr + data.size(); @@ -935,8 +954,9 @@ void Asset::eraseBufferView(S32 bufferView)  LLViewerFetchedTexture* fetch_texture(const LLUUID& id); -bool Image::prep(Asset& asset) +bool Image::prep(Asset& asset, bool loadIntoVRAM)  { +    mLoadIntoTexturePipe = loadIntoVRAM;      LLUUID id;      if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())      { // loaded from an asset, fetch the texture from the asset system @@ -951,12 +971,12 @@ bool Image::prep(Asset& asset)      { // embedded in a buffer, load the texture from the buffer          BufferView& bufferView = asset.mBufferViews[mBufferView];          Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; - -        U8* data = buffer.mData.data() + bufferView.mByteOffset; - -        mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType); - -        if (mTexture.isNull()) +        if (mLoadIntoTexturePipe) +        { +            U8* data = buffer.mData.data() + bufferView.mByteOffset; +            mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType); +        } +        else if (mTexture.isNull() && mLoadIntoTexturePipe)          {              LL_WARNS("GLTF") << "Failed to load image from buffer:" << LL_ENDL;              LL_WARNS("GLTF") << "  image: " << mName << LL_ENDL; @@ -971,12 +991,12 @@ bool Image::prep(Asset& asset)          std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri;          LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file); -        if (tracking_id.notNull()) +        if (tracking_id.notNull() && mLoadIntoTexturePipe)          {              LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);              mTexture = LLViewerTextureManager::getFetchedTexture(world_id);          } -        else +        else if (mLoadIntoTexturePipe)          {              LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;              LL_WARNS("GLTF") << "  image: " << mName << LL_ENDL; @@ -991,7 +1011,7 @@ bool Image::prep(Asset& asset)          return false;      } -    if (!asset.mFilename.empty()) +    if (!asset.mFilename.empty() && mLoadIntoTexturePipe)      { // local preview, boost image so it doesn't discard and force to save raw image in case we save out or upload          mTexture->setBoostLevel(LLViewerTexture::BOOST_PREVIEW);          mTexture->forceToSaveRawImage(0, F32_MAX); diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h index 27821659db..b9554d753c 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -286,6 +286,7 @@ namespace LL              void serialize(boost::json::object& dst) const;          }; +        // Image is for images that we want to load for the given asset.  This acts as an interface into the viewer's texture pipe.          class Image          {          public: @@ -301,6 +302,8 @@ namespace LL              S32 mBits = -1;              S32 mPixelType = -1; +            bool mLoadIntoTexturePipe = false; +              LLPointer<LLViewerFetchedTexture> mTexture;              const Image& operator=(const Value& src); @@ -316,7 +319,7 @@ namespace LL              // preserve only uri and name              void clearData(Asset& asset); -            bool prep(Asset& asset); +            bool prep(Asset& asset, bool loadIntoVRAM);          };          // Render Batch -- vertex buffer and list of primitives to render using @@ -391,6 +394,10 @@ namespace LL              // UBO for storing material data              U32 mMaterialsUBO = 0; +            bool mLoadIntoVRAM = false; + +            std::vector<std::string> mUnsupportedExtensions; +            std::vector<std::string> mIgnoredExtensions;              // prepare for first time use              bool prep(); @@ -428,12 +435,12 @@ namespace LL              // accepts .gltf and .glb files              // Any existing data will be lost              // returns result of prep() on success -            bool load(std::string_view filename); +            bool load(std::string_view filename, bool loadIntoVRAM);              // load .glb contents from memory              // data - binary contents of .glb file              // returns result of prep() on success -            bool loadBinary(const std::string& data); +            bool loadBinary(const std::string& data, bool loadIntoVRAM);              const Asset& operator=(const Value& src);              void serialize(boost::json::object& dst) const; diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h index ef9bba8128..be36c5e90b 100644 --- a/indra/newview/gltf/buffer_util.h +++ b/indra/newview/gltf/buffer_util.h @@ -159,6 +159,12 @@ namespace LL          }          template<> +        inline void copyVec3<F32, LLColor4U>(F32* src, LLColor4U& dst) +        { +            dst.set((U8)(src[0] * 255.f), (U8)(src[1] * 255.f), (U8)(src[2] * 255.f), 255); +        } + +        template<>          inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)          {              dst.set((U8)(src[0]), (U8)(src[1]), (U8)(src[2]), 255); @@ -369,6 +375,11 @@ namespace LL          template<class T>          inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)          { +            if (accessor.mBufferView == INVALID_INDEX) +            { +                LL_WARNS("GLTF") << "Invalid buffer" << LL_ENDL; +                return; +            }              const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];              const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];              const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp new file mode 100644 index 0000000000..dd1d327683 --- /dev/null +++ b/indra/newview/gltf/llgltfloader.cpp @@ -0,0 +1,1822 @@ +/** + * @file LLGLTFLoader.cpp + * @brief LLGLTFLoader class implementation + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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 "llgltfloader.h" +#include "meshoptimizer.h" +#include <glm/gtc/packing.hpp> + +// Import & define single-header gltf import/export lib +#define TINYGLTF_IMPLEMENTATION +#define TINYGLTF_USE_CPP14  // default is C++ 11 + +// tinygltf by default loads image files using STB +#define STB_IMAGE_IMPLEMENTATION +// to use our own image loading: +// 1. replace this definition with TINYGLTF_NO_STB_IMAGE +// 2. provide image loader callback with TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data) + +// tinygltf saves image files using STB +#define STB_IMAGE_WRITE_IMPLEMENTATION +// similarly, can override with TINYGLTF_NO_STB_IMAGE_WRITE and TinyGLTF::SetImageWriter(fxn, data) + +// Additionally, disable inclusion of STB header files entirely with +// TINYGLTF_NO_INCLUDE_STB_IMAGE +// TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE +#include "tinygltf/tiny_gltf.h" + + +// TODO: includes inherited from dae loader.  Validate / prune + +#include "llsdserialize.h" +#include "lljoint.h" +#include "llbase64.h" +#include "lldir.h" + +#include "llmatrix4a.h" + +#include <boost/regex.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/exception/diagnostic_information.hpp> +#include <fstream> + +static const std::string lod_suffix[LLModel::NUM_LODS] = +{ +    "_LOD0", +    "_LOD1", +    "_LOD2", +    "", +    "_PHYS", +}; + +// Premade rotation matrix, GLTF is Y-up while SL is Z-up +static const glm::mat4 coord_system_rotation( +    1.f, 0.f, 0.f, 0.f, +    0.f, 0.f, 1.f, 0.f, +    0.f, -1.f, 0.f, 0.f, +    0.f, 0.f, 0.f, 1.f +); + + +static const glm::mat4 coord_system_rotationxy( +    0.f, 1.f, 0.f, 0.f, +    -1.f, 0.f, 0.f, 0.f, +    0.f, 0.f, 1.f, 0.f, +    0.f, 0.f, 0.f, 1.f +); + +static const S32 VERTEX_SPLIT_SAFETY_MARGIN = 3 * 3 + 1; // 10 vertices: 3 complete triangles plus remapping overhead +static const S32 VERTEX_LIMIT = USHRT_MAX - VERTEX_SPLIT_SAFETY_MARGIN; + +LLGLTFLoader::LLGLTFLoader(std::string                filename, +    S32                                               lod, +    LLModelLoader::load_callback_t                    load_cb, +    LLModelLoader::joint_lookup_func_t                joint_lookup_func, +    LLModelLoader::texture_load_func_t                texture_load_func, +    LLModelLoader::state_callback_t                   state_cb, +    void *                                            opaque_userdata, +    JointTransformMap &                               jointTransformMap, +    JointNameSet &                                    jointsFromNodes, +    std::map<std::string, std::string, std::less<>> & jointAliasMap, +    U32                                               maxJointsPerMesh, +    U32                                               modelLimit, +    U32                                               debugMode, +    std::vector<LLJointData>                          viewer_skeleton) //, +    //bool                                            preprocess) +    : LLModelLoader( filename, +                     lod, +                     load_cb, +                     joint_lookup_func, +                     texture_load_func, +                     state_cb, +                     opaque_userdata, +                     jointTransformMap, +                     jointsFromNodes, +                     jointAliasMap, +                     maxJointsPerMesh, +                     modelLimit, +                     debugMode) +    , mViewerJointData(viewer_skeleton) +    , mGltfLoaded(false) +    , mApplyXYRotation(false) +{ +} + +LLGLTFLoader::~LLGLTFLoader() {} + +bool LLGLTFLoader::OpenFile(const std::string &filename) +{ +    // Clear the material cache for new file +    mMaterialCache.clear(); + +    tinygltf::TinyGLTF loader; +    std::string filename_lc(filename); +    LLStringUtil::toLower(filename_lc); + +    try +    { +        mGltfLoaded = mGLTFAsset.load(filename, false); +    } +    catch (const std::exception& e) +    { +        LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL; +        LLSD args; +        args["Message"] = "ParsingErrorException"; +        args["FILENAME"] = filename; +        args["EXCEPTION"] = e.what(); +        mWarningsArray.append(args); +        setLoadState(ERROR_PARSING); +        return false; +    } +    catch (...) +    { +        LOG_UNHANDLED_EXCEPTION("LLGLTFLoader"); +        LLSD args; +        args["Message"] = "ParsingErrorException"; +        args["FILENAME"] = filename; +        args["EXCEPTION"] = boost::current_exception_diagnostic_information(); +        mWarningsArray.append(args); +        setLoadState(ERROR_PARSING); +        return false; +    } + +    if (!mGltfLoaded) +    { +        notifyUnsupportedExtension(true); + +        for (const auto& buffer : mGLTFAsset.mBuffers) +        { +            if (buffer.mByteLength > 0 && buffer.mData.empty()) +            { +                bool bin_file = buffer.mUri.ends_with(".bin"); +                LLSD args; +                args["Message"] = bin_file ? "ParsingErrorMissingBufferBin" : "ParsingErrorMissingBuffer"; +                args["BUFFER_NAME"] = buffer.mName; +                args["BUFFER_URI"] = buffer.mUri; +                mWarningsArray.append(args); +            } +        } +        setLoadState(ERROR_PARSING); +        return false; +    } + +    notifyUnsupportedExtension(false); + +    bool meshesLoaded = parseMeshes(); + +    setLoadState(DONE); + +    return meshesLoaded; +} + +void LLGLTFLoader::addModelToScene( +    LLModel* pModel, +    const std::string& model_name, +    U32 submodel_limit, +    const LLMatrix4& transformation, +    const LLVolumeParams& volume_params, +    const material_map& mats) +{ +    U32 volume_faces = pModel->getNumVolumeFaces(); + +    // Side-steps all manner of issues when splitting models +    // and matching lower LOD materials to base models +    // +    pModel->sortVolumeFacesByMaterialName(); + +    int submodelID = 0; + +    // remove all faces that definitely won't fit into one model and submodel limit +    U32 face_limit = (submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES; +    if (face_limit < volume_faces) +    { +        LL_WARNS("GLTF_IMPORT") << "Model contains " << volume_faces +            << " faces, exceeding the limit of " << face_limit << LL_ENDL; + +        LLSD args; +        args["Message"] = "ModelTooManySubmodels"; +        args["MODEL_NAME"] = pModel->mLabel; +        args["SUBMODEL_COUNT"] = static_cast<S32>(llfloor((F32)volume_faces / LL_SCULPT_MESH_MAX_FACES)); +        args["SUBMODEL_LIMIT"] = static_cast<S32>(submodel_limit); +        mWarningsArray.append(args); + +        pModel->setNumVolumeFaces(face_limit); +    } + +    LLVolume::face_list_t remainder; +    std::vector<LLModel*> ready_models; +    LLModel* current_model = pModel; + +    do +    { +        current_model->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder); + +        volume_faces = static_cast<U32>(remainder.size()); + +        // Don't add to scene yet because weights and materials aren't ready. +        // Just save it +        ready_models.push_back(current_model); + +        // If we have left-over volume faces, create another model +        // to absorb them. +        if (volume_faces) +        { +            LLModel* next = new LLModel(volume_params, 0.f); +            next->ClearFacesAndMaterials(); +            next->mSubmodelID = ++submodelID; + +            std::string instance_name = model_name; +            if (next->mSubmodelID > 0) +            { +                instance_name += (char)((int)'a' + next->mSubmodelID); +            } +            // Check for duplicates and add copy suffix if needed +            int duplicate_count = 0; +            for (const auto& inst : mScene[transformation]) +            { +                if (inst.mLabel == instance_name) +                { +                    ++duplicate_count; +                } +            } +            if (duplicate_count > 0) { +                instance_name += "_copy_" + std::to_string(duplicate_count); +            } +            next->mLabel = instance_name; + +            next->getVolumeFaces() = remainder; +            next->mNormalizedScale = current_model->mNormalizedScale; +            next->mNormalizedTranslation = current_model->mNormalizedTranslation; +            next->mSkinWeights = current_model->mSkinWeights; +            next->mPosition = current_model->mPosition; + +            const LLMeshSkinInfo& current_skin_info = current_model->mSkinInfo; +            LLMeshSkinInfo& next_skin_info = next->mSkinInfo; +            next_skin_info.mJointNames = current_skin_info.mJointNames; +            next_skin_info.mJointNums = current_skin_info.mJointNums; +            next_skin_info.mBindShapeMatrix = current_skin_info.mBindShapeMatrix; +            next_skin_info.mInvBindMatrix = current_skin_info.mInvBindMatrix; +            next_skin_info.mAlternateBindMatrix = current_skin_info.mAlternateBindMatrix; +            next_skin_info.mPelvisOffset = current_skin_info.mPelvisOffset; + + +            if (current_model->mMaterialList.size() > LL_SCULPT_MESH_MAX_FACES) +            { +                next->mMaterialList.assign(current_model->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, current_model->mMaterialList.end()); +                current_model->mMaterialList.resize(LL_SCULPT_MESH_MAX_FACES); +            } + +            current_model = next; +        } + +        remainder.clear(); + +    } while (volume_faces); + +    for (auto model : ready_models) +    { +        // remove unused/redundant vertices +        model->remapVolumeFaces(); + +        mModelList.push_back(model); + +        std::map<std::string, LLImportMaterial> materials; +        for (U32 i = 0; i < (U32)model->mMaterialList.size(); ++i) +        { +            material_map::const_iterator found = mats.find(model->mMaterialList[i]); +            if (found != mats.end()) +            { +                materials[model->mMaterialList[i]] = found->second; +            } +            else +            { +                materials[model->mMaterialList[i]] = LLImportMaterial(); +            } +        } +        // Keep base name for scene instance. +        std::string instance_name = model->mLabel; +        // Add suffix. Suffix is nessesary for model matching logic +        // because sometimes higher lod can be used as a lower one, so models +        // need unique names not just in scope of one lod, but across lods. +        model->mLabel += lod_suffix[mLod]; +        mScene[transformation].push_back(LLModelInstance(model, instance_name, transformation, materials)); +        stretch_extents(model, transformation); +    } +} + +bool LLGLTFLoader::parseMeshes() +{ +    if (!mGltfLoaded) return false; + +    // 2022-04 DJH Volume params from dae example. TODO understand PCODE +    LLVolumeParams volume_params; +    volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + +    mTransform.setIdentity(); + +    for (auto& node : mGLTFAsset.mNodes) +    { +        // Make node matrix valid for correct transformation +        node.makeMatrixValid(); +    } + +    if (mGLTFAsset.mSkins.size() > 0) +    { +        checkForXYrotation(mGLTFAsset.mSkins[0]); +        populateJointGroups(); +    } + +    // Populate the joints from skins first. +    // Multiple meshes can share the same skin, so preparing skins beforehand. +    for (S32 i = 0; i < mGLTFAsset.mSkins.size(); i++) +    { +        populateJointsFromSkin(i); +    } + +    // Track how many times each mesh name has been used +    std::map<std::string, S32> mesh_name_counts; + +    // For now use mesh count, but might be better to do 'mNodes.size() - joints count'. +    U32 submodel_limit = mGLTFAsset.mMeshes.size() > 0 ? mGeneratedModelLimit / (U32)mGLTFAsset.mMeshes.size() : 0; + +    // Check if we have scenes defined +    if (!mGLTFAsset.mScenes.empty()) +    { +        // Process the default scene (or first scene if no default) +        S32 scene_idx = mGLTFAsset.mScene >= 0 ? mGLTFAsset.mScene : 0; + +        if (scene_idx < mGLTFAsset.mScenes.size()) +        { +            const LL::GLTF::Scene& scene = mGLTFAsset.mScenes[scene_idx]; + +            LL_INFOS("GLTF_IMPORT") << "Processing scene " << scene_idx << " with " << scene.mNodes.size() << " root nodes" << LL_ENDL; + +            // Process all root nodes defined in the scene +            for (S32 root_idx : scene.mNodes) +            { +                if (root_idx >= 0 && root_idx < static_cast<S32>(mGLTFAsset.mNodes.size())) +                { +                    processNodeHierarchy(root_idx, mesh_name_counts, submodel_limit, volume_params); +                } +            } +        } +    } +    else +    { +        LL_WARNS("GLTF_IMPORT") << "No scenes defined in GLTF file" << LL_ENDL; + +        LLSD args; +        args["Message"] = "NoScenesFound"; +        mWarningsArray.append(args); +        return false; +    } + +    checkGlobalJointUsage(); + +    return true; +} + +void LLGLTFLoader::processNodeHierarchy(S32 node_idx, std::map<std::string, S32>& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params) +{ +    if (node_idx < 0 || node_idx >= static_cast<S32>(mGLTFAsset.mNodes.size())) +        return; + +    const LL::GLTF::Node& node = mGLTFAsset.mNodes[node_idx]; + +    LL_DEBUGS("GLTF_IMPORT") << "Processing node " << node_idx << " (" << node.mName << ")" +                            << " - has mesh: " << (node.mMesh >= 0 ? "yes" : "no") +                            << " - children: " << node.mChildren.size() << LL_ENDL; + +    // Process this node's mesh if it has one +    if (node.mMesh >= 0 && node.mMesh < mGLTFAsset.mMeshes.size()) +    { +        LLMatrix4    transformation; +        material_map mats; + +        LLModel* pModel = new LLModel(volume_params, 0.f); +        const LL::GLTF::Mesh& mesh = mGLTFAsset.mMeshes[node.mMesh]; + +        // Get base mesh name and track usage +        std::string base_name = getLodlessLabel(mesh); +        if (base_name.empty()) +        { +            base_name = "mesh_" + std::to_string(node.mMesh); +        } + +        S32 instance_count = mesh_name_counts[base_name]++; + +        // make name unique +        if (instance_count > 0) +        { +            base_name = base_name + "_copy_" + std::to_string(instance_count); +        } + +        if (populateModelFromMesh(pModel, base_name, mesh, node, mats) && +            (LLModel::NO_ERRORS == pModel->getStatus()) && +            validate_model(pModel)) +        { +            mTransform.setIdentity(); +            transformation = mTransform; + +            // adjust the transformation to compensate for mesh normalization +            LLVector3 mesh_scale_vector; +            LLVector3 mesh_translation_vector; +            pModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + +            LLMatrix4 mesh_translation; +            mesh_translation.setTranslation(mesh_translation_vector); +            mesh_translation *= transformation; +            transformation = mesh_translation; + +            LLMatrix4 mesh_scale; +            mesh_scale.initScale(mesh_scale_vector); +            mesh_scale *= transformation; +            transformation = mesh_scale; + +            if (node.mSkin >= 0) +            { +                // "Bind Shape Matrix" is supposed to transform the geometry of the skinned mesh +                // into the coordinate space of the joints. +                // In GLTF, this matrix is omitted, and it is assumed that this transform is either +                // premultiplied with the mesh data, or postmultiplied to the inverse bind matrices. +                // +                // TODO: There appears to be missing rotation when joints rotate the model +                // or inverted bind matrices are missing inherited rotation +                // (based of values the 'bento shoes' mesh might be missing 90 degrees horizontaly +                // prior to skinning) + +                pModel->mSkinInfo.mBindShapeMatrix.loadu(mesh_scale); +                LL_INFOS("GLTF_DEBUG") << "Model: " << pModel->mLabel << " mBindShapeMatrix: " << pModel->mSkinInfo.mBindShapeMatrix << LL_ENDL; +            } + +            if (transformation.determinant() < 0) +            { // negative scales are not supported +                LL_INFOS("GLTF_IMPORT") << "Negative scale detected, unsupported post-normalization transform.  domInstance_geometry: " +                           << pModel->mLabel << LL_ENDL; +                LLSD args; +                args["Message"] = "NegativeScaleNormTrans"; +                args["LABEL"]   = pModel->mLabel; +                mWarningsArray.append(args); +            } + +            addModelToScene(pModel, base_name, submodel_limit, transformation, volume_params, mats); +            mats.clear(); +        } +        else +        { +            setLoadState(ERROR_MODEL + pModel->getStatus()); +            delete pModel; +            return; +        } +    } +    else if (node.mMesh >= 0) +    { +        // Log invalid mesh reference +        LL_WARNS("GLTF_IMPORT") << "Node " << node_idx << " (" << node.mName +                                << ") references invalid mesh " << node.mMesh +                                << " (total meshes: " << mGLTFAsset.mMeshes.size() << ")" << LL_ENDL; + +        LLSD args; +        args["Message"] = "InvalidMeshReference"; +        args["NODE_NAME"] = node.mName; +        args["MESH_INDEX"] = node.mMesh; +        args["TOTAL_MESHES"] = static_cast<S32>(mGLTFAsset.mMeshes.size()); +        mWarningsArray.append(args); +    } + +    // Process all children recursively +    for (S32 child_idx : node.mChildren) +    { +        processNodeHierarchy(child_idx, mesh_name_counts, submodel_limit, volume_params); +    } +} + +void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const +{ +    if (node_index < 0 || node_index >= static_cast<S32>(asset.mNodes.size())) +    { +        combined_transform = glm::mat4(1.0f); +        return; +    } + +    const auto& node = asset.mNodes[node_index]; + +    // Ensure the node's matrix is valid +    const_cast<LL::GLTF::Node&>(node).makeMatrixValid(); + +    // Start with this node's transform +    combined_transform = node.mMatrix; + +    // Find and apply parent transform if it exists +    for (size_t i = 0; i < asset.mNodes.size(); ++i) +    { +        const auto& potential_parent = asset.mNodes[i]; +        auto it = std::find(potential_parent.mChildren.begin(), potential_parent.mChildren.end(), node_index); + +        if (it != potential_parent.mChildren.end()) +        { +            // Found parent - recursively get its combined transform and apply it +            glm::mat4 parent_transform; +            computeCombinedNodeTransform(asset, static_cast<S32>(i), parent_transform); +            combined_transform = parent_transform * combined_transform; +            return; // Early exit - a node can only have one parent +        } +    } +} + +bool LLGLTFLoader::addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx) +{ +    const std::string& legal_name = mJointNames[gltf_skin_idx][gltf_joint_idx]; +    if (legal_name.empty()) +    { +        llassert(false); // should have been stopped by gltf_joint_index_use[i] == -1 +        return false; +    } +    skin_info.mJointNames.push_back(legal_name); +    skin_info.mJointNums.push_back(-1); + +    // In scope of same skin multiple meshes reuse same bind matrices +    skin_info.mInvBindMatrix.push_back(mInverseBindMatrices[gltf_skin_idx][gltf_joint_idx]); +    skin_info.mAlternateBindMatrix.push_back(mAlternateBindMatrices[gltf_skin_idx][gltf_joint_idx]); + +    // Track joint usage for this skin, for the sake of unused joints detection +    mJointUsage[gltf_skin_idx][gltf_joint_idx]++; + +    return true; +} + +LLGLTFLoader::LLGLTFImportMaterial LLGLTFLoader::processMaterial(S32 material_index, S32 fallback_index) +{ +    // Check cache first +    auto cached = mMaterialCache.find(material_index); +    if (cached != mMaterialCache.end()) +    { +        return cached->second; +    } + +    LLImportMaterial impMat; +    impMat.mDiffuseColor = LLColor4::white; // Default color + +    // Generate material name +    std::string materialName = generateMaterialName(material_index, fallback_index); + +    // Process material if available +    if (material_index >= 0 && material_index < mGLTFAsset.mMaterials.size()) +    { +        LL::GLTF::Material* material = &mGLTFAsset.mMaterials[material_index]; + +        // Set diffuse color from base color factor +        impMat.mDiffuseColor = LLColor4( +            material->mPbrMetallicRoughness.mBaseColorFactor[0], +            material->mPbrMetallicRoughness.mBaseColorFactor[1], +            material->mPbrMetallicRoughness.mBaseColorFactor[2], +            material->mPbrMetallicRoughness.mBaseColorFactor[3] +        ); + +        // Process base color texture if it exists +        if (material->mPbrMetallicRoughness.mBaseColorTexture.mIndex >= 0) +        { +            S32 texIndex = material->mPbrMetallicRoughness.mBaseColorTexture.mIndex; +            std::string filename = processTexture(texIndex, "base_color", material->mName); + +            if (!filename.empty()) +            { +                impMat.mDiffuseMapFilename = filename; +                impMat.mDiffuseMapLabel = material->mName.empty() ? filename : material->mName; + +                // Check if the texture is already loaded +                S32 sourceIndex; +                if (validateTextureIndex(texIndex, sourceIndex)) +                { +                    LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex]; +                    if (image.mTexture.notNull()) +                    { +                        mTexturesNeedScaling |= image.mHeight > LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT || image.mWidth > LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT; +                        impMat.setDiffuseMap(image.mTexture->getID()); +                        LL_INFOS("GLTF_IMPORT") << "Using existing texture ID: " << image.mTexture->getID().asString() << LL_ENDL; +                    } +                    else +                    { +                        LL_INFOS("GLTF_IMPORT") << "Texture needs loading: " << impMat.mDiffuseMapFilename << LL_ENDL; +                    } +                } +            } +        } +    } + +    // Create cached material with both material and name +    LLGLTFImportMaterial cachedMat(impMat, materialName); + +    // Cache the processed material +    mMaterialCache[material_index] = cachedMat; +    return cachedMat; +} + +std::string LLGLTFLoader::processTexture(S32 texture_index, const std::string& texture_type, const std::string& material_name) +{ +    S32 sourceIndex; +    if (!validateTextureIndex(texture_index, sourceIndex)) +        return ""; + +    LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex]; + +    // Process URI-based textures +    if (!image.mUri.empty()) +    { +        std::string filename = image.mUri; +        size_t pos = filename.find_last_of("/\\"); +        if (pos != std::string::npos) +        { +            filename = filename.substr(pos + 1); +        } + +        LL_INFOS("GLTF_IMPORT") << "Found texture: " << filename << " for material: " << material_name << LL_ENDL; + +        LLSD args; +        args["Message"] = "TextureFound"; +        args["TEXTURE_NAME"] = filename; +        args["MATERIAL_NAME"] = material_name; +        mWarningsArray.append(args); + +        return filename; +    } + +    // Process embedded textures +    if (image.mBufferView >= 0) +    { +        return extractTextureToTempFile(texture_index, texture_type); +    } + +    return ""; +} + +bool LLGLTFLoader::validateTextureIndex(S32 texture_index, S32& source_index) +{ +    if (texture_index < 0 || texture_index >= mGLTFAsset.mTextures.size()) +        return false; + +    source_index = mGLTFAsset.mTextures[texture_index].mSource; +    if (source_index < 0 || source_index >= mGLTFAsset.mImages.size()) +        return false; + +    return true; +} + +std::string LLGLTFLoader::generateMaterialName(S32 material_index, S32 fallback_index) +{ +    if (material_index >= 0 && material_index < mGLTFAsset.mMaterials.size()) +    { +        LL::GLTF::Material* material = &mGLTFAsset.mMaterials[material_index]; +        std::string materialName = material->mName; + +        if (materialName.empty()) +        { +            materialName = "mat" + std::to_string(material_index); +        } +        return materialName; +    } +    else +    { +        return fallback_index >= 0 ? "mat_default" + std::to_string(fallback_index) : "mat_default"; +    } +} + +bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const std::string& base_name, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats) +{ +    // Set the requested label for the floater display and uploading +    pModel->mRequestedLabel = gDirUtilp->getBaseFileName(mFilename, true); +    // Set only name, suffix will be added later +    pModel->mLabel = base_name; + +    LL_DEBUGS("GLTF_DEBUG") << "Processing model " << pModel->mLabel << LL_ENDL; + +    pModel->ClearFacesAndMaterials(); + +    S32 skinIdx = nodeno.mSkin; + +    // Compute final combined transform matrix (hierarchy + coordinate rotation) +    S32 node_index = static_cast<S32>(&nodeno - &mGLTFAsset.mNodes[0]); +    glm::mat4 hierarchy_transform; +    computeCombinedNodeTransform(mGLTFAsset, node_index, hierarchy_transform); + +    // Combine transforms: coordinate rotation applied to hierarchy transform +    glm::mat4 final_transform = coord_system_rotation * hierarchy_transform; +    if (mApplyXYRotation) +    { +        final_transform = coord_system_rotationxy * final_transform; +    } + +    // Check if we have a negative scale (flipped coordinate system) +    bool hasNegativeScale = glm::determinant(final_transform) < 0.0f; + +    // Pre-compute normal transform matrix (transpose of inverse of upper-left 3x3) +    const glm::mat3 normal_transform = glm::transpose(glm::inverse(glm::mat3(final_transform))); + +    // Mark unsuported joints with '-1' so that they won't get added into weights +    // GLTF maps all joints onto all meshes. Gather use count per mesh to cut unused ones. +    std::vector<S32> gltf_joint_index_use; +    if (skinIdx >= 0 && mGLTFAsset.mSkins.size() > skinIdx) +    { +        LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx]; + +        size_t jointCnt = gltf_skin.mJoints.size(); +        gltf_joint_index_use.resize(jointCnt, 0); + +        for (size_t i = 0; i < jointCnt; ++i) +        { +            if (mJointNames[skinIdx][i].empty()) +            { +                // This might need to hold a substitute index +                gltf_joint_index_use[i] = -1; // mark as unsupported +            } +        } +    } + +    for (size_t prim_idx = 0; prim_idx < mesh.mPrimitives.size(); ++prim_idx) +    { +        const LL::GLTF::Primitive& prim = mesh.mPrimitives[prim_idx]; + +        // So primitives already have all of the data we need for a given face in SL land. +        // Primitives may only ever have a single material assigned to them - as the relation is 1:1 in terms of intended draw call +        // count. Just go ahead and populate faces direct from the GLTF primitives here. -Geenz 2025-04-07 +        LLVolumeFace face; +        std::vector<GLTFVertex> vertices; + +        // Use cached material processing +        LLGLTFImportMaterial cachedMat = processMaterial(prim.mMaterial, pModel->getNumVolumeFaces() - 1); +        LLImportMaterial impMat = cachedMat; +        std::string materialName = cachedMat.name; +        mats[materialName] = impMat; + +        if (prim.getIndexCount() % 3 != 0) +        { +            LL_WARNS("GLTF_IMPORT") << "Mesh '" << mesh.mName << "' primitive " << prim_idx +                << ": Invalid index count " << prim.getIndexCount() +                << " (not divisible by 3). GLTF files must contain triangulated geometry." << LL_ENDL; + +            LLSD args; +            args["Message"] = "InvalidGeometryNonTriangulated"; +            args["MESH_NAME"] = mesh.mName; +            args["PRIMITIVE_INDEX"] = static_cast<S32>(prim_idx); +            args["INDEX_COUNT"] = static_cast<S32>(prim.getIndexCount()); +            mWarningsArray.append(args); +            return false; // Skip this primitive +        } + +        // Apply the global scale and center offset to all vertices +        for (U32 i = 0; i < prim.getVertexCount(); i++) +        { +            // Use pre-computed final_transform +            glm::vec4 pos(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2], 1.0f); +            glm::vec4 transformed_pos = final_transform * pos; + +            GLTFVertex vert; +            vert.position = glm::vec3(transformed_pos); + +            if (!prim.mNormals.empty()) +            { +                // Use pre-computed normal_transform +                glm::vec3 normal_vec(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]); +                vert.normal = glm::normalize(normal_transform * normal_vec); +            } +            else +            { +                // Use default normal (pointing up in model space) +                vert.normal = glm::normalize(normal_transform * glm::vec3(0.0f, 0.0f, 1.0f)); +                LL_DEBUGS("GLTF_IMPORT") << "No normals found for primitive, using default normal." << LL_ENDL; +            } + +            vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], -prim.mTexCoords0[i][1]); + +            if (skinIdx >= 0) +            { +                vert.weights = glm::vec4(prim.mWeights[i]); + +                auto accessorIdx = prim.mAttributes.at("JOINTS_0"); +                LL::GLTF::Accessor::ComponentType componentType = LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE; +                if (accessorIdx >= 0) +                { +                    auto accessor = mGLTFAsset.mAccessors[accessorIdx]; +                    componentType = accessor.mComponentType; +                } + +                // The GLTF spec allows for either an unsigned byte for joint indices, or an unsigned short. +                // Detect and unpack accordingly. +                if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE) +                { +                    auto ujoint = glm::unpackUint4x8((U32)(prim.mJoints[i] & 0xFFFFFFFF)); +                    vert.joints = glm::u16vec4(ujoint.x, ujoint.y, ujoint.z, ujoint.w); +                } +                else if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_SHORT) +                { +                    vert.joints = glm::unpackUint4x16(prim.mJoints[i]); +                } +                else +                { +                    vert.joints = glm::zero<glm::u16vec4>(); +                    vert.weights = glm::zero<glm::vec4>(); +                } +            } +            vertices.push_back(vert); +        } + +        // Check for empty vertex array before processing +        if (vertices.empty()) +        { +            LL_WARNS("GLTF_IMPORT") << "Empty vertex array for primitive " << prim_idx << " in model " << mesh.mName << LL_ENDL; +            LLSD args; +            args["Message"] = "EmptyVertexArray"; +            args["MESH_NAME"] = mesh.mName; +            args["PRIMITIVE_INDEX"] = static_cast<S32>(prim_idx); +            args["INDEX_COUNT"] = static_cast<S32>(prim.getIndexCount()); +            mWarningsArray.append(args); +            return false; // Skip this primitive +        } + +        std::vector<LLVolumeFace::VertexData> faceVertices; +        glm::vec3 min = glm::vec3(FLT_MAX); +        glm::vec3 max = glm::vec3(-FLT_MAX); + +        for (U32 i = 0; i < vertices.size(); i++) +        { +            LLVolumeFace::VertexData vert; + +            // Update min/max bounds +            if (i == 0) +            { +                min = max = vertices[i].position; +            } +            else +            { +                min.x = std::min(min.x, vertices[i].position.x); +                min.y = std::min(min.y, vertices[i].position.y); +                min.z = std::min(min.z, vertices[i].position.z); +                max.x = std::max(max.x, vertices[i].position.x); +                max.y = std::max(max.y, vertices[i].position.y); +                max.z = std::max(max.z, vertices[i].position.z); +            } + +            LLVector4a position = LLVector4a(vertices[i].position.x, vertices[i].position.y, vertices[i].position.z); +            LLVector4a normal = LLVector4a(vertices[i].normal.x, vertices[i].normal.y, vertices[i].normal.z); +            vert.setPosition(position); +            vert.setNormal(normal); +            vert.mTexCoord = LLVector2(vertices[i].uv0.x, vertices[i].uv0.y); +            faceVertices.push_back(vert); + +            if (skinIdx >= 0) +            { +                // create list of weights that influence this vertex +                LLModel::weight_list weight_list; + +                // Drop joints that viewer doesn't support (negative in gltf_joint_index_use_count) +                // don't reindex them yet, more indexes will be removed +                // Also drop joints that have no weight. GLTF stores 4 per vertex, so there might be +                // 'empty' ones +                if (gltf_joint_index_use[vertices[i].joints.x] >= 0 +                    && vertices[i].weights.x > 0.f) +                { +                    weight_list.push_back(LLModel::JointWeight(vertices[i].joints.x, vertices[i].weights.x)); +                    gltf_joint_index_use[vertices[i].joints.x]++; +                } +                if (gltf_joint_index_use[vertices[i].joints.y] >= 0 +                    && vertices[i].weights.y > 0.f) +                { +                    weight_list.push_back(LLModel::JointWeight(vertices[i].joints.y, vertices[i].weights.y)); +                    gltf_joint_index_use[vertices[i].joints.y]++; +                } +                if (gltf_joint_index_use[vertices[i].joints.z] >= 0 +                    && vertices[i].weights.z > 0.f) +                { +                    weight_list.push_back(LLModel::JointWeight(vertices[i].joints.z, vertices[i].weights.z)); +                    gltf_joint_index_use[vertices[i].joints.z]++; +                } +                if (gltf_joint_index_use[vertices[i].joints.w] >= 0 +                    && vertices[i].weights.w > 0.f) +                { +                    weight_list.push_back(LLModel::JointWeight(vertices[i].joints.w, vertices[i].weights.w)); +                    gltf_joint_index_use[vertices[i].joints.w]++; +                } + +                std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); + +                std::vector<LLModel::JointWeight> wght; +                F32                               total = 0.f; + +                for (U32 j = 0; j < llmin((U32)4, (U32)weight_list.size()); ++j) +                { +                    // take up to 4 most significant weights +                    // Ported from the DAE loader - however, GLTF right now only supports up to four weights per vertex. +                    wght.push_back(weight_list[j]); +                    total += weight_list[j].mWeight; +                } + +                if (total != 0.f) +                { +                    F32 scale = 1.f / total; +                    if (scale != 1.f) +                    { // normalize weights +                        for (U32 j = 0; j < wght.size(); ++j) +                        { +                            wght[j].mWeight *= scale; +                        } +                    } +                } + +                if (wght.size() > 0) +                { +                    pModel->mSkinWeights[LLVector3(vertices[i].position)] = wght; +                } +            } +        } + +        // Indices handling +        if (faceVertices.size() >= VERTEX_LIMIT) +        { +            // Will have to remap 32 bit indices into 16 bit indices +            // For the sake of simplicity build vector of 32 bit indices first +            std::vector<U32> indices_32; +            for (U32 i = 0; i < prim.getIndexCount(); i += 3) +            { +                // When processing indices, flip winding order if needed +                if (hasNegativeScale) +                { +                    // Flip winding order for negative scale +                    indices_32.push_back(prim.mIndexArray[i]); +                    indices_32.push_back(prim.mIndexArray[i + 2]); // Swap these two +                    indices_32.push_back(prim.mIndexArray[i + 1]); +                } +                else +                { +                    indices_32.push_back(prim.mIndexArray[i]); +                    indices_32.push_back(prim.mIndexArray[i + 1]); +                    indices_32.push_back(prim.mIndexArray[i + 2]); +                } +            } + +            // Generates a vertex remap table with no gaps in the resulting sequence +            std::vector<U32> remap(faceVertices.size()); +            size_t vertex_count = meshopt_generateVertexRemap(&remap[0], &indices_32[0], indices_32.size(), &faceVertices[0], faceVertices.size(), sizeof(LLVolumeFace::VertexData)); + +            // Manually remap vertices +            std::vector<LLVolumeFace::VertexData> optimized_vertices(vertex_count); +            for (size_t i = 0; i < vertex_count; ++i) +            { +                optimized_vertices[i] = faceVertices[remap[i]]; +            } + +            std::vector<U32> optimized_indices(indices_32.size()); +            meshopt_remapIndexBuffer(&optimized_indices[0], &indices_32[0], indices_32.size(), &remap[0]); + +            // Sort indices to improve mesh splits (reducing amount of duplicated indices) +            meshopt_optimizeVertexCache(&optimized_indices[0], &optimized_indices[0], indices_32.size(), vertex_count); + +            std::vector<U16> indices_16; +            std::vector<S64> vertices_remap; +            vertices_remap.resize(vertex_count, -1); +            S32 created_faces = 0; +            std::vector<LLVolumeFace::VertexData> face_verts; +            min = glm::vec3(FLT_MAX); +            max = glm::vec3(-FLT_MAX); + +            for (size_t idx = 0; idx < optimized_indices.size(); idx++) +            { +                size_t vert_index = optimized_indices[idx]; +                if (vertices_remap[vert_index] == -1) +                { +                    // First encounter, add it +                    size_t new_vert_idx = face_verts.size(); +                    vertices_remap[vert_index] = (S64)new_vert_idx; +                    face_verts.push_back(optimized_vertices[vert_index]); +                    vert_index = new_vert_idx; + +                    // Update min/max bounds +                    const LLVector4a& vec = face_verts[new_vert_idx].getPosition(); +                    if (new_vert_idx == 0) +                    { +                        min.x = vec[0]; +                        min.y = vec[1]; +                        min.z = vec[2]; +                        max = min; +                    } +                    else +                    { +                        min.x = std::min(min.x, vec[0]); +                        min.y = std::min(min.y, vec[1]); +                        min.z = std::min(min.z, vec[2]); +                        max.x = std::max(max.x, vec[0]); +                        max.y = std::max(max.y, vec[1]); +                        max.z = std::max(max.z, vec[2]); +                    } +                } +                else +                { +                    // already in vector, get position +                    vert_index = (size_t)vertices_remap[vert_index]; +                } +                indices_16.push_back((U16)vert_index); + +                if (indices_16.size() % 3 == 0 && face_verts.size() >= VERTEX_LIMIT) +                { +                    LLVolumeFace face; +                    face.fillFromLegacyData(face_verts, indices_16); +                    face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0); +                    face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0); +                    pModel->getVolumeFaces().push_back(face); +                    pModel->getMaterialList().push_back(materialName); +                    created_faces++; + +                    std::fill(vertices_remap.begin(), vertices_remap.end(), -1); +                    indices_16.clear(); +                    face_verts.clear(); + +                    min = glm::vec3(FLT_MAX); +                    max = glm::vec3(-FLT_MAX); +                } +            } +            if (indices_16.size() > 0 && face_verts.size() > 0) +            { +                LLVolumeFace face; +                face.fillFromLegacyData(face_verts, indices_16); +                face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0); +                face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0); +                pModel->getVolumeFaces().push_back(face); +                pModel->getMaterialList().push_back(materialName); +                created_faces++; +            } + +            LL_INFOS("GLTF_IMPORT") << "Primitive " << (S32)prim_idx << " from model " << pModel->mLabel +                << " is over vertices limit, it was split into " << created_faces +                << " faces" << LL_ENDL; +            LLSD args; +            args["Message"] = "ModelSplitPrimitive"; +            args["MODEL_NAME"] = pModel->mLabel; +            args["FACE_COUNT"] = created_faces; +            mWarningsArray.append(args); +        } +        else +        { +            // can use indices directly +            std::vector<U16> indices; +            for (U32 i = 0; i < prim.getIndexCount(); i += 3) +            { +                // When processing indices, flip winding order if needed +                if (hasNegativeScale) +                { +                    // Flip winding order for negative scale +                    indices.push_back(prim.mIndexArray[i]); +                    indices.push_back(prim.mIndexArray[i + 2]); // Swap these two +                    indices.push_back(prim.mIndexArray[i + 1]); +                } +                else +                { +                    indices.push_back(prim.mIndexArray[i]); +                    indices.push_back(prim.mIndexArray[i + 1]); +                    indices.push_back(prim.mIndexArray[i + 2]); +                } +            } + +            face.fillFromLegacyData(faceVertices, indices); +            face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0); +            face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0); + +            pModel->getVolumeFaces().push_back(face); +            pModel->getMaterialList().push_back(materialName); +        } +    } + +    // Call normalizeVolumeFacesAndWeights to compute proper extents +    pModel->normalizeVolumeFacesAndWeights(); + +    // Fill joint names, bind matrices and remap weight indices +    if (skinIdx >= 0) +    { +        LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx]; +        LLMeshSkinInfo& skin_info = pModel->mSkinInfo; +        S32 valid_joints_count = mValidJointsCount[skinIdx]; + +        S32 replacement_index = 0; +        std::vector<S32> gltfindex_to_joitindex_map; +        size_t jointCnt = gltf_skin.mJoints.size(); +        gltfindex_to_joitindex_map.resize(jointCnt, -1); + +        if (valid_joints_count > (S32)mMaxJointsPerMesh) +        { +            std::map<std::string, S32> goup_use_count; + +            for (const auto& elem : mJointGroups) +            { +                goup_use_count[elem.second.mGroup] = 0; +                goup_use_count[elem.second.mParentGroup] = 0; +            } + +            // Assume that 'Torso' group is always in use since that's what everything else is attached to +            goup_use_count["Torso"] = 1; +            // Note that Collisions and Extra groups are all over the place, might want to include them from the start +            // or add individual when parents are added + +            // Check which groups are in use +            for (size_t i = 0; i < jointCnt; ++i) +            { +                std::string& joint_name = mJointNames[skinIdx][i]; +                if (!joint_name.empty()) +                { +                    if (gltf_joint_index_use[i] > 0) +                    { +                        const JointGroups &group = mJointGroups[joint_name]; +                        // Joint in use, increment it's groups +                        goup_use_count[group.mGroup]++; +                        goup_use_count[group.mParentGroup]++; +                    } +                } +            } + +            // 1. add joints that are in use directly +            for (size_t i = 0; i < jointCnt; ++i) +            { +                // Process joint name and idnex +                S32 joint = gltf_skin.mJoints[i]; +                if (gltf_joint_index_use[i] <= 0) +                { +                    // unsupported (-1) joint, drop it +                    // unused (0) joint, drop it +                    continue; +                } + +                if (addJointToModelSkin(skin_info, skinIdx, i)) +                { +                    gltfindex_to_joitindex_map[i] = replacement_index++; +                } +            } + +            // 2. add joints from groups that this model's joints belong to +            // It's perfectly valid to have more joints than is in use +            // Ex: sandals that make your legs digitigrade despite not skining to +            // knees or the like. +            // Todo: sort and add by usecount +            for (size_t i = 0; i < jointCnt; ++i) +            { +                S32 joint = gltf_skin.mJoints[i]; +                if (gltf_joint_index_use[i] != 0) +                { +                    // this step needs only joints that have zero uses +                    continue; +                } +                if (skin_info.mInvBindMatrix.size() > mMaxJointsPerMesh) +                { +                    break; +                } +                const std::string& legal_name = mJointNames[skinIdx][i]; +                std::string group_name = mJointGroups[legal_name].mGroup; +                if (goup_use_count[group_name] > 0) +                { +                    if (addJointToModelSkin(skin_info, skinIdx, i)) +                    { +                        gltfindex_to_joitindex_map[i] = replacement_index++; +                    } +                } +            } +        } +        else +        { +            // Less than 110, just add every valid joint +            for (size_t i = 0; i < jointCnt; ++i) +            { +                // Process joint name and idnex +                S32 joint = gltf_skin.mJoints[i]; +                if (gltf_joint_index_use[i] < 0) +                { +                    // unsupported (-1) joint, drop it +                    continue; +                } + +                if (addJointToModelSkin(skin_info, skinIdx, i)) +                { +                    gltfindex_to_joitindex_map[i] = replacement_index++; +                } +            } +        } + +        if (skin_info.mInvBindMatrix.size() > mMaxJointsPerMesh) +        { +            // mMaxJointsPerMesh ususlly is equal to LL_MAX_JOINTS_PER_MESH_OBJECT +            // and is 110. +            LL_WARNS("GLTF_IMPORT") << "Too many jonts in " << pModel->mLabel +                << " Count: " << (S32)skin_info.mInvBindMatrix.size() +                << " Limit:" << (S32)mMaxJointsPerMesh << LL_ENDL; +            LLSD args; +            args["Message"] = "ModelTooManyJoints"; +            args["MODEL_NAME"] = pModel->mLabel; +            args["JOINT_COUNT"] = (S32)skin_info.mInvBindMatrix.size(); +            args["MAX"] = (S32)mMaxJointsPerMesh; +            mWarningsArray.append(args); +        } + +        // Remap indices for pModel->mSkinWeights +        for (auto& weights : pModel->mSkinWeights) +        { +            for (auto& weight : weights.second) +            { +                weight.mJointIdx = gltfindex_to_joitindex_map[weight.mJointIdx]; +            } +        } +    } + +    return true; +} + +void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx) +{ +    const LL::GLTF::Skin& skin = mGLTFAsset.mSkins[skin_idx]; + +    LL_INFOS("GLTF_DEBUG") << "populateJointFromSkin: Processing skin " << skin_idx << " with " << skin.mJoints.size() << " joints" << LL_ENDL; + +    if (skin.mInverseBindMatrices > 0 && skin.mJoints.size() != skin.mInverseBindMatricesData.size()) +    { +        LL_INFOS("GLTF_IMPORT") << "Bind matrices count mismatch joints count" << LL_ENDL; +        LLSD args; +        args["Message"] = "InvBindCountMismatch"; +        mWarningsArray.append(args); +    } + +    S32 joint_count = (S32)skin.mJoints.size(); +    S32 inverse_count = (S32)skin.mInverseBindMatricesData.size(); +    if (mInverseBindMatrices.size() <= skin_idx) +    { +        mInverseBindMatrices.resize(skin_idx + 1); +        mAlternateBindMatrices.resize(skin_idx + 1); +        mJointNames.resize(skin_idx + 1); +        mJointUsage.resize(skin_idx + 1); +        mValidJointsCount.resize(skin_idx + 1, 0); +    } + +    // fill up joints related data +    joints_data_map_t joints_data; +    joints_name_to_node_map_t names_to_nodes; +    for (S32 i = 0; i < joint_count; i++) +    { +        S32 joint = skin.mJoints[i]; +        const LL::GLTF::Node &jointNode = mGLTFAsset.mNodes[joint]; +        JointNodeData& data = joints_data[joint]; +        data.mNodeIdx = joint; +        data.mJointListIdx = i; +        data.mGltfRestMatrix = buildGltfRestMatrix(joint, skin); +        data.mGltfMatrix = jointNode.mMatrix; +        data.mOverrideMatrix = glm::mat4(1.f); + +        if (mJointMap.find(jointNode.mName) != mJointMap.end()) +        { +            data.mName = mJointMap[jointNode.mName]; +            data.mIsValidViewerJoint = true; +            mValidJointsCount[skin_idx]++; +        } +        else +        { +            data.mName = jointNode.mName; +            data.mIsValidViewerJoint = false; +        } +        names_to_nodes[data.mName] = joint; + +        for (S32 child : jointNode.mChildren) +        { +            JointNodeData& child_data = joints_data[child]; +            child_data.mParentNodeIdx = joint; +            child_data.mIsParentValidViewerJoint = data.mIsValidViewerJoint; +        } +    } + +    // Go over viewer joints and build overrides +    // This is needed because gltf skeleton doesn't necessarily match viewer's skeleton. +    glm::mat4 ident(1.0); +    for (auto &viewer_data : mViewerJointData) +    { +        buildOverrideMatrix(viewer_data, joints_data, names_to_nodes, ident, ident); +    } + +    for (S32 i = 0; i < joint_count; i++) +    { +        S32 joint = skin.mJoints[i]; +        const LL::GLTF::Node &jointNode = mGLTFAsset.mNodes[joint]; +        std::string legal_name(jointNode.mName); + +        // Viewer supports a limited set of joints, mark them as legal +        bool legal_joint = false; +        if (mJointMap.find(legal_name) != mJointMap.end()) +        { +            legal_name = mJointMap[legal_name]; +            legal_joint = true; +            mJointNames[skin_idx].push_back(legal_name); +        } +        else +        { +            mJointNames[skin_idx].emplace_back(); +        } +        mJointUsage[skin_idx].push_back(0); + +        // Compute bind matrices + +        if (!legal_joint) +        { +            // Add placeholder to not break index. +            // Not going to be used by viewer, will be stripped from skin_info. +            LLMatrix4 gltf_transform; +            gltf_transform.setIdentity(); +            mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform)); +        } +        else if (inverse_count > i) +        { +            // Transalte existing bind matrix to viewer's overriden skeleton +            glm::mat4 original_bind_matrix = glm::inverse(skin.mInverseBindMatricesData[i]); +            glm::mat4 rotated_original = coord_system_rotation * original_bind_matrix; +            glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(joints_data, joint, legal_name); +            glm::mat4 tranlated_original = skeleton_transform * rotated_original; +            glm::mat4 final_inverse_bind_matrix = glm::inverse(tranlated_original); + +            LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(final_inverse_bind_matrix)); +            LL_DEBUGS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " Translated val: " << gltf_transform << LL_ENDL; +            mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform)); +        } +        else +        { +            // If bind matrices aren't present (they are optional in gltf), +            // assume an identy matrix +            // todo: find a model with this, might need to use YZ rotated matrix +            glm::mat4 inv_bind(1.0f); +            glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(joints_data, joint, legal_name); +            inv_bind = glm::inverse(skeleton_transform * inv_bind); + +            LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(inv_bind)); +            LL_DEBUGS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " Generated val: " << gltf_transform << LL_ENDL; +            mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform)); +        } + +        // Compute Alternative matrices also known as overrides +        LLMatrix4 original_joint_transform(glm::value_ptr(joints_data[joint].mOverrideMatrix)); + +        // Viewer seems to care only about translation part, +        // but for parity with collada taking original value +        LLMatrix4 newInverse = LLMatrix4(mInverseBindMatrices[skin_idx].back().getF32ptr()); +        newInverse.setTranslation(original_joint_transform.getTranslation()); + +        LL_DEBUGS("GLTF_DEBUG") << "mAlternateBindMatrix name: " << legal_name << " val: " << newInverse << LL_ENDL; +        mAlternateBindMatrices[skin_idx].push_back(LLMatrix4a(newInverse)); + +        if (legal_joint) +        { +            // Might be needed for uploader UI to correctly identify overriden joints +            // but going to be incorrect if multiple skins are present +            mJointList[legal_name] = newInverse; +            mJointsFromNode.push_front(legal_name); +        } +    } + +    S32 valid_joints = mValidJointsCount[skin_idx]; +    if (valid_joints < joint_count) +    { +        LL_INFOS("GLTF_IMPORT") << "Skin " << skin_idx +            << " defines " << joint_count +            << " joints, but only " << valid_joints +            << " were recognized and are compatible." << LL_ENDL; +        LLSD args; +        args["Message"] = "SkinUsupportedJoints"; +        args["SKIN_INDEX"] = skin_idx; +        args["JOINT_COUNT"] = joint_count; +        args["LEGAL_COUNT"] = valid_joints; +        mWarningsArray.append(args); +    } +} + +void LLGLTFLoader::populateJointGroups() +{ +    std::string parent; +    for (auto& viewer_data : mViewerJointData) +    { +        buildJointGroup(viewer_data, parent); +    } +} + +void LLGLTFLoader::buildJointGroup(LLJointData& viewer_data, const std::string &parent_group) +{ +    JointGroups& jount_group_data = mJointGroups[viewer_data.mName]; +    jount_group_data.mGroup = viewer_data.mGroup; +    jount_group_data.mParentGroup = parent_group; + +    for (LLJointData& child_data : viewer_data.mChildren) +    { +        buildJointGroup(child_data, viewer_data.mGroup); +    } +} + +void LLGLTFLoader::buildOverrideMatrix(LLJointData& viewer_data, joints_data_map_t &gltf_nodes, joints_name_to_node_map_t &names_to_nodes, glm::mat4& parent_rest, glm::mat4& parent_support_rest) const +{ +    glm::mat4 rest(1.f); +    joints_name_to_node_map_t::iterator found_node = names_to_nodes.find(viewer_data.mName); +    if (found_node != names_to_nodes.end()) +    { +        S32 gltf_node_idx = found_node->second; +        JointNodeData& node = gltf_nodes[gltf_node_idx]; +        node.mIsOverrideValid = true; +        node.mViewerRestMatrix = viewer_data.mRestMatrix; + +        glm::mat4 gltf_joint_rest_pose = coord_system_rotation * node.mGltfRestMatrix; +        if (mApplyXYRotation) +        { +            gltf_joint_rest_pose = coord_system_rotationxy * gltf_joint_rest_pose; +        } + +        glm::mat4 translated_joint; +        // Example: +        // Viewer has pelvis->spine1->spine2->torso. +        // gltf example model has pelvis->torso +        // By doing glm::inverse(transalted_rest_spine2) * gltf_rest_torso +        // We get what torso would have looked like if gltf had a spine2 +        if (viewer_data.mIsJoint) +        { +            translated_joint = glm::inverse(parent_rest) * gltf_joint_rest_pose; +        } +        else +        { +            translated_joint = glm::inverse(parent_support_rest) * gltf_joint_rest_pose; +        } + +        glm::vec3 translation_override; +        glm::vec3 skew; +        glm::vec3 scale; +        glm::vec4 perspective; +        glm::quat rotation; +        glm::decompose(translated_joint, scale, rotation, translation_override, skew, perspective); + +        // Viewer allows overrides, which are base joint with applied translation override. +        // fortunately normal bones use only translation, without rotation or scale +        node.mOverrideMatrix = glm::recompose(glm::vec3(1, 1, 1), glm::identity<glm::quat>(), translation_override, glm::vec3(0, 0, 0), glm::vec4(0, 0, 0, 1)); + +        glm::mat4 overriden_joint = node.mOverrideMatrix; + +        // todo: if gltf bone had rotation or scale, they probably should be saved here +        // then applied to bind matrix +        rest = parent_rest * overriden_joint; +        if (viewer_data.mIsJoint) +        { +            node.mOverrideRestMatrix = rest; +        } +        else +        { +            // This is likely incomplete or even wrong. +            // Viewer Collision bones specify rotation and scale. +            // Importer should apply rotation and scale to this matrix and save as needed +            // then subsctruct them from bind matrix +            // Todo: get models that use collision bones, made by different programs + +            overriden_joint = glm::scale(overriden_joint, viewer_data.mScale); +            node.mOverrideRestMatrix = parent_support_rest * overriden_joint; +        } +    } +    else +    { +        // No override for this joint +        rest = parent_rest * viewer_data.mJointMatrix; +    } + +    glm::mat4 support_rest(1.f); +    if (viewer_data.mSupport == LLJointData::SUPPORT_BASE) +    { +        support_rest = rest; +    } +    else +    { +        support_rest = parent_support_rest; +    } + +    for (LLJointData& child_data : viewer_data.mChildren) +    { +        buildOverrideMatrix(child_data, gltf_nodes, names_to_nodes, rest, support_rest); +    } +} + +glm::mat4 LLGLTFLoader::buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const +{ +    // This is inefficient since we are recalculating some joints multiple times over +    // Todo: cache it? + +    if (joint_node_index < 0 || joint_node_index >= static_cast<S32>(mGLTFAsset.mNodes.size())) +    { +        return glm::mat4(1.0f); +    } + +    const auto& node = mGLTFAsset.mNodes[joint_node_index]; + +    // Find and apply parent transform if it exists +    for (size_t i = 0; i < mGLTFAsset.mNodes.size(); ++i) +    { +        const auto& potential_parent = mGLTFAsset.mNodes[i]; +        auto it = std::find(potential_parent.mChildren.begin(), potential_parent.mChildren.end(), joint_node_index); + +        if (it != potential_parent.mChildren.end()) +        { +            // Found parent +            if (std::find(gltf_skin.mJoints.begin(), gltf_skin.mJoints.end(), joint_node_index) != gltf_skin.mJoints.end()) +            { +                // parent is a joint - recursively combine transform +                // assumes that matrix is already valid +                return buildGltfRestMatrix(static_cast<S32>(i), gltf_skin) * node.mMatrix; +            } +        } +    } +    // Should we return armature or stop earlier? +    return node.mMatrix; +} + +glm::mat4 LLGLTFLoader::buildGltfRestMatrix(S32 joint_node_index, const joints_data_map_t& joint_data) const +{ +    // This is inefficient since we are recalculating some joints multiple times over +    // Todo: cache it? + +    if (joint_node_index < 0 || joint_node_index >= static_cast<S32>(mGLTFAsset.mNodes.size())) +    { +        return glm::mat4(1.0f); +    } + +    auto& data = joint_data.at(joint_node_index); + +    if (data.mParentNodeIdx >=0) +    { +        return buildGltfRestMatrix(data.mParentNodeIdx, joint_data) * data.mGltfMatrix; +    } +    // Should we return armature or stop earlier? +    return data.mGltfMatrix; +} + +// This function computes the transformation matrix needed to convert from GLTF skeleton space +// to viewer skeleton space for a specific joint + +glm::mat4 LLGLTFLoader::computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const +{ +    const JointNodeData& node_data = joints_data_map.at(gltf_node_index); +    if (!node_data.mIsOverrideValid) +    { +        // For now assume they are identical and return an identity (for ease of debuging) +        return glm::mat4(1.0f); +    } + +    // Get the GLTF joint's rest pose (in GLTF coordinate system) +    const glm::mat4 &gltf_joint_rest_pose = node_data.mGltfRestMatrix; +    glm::mat4 rest_pose = coord_system_rotation * gltf_joint_rest_pose; + +    LL_INFOS("GLTF_DEBUG") << "rest matrix for joint " << joint_name << ": "; + +    LLMatrix4 transform(glm::value_ptr(rest_pose)); + +    LL_CONT << transform << LL_ENDL; + +    // Compute transformation from GLTF space to viewer space +    // This assumes both skeletons are in rest pose initially +    return node_data.mOverrideRestMatrix * glm::inverse(rest_pose); +} + +bool LLGLTFLoader::checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx) +{ +    glm::mat4 gltf_joint_rest = buildGltfRestMatrix(joint_idx, gltf_skin); +    glm::mat4 test_mat = glm::inverse(gltf_joint_rest) * gltf_skin.mInverseBindMatricesData[bind_indx]; +    // Normally for shoulders it should be something close to +    // {1,0,0,0;0,-1,0,0;0,0,-1,0;0,0,0,1} +    // rotated one will look like +    // {0,0,0,-1;1,0,0,0;0,-1,0,0;0,0,0,1} +    // Todo: This is a cheap hack, +    // figure out how rotation is supposed to work +    return abs(test_mat[0][0]) < 0.5 && abs(test_mat[1][1]) < 0.5 && abs(test_mat[2][2]) < 0.5; +} + +void LLGLTFLoader::checkForXYrotation(const LL::GLTF::Skin& gltf_skin) +{ +    // HACK: figure out model's rotation from shoulders' matrix. +    // This is wrong on many levels: +    // Too limited (only models that have shoulders), +    // Will not work well with things that emulate 3 hands in some manner +    // Only supports xy 90 degree rotation +    // Todo: figure out how to find skeleton's orientation Correctly +    // when model is rotated at a triangle level +    constexpr char right_shoulder_str[] = "mShoulderRight"; +    constexpr char left_shoulder_str[] = "mShoulderLeft"; + +    S32 size = (S32)gltf_skin.mJoints.size(); +    S32 joints_found = 0; +    for (S32 i= 0; i < size; i++) +    { +        S32 joint = gltf_skin.mJoints[i]; +        const LL::GLTF::Node &joint_node = mGLTFAsset.mNodes[joint]; + +        // todo: we are doing this search thing everywhere, +        // just pre-translate every joint +        JointMap::iterator found = mJointMap.find(joint_node.mName); +        if (found == mJointMap.end()) +        { +            // unsupported joint +            continue; +        } +        if (found->second == right_shoulder_str || found->second == left_shoulder_str) +        { +            if (checkForXYrotation(gltf_skin, joint, i)) +            { +                joints_found++; +            } +            else +            { +                return; +            } +        } +    } + +    if (joints_found == 2) +    { +        // Both joints in a weird position/rotation, assume rotated model +        mApplyXYRotation = true; +    } +} + +void LLGLTFLoader::checkGlobalJointUsage() +{ +    // Check if some joints remained unused +    for (S32 skin_idx = 0; skin_idx < (S32)mGLTFAsset.mSkins.size(); ++skin_idx) +    { +        const LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skin_idx]; +        S32 joint_count = (S32)gltf_skin.mJoints.size(); +        S32 used_joints = 0; +        for (S32 i = 0; i < joint_count; ++i) +        { +            S32 joint = gltf_skin.mJoints[i]; +            if (mJointUsage[skin_idx][i] == 0) +            { +                // Joint is unused, log it +                LL_INFOS("GLTF_DEBUG") << "Joint " << mJointNames[skin_idx][i] +                    << " in skin " << skin_idx << " is unused." << LL_ENDL; +            } +            else +            { +                used_joints++; +            } +        } + +        S32 valid_joints = mValidJointsCount[skin_idx]; +        if (valid_joints > used_joints) +        { +            S32 unsed_joints = valid_joints - used_joints; +            LL_INFOS("GLTF_IMPORT") << "Skin " << skin_idx +                << " declares " << valid_joints +                << " valid joints, of them " << unsed_joints +                << " remained unused" << LL_ENDL; +            LLSD args; +            args["Message"] = "SkinUnusedJoints"; +            args["SKIN_INDEX"] = (S32)skin_idx; +            args["JOINT_COUNT"] = valid_joints; +            args["USED_COUNT"] = used_joints; +            mWarningsArray.append(args); +        } +    } +} + +std::string LLGLTFLoader::extractTextureToTempFile(S32 textureIndex, const std::string& texture_type) +{ +    if (textureIndex < 0 || textureIndex >= mGLTFAsset.mTextures.size()) +        return ""; + +    S32 sourceIndex = mGLTFAsset.mTextures[textureIndex].mSource; +    if (sourceIndex < 0 || sourceIndex >= mGLTFAsset.mImages.size()) +        return ""; + +    LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex]; + +    // Handle URI-based textures +    if (!image.mUri.empty()) +    { +        return image.mUri; // Return URI directly +    } + +    // Handle embedded textures +    if (image.mBufferView >= 0) +    { +        if (image.mBufferView < mGLTFAsset.mBufferViews.size()) +        { +            const LL::GLTF::BufferView& buffer_view = mGLTFAsset.mBufferViews[image.mBufferView]; +            if (buffer_view.mBuffer < mGLTFAsset.mBuffers.size()) +            { +                const LL::GLTF::Buffer& buffer = mGLTFAsset.mBuffers[buffer_view.mBuffer]; + +                if (buffer_view.mByteOffset + buffer_view.mByteLength <= buffer.mData.size()) +                { +                    // Extract image data +                    const U8* data_ptr = &buffer.mData[buffer_view.mByteOffset]; +                    U32 data_size = buffer_view.mByteLength; + +                    // Determine the file extension +                    std::string extension = ".png"; // Default +                    if (!image.mMimeType.empty()) +                    { +                        if (image.mMimeType == "image/jpeg") +                            extension = ".jpg"; +                        else if (image.mMimeType == "image/png") +                            extension = ".png"; +                    } +                    else if (data_size >= 4) +                    { +                        if (data_ptr[0] == 0xFF && data_ptr[1] == 0xD8) +                            extension = ".jpg"; // JPEG magic bytes +                        else if (data_ptr[0] == 0x89 && data_ptr[1] == 0x50 && data_ptr[2] == 0x4E && data_ptr[3] == 0x47) +                            extension = ".png"; // PNG magic bytes +                    } + +                    // Create a temporary file +                    std::string temp_dir = gDirUtilp->getTempDir(); +                    std::string temp_filename = temp_dir + gDirUtilp->getDirDelimiter() + +                                               "gltf_embedded_" + texture_type + "_" + std::to_string(sourceIndex) + extension; + +                    // Write the image data to the temporary file +                    std::ofstream temp_file(temp_filename, std::ios::binary); +                    if (temp_file.is_open()) +                    { +                        temp_file.write(reinterpret_cast<const char*>(data_ptr), data_size); +                        temp_file.close(); + +                        LL_INFOS("GLTF_IMPORT") << "Extracted embedded " << texture_type << " texture to: " << temp_filename << LL_ENDL; +                        return temp_filename; +                    } +                    else +                    { +                        LL_WARNS("GLTF_IMPORT") << "Failed to create temporary file for " << texture_type << " texture: " << temp_filename << LL_ENDL; + +                        LLSD args; +                        args["Message"] = "FailedToCreateTempFile"; +                        args["TEXTURE_INDEX"] = sourceIndex; +                        args["TEXTURE_TYPE"]  = texture_type; +                        args["TEMP_FILE"] = temp_filename; +                        mWarningsArray.append(args); +                    } +                } +            } +        } +    } + +    return ""; +} + +void LLGLTFLoader::notifyUnsupportedExtension(bool unsupported) +{ +    std::vector<std::string> extensions = unsupported ? mGLTFAsset.mUnsupportedExtensions : mGLTFAsset.mIgnoredExtensions; +    if (extensions.size() > 0) +    { +        LLSD args; +        args["Message"] = unsupported ? "UnsupportedExtension" : "IgnoredExtension"; +        std::string del; +        std::string ext; +        for (auto& extension : extensions) +        { +            ext += del; +            ext += extension; +            del = ","; +        } +        args["EXT"] = ext; +        mWarningsArray.append(args); + +        LL_WARNS("GLTF_IMPORT") << "Model uses unsupported extension: " << ext << LL_ENDL; +    } +} + +size_t LLGLTFLoader::getSuffixPosition(const std::string &label) +{ +    if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1)) +    { +        return label.rfind('_'); +    } +    return -1; +} + +std::string LLGLTFLoader::getLodlessLabel(const LL::GLTF::Mesh& mesh) +{ +    size_t ext_pos = getSuffixPosition(mesh.mName); +    if (ext_pos != -1) +    { +        return mesh.mName.substr(0, ext_pos); +    } +    return mesh.mName; +} + diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h new file mode 100644 index 0000000000..e8b91996c7 --- /dev/null +++ b/indra/newview/gltf/llgltfloader.h @@ -0,0 +1,216 @@ +/** + * @file LLGLTFLoader.h + * @brief LLGLTFLoader class definition + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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_LLGLTFLoader_H +#define LL_LLGLTFLoader_H + +#include "tinygltf/tiny_gltf.h" + +#include "asset.h" + +#include "llglheaders.h" +#include "lljointdata.h" +#include "llmodelloader.h" + +class LLGLTFLoader : public LLModelLoader +{ +  public: +    typedef std::map<std::string, LLImportMaterial> material_map; +    typedef std::map<std::string, std::string> joint_viewer_parent_map_t; +    typedef std::map<std::string, glm::mat4> joint_viewer_rest_map_t; +    typedef std::map<S32, glm::mat4> joint_node_mat4_map_t; + +    struct JointNodeData +    { +        JointNodeData() +            : mJointListIdx(-1) +            , mNodeIdx(-1) +            , mParentNodeIdx(-1) +            , mIsValidViewerJoint(false) +            , mIsParentValidViewerJoint(false) +            , mIsOverrideValid(false) +        { + +        } +        S32 mJointListIdx; +        S32 mNodeIdx; +        S32 mParentNodeIdx; +        glm::mat4 mGltfRestMatrix; +        glm::mat4 mViewerRestMatrix; +        glm::mat4 mOverrideRestMatrix; +        glm::mat4 mGltfMatrix; +        glm::mat4 mOverrideMatrix; +        std::string mName; +        bool mIsValidViewerJoint; +        bool mIsParentValidViewerJoint; +        bool mIsOverrideValid; +    }; +    typedef std::map <S32, JointNodeData> joints_data_map_t; +    typedef std::map <std::string, S32> joints_name_to_node_map_t; + +    class LLGLTFImportMaterial : public LLImportMaterial +    { +    public: +        std::string name; +        LLGLTFImportMaterial() = default; +        LLGLTFImportMaterial(const LLImportMaterial& mat, const std::string& n) : LLImportMaterial(mat), name(n) {} +    }; + +    LLGLTFLoader(std::string filename, +                    S32                                               lod, +                    LLModelLoader::load_callback_t                    load_cb, +                    LLModelLoader::joint_lookup_func_t                joint_lookup_func, +                    LLModelLoader::texture_load_func_t                texture_load_func, +                    LLModelLoader::state_callback_t                   state_cb, +                    void *                                            opaque_userdata, +                    JointTransformMap &                               jointTransformMap, +                    JointNameSet &                                    jointsFromNodes, +                    std::map<std::string, std::string, std::less<>> & jointAliasMap, +                    U32                                               maxJointsPerMesh, +                    U32                                               modelLimit, +                    U32                                               debugMode, +                    std::vector<LLJointData>                          viewer_skeleton); //, +                    //bool                                            preprocess ); +    virtual ~LLGLTFLoader(); + +    virtual bool OpenFile(const std::string &filename); + +    struct GLTFVertex +    { +        glm::vec3 position; +        glm::vec3 normal; +        glm::vec2 uv0; +        glm::u16vec4 joints; +        glm::vec4 weights; +    }; + +protected: +    LL::GLTF::Asset mGLTFAsset; +    tinygltf::Model mGltfModel; +    bool            mGltfLoaded = false; +    bool            mApplyXYRotation = false; + +    // GLTF isn't aware of viewer's skeleton and uses it's own, +    // so need to take viewer's joints and use them to +    // recalculate iverse bind matrices +    std::vector<LLJointData>             mViewerJointData; + +    // vector of vectors because of a posibility of having more than one skin +    typedef std::vector<LLMeshSkinInfo::matrix_list_t> bind_matrices_t; +    typedef std::vector<std::vector<std::string> > joint_names_t; +    bind_matrices_t                     mInverseBindMatrices; +    bind_matrices_t                     mAlternateBindMatrices; +    joint_names_t                       mJointNames; // empty string when no legal name for a given idx +    std::vector<std::vector<S32>>       mJointUsage; // detect and warn about unsed joints + +    // what group a joint belongs to. +    // For purpose of stripping unused groups when joints are over limit. +    struct JointGroups +    { +        std::string mGroup; +        std::string mParentGroup; +    }; +    typedef std::map<std::string, JointGroups, std::less<> > joint_to_group_map_t; +    joint_to_group_map_t mJointGroups; + +    // per skin joint count, needs to be tracked for the sake of limits check. +    std::vector<S32>                    mValidJointsCount; + +    // Cached material information +    typedef std::map<S32, LLGLTFImportMaterial> MaterialCache; +    MaterialCache mMaterialCache; + +private: +    bool parseMeshes(); +    void computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const; +    void processNodeHierarchy(S32 node_idx, std::map<std::string, S32>& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params); +    bool addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx); +    LLGLTFImportMaterial processMaterial(S32 material_index, S32 fallback_index); +    std::string processTexture(S32 texture_index, const std::string& texture_type, const std::string& material_name); +    bool validateTextureIndex(S32 texture_index, S32& source_index); +    std::string generateMaterialName(S32 material_index, S32 fallback_index = -1); +    bool populateModelFromMesh(LLModel* pModel, const std::string& base_name, const LL::GLTF::Mesh &mesh, const LL::GLTF::Node &node, material_map& mats); +    void populateJointsFromSkin(S32 skin_idx); +    void populateJointGroups(); +    void addModelToScene(LLModel* pModel, const std::string& model_name, U32 submodel_limit, const LLMatrix4& transformation, const LLVolumeParams& volume_params, const material_map& mats); +    void buildJointGroup(LLJointData& viewer_data, const std::string& parent_group); +    void buildOverrideMatrix(LLJointData& data, joints_data_map_t &gltf_nodes, joints_name_to_node_map_t &names_to_nodes, glm::mat4& parent_rest, glm::mat4& support_rest) const; +    glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const; +    glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const joints_data_map_t& joint_data) const; +    glm::mat4 computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const; +    bool checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx); +    void checkForXYrotation(const LL::GLTF::Skin& gltf_skin); +    void checkGlobalJointUsage(); + +    std::string extractTextureToTempFile(S32 textureIndex, const std::string& texture_type); + +    void notifyUnsupportedExtension(bool unsupported); + +    static size_t getSuffixPosition(const std::string& label); +    static std::string getLodlessLabel(const LL::GLTF::Mesh& mesh); + +    //    bool mPreprocessGLTF; + +    /*  Below inherited from dae loader - unknown if/how useful here + +    void processElement(gltfElement *element, bool &badElement, GLTF *gltf); +    void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin); + +    material_map     getMaterials(LLModel *model, gltfInstance_geometry *instance_geo, GLTF *gltf); +    LLImportMaterial profileToMaterial(gltfProfile_COMMON *material, GLTF *gltf); +    LLColor4         getGltfColor(gltfElement *element); + +    gltfElement *getChildFromElement(gltfElement *pElement, std::string const &name); + +    bool isNodeAJoint(gltfNode *pNode); +    void processJointNode(gltfNode *pNode, std::map<std::string, LLMatrix4> &jointTransforms); +    void extractTranslation(gltfTranslate *pTranslate, LLMatrix4 &transform); +    void extractTranslationViaElement(gltfElement *pTranslateElement, LLMatrix4 &transform); +    void extractTranslationViaSID(gltfElement *pElement, LLMatrix4 &transform); +    void buildJointToNodeMappingFromScene(gltfElement *pRoot); +    void processJointToNodeMapping(gltfNode *pNode); +    void processChildJoints(gltfNode *pParentNode); + +    bool verifyCount(int expected, int result); + +    // Verify that a controller matches vertex counts +    bool verifyController(gltfController *pController); + +    static bool addVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh, LLSD &log_msg); +    static bool createVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh); + +    static LLModel *loadModelFromGltfMesh(gltfMesh *mesh); + +    // Loads a mesh breaking it into one or more models as necessary +    // to get around volume face limitations while retaining >8 materials +    // +    bool loadModelsFromGltfMesh(gltfMesh *mesh, std::vector<LLModel *> &models_out, U32 submodel_limit); + +    static std::string preprocessGLTF(std::string filename); +    */ + +}; +#endif  // LL_LLGLTFLLOADER_H diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index 16a4332b25..ac452b38a0 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -319,7 +319,7 @@ void GLTFSceneManager::load(const std::string& filename)  {      std::shared_ptr<Asset> asset = std::make_shared<Asset>(); -    if (asset->load(filename)) +    if (asset->load(filename, true))      {          gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions          asset->updateTransforms(); diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 085155714a..83b3d26560 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -3430,11 +3430,14 @@ void LLAgent::initOriginGlobal(const LLVector3d &origin_global)  bool LLAgent::leftButtonGrabbed() const  { -    const bool camera_mouse_look = gAgentCamera.cameraMouselook(); -    return (!camera_mouse_look && mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0) -        || (camera_mouse_look && mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0) -        || (!camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_LBUTTON_DOWN_INDEX] > 0) -        || (camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0); +    if (gAgentCamera.cameraMouselook()) +    { +        return mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0; +    } +    else +    { +        return mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0; +    }  }  bool LLAgent::rotateGrabbed() const diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 3202d1c0d6..339656089c 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -1987,16 +1987,6 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(bool *hit_limit)                  isConstrained = true;              }          } - -// JC - Could constrain camera based on parcel stuff here. -//          LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromPosGlobal(camera_position_global); -// -//          if (regionp && !regionp->mParcelOverlay->isBuildCameraAllowed(regionp->getPosRegionFromGlobal(camera_position_global))) -//          { -//              camera_position_global = last_position_global; -// -//              isConstrained = true; -//          }      }      // Don't let camera go underground diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index cd4222dddf..25f5cbd78f 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -1094,12 +1094,12 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it      {          gAgentAvatarp->setCompositeUpdatesEnabled(true); -        // If we have not yet declouded, we may want to use +        // If we have not yet loaded core parts, we may want to use          // baked texture UUIDs sent from the first objectUpdate message -        // don't overwrite these. If we have already declouded, we've saved -        // these ids as the last known good textures and can invalidate without -        // re-clouding. -        if (!gAgentAvatarp->getIsCloud()) +        // don't overwrite these. If we have parts already, we've saved +        // these texture ids as the last known good textures and can +        // invalidate without having to recloud avatar. +        if (!gAgentAvatarp->getHasMissingParts())          {              gAgentAvatarp->invalidateAll();          } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 5d70bfbc9e..6fa23727b1 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -856,7 +856,7 @@ void LLWearableHoldingPattern::checkMissingWearables()              // was requested but none was found, create a default asset as a replacement.              // In all other cases, don't do anything.              // For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud -            // due to logic in LLVOAvatarSelf::getIsCloud(). +            // due to logic in LLVOAvatarSelf::getHasMissingParts().              // For non-critical types (tatoo, socks, etc.) the wearable will just be missing.              (requested_by_type[type] > 0) &&              ((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT))) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f00301e942..66dadf7545 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -268,6 +268,16 @@ using namespace LL;  #include "glib.h"  #endif // (LL_LINUX) && LL_GTK +#ifdef LL_DISCORD +#define DISCORDPP_IMPLEMENTATION +#include <discordpp.h> +static std::shared_ptr<discordpp::Client> gDiscordClient; +static uint64_t gDiscordTimestampsStart; +static std::string gDiscordActivityDetails; +static int32_t gDiscordPartyCurrentSize; +static int32_t gDiscordPartyMaxSize; +#endif +  static LLAppViewerListener sAppViewerListener(LLAppViewer::instance);  ////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor @@ -1334,6 +1344,13 @@ bool LLAppViewer::frame()  bool LLAppViewer::doFrame()  { +#ifdef LL_DISCORD +    { +        LL_PROFILE_ZONE_NAMED("discord_callbacks"); +        discordpp::RunCallbacks(); +    } +#endif +      LL_RECORD_BLOCK_TIME(FTM_FRAME);      {      // and now adjust the visuals from previous frame. @@ -2260,10 +2277,7 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)  // Callback for LLError::LLUserWarningMsg  void errorHandler(const std::string& title_string, const std::string& message_string, S32 code)  { -    if (!message_string.empty()) -    { -        OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); -    } +    // message is going to hang viewer, create marker first      switch (code)      {      case LLError::LLUserWarningMsg::ERROR_OTHER: @@ -2271,6 +2285,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st          break;      case LLError::LLUserWarningMsg::ERROR_BAD_ALLOC:          LLAppViewer::instance()->createErrorMarker(LAST_EXEC_BAD_ALLOC); +        // When system run out of memory and errorHandler gets called from a thread, +        // main thread might keep going while OSMessageBox freezes the caller. +        // Todo: handle it better, but for now disconnect to avoid making things worse +        gDisconnected = true;          break;      case LLError::LLUserWarningMsg::ERROR_MISSING_FILES:          LLAppViewer::instance()->createErrorMarker(LAST_EXEC_MISSING_FILES); @@ -2278,6 +2296,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st      default:          break;      } +    if (!message_string.empty()) +    { +        OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); +    }  }  void LLAppViewer::initLoggingAndGetLastDuration() @@ -3103,7 +3125,15 @@ bool LLAppViewer::initWindow()          .height(gSavedSettings.getU32("WindowHeight"))          .min_width(gSavedSettings.getU32("MinWindowWidth"))          .min_height(gSavedSettings.getU32("MinWindowHeight")) +#ifdef LL_DARWIN +        // Setting it to true causes black screen with no UI displayed. +        // Given that it's a DEBUG settings and application goes fullscreen +        // on mac simply by expanding it, it was decided to not support/use +        // this setting on mac. +        .fullscreen(false) +#else // LL_DARWIN          .fullscreen(gSavedSettings.getBOOL("FullScreen")) +#endif          .ignore_pixel_depth(ignorePixelDepth)          .first_run(mIsFirstRun); @@ -4289,8 +4319,8 @@ bool LLAppViewer::initCache()      const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName");      const U32 MB = 1024 * 1024; -    const uintmax_t MIN_CACHE_SIZE = 256 * MB; -    const uintmax_t MAX_CACHE_SIZE = 9984ll * MB; +    const uintmax_t MIN_CACHE_SIZE = 896 * MB; +    const uintmax_t MAX_CACHE_SIZE = 32768ll * MB;      const uintmax_t setting_cache_total_size = uintmax_t(gSavedSettings.getU32("CacheSize")) * MB;      const uintmax_t cache_total_size = llclamp(setting_cache_total_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE);      const F64 disk_cache_percent = gSavedSettings.getF32("DiskCachePercentOfTotal"); @@ -5694,9 +5724,31 @@ void LLAppViewer::forceErrorThreadCrash()      thread->start();  } -void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs) +void LLAppViewer::forceExceptionThreadCrash() +{ +    class LLCrashTestThread : public LLThread +    { +    public: + +        LLCrashTestThread() : LLThread("Crash logging test thread") +        { +        } + +        void run() +        { +            const std::string exception_text = "This is a deliberate exception in a thread"; +            throw std::runtime_error(exception_text); +        } +    }; + +    LL_WARNS() << "This is a deliberate exception in a thread" << LL_ENDL; +    LLCrashTestThread* thread = new LLCrashTestThread(); +    thread->start(); +} + +void LLAppViewer::initMainloopTimeout(std::string_view state, F32 secs)  { -    if(!mMainloopTimeout) +    if (!mMainloopTimeout)      {          mMainloopTimeout = new LLWatchdogTimeout();          resumeMainloopTimeout(state, secs); @@ -5705,20 +5757,20 @@ void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs)  void LLAppViewer::destroyMainloopTimeout()  { -    if(mMainloopTimeout) +    if (mMainloopTimeout)      {          delete mMainloopTimeout; -        mMainloopTimeout = NULL; +        mMainloopTimeout = nullptr;      }  } -void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs) +void LLAppViewer::resumeMainloopTimeout(std::string_view state, F32 secs)  { -    if(mMainloopTimeout) +    if (mMainloopTimeout)      { -        if(secs < 0.0f) +        if (secs < 0.0f)          { -            static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60); +            static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60.f);              secs = mainloop_timeout;          } @@ -5729,19 +5781,19 @@ void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs)  void LLAppViewer::pauseMainloopTimeout()  { -    if(mMainloopTimeout) +    if (mMainloopTimeout)      {          mMainloopTimeout->stop();      }  } -void LLAppViewer::pingMainloopTimeout(const std::string& state, F32 secs) +void LLAppViewer::pingMainloopTimeout(std::string_view state, F32 secs)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; -    if(mMainloopTimeout) +    if (mMainloopTimeout)      { -        if(secs < 0.0f) +        if (secs < 0.0f)          {              static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60);              secs = mainloop_timeout; @@ -5869,3 +5921,180 @@ void LLAppViewer::metricsSend(bool enable_reporting)      gViewerAssetStats->restart();  } +#ifdef LL_DISCORD + +void LLAppViewer::initDiscordSocial() +{ +    gDiscordPartyCurrentSize = 1; +    gDiscordPartyMaxSize = 0; +    gDiscordTimestampsStart = time(nullptr); +    gDiscordClient = std::make_shared<discordpp::Client>(); +    gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) { +        if (status == discordpp::Client::Status::Ready) +        { +            updateDiscordActivity(); +        } +    }); +    if (gSavedSettings.getBOOL("EnableDiscord")) +    { +        auto credential = gSecAPIHandler->loadCredential("Discord"); +        if (credential.notNull()) +        { +            gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, credential->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) { +                if (result.Successful()) +                    gDiscordClient->Connect(); +                else +                    LL_WARNS("Discord") << result.Error() << LL_ENDL; +            }); +        } +        else +        { +            LL_WARNS("Discord") << "Integration was enabled, but no credentials. Disabling integration." << LL_ENDL; +            gSavedSettings.setBOOL("EnableDiscord", false); +        } +    } +} + +void LLAppViewer::toggleDiscordIntegration(const LLSD& value) +{ +    static const uint64_t APPLICATION_ID = 1394782217405862001; +    if (value.asBoolean()) +    { +        discordpp::AuthorizationArgs args{}; +        args.SetClientId(APPLICATION_ID); +        args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); +        auto codeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier(); +        args.SetCodeChallenge(codeVerifier.Challenge()); +        gDiscordClient->Authorize(args, [codeVerifier](auto result, auto code, auto redirectUri) { +            if (result.Successful()) +            { +                gDiscordClient->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) { +                    if (result.Successful()) +                    { +                        gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [accessToken](discordpp::ClientResult result) { +                            if (result.Successful()) +                            { +                                LLSD authenticator = LLSD::emptyMap(); +                                authenticator["token"] = accessToken; +                                gSecAPIHandler->saveCredential(gSecAPIHandler->createCredential("Discord", LLSD::emptyMap(), authenticator), true); +                                gDiscordClient->Connect(); +                            } +                            else +                            { +                                LL_WARNS("Discord") << result.Error() << LL_ENDL; +                            } +                        }); +                    } +                    else +                    { +                        LL_WARNS("Discord") << result.Error() << LL_ENDL; +                    } +                }); +            } +            else +            { +                LL_WARNS("Discord") << result.Error() << LL_ENDL; +                gSavedSettings.setBOOL("EnableDiscord", false); +            } +        }); +    } +    else +    { +        gDiscordClient->Disconnect(); +        auto credential = gSecAPIHandler->loadCredential("Discord"); +        if (credential.notNull()) +        { +            gDiscordClient->RevokeToken(APPLICATION_ID, credential->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) { +                if (result.Successful()) +                    LL_INFOS("Discord") << "Access token successfully revoked." << LL_ENDL; +                else +                    LL_WARNS("Discord") << "No access token to revoke." << LL_ENDL; +            }); +            auto cred = new LLCredential("Discord"); +            gSecAPIHandler->deleteCredential(cred); +        } +        else +        { +            LL_WARNS("Discord") << "Credentials are already nonexistent." << LL_ENDL; +        } +    } +} + +void LLAppViewer::updateDiscordActivity() +{ +    LL_PROFILE_ZONE_SCOPED; +    discordpp::Activity activity; +    activity.SetType(discordpp::ActivityTypes::Playing); +    discordpp::ActivityTimestamps timestamps; +    timestamps.SetStart(gDiscordTimestampsStart); +    activity.SetTimestamps(timestamps); + +    if (gAgent.getID() == LLUUID::null) +    { +        gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); +        return; +    } + +    static LLCachedControl<bool> show_details(gSavedSettings, "ShowDiscordActivityDetails", false); +    if (show_details) +    { +        if (gDiscordActivityDetails.empty()) +        { +            LLAvatarName av_name; +            LLAvatarNameCache::get(gAgent.getID(), &av_name); +            gDiscordActivityDetails = av_name.getUserName(); +            auto displayName = av_name.getDisplayName(); +            if (gDiscordActivityDetails != displayName) +                gDiscordActivityDetails = displayName + " (" + gDiscordActivityDetails + ")"; +        } +        activity.SetDetails(gDiscordActivityDetails); +    } + +    static LLCachedControl<bool> show_state(gSavedSettings, "ShowDiscordActivityState", false); +    if (show_state) +    { +        auto agent_pos_region = gAgent.getPositionAgent(); +        S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f); +        S32 pos_y = S32(agent_pos_region.mV[VY] + 0.5f); +        S32 pos_z = S32(agent_pos_region.mV[VZ] + 0.5f); +        F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared(); +        const F32 FLY_CUTOFF = 6.f; +        const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; +        const F32 WALK_CUTOFF = 1.5f; +        const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; +        if (velocity_mag_sq > FLY_CUTOFF_SQ) +        { +            pos_x -= pos_x % 4; +            pos_y -= pos_y % 4; +        } +        else if (velocity_mag_sq > WALK_CUTOFF_SQ) +        { +            pos_x -= pos_x % 2; +            pos_y -= pos_y % 2; +        } +        auto location = llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z); +        activity.SetState(location); + +        discordpp::ActivityParty party; +        party.SetId(location); +        party.SetCurrentSize(gDiscordPartyCurrentSize); +        party.SetMaxSize(gDiscordPartyMaxSize); +        activity.SetParty(party); +    } + +    gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); +} + +void LLAppViewer::updateDiscordPartyCurrentSize(int32_t size) +{ +    gDiscordPartyCurrentSize = size; +    updateDiscordActivity(); +} + +void LLAppViewer::updateDiscordPartyMaxSize(int32_t size) +{ +    gDiscordPartyMaxSize = size; +    updateDiscordActivity(); +} + +#endif diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 3da0246ccf..14e96afe94 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -175,6 +175,7 @@ public:      virtual void forceErrorCoroprocedureCrash();      virtual void forceErrorWorkQueueCrash();      virtual void forceErrorThreadCrash(); +    virtual void forceExceptionThreadCrash();      // The list is found in app_settings/settings_files.xml      // but since they are used explicitly in code, @@ -197,11 +198,11 @@ public:      // For thread debugging.      // llstartup needs to control init.      // llworld, send_agent_pause() also controls pause/resume. -    void initMainloopTimeout(const std::string& state, F32 secs = -1.0f); +    void initMainloopTimeout(std::string_view state, F32 secs = -1.0f);      void destroyMainloopTimeout();      void pauseMainloopTimeout(); -    void resumeMainloopTimeout(const std::string& state = "", F32 secs = -1.0f); -    void pingMainloopTimeout(const std::string& state, F32 secs = -1.0f); +    void resumeMainloopTimeout(std::string_view state = "", F32 secs = -1.0f); +    void pingMainloopTimeout(std::string_view state, F32 secs = -1.0f);      // Handle the 'login completed' event.      // *NOTE:Mani Fix this for login abstraction!! @@ -250,6 +251,14 @@ public:      // Note: mQuitRequested can be aborted by user.      void outOfMemorySoftQuit(); +#ifdef LL_DISCORD +    static void initDiscordSocial(); +    static void toggleDiscordIntegration(const LLSD& value); +    static void updateDiscordActivity(); +    static void updateDiscordPartyCurrentSize(int32_t size); +    static void updateDiscordPartyMaxSize(int32_t size); +#endif +  protected:      virtual bool initWindow(); // Initialize the viewer's window.      virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 4f5fa53312..8477bd3044 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -448,6 +448,7 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,      // *FIX: global      gIconResource = MAKEINTRESOURCE(IDI_LL_ICON); +    gIconSmallResource = MAKEINTRESOURCE(IDI_LL_ICON_SMALL);      LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(ll_convert_wide_to_string(pCmdLine).c_str()); @@ -816,6 +817,29 @@ bool LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)      return false;  } +bool LLAppViewerWin32::initWindow() +{ +    // This is a workaround/hotfix for a change in Windows 11 24H2 (and possibly later) +    // Where the window width and height need to correctly reflect an available FullScreen size +    if (gSavedSettings.getBOOL("FullScreen")) +    { +        DEVMODE dev_mode; +        ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); +        dev_mode.dmSize = sizeof(DEVMODE); +        if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode)) +        { +            gSavedSettings.setU32("WindowWidth", dev_mode.dmPelsWidth); +            gSavedSettings.setU32("WindowHeight", dev_mode.dmPelsHeight); +        } +        else +        { +            LL_WARNS("AppInit") << "Unable to set WindowWidth and WindowHeight for FullScreen mode" << LL_ENDL; +        } +    } + +    return LLAppViewer::initWindow(); +} +  void LLAppViewerWin32::initLoggingAndGetLastDuration()  {      LLAppViewer::initLoggingAndGetLastDuration(); diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h index 250e72edf3..3fad53ec72 100644 --- a/indra/newview/llappviewerwin32.h +++ b/indra/newview/llappviewerwin32.h @@ -46,6 +46,7 @@ public:      bool reportCrashToBugsplat(void* pExcepInfo) override;  protected: +    bool initWindow() override; // Override to initialize the viewer's window.      void initLoggingAndGetLastDuration() override; // Override to clean stack_trace info.      void initConsole() override; // Initialize OS level debugging console.      bool initHardwareTest() override; // Win32 uses DX9 to test hardware. diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index 716e6cd9e3..41e954b7fa 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -59,7 +59,7 @@ LLFilePicker LLFilePicker::sInstance;  #define XML_FILTER L"XML files (*.xml)\0*.xml\0"  #define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"  #define RAW_FILTER L"RAW files (*.raw)\0*.raw\0" -#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0" +#define MODEL_FILTER L"Model files (*.dae, *.gltf, *.glb)\0*.dae;*.gltf;*.glb\0"  #define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"  #define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0"  #define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0" @@ -217,6 +217,8 @@ bool LLFilePicker::setupFilter(ELoadFilter filter)          break;      case FFLOAD_MODEL:          mOFN.lpstrFilter = MODEL_FILTER \ +            COLLADA_FILTER \ +            MATERIAL_FILTER \              L"\0";          break;      case FFLOAD_MATERIAL: @@ -671,6 +673,8 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF          case FFLOAD_HDRI:              allowedv->push_back("exr");          case FFLOAD_MODEL: +            allowedv->push_back("gltf"); +            allowedv->push_back("glb");          case FFLOAD_COLLADA:              allowedv->push_back("dae");              break; diff --git a/indra/newview/llfloateravatar.cpp b/indra/newview/llfloateravatarwelcomepack.cpp index 404316275d..82e44d1398 100644 --- a/indra/newview/llfloateravatar.cpp +++ b/indra/newview/llfloateravatarwelcomepack.cpp @@ -1,7 +1,7 @@  /** - * @file llfloateravatar.h - * @author Leyla Farazha - * @brief floater for the avatar changer + * @file llfloateravatarwelcomepack.cpp + * @author Callum Prentice (callum@lindenlab.com) + * @brief Floater container for the Avatar Welcome Pack we app   *   * $LicenseInfo:firstyear=2011&license=viewerlgpl$   * Second Life Viewer Source Code @@ -27,17 +27,16 @@  #include "llviewerprecompiledheaders.h" -#include "llfloateravatar.h" +#include "llfloateravatarwelcomepack.h"  #include "lluictrlfactory.h"  #include "llmediactrl.h" - -LLFloaterAvatar::LLFloaterAvatar(const LLSD& key) +LLFloaterAvatarWelcomePack::LLFloaterAvatarWelcomePack(const LLSD& key)      :   LLFloater(key)  {  } -LLFloaterAvatar::~LLFloaterAvatar() +LLFloaterAvatarWelcomePack::~LLFloaterAvatarWelcomePack()  {      if (mAvatarPicker)      { @@ -47,15 +46,13 @@ LLFloaterAvatar::~LLFloaterAvatar()      }  } -bool LLFloaterAvatar::postBuild() +bool LLFloaterAvatarWelcomePack::postBuild()  {      mAvatarPicker = findChild<LLMediaCtrl>("avatar_picker_contents");      if (mAvatarPicker)      {          mAvatarPicker->clearCache();      } -    enableResizeCtrls(true, true, false); +      return true;  } - - diff --git a/indra/newview/llfloateravatar.h b/indra/newview/llfloateravatarwelcomepack.h index fb591c8306..a332d46708 100644 --- a/indra/newview/llfloateravatar.h +++ b/indra/newview/llfloateravatarwelcomepack.h @@ -1,7 +1,7 @@  /** - * @file llfloateravatar.h - * @author Leyla Farazha - * @brief floater for the avatar changer + * @file llfloateravatarwelcomepack.h + * @author Callum Prentice (callum@lindenlab.com) + * @brief Floater container for the Avatar Welcome Pack we app   *   * $LicenseInfo:firstyear=2011&license=viewerlgpl$   * Second Life Viewer Source Code @@ -25,22 +25,21 @@   * $/LicenseInfo$   */ -#ifndef LL_FLOATER_AVATAR_H -#define LL_FLOATER_AVATAR_H +#pragma once  #include "llfloater.h" +  class LLMediaCtrl; -class LLFloaterAvatar: +class LLFloaterAvatarWelcomePack:      public LLFloater  {      friend class LLFloaterReg; -private: -    LLFloaterAvatar(const LLSD& key); -    ~LLFloaterAvatar(); -    bool postBuild() override; -    LLMediaCtrl* mAvatarPicker; -}; +    private: +        LLFloaterAvatarWelcomePack(const LLSD& key); +        ~LLFloaterAvatarWelcomePack(); +        bool postBuild() override; -#endif +        LLMediaCtrl* mAvatarPicker; +}; diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 7450be45f1..392079efe4 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -179,7 +179,7 @@ void LLFloaterBvhPreview::setAnimCallbacks()      getChild<LLUICtrl>("ease_out_time")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateEaseOut, this, _1));  } -std::map <std::string, std::string> LLFloaterBvhPreview::getJointAliases() +std::map<std::string, std::string, std::less<>> LLFloaterBvhPreview::getJointAliases()  {      LLPointer<LLVOAvatar> av = (LLVOAvatar*)mAnimPreview->getDummyAvatar();      return av->getJointAliases(); @@ -252,7 +252,7 @@ bool LLFloaterBvhPreview::postBuild()                  ELoadStatus load_status = E_ST_OK;                  S32 line_number = 0; -                std::map<std::string, std::string> joint_alias_map = getJointAliases(); +                auto joint_alias_map = getJointAliases();                  loaderp = new LLBVHLoader(file_buffer, load_status, line_number, joint_alias_map);                  std::string status = getString(STATUS[load_status]); diff --git a/indra/newview/llfloaterbvhpreview.h b/indra/newview/llfloaterbvhpreview.h index c6b75c00b2..1eb7f686fd 100644 --- a/indra/newview/llfloaterbvhpreview.h +++ b/indra/newview/llfloaterbvhpreview.h @@ -108,7 +108,7 @@ public:                                         S32 status, LLExtStat ext_status);  private:      void setAnimCallbacks() ; -    std::map <std::string, std::string> getJointAliases(); +    std::map<std::string, std::string, std::less<>> getJointAliases();  protected: diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index f254fdafaf..42a5df5d17 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -32,6 +32,7 @@  #include "llimagetga.h"  #include "llimagejpeg.h"  #include "llimagepng.h" +#include "llimagej2c.h"  #include "llagent.h"  #include "llagentbenefits.h" @@ -43,6 +44,10 @@  #include "llrender.h"  #include "llface.h"  #include "llfocusmgr.h" +#include "llfilesystem.h" +#include "llfloaterperms.h" +#include "llnotificationsutil.h" +#include "llstatusbar.h"    // can_afford_transaction()  #include "lltextbox.h"  #include "lltoolmgr.h"  #include "llui.h" @@ -52,6 +57,7 @@  #include "llvoavatar.h"  #include "pipeline.h"  #include "lluictrlfactory.h" +#include "llviewermenufile.h"   // upload_new_resource()  #include "llviewershadermgr.h"  #include "llviewertexturelist.h"  #include "llstring.h" @@ -140,7 +146,7 @@ bool LLFloaterImagePreview::postBuild()          }      } -    getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this)); +    getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterImagePreview::onBtnOK, this));      return true;  } @@ -244,6 +250,59 @@ void LLFloaterImagePreview::clearAllPreviewTextures()  }  //----------------------------------------------------------------------------- +// onBtnOK() +//----------------------------------------------------------------------------- +void LLFloaterImagePreview::onBtnOK() +{ +    getChildView("ok_btn")->setEnabled(false); // don't allow inadvertent extra uploads + +    S32 expected_upload_cost = getExpectedUploadCost(); +    if (can_afford_transaction(expected_upload_cost)) +    { +        LL_INFOS() << "saving texture: " << mRawImagep->getWidth() << "x" << mRawImagep->getHeight() << LL_ENDL; +        // gen a new uuid for this asset +        LLTransactionID tid; +        tid.generate(); +        LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + +        LLPointer<LLImageJ2C> formatted = new LLImageJ2C; + +        if (formatted->encode(mRawImagep, 0.0f)) +        { +            LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE); +            fmt_file.write(formatted->getData(), formatted->getDataSize()); + +            LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo( +                tid, LLAssetType::AT_TEXTURE, +                getChild<LLUICtrl>("name_form")->getValue().asString(), +                getChild<LLUICtrl>("description_form")->getValue().asString(), +                0, +                LLFolderType::FT_NONE, LLInventoryType::IT_NONE, +                LLFloaterPerms::getNextOwnerPerms("Uploads"), +                LLFloaterPerms::getGroupPerms("Uploads"), +                LLFloaterPerms::getEveryonePerms("Uploads"), +                expected_upload_cost +            )); + +            upload_new_resource(assetUploadInfo); +        } +        else +        { +            LLNotificationsUtil::add("ErrorEncodingImage"); +            LL_WARNS() << "Error encoding image" << LL_ENDL; +        } +    } +    else +    { +        LLSD args; +        args["COST"] = llformat("%d", expected_upload_cost); +        LLNotificationsUtil::add("ErrorCannotAffordUpload", args); +    } + +    closeFloater(false); +} + +//-----------------------------------------------------------------------------  // draw()  //-----------------------------------------------------------------------------  void LLFloaterImagePreview::draw() @@ -364,19 +423,6 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)          return false;      } -    S32 max_width = gSavedSettings.getS32("max_texture_dimension_X"); -    S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y"); - -    if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height)) -    { -        LLStringUtil::format_map_t args; -        args["WIDTH"] = llformat("%d", max_width); -        args["HEIGHT"] = llformat("%d", max_height); - -        mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args); -        return false; -    } -      // Load the image      LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);      if (image.isNull()) @@ -399,6 +445,46 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)          image->setLastError("Image files with less than 3 or more than 4 components are not supported.");          return false;      } +    // Downscale images to fit the max_texture_dimensions_* +    S32 max_width  = gSavedSettings.getS32("max_texture_dimension_X"); +    S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y"); + +    S32 orig_width  = raw_image->getWidth(); +    S32 orig_height = raw_image->getHeight(); + +    if (orig_width > max_width || orig_height > max_height) +    { +        // Calculate scale factors +        F32 width_scale  = (F32)max_width / (F32)orig_width; +        F32 height_scale = (F32)max_height / (F32)orig_height; +        F32 scale        = llmin(width_scale, height_scale); + +        // Calculate new dimensions, preserving aspect ratio +        S32 new_width  = LLImageRaw::contractDimToPowerOfTwo( +            llclamp((S32)llroundf(orig_width * scale), 4, max_width) +        ); +        S32 new_height = LLImageRaw::contractDimToPowerOfTwo( +            llclamp((S32)llroundf(orig_height * scale), 4, max_height) +        ); + +        if (!raw_image->scale(new_width, new_height)) +        { +            LL_WARNS() << "Failed to scale image from " +                       << orig_width << "x" << orig_height +                       << " to " << new_width << "x" << new_height << LL_ENDL; +            return false; +        } + +        // Inform the resident about the resized image +        LLSD subs; +        subs["[ORIGINAL_WIDTH]"]  = orig_width; +        subs["[ORIGINAL_HEIGHT]"] = orig_height; +        subs["[NEW_WIDTH]"]       = new_width; +        subs["[NEW_HEIGHT]"]      = new_height; +        subs["[MAX_WIDTH]"]       = max_width; +        subs["[MAX_HEIGHT]"]      = max_height; +        LLNotificationsUtil::add("ImageUploadResized", subs); +    }      raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);      mRawImagep = raw_image; diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h index 9bc57246cf..5e5f4932c2 100644 --- a/indra/newview/llfloaterimagepreview.h +++ b/indra/newview/llfloaterimagepreview.h @@ -126,6 +126,8 @@ public:      void clearAllPreviewTextures(); +    void onBtnOK(); +  protected:      static void     onPreviewTypeCommit(LLUICtrl*,void*);      void            draw() override; diff --git a/indra/newview/llfloatermediasettings.cpp b/indra/newview/llfloatermediasettings.cpp index 2496887c9d..81eab52e6c 100644 --- a/indra/newview/llfloatermediasettings.cpp +++ b/indra/newview/llfloatermediasettings.cpp @@ -180,8 +180,15 @@ void LLFloaterMediaSettings::onClose(bool app_quitting)  ////////////////////////////////////////////////////////////////////////////////  //static -void LLFloaterMediaSettings::initValues( const LLSD& media_settings, bool editable ) +void LLFloaterMediaSettings::initValues( const LLSD& media_settings, bool editable, bool has_media_info, bool multiple_media, bool multiple_valid_media)  { +    if (!sInstance) +    { +        return; +    } +    sInstance->mIdenticalHasMediaInfo = has_media_info; +    sInstance->mMultipleMedia = multiple_media; +    sInstance->mMultipleValidMedia = multiple_valid_media;      if (sInstance->hasFocus()) return;      // Clear values diff --git a/indra/newview/llfloatermediasettings.h b/indra/newview/llfloatermediasettings.h index 38730ddc98..7ed7ab246f 100644 --- a/indra/newview/llfloatermediasettings.h +++ b/indra/newview/llfloatermediasettings.h @@ -48,7 +48,7 @@ public:      static LLFloaterMediaSettings* getInstance();      static bool instanceExists();      static void apply(); -    static void initValues( const LLSD& media_settings , bool editable); +    static void initValues( const LLSD& media_settings , bool editable, bool has_media_info, bool multiple_media, bool multiple_valid_media);      static void clearValues( bool editable);      LLPanelMediaSettingsSecurity* getPanelSecurity(){return mPanelMediaSettingsSecurity;}; diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index b5489d2dd8..6a6766fb3f 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -64,6 +64,7 @@  #include "llcallbacklist.h"  #include "llviewertexteditor.h"  #include "llviewernetwork.h" +#include "llmaterialeditor.h"  //static @@ -619,11 +620,9 @@ void LLFloaterModelPreview::onJointListSelection()      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) @@ -1341,26 +1340,26 @@ void LLFloaterModelPreview::addStringToLog(const std::string& message, const LLS      {          std::string str;          switch (lod) -{ +        {          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);      } -    } +}  // static  void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash) @@ -1488,7 +1487,7 @@ void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)      {          // Populate table -        std::map<std::string, std::string> joint_alias_map; +        std::map<std::string, std::string, std::less<>> joint_alias_map;          mModelPreview->getJointAliases(joint_alias_map);          S32 conflicts = 0; diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index c881821153..fff005872c 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -366,6 +366,11 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)      mCommitCallbackRegistrar.add("Pref.ClearLog",               boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));      mCommitCallbackRegistrar.add("Pref.DeleteTranscripts",      boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));      mCommitCallbackRegistrar.add("UpdateFilter", boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false)); // <FS:ND/> Hook up for filtering +#ifdef LL_DISCORD +    gSavedSettings.getControl("EnableDiscord")->getCommitSignal()->connect(boost::bind(&LLAppViewer::toggleDiscordIntegration, _2)); +    gSavedSettings.getControl("ShowDiscordActivityDetails")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity)); +    gSavedSettings.getControl("ShowDiscordActivityState")->getCommitSignal()->connect(boost::bind(&LLAppViewer::updateDiscordActivity)); +#endif  }  void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType type ) @@ -523,6 +528,10 @@ bool LLFloaterPreference::postBuild()          getChild<LLComboBox>("language_combobox")->add("System default", LLSD("default"), ADD_TOP, true);      } +#ifndef LL_DISCORD +    getChild<LLTabContainer>("privacy_tab_container")->childDisable("privacy_preferences_discord"); +#endif +      return true;  } diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 8cc01f6dc6..01108b5cfa 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -207,14 +207,14 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp)          mSettingNameText->setToolTip(controlp->getName());          mComment->setVisible(true); -        std::string old_text = mComment->getText();          std::string new_text = controlp->getComment();          // Don't setText if not nessesary, it will reset scroll          // This is a debug UI that reads from xml, there might          // be use cases where comment changes, but not the name -        if (old_text != new_text) +        if (mOldText != new_text)          {              mComment->setText(controlp->getComment()); +            mOldText = new_text;          }          mValSpinner1->setMaxValue(F32_MAX); @@ -467,6 +467,7 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp)            }            default:              mComment->setText(std::string("unknown")); +            mOldText = "unknown";              break;          }      } diff --git a/indra/newview/llfloatersettingsdebug.h b/indra/newview/llfloatersettingsdebug.h index b813cf4a74..8781cd3b67 100644 --- a/indra/newview/llfloatersettingsdebug.h +++ b/indra/newview/llfloatersettingsdebug.h @@ -82,6 +82,7 @@ protected:      LLColorSwatchCtrl* mColorSwatch = nullptr;      std::string mSearchFilter; +    std::string mOldText;  };  #endif //LLFLOATERDEBUGSETTINGS_H diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index 990a299c50..c3bc24c6b9 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -1042,7 +1042,9 @@ void LLFloaterUIPreview::getExecutablePath(const std::vector<std::string>& filen          {              CFStringRef executable_cfstr = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, CFSTR("CFBundleExecutable"));  // get the name of the actual executable (e.g. TextEdit or firefox-bin)              int max_file_length = 256;                                                                                      // (max file name length is 255 in OSX) -            char executable_buf[max_file_length]; + +            // Xcode 26: VLAs are a clang extension.  Just create the buffer and delete it after. +            char *executable_buf = new char [max_file_length];              if(CFStringGetCString(executable_cfstr, executable_buf, max_file_length, kCFStringEncodingMacRoman))            // convert CFStringRef to char*              {                  executable_path += std::string("/Contents/MacOS/") + std::string(executable_buf);                           // append path to executable directory and then executable name to exec path @@ -1052,6 +1054,7 @@ void LLFloaterUIPreview::getExecutablePath(const std::vector<std::string>& filen                  std::string warning = "Unable to get CString from CFString for executable path";                  popupAndPrintWarning(warning);              } +            delete [] executable_buf;          }          else          { diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index a798ba31ee..03979edbc1 100755 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -169,6 +169,52 @@ public:  };  LLWorldMapHandler gWorldMapHandler; +// handle secondlife:///app/worldmap_global/{GLOBAL_COORDS} URLs +class LLWorldMapGlobalHandler : public LLCommandHandler +{ +public: +    LLWorldMapGlobalHandler() : LLCommandHandler("worldmap_global", UNTRUSTED_THROTTLE) +    {} + +    virtual bool canHandleUntrusted( +        const LLSD& params, +        const LLSD& query_map, +        LLMediaCtrl* web, +        const std::string& nav_type) +    { +        if (nav_type == NAV_TYPE_CLICKED +            || nav_type == NAV_TYPE_EXTERNAL) +        { +            // NAV_TYPE_EXTERNAL will be throttled +            return true; +        } + +        return false; +    } + +    bool handle(const LLSD& params, +                const LLSD& query_map, +                const std::string& grid, +                LLMediaCtrl* web) +    { +        if (params.size() < 3) +        { +            LL_WARNS() << "Correct global coordinates are not provided." << LL_ENDL; +            return true; +        } + +        LLVector3d parcel_global_pos = LLVector3d(params[0].asInteger(), params[1].asInteger(), params[2].asInteger()); +        LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance(); +        if (!parcel_global_pos.isExactlyZero() && worldmap_instance) +        { +            worldmap_instance->trackLocation(parcel_global_pos); +            LLFloaterReg::showInstance("world_map", "center"); +        } +        return true; +    } +}; +LLWorldMapGlobalHandler gWorldMapGlobalHandler; +  // SocialMap handler secondlife:///app/maptrackavatar/id  class LLMapTrackAvatarHandler : public LLCommandHandler  { @@ -325,11 +371,9 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)      mWaitingForTracker(false),      mIsClosing(false),      mSetToUserPosition(true), +    mProcessingSearchUpdate(false),      mTrackedLocation(0.0,0.0,0.0),      mTrackedStatus(LLTracker::TRACKING_NOTHING), -    mListFriendCombo(nullptr), -    mListLandmarkCombo(nullptr), -    mListSearchResults(nullptr),      mParcelInfoObserver(nullptr),      mShowParcelInfo(false)  { @@ -341,7 +385,7 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)      mCommitCallbackRegistrar.add("WMap.Location",       boost::bind(&LLFloaterWorldMap::onLocationCommit, this));      mCommitCallbackRegistrar.add("WMap.AvatarCombo",    boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this));      mCommitCallbackRegistrar.add("WMap.Landmark",       boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit, this)); -    mCommitCallbackRegistrar.add("WMap.SearchResult",   boost::bind(&LLFloaterWorldMap::onCommitSearchResult, this)); +    mCommitCallbackRegistrar.add("WMap.SearchResult", [this](LLUICtrl* ctrl, const LLSD& data) { LLFloaterWorldMap::onCommitSearchResult(false); });      mCommitCallbackRegistrar.add("WMap.GoHome",         boost::bind(&LLFloaterWorldMap::onGoHome, this));      mCommitCallbackRegistrar.add("WMap.Teleport",       boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));      mCommitCallbackRegistrar.add("WMap.ShowTarget",     boost::bind(&LLFloaterWorldMap::onShowTargetBtn, this)); @@ -383,32 +427,33 @@ bool LLFloaterWorldMap::postBuild()      mTeleportCoordSpinY = getChild<LLUICtrl>("teleport_coordinate_y");      mTeleportCoordSpinZ = getChild<LLUICtrl>("teleport_coordinate_z"); -    LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo"); -    avatar_combo->selectFirstItem(); -    avatar_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this) ); -    avatar_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); -    mListFriendCombo = dynamic_cast<LLCtrlListInterface *>(avatar_combo); +    mFriendCombo = getChild<LLComboBox>("friend combo"); +    mFriendCombo->selectFirstItem(); +    mFriendCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this)); +    mFriendCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));      mLocationEditor = getChild<LLSearchEditor>("location");      mLocationEditor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1)); -    mLocationEditor->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this)); +    mLocationEditor->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this)); -    getChild<LLScrollListCtrl>("search_results")->setDoubleClickCallback( boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this)); -    mListSearchResults = childGetListInterface("search_results"); +    mSearchResults = getChild<LLScrollListCtrl>("search_results"); +    mSearchResults->setDoubleClickCallback(boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this)); -    LLComboBox *landmark_combo = getChild<LLComboBox>( "landmark combo"); -    landmark_combo->selectFirstItem(); -    landmark_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this) ); -    landmark_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); -    mListLandmarkCombo = dynamic_cast<LLCtrlListInterface *>(landmark_combo); +    mLandmarkCombo = getChild<LLComboBox>("landmark combo"); +    mLandmarkCombo->selectFirstItem(); +    mLandmarkCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this)); +    mLandmarkCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));      mZoomSlider = getChild<LLSliderCtrl>("zoom slider");      F32 slider_zoom = mMapView->getZoom();      mZoomSlider->setValue(slider_zoom); +    mTrackCtrlsPanel = getChild<LLPanel>("layout_panel_4"); +    mSearchButton = getChild<LLButton>("DoSearch"); +      getChild<LLPanel>("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this)); -    setDefaultBtn(NULL); +    mTrackCtrlsPanel->setDefaultBtn(nullptr);      onChangeMaturity(); @@ -608,7 +653,6 @@ void LLFloaterWorldMap::draw()      }      mTeleportButton->setEnabled((bool)tracking_status); -    //  getChildView("Clear")->setEnabled((bool)tracking_status);      mShowDestinationButton->setEnabled((bool)tracking_status || LLWorldMap::getInstance()->isTracking());      mCopySlurlButton->setEnabled((mSLURL.isValid()) ); @@ -700,26 +744,24 @@ void LLFloaterWorldMap::requestParcelInfo(const LLVector3d& pos_global, const LL      }  } -void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string& name ) +void LLFloaterWorldMap::trackAvatar(const LLUUID& avatar_id, const std::string& name)  {      mShowParcelInfo = false; -    LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo"); -    if (!iface) return;      buildAvatarIDList(); -    if(iface->setCurrentByID(avatar_id) || gAgent.isGodlike()) +    if (mFriendCombo->setCurrentByID(avatar_id) || gAgent.isGodlike())      {          // *HACK: Adjust Z values automatically for liaisons & gods so          // they swoop down when they click on the map. Requested          // convenience. -        if(gAgent.isGodlike()) +        if (gAgent.isGodlike())          {              mTeleportCoordSpinZ->setValue(LLSD(200.f));          }          // Don't re-request info if we already have it or we won't have it in time to teleport          if (mTrackedStatus != LLTracker::TRACKING_AVATAR || avatar_id != mTrackedAvatarID)          { -            mTrackedStatus = LLTracker::TRACKING_AVATAR; +            mTrackedStatus   = LLTracker::TRACKING_AVATAR;              mTrackedAvatarID = avatar_id;              LLTracker::trackAvatar(avatar_id, name);          } @@ -728,52 +770,45 @@ void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string&      {          LLTracker::stopTracking(false);      } -    setDefaultBtn("Teleport"); +    mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);  } -void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id ) +void LLFloaterWorldMap::trackLandmark(const LLUUID& landmark_item_id)  {      mShowParcelInfo = false; -    LLCtrlSelectionInterface *iface = childGetSelectionInterface("landmark combo"); -    if (!iface) return;      buildLandmarkIDLists();      bool found = false; -    S32 idx; +    S32  idx;      for (idx = 0; idx < mLandmarkItemIDList.size(); idx++)      { -        if ( mLandmarkItemIDList.at(idx) == landmark_item_id) +        if (mLandmarkItemIDList.at(idx) == landmark_item_id)          {              found = true;              break;          }      } -    if (found && iface->setCurrentByID( landmark_item_id ) ) +    if (found && mLandmarkCombo->setCurrentByID(landmark_item_id))      { -        LLUUID asset_id = mLandmarkAssetIDList.at( idx ); -        std::string name; -        LLComboBox* combo = getChild<LLComboBox>( "landmark combo"); -        if (combo) name = combo->getSimple(); -        mTrackedStatus = LLTracker::TRACKING_LANDMARK; -        LLTracker::trackLandmark(mLandmarkAssetIDList.at( idx ),    // assetID -                                 mLandmarkItemIDList.at( idx ), // itemID -                                 name);         // name +        LLUUID      asset_id = mLandmarkAssetIDList.at(idx); +        std::string name     = mLandmarkCombo->getSimple(); +        mTrackedStatus       = LLTracker::TRACKING_LANDMARK; +        LLTracker::trackLandmark(mLandmarkAssetIDList.at(idx), // assetID +                                 mLandmarkItemIDList.at(idx),  // itemID +                                 name);                        // name -        if( asset_id != sHomeID ) +        if (asset_id != sHomeID)          {              // start the download process -            gLandmarkList.getAsset( asset_id); +            gLandmarkList.getAsset(asset_id);          } - -        // We have to download both region info and landmark data, so set busy. JC -        //      getWindow()->incBusyCount();      }      else      {          LLTracker::stopTracking(false);      } -    setDefaultBtn("Teleport"); +    mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);  } @@ -782,7 +817,7 @@ void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info)      mShowParcelInfo = false;      mTrackedStatus = LLTracker::TRACKING_LOCATION;      LLTracker::trackLocation(event_info.getGlobalPosition(), event_info.getName(), event_info.getToolTip(), LLTracker::LOCATION_EVENT); -    setDefaultBtn("Teleport"); +    mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);  }  void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item) @@ -790,11 +825,12 @@ void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)      mShowParcelInfo = false;      mTrackedStatus = LLTracker::TRACKING_LOCATION;      LLTracker::trackLocation(item.getGlobalPosition(), item.getName(), item.getToolTip(), LLTracker::LOCATION_ITEM); -    setDefaultBtn("Teleport"); +    mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);  }  void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)  { +    mProcessingSearchUpdate = false;      LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);      if (!sim_info)      { @@ -804,7 +840,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)          S32 world_x = S32(pos_global.mdV[0] / 256);          S32 world_y = S32(pos_global.mdV[1] / 256);          LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true); -        setDefaultBtn(""); +        mTrackCtrlsPanel->setDefaultBtn(nullptr);          // clicked on a non-region - turn off coord display          enableTeleportCoordsDisplay( false ); @@ -818,7 +854,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)          LLTracker::stopTracking(false);          LLWorldMap::getInstance()->setTracking(pos_global);          LLWorldMap::getInstance()->setTrackingInvalid(); -        setDefaultBtn(""); +        mTrackCtrlsPanel->setDefaultBtn(nullptr);          // clicked on a down region - turn off coord display          enableTeleportCoordsDisplay( false ); @@ -849,7 +885,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)      // we have a valid region - turn on coord display      enableTeleportCoordsDisplay( true ); -    setDefaultBtn("Teleport"); +    mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);  }  // enable/disable teleport destination coordinates @@ -934,7 +970,10 @@ void LLFloaterWorldMap::updateLocation()              }          } -        mLocationEditor->setValue(sim_name); +        if (!mProcessingSearchUpdate) +        { +            mLocationEditor->setValue(sim_name); +        }          // refresh coordinate display to reflect where user clicked.          LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal(); @@ -964,7 +1003,7 @@ void LLFloaterWorldMap::trackURL(const std::string& region_name, S32 x_coord, S3          local_pos.mV[VZ] = (F32)z_coord;          LLVector3d global_pos = sim_info->getGlobalPos(local_pos);          trackLocation(global_pos); -        setDefaultBtn("Teleport"); +        mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);      }      else      { @@ -1025,17 +1064,14 @@ void LLFloaterWorldMap::observeFriends()  void LLFloaterWorldMap::friendsChanged()  { -    LLAvatarTracker& t = LLAvatarTracker::instance(); -    const LLUUID& avatar_id = t.getAvatarID(); +    LLAvatarTracker& t         = LLAvatarTracker::instance(); +    const LLUUID&    avatar_id = t.getAvatarID();      buildAvatarIDList(); -    if(avatar_id.notNull()) +    if (avatar_id.notNull())      { -        LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");          const LLRelationship* buddy_info = t.getBuddyInfo(avatar_id); -        if(!iface || -           !iface->setCurrentByID(avatar_id) || -           (buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) || -           gAgent.isGodlike()) +        if (!mFriendCombo->setCurrentByID(avatar_id) || +            (buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) || gAgent.isGodlike())          {              LLTracker::stopTracking(false);          } @@ -1045,15 +1081,12 @@ void LLFloaterWorldMap::friendsChanged()  // No longer really builds a list.  Instead, just updates mAvatarCombo.  void LLFloaterWorldMap::buildAvatarIDList()  { -    LLCtrlListInterface *list = mListFriendCombo; -    if (!list) return; -      // Delete all but the "None" entry -    S32 list_size = list->getItemCount(); +    S32 list_size = mFriendCombo->getItemCount();      if (list_size > 1)      { -        list->selectItemRange(1, -1); -        list->operateOnSelection(LLCtrlListInterface::OP_DELETE); +        mFriendCombo->selectItemRange(1, -1); +        mFriendCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);      }      // Get all of the calling cards for avatar that are currently online @@ -1061,29 +1094,26 @@ void LLFloaterWorldMap::buildAvatarIDList()      LLAvatarTracker::instance().applyFunctor(collector);      LLCollectMappableBuddies::buddy_map_t::iterator it;      LLCollectMappableBuddies::buddy_map_t::iterator end; -    it = collector.mMappable.begin(); +    it  = collector.mMappable.begin();      end = collector.mMappable.end(); -    for( ; it != end; ++it) +    for (; it != end; ++it)      { -        list->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first); +        mFriendCombo->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first);      } -    list->setCurrentByID( LLAvatarTracker::instance().getAvatarID() ); -    list->selectFirstItem(); +    mFriendCombo->setCurrentByID(LLAvatarTracker::instance().getAvatarID()); +    mFriendCombo->selectFirstItem();  }  void LLFloaterWorldMap::buildLandmarkIDLists()  { -    LLCtrlListInterface *list = mListLandmarkCombo; -    if (!list) return; -      // Delete all but the "None" entry -    S32 list_size = list->getItemCount(); +    S32 list_size = mLandmarkCombo->getItemCount();      if (list_size > 1)      { -        list->selectItemRange(1, -1); -        list->operateOnSelection(LLCtrlListInterface::OP_DELETE); +        mLandmarkCombo->selectItemRange(1, -1); +        mLandmarkCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);      }      mLandmarkItemIDList.clear(); @@ -1115,13 +1145,13 @@ void LLFloaterWorldMap::buildLandmarkIDLists()      {          LLInventoryItem* item = items.at(i); -        list->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID()); +        mLandmarkCombo->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());          mLandmarkAssetIDList.push_back( item->getAssetUUID() );          mLandmarkItemIDList.push_back( item->getUUID() );      } -    list->selectFirstItem(); +    mLandmarkCombo->selectFirstItem();  } @@ -1139,10 +1169,9 @@ F32 LLFloaterWorldMap::getDistanceToDestination(const LLVector3d &destination,  void LLFloaterWorldMap::clearLocationSelection(bool clear_ui, bool dest_reached)  { -    LLCtrlListInterface *list = mListSearchResults; -    if (list && (!dest_reached || (list->getItemCount() == 1))) +    if (!dest_reached || (mSearchResults->getItemCount() == 1))      { -        list->operateOnAll(LLCtrlListInterface::OP_DELETE); +        mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);      }      LLWorldMap::getInstance()->cancelTracking();      mCompletingRegionName = ""; @@ -1153,11 +1182,7 @@ void LLFloaterWorldMap::clearLandmarkSelection(bool clear_ui)  {      if (clear_ui || !childHasKeyboardFocus("landmark combo"))      { -        LLCtrlListInterface *list = mListLandmarkCombo; -        if (list) -        { -            list->selectByValue( "None" ); -        } +        mLandmarkCombo->selectByValue("None");      }  } @@ -1167,10 +1192,9 @@ void LLFloaterWorldMap::clearAvatarSelection(bool clear_ui)      if (clear_ui || !childHasKeyboardFocus("friend combo"))      {          mTrackedStatus = LLTracker::TRACKING_NOTHING; -        LLCtrlListInterface *list = mListFriendCombo; -        if (list && list->getSelectedValue().asString() != "None") +        if (mFriendCombo->getSelectedValue().asString() != "None")          { -            list->selectByValue( "None" ); +            mFriendCombo->selectByValue("None");          }      }  } @@ -1223,28 +1247,25 @@ void LLFloaterWorldMap::onGoHome()  {      gAgent.teleportHome();      closeFloater(); +    mProcessingSearchUpdate = false;  } -void LLFloaterWorldMap::onLandmarkComboPrearrange( ) +void LLFloaterWorldMap::onLandmarkComboPrearrange()  { -    if( mIsClosing ) +    if (mIsClosing)      {          return;      } -    LLCtrlListInterface *list = mListLandmarkCombo; -    if (!list) return; - -    LLUUID current_choice = list->getCurrentID(); +    LLUUID current_choice = mLandmarkCombo->getCurrentID();      buildLandmarkIDLists(); -    if( current_choice.isNull() || !list->setCurrentByID( current_choice ) ) +    if (current_choice.isNull() || !mLandmarkCombo->setCurrentByID(current_choice))      {          LLTracker::stopTracking(false);      } -  }  void LLFloaterWorldMap::onComboTextEntry() @@ -1264,33 +1285,28 @@ void LLFloaterWorldMap::onSearchTextEntry( )  void LLFloaterWorldMap::onLandmarkComboCommit()  { -    if( mIsClosing ) +    if (mIsClosing)      {          return;      } -    LLCtrlListInterface *list = mListLandmarkCombo; -    if (!list) return; -      LLUUID asset_id; -    LLUUID item_id = list->getCurrentID(); +    LLUUID item_id = mLandmarkCombo->getCurrentID();      LLTracker::stopTracking(false); -    //RN: stopTracking() clears current combobox selection, need to reassert it here -    list->setCurrentByID(item_id); +    // RN: stopTracking() clears current combobox selection, need to reassert it here +    mLandmarkCombo->setCurrentByID(item_id); -    if( item_id.isNull() ) -    { -    } -    else if( item_id == sHomeID ) +    if (item_id.isNull()) {} +    else if (item_id == sHomeID)      {          asset_id = sHomeID;      }      else      { -        LLInventoryItem* item = gInventory.getItem( item_id ); -        if( item ) +        LLInventoryItem* item = gInventory.getItem(item_id); +        if (item)          {              asset_id = item->getAssetUUID();          } @@ -1301,34 +1317,31 @@ void LLFloaterWorldMap::onLandmarkComboCommit()          }      } -    trackLandmark( item_id); +    trackLandmark(item_id);      onShowTargetBtn();      // Reset to user postion if nothing is tracked -    mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING ); +    mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);  }  // static -void LLFloaterWorldMap::onAvatarComboPrearrange( ) +void LLFloaterWorldMap::onAvatarComboPrearrange()  { -    if( mIsClosing ) +    if (mIsClosing)      {          return;      } -    LLCtrlListInterface *list = mListFriendCombo; -    if (!list) return; -      LLUUID current_choice; -    if( LLAvatarTracker::instance().haveTrackingInfo() ) +    if (LLAvatarTracker::instance().haveTrackingInfo())      {          current_choice = LLAvatarTracker::instance().getAvatarID();      }      buildAvatarIDList(); -    if( !list->setCurrentByID( current_choice ) || current_choice.isNull() ) +    if (!mFriendCombo->setCurrentByID(current_choice) || current_choice.isNull())      {          LLTracker::stopTracking(false);      } @@ -1336,26 +1349,21 @@ void LLFloaterWorldMap::onAvatarComboPrearrange( )  void LLFloaterWorldMap::onAvatarComboCommit()  { -    if( mIsClosing ) +    if (mIsClosing)      {          return;      } -    LLCtrlListInterface *list = mListFriendCombo; -    if (!list) return; - -    const LLUUID& new_avatar_id = list->getCurrentID(); +    const LLUUID& new_avatar_id = mFriendCombo->getCurrentID();      if (new_avatar_id.notNull())      { -        std::string name; -        LLComboBox* combo = getChild<LLComboBox>("friend combo"); -        if (combo) name = combo->getSimple(); +        std::string name = mFriendCombo->getSimple();          trackAvatar(new_avatar_id, name);          onShowTargetBtn();      }      else -    {   // Reset to user postion if nothing is tracked -        mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING ); +    { // Reset to user postion if nothing is tracked +        mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);      }  } @@ -1375,11 +1383,11 @@ void LLFloaterWorldMap::updateSearchEnabled()      if (childHasKeyboardFocus("location") &&          mLocationEditor->getValue().asString().length() > 0)      { -        setDefaultBtn("DoSearch"); +        mTrackCtrlsPanel->setDefaultBtn(mSearchButton);      }      else      { -        setDefaultBtn(NULL); +        mTrackCtrlsPanel->setDefaultBtn(nullptr);      }  } @@ -1409,6 +1417,7 @@ void LLFloaterWorldMap::onLocationCommit()      {          return;      } +    mProcessingSearchUpdate = true;      LLStringUtil::toLower(str);      mCompletingRegionName = str; @@ -1430,6 +1439,7 @@ void LLFloaterWorldMap::onCoordinatesCommit()      {          return;      } +    mProcessingSearchUpdate = false;      S32 x_coord = (S32)mTeleportCoordSpinX->getValue().asReal();      S32 y_coord = (S32)mTeleportCoordSpinY->getValue().asReal(); @@ -1443,6 +1453,7 @@ void LLFloaterWorldMap::onCoordinatesCommit()  void LLFloaterWorldMap::onClearBtn()  {      mTrackedStatus = LLTracker::TRACKING_NOTHING; +    mProcessingSearchUpdate = false;      LLTracker::stopTracking(true);      LLWorldMap::getInstance()->cancelTracking();      mSLURL = LLSLURL();                 // Clear the SLURL since it's invalid @@ -1459,6 +1470,7 @@ void LLFloaterWorldMap::onShowAgentBtn()      mMapView->setPanWithInterpTime(0, 0, false, 0.1f);  // false == animate      // Set flag so user's location will be displayed if not tracking anything else      mSetToUserPosition = true; +    mProcessingSearchUpdate = false;  }  void LLFloaterWorldMap::onClickTeleportBtn() @@ -1487,8 +1499,9 @@ void LLFloaterWorldMap::onExpandCollapseBtn()      std::string image_name = getString(toggle_collapse ? "expand_icon" : "collapse_icon");      std::string tooltip = getString(toggle_collapse ? "expand_tooltip" : "collapse_tooltip"); -    getChild<LLIconCtrl>("expand_collapse_icon")->setImage(LLUI::getUIImage(image_name)); -    getChild<LLIconCtrl>("expand_collapse_icon")->setToolTip(tooltip); +    LLIconCtrl* expandCollapseIcon = getChild<LLIconCtrl>("expand_collapse_icon"); +    expandCollapseIcon->setImage(LLUI::getUIImage(image_name)); +    expandCollapseIcon->setToolTip(tooltip);      getChild<LLPanel>("expand_btn_panel")->setToolTip(tooltip);  } @@ -1613,6 +1626,12 @@ void LLFloaterWorldMap::teleport()              gAgent.teleportViaLocation( pos_global );          }      } + +    if (mProcessingSearchUpdate) +    { +        mProcessingSearchUpdate = false; +        mTrackedSimName.clear(); +    }  }  void LLFloaterWorldMap::flyToLandmark() @@ -1680,9 +1699,9 @@ void LLFloaterWorldMap::teleportToAvatar()  void LLFloaterWorldMap::flyToAvatar()  { -    if( LLAvatarTracker::instance().haveTrackingInfo() ) +    if (LLAvatarTracker::instance().haveTrackingInfo())      { -        gAgent.startAutoPilotGlobal( LLAvatarTracker::instance().getGlobalPos() ); +        gAgent.startAutoPilotGlobal(LLAvatarTracker::instance().getGlobalPos());      }  } @@ -1693,8 +1712,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)          return;      } -    LLScrollListCtrl *list = getChild<LLScrollListCtrl>("search_results"); -    list->operateOnAll(LLCtrlListInterface::OP_DELETE); +    mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);      auto name_length = mCompletingRegionName.length(); @@ -1722,7 +1740,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)              value["id"] = info->getName();              value["columns"][0]["column"] = "sim_name";              value["columns"][0]["value"] = info->getName(); -            list->addElement(value); +            mSearchResults->addElement(value);              num_results++;          }      } @@ -1737,21 +1755,24 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)          // if match found, highlight it and go          if (!match.isUndefined())          { -            list->selectByValue(match); +            mSearchResults->selectByValue(match); +            mSearchResults->setFocus(true); +            onCommitSearchResult(false /*fully commit the only option*/);          } -        // else select first found item +        // else let user decide          else          { -            list->selectFirstItem(); +            mSearchResults->selectFirstItem(); +            mSearchResults->setFocus(true); +            onCommitSearchResult(true /*don't update text field*/);          } -        getChild<LLUICtrl>("search_results")->setFocus(true); -        onCommitSearchResult();      }      else      {          // if we found nothing, say "none" -        list->setCommentText(LLTrans::getString("worldmap_results_none_found")); -        list->operateOnAll(LLCtrlListInterface::OP_DESELECT); +        mProcessingSearchUpdate = false; +        mSearchResults->setCommentText(LLTrans::getString("worldmap_results_none_found")); +        mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);      }  } @@ -1763,13 +1784,9 @@ void LLFloaterWorldMap::onTeleportFinished()      }  } -void LLFloaterWorldMap::onCommitSearchResult() +void LLFloaterWorldMap::onCommitSearchResult(bool from_search)  { -    LLCtrlListInterface *list = mListSearchResults; -    if (!list) return; - -    LLSD selected_value = list->getSelectedValue(); -    std::string sim_name = selected_value.asString(); +    std::string sim_name = mSearchResults->getSelectedValue().asString();      if (sim_name.empty())      {          return; @@ -1785,7 +1802,7 @@ void LLFloaterWorldMap::onCommitSearchResult()          {              LLVector3d pos_global = info->getGlobalOrigin(); -            const F64 SIM_COORD_DEFAULT = 128.0; +            constexpr F64 SIM_COORD_DEFAULT = 128.0;              LLVector3 pos_local(SIM_COORD_DEFAULT, SIM_COORD_DEFAULT, 0.0f);              // Did this value come from a trackURL() request? @@ -1798,9 +1815,15 @@ void LLFloaterWorldMap::onCommitSearchResult()              pos_global.mdV[VY] += (F64)pos_local.mV[VY];              pos_global.mdV[VZ] = (F64)pos_local.mV[VZ]; -            mLocationEditor->setValue(sim_name); +            // Commiting search string automatically selects first item in the search list, +            // in such case onCommitSearchResult shouldn't modify search string +            if (!from_search) +            { +                mLocationEditor->setValue(sim_name); +            }              trackLocation(pos_global); -            setDefaultBtn("Teleport"); +            mProcessingSearchUpdate = from_search; +            mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);              break;          }      } diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 2f2b2b7a0d..9558ca2615 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -51,6 +51,8 @@ class LLCheckBoxCtrl;  class LLSliderCtrl;  class LLSpinCtrl;  class LLSearchEditor; +class LLComboBox; +class LLScrollListCtrl;  class LLWorldMapParcelInfoObserver : public LLRemoteParcelInfoObserver  { @@ -174,7 +176,7 @@ protected:      void            onLocationFocusChanged( LLFocusableElement* ctrl );      void            onLocationCommit();      void            onCoordinatesCommit(); -    void            onCommitSearchResult(); +    void            onCommitSearchResult(bool from_search);      void            onTeleportFinished(); @@ -211,6 +213,7 @@ private:      bool                    mIsClosing;      bool                    mSetToUserPosition; +    bool                    mProcessingSearchUpdate; // Don't update search string from what user set it to      LLVector3d              mTrackedLocation;      LLTracker::ETrackingStatus mTrackedStatus; @@ -218,14 +221,11 @@ private:      LLUUID                  mTrackedAvatarID;      LLSLURL                 mSLURL; -    LLCtrlListInterface *   mListFriendCombo; -    LLCtrlListInterface *   mListLandmarkCombo; -    LLCtrlListInterface *   mListSearchResults; -      LLButton*               mTeleportButton = nullptr;      LLButton*               mShowDestinationButton = nullptr;      LLButton*               mCopySlurlButton = nullptr;      LLButton*               mGoHomeButton = nullptr; +    LLButton*               mSearchButton = nullptr;      LLCheckBoxCtrl*         mPeopleCheck = nullptr;      LLCheckBoxCtrl*         mInfohubCheck = nullptr; @@ -245,6 +245,13 @@ private:      LLSliderCtrl*           mZoomSlider = nullptr; +    LLComboBox*             mLandmarkCombo = nullptr; +    LLComboBox*             mFriendCombo = nullptr; + +    LLScrollListCtrl*       mSearchResults = nullptr; + +    LLPanel*                mTrackCtrlsPanel = nullptr; +      boost::signals2::connection mTeleportFinishConnection;  }; diff --git a/indra/newview/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp index d0d2ee191a..776d2dd31e 100644 --- a/indra/newview/llhudeffectlookat.cpp +++ b/indra/newview/llhudeffectlookat.cpp @@ -37,6 +37,7 @@  #include "lldrawable.h"  #include "llviewerobjectlist.h"  #include "llviewercontrol.h" +#include "llvoavatarself.h"  #include "llrendersphere.h"  #include "llselectmgr.h"  #include "llglheaders.h" @@ -397,6 +398,21 @@ bool LLHUDEffectLookAt::setLookAt(ELookAtType target_type, LLViewerObject *objec          return false;      } +    static LLCachedControl<bool> enable_lookat_hints(gSavedSettings, "EnableLookAtTarget", true); +    if (!enable_lookat_hints) +    { +        // Clear the effect so it doesn't linger around if it gets disabled +        if (mTargetType != LOOKAT_TARGET_IDLE) +        { +            mTargetObject = gAgentAvatarp; +            mTargetType = LOOKAT_TARGET_IDLE; +            mTargetOffsetGlobal.set(2.f, 0.f, 0.f); +            setDuration(3.f); +            setNeedsSendToSim(true); +        } +        return false; +    } +      if (target_type >= LOOKAT_NUM_TARGETS)      {          LL_WARNS() << "Bad target_type " << (int)target_type << " - ignoring." << LL_ENDL; @@ -409,6 +425,29 @@ bool LLHUDEffectLookAt::setLookAt(ELookAtType target_type, LLViewerObject *objec          return false;      } +    static LLCachedControl<bool> limit_lookat_hints(gSavedSettings, "LimitLookAtTarget", true); +    // Don't affect the look at if object is gAgentAvatarp (cursor head follow) +    if (limit_lookat_hints && object != gAgentAvatarp) +    { +        // If it is a object +        if (object) +        { +            position += object->getRenderPosition(); +            object = NULL; +        } + +        LLVector3 agentHeadPosition = gAgentAvatarp->mHeadp->getWorldPosition(); +        float dist = (float)dist_vec(agentHeadPosition, position); + +        static LLCachedControl<F32> limit_lookat_hints_distance(gSavedSettings, "LimitLookAtTargetDistance", 2.0f); +        if (dist > limit_lookat_hints_distance) +        { +            LLVector3 headOffset = position - agentHeadPosition; +            headOffset *= limit_lookat_hints_distance / dist; +            position.setVec(agentHeadPosition + headOffset); +        } +    } +      F32 current_time  = mTimer.getElapsedTimeF32();      // type of lookat behavior or target object has changed diff --git a/indra/newview/llhudeffectpointat.cpp b/indra/newview/llhudeffectpointat.cpp index eeb38cd6aa..c600010f6b 100644 --- a/indra/newview/llhudeffectpointat.cpp +++ b/indra/newview/llhudeffectpointat.cpp @@ -34,6 +34,7 @@  #include "llagent.h"  #include "llagentcamera.h"  #include "lldrawable.h" +#include "llviewercontrol.h"  #include "llviewerobjectlist.h"  #include "llvoavatar.h"  #include "message.h" @@ -226,6 +227,19 @@ bool LLHUDEffectPointAt::setPointAt(EPointAtType target_type, LLViewerObject *ob          return false;      } +    static LLCachedControl<bool> enable_selection_hints(gSavedSettings, "EnableSelectionHints", true); +    if (!enable_selection_hints) +    { +        // Clear the effect so it doesn't linger around if it gets disabled +        if (mTargetType != POINTAT_TARGET_NONE) +        { +            clearPointAtTarget(); +            setDuration(1.f); +            setNeedsSendToSim(true); +        } +        return false; +    } +      if (target_type >= POINTAT_NUM_TARGETS)      {          LL_WARNS() << "Bad target_type " << (int)target_type << " - ignoring." << LL_ENDL; diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index fd0d8b696f..c092b4c91a 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -225,10 +225,6 @@ void LLHUDText::renderText()              }              text_color = segment_iter->mColor; -            if (mOnHUDAttachment) -            { -                text_color = linearColor4(text_color); -            }              text_color.mV[VALPHA] *= alpha_factor;              hud_render_text(segment_iter->getText(), render_position, *fontp, style, shadow, x_offset, y_offset, text_color, mOnHUDAttachment); diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 4c02511268..7cd0171a37 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1520,10 +1520,10 @@ void LLIMProcessing::requestOfflineMessages()      if (!requested          && gMessageSystem          && !gDisconnected -        && LLMuteList::getInstance()->isLoaded()          && isAgentAvatarValid()          && gAgent.getRegion() -        && gAgent.getRegion()->capabilitiesReceived()) +        && gAgent.getRegion()->capabilitiesReceived() +        && (LLMuteList::getInstance()->isLoaded() || LLMuteList::getInstance()->getLoadFailed()))      {          std::string cap_url = gAgent.getRegionCapability("ReadOfflineMsgs"); diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 6c5421596b..99c2d6e410 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -1033,7 +1033,7 @@ void LLInventoryFilter::setFilterSubString(const std::string& string)              boost::char_separator<char> sep("+");              tokenizer tokens(filter_sub_string_new, sep); -            for (auto token_iter : tokens) +            for (const auto& token_iter : tokens)              {                  mFilterTokens.push_back(token_iter);              } @@ -1099,7 +1099,7 @@ void LLInventoryFilter::setFilterSubString(const std::string& string)          }          // Cancel out UUID once the search string is modified -        if (mFilterOps.mFilterTypes == FILTERTYPE_UUID) +        if (mFilterOps.mFilterTypes & FILTERTYPE_UUID)          {              mFilterOps.mFilterTypes &= ~FILTERTYPE_UUID;              mFilterOps.mFilterUUID = LLUUID::null; @@ -1786,7 +1786,7 @@ std::string LLInventoryFilter::getEmptyLookupMessage(bool is_empty_folder) const      }  } -bool LLInventoryFilter::areDateLimitsSet() +bool LLInventoryFilter::areDateLimitsSet() const  {      return mFilterOps.mMinDate != time_min()          || mFilterOps.mMaxDate != time_max() diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 2bdc646084..c0164e04e4 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -356,7 +356,7 @@ public:      bool checkAgainstFilterFavorites(const LLUUID& object_id) const;  private: -    bool                areDateLimitsSet(); +    bool                areDateLimitsSet() const;      bool                checkAgainstFilterSubString(const std::string& desc) const;      bool                checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const;      bool                checkAgainstFilterType(const LLInventoryItem* item) const; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 0365b06edb..d1fd82a7f1 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -2342,9 +2342,9 @@ bool can_move_to_my_outfits_as_outfit(LLInventoryModel* model, LLInventoryCatego          return false;      } -    if (items->size() == 0) +    if (items->size() == 0 && inv_cat->getPreferredType() != LLFolderType::FT_OUTFIT)      { -        // Nothing to move(create) +        // Nothing to create an outfit folder from          return false;      } diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp index 3bf50c8e65..3e5d6d1171 100644 --- a/indra/newview/llmaterialeditor.cpp +++ b/indra/newview/llmaterialeditor.cpp @@ -137,7 +137,8 @@ LLFloaterComboOptions* LLFloaterComboOptions::showUI(          {              combo_picker->mComboOptions->addSimpleElement(*iter);          } -        combo_picker->mComboOptions->selectFirstItem(); +        // select 'Bulk Upload All' option +        combo_picker->mComboOptions->selectNthItem((S32)options.size() - 1);          combo_picker->openFloater(LLSD(title));          combo_picker->setFocus(true); @@ -1332,15 +1333,6 @@ const std::string LLMaterialEditor::buildMaterialDescription()          desc << mNormalName;      } -    // trim last char if it's a ',' in case there is no normal texture -    // present and the code above inserts one -    // (no need to check for string length - always has initial string) -    std::string::iterator iter = desc.str().end() - 1; -    if (*iter == ',') -    { -        desc.str().erase(iter); -    } -      // sanitize the material description so that it's compatible with the inventory      // note: split this up because clang doesn't like operating directly on the      // str() - error: lvalue reference to type 'basic_string<...>' cannot bind to a @@ -1348,6 +1340,15 @@ const std::string LLMaterialEditor::buildMaterialDescription()      std::string inv_desc = desc.str();      LLInventoryObject::correctInventoryName(inv_desc); +    // trim last char if it's a ',' in case there is no normal texture +    // present and the code above inserts one +    // (no need to check for string length - always has initial string) +    std::string::iterator iter = inv_desc.end() - 1; +    if (*iter == ',') +    { +        inv_desc.erase(iter); +    } +      return inv_desc;  } @@ -2483,6 +2484,42 @@ void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std::      pack_textures(base_color_img, normal_img, mr_img, emissive_img, occlusion_img,          mBaseColorJ2C, mNormalJ2C, mMetallicRoughnessJ2C, mEmissiveJ2C); +    if (open_floater) +    { +        bool textures_scaled = false; +        if (mBaseColorFetched && mBaseColorJ2C +            && (mBaseColorFetched->getWidth() != mBaseColorJ2C->getWidth() +                || mBaseColorFetched->getHeight() != mBaseColorJ2C->getHeight())) +        { +            textures_scaled = true; +        } +        else if (mNormalFetched && mNormalJ2C +            && (mNormalFetched->getWidth() != mNormalJ2C->getWidth() +                || mNormalFetched->getHeight() != mNormalJ2C->getHeight())) +        { +            textures_scaled = true; +        } +        else if (mMetallicRoughnessFetched && mMetallicRoughnessJ2C +            && (mMetallicRoughnessFetched->getWidth() != mMetallicRoughnessJ2C->getWidth() +                || mMetallicRoughnessFetched->getHeight() != mMetallicRoughnessJ2C->getHeight())) +        { +            textures_scaled = true; +        } +        else if (mEmissiveFetched && mEmissiveJ2C +            && (mEmissiveFetched->getWidth() != mEmissiveJ2C->getWidth() +                || mEmissiveFetched->getHeight() != mEmissiveJ2C->getHeight())) +        { +            textures_scaled = true; +        } + +        if (textures_scaled) +        { +            LLSD args; +            args["MAX_SIZE"] = LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT; +            LLNotificationsUtil::add("MaterialImagesWereScaled", args); +        } +    } +      LLUUID base_color_id;      if (mBaseColorFetched.notNull())      { @@ -2689,10 +2726,8 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c          // so we can include everything          if (stripped_uri.length() > 0)          { -            // example "DamagedHelmet: base layer" +            // example "base layer"              return STRINGIZE( -                mMaterialNameShort << -                ": " <<                  stripped_uri <<                  " (" <<                  texture_type << @@ -2701,28 +2736,17 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c          }          else          // uri doesn't include the type (because the uri is empty) -        // so we must reorganize the string a bit to include the name -        // and an explicit name type +        // include an explicit name type          { -            // example "DamagedHelmet: (Emissive)" -            return STRINGIZE( -                mMaterialNameShort << -                " (" << -                texture_type << -                ")" -            ); +            // example "Emissive" +            return texture_type;          }      }      else -    // uri includes the type so just use it directly with the -    // name of the material +    // uri includes the type so just use it directly      { -        return STRINGIZE( -            // example: AlienBust: normal_layer -            mMaterialNameShort << -            ": " << -            stripped_uri -        ); +        // example: "normal_layer" +        return stripped_uri;      }  } diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index a7476ba6c4..07d68fc3ec 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -2380,7 +2380,13 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p      LLPointer<LLVolume> volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod));      if (volume->unpackVolumeFaces(data, data_size))      { -        if (volume->getNumFaces() > 0) +        // Use LLVolume::getNumVolumeFaces() here and not LLVolume::getNumFaces(), +        // because setMeshAssetLoaded() has not yet been called for this volume +        // (it is set later in LLMeshRepository::notifyMeshLoaded()), and +        // getNumFaces() would return the number of faces in the LLProfile +        // instead. HB +        S32 num_faces = volume->getNumVolumeFaces(); +        if (num_faces > 0)          {              // if we have a valid SkinInfo, cache per-joint bounding boxes for this LOD              LLPointer<LLMeshSkinInfo> skin_info = nullptr; @@ -2394,7 +2400,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p              }              if (skin_info.notNull() && isAgentAvatarValid())              { -                for (S32 i = 0; i < volume->getNumFaces(); ++i) +                for (S32 i = 0; i < num_faces; ++i)                  {                      // NOTE: no need to lock gAgentAvatarp as the state being checked is not changed after initialization                      LLVolumeFace& face = volume->getVolumeFace(i); @@ -2413,6 +2419,11 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p                  // might be good idea to turn mesh into pointer to avoid making a copy                  mesh.mVolume = NULL;              } +            { +                // make sure skin info is not removed from list while we are decreasing reference count +                LLMutexLock lock(mSkinMapMutex); +                skin_info = nullptr; +            }              return MESH_OK;          }      } @@ -2721,10 +2732,14 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&      S32 instance_num = 0; -    for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) +    // Handle models, ignore submodels for now. +    // Probably should pre-sort by mSubmodelID instead of running twice. +    // Note: mInstance should be sorted by model name for the sake of +    // deterministic order. +    for (auto& iter : mInstance)      {          LLMeshUploadData data; -        data.mBaseModel = iter->first; +        data.mBaseModel = iter.first;          if (data.mBaseModel->mSubmodelID)          { @@ -2733,7 +2748,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&              continue;          } -        LLModelInstance& first_instance = *(iter->second.begin()); +        LLModelInstance& first_instance = *(iter.second.begin());          for (S32 i = 0; i < 5; i++)          {              data.mModel[i] = first_instance.mLOD[i]; @@ -2767,7 +2782,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&                  mUploadSkin,                  mUploadJoints,                  mLockScaleIfJointPosition, -                false, +                LLModel::WRITE_BINARY,                  false,                  data.mBaseModel->mSubmodelID); @@ -2780,8 +2795,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&          }          // For all instances that use this model -        for (instance_list::iterator instance_iter = iter->second.begin(); -             instance_iter != iter->second.end(); +        for (instance_list::iterator instance_iter = iter.second.begin(); +             instance_iter != iter.second.end();               ++instance_iter)          { @@ -2881,10 +2896,11 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&          }      } -    for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) +    // Now handle the submodels. +    for (auto& iter : mInstance)      {          LLMeshUploadData data; -        data.mBaseModel = iter->first; +        data.mBaseModel = iter.first;          if (!data.mBaseModel->mSubmodelID)          { @@ -2893,7 +2909,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&              continue;          } -        LLModelInstance& first_instance = *(iter->second.begin()); +        LLModelInstance& first_instance = *(iter.second.begin());          for (S32 i = 0; i < 5; i++)          {              data.mModel[i] = first_instance.mLOD[i]; @@ -2927,7 +2943,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&                  mUploadSkin,                  mUploadJoints,                  mLockScaleIfJointPosition, -                false, +                LLModel::WRITE_BINARY,                  false,                  data.mBaseModel->mSubmodelID); @@ -2940,8 +2956,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, std::vector<std::string>&          }          // For all instances that use this model -        for (instance_list::iterator instance_iter = iter->second.begin(); -             instance_iter != iter->second.end(); +        for (instance_list::iterator instance_iter = iter.second.begin(); +             instance_iter != iter.second.end();               ++instance_iter)          { diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 0c3a3559c2..2b772f7803 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -674,7 +674,22 @@ public:      typedef std::vector<LLModelInstance> instance_list;      instance_list   mInstanceList; -    typedef std::map<LLPointer<LLModel>, instance_list> instance_map; +    // Upload should happen in deterministic order, so sort instances by model name. +    struct LLUploadModelInstanceLess +    { +        inline bool operator()(const LLPointer<LLModel>& a, const LLPointer<LLModel>& b) const +        { +            if (a.isNull() || b.isNull()) +            { +                llassert(false); // We are uploading these models, they shouldn't be null. +                return true; +            } +            // Note: probably can sort by mBaseModel->mSubmodelID here as well to avoid +            // running over the list twice in wholeModelToLLSD. +            return a->mLabel < b->mLabel; +        } +    }; +    typedef std::map<LLPointer<LLModel>, instance_list, LLUploadModelInstanceLess> instance_map;      instance_map    mInstance;      LLMutex*        mMutex; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 422cf34336..e0649e1d88 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -30,7 +30,7 @@  #include "llmodelloader.h"  #include "lldaeloader.h" -#include "llgltfloader.h" +#include "gltf/llgltfloader.h"  #include "llfloatermodelpreview.h"  #include "llagent.h" @@ -40,6 +40,7 @@  #include "lldrawable.h"  #include "llface.h"  #include "lliconctrl.h" +#include "lljointdata.h"  #include "llmatrix4a.h"  #include "llmeshrepository.h"  #include "llmeshoptimizer.h" @@ -163,10 +164,14 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)      , mPhysicsSearchLOD(LLModel::LOD_PHYSICS)      , mResetJoints(false)      , mModelNoErrors(true) +    , mLoading(false) +    , mModelLoader(nullptr)      , mLastJointUpdate(false)      , mFirstSkinUpdate(true)      , mHasDegenerate(false) -    , mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false)) +    , mNumOfFetchingTextures(0) +    , mTexturesNeedScaling(false) +    , mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebugVerboseLogging", false))  {      mNeedsUpdate = true;      mCameraDistance = 0.f; @@ -175,11 +180,9 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)      mCameraZoom = 1.f;      mTextureName = 0;      mPreviewLOD = 0; -    mModelLoader = NULL;      mMaxTriangleLimit = 0;      mDirty = false;      mGenLOD = false; -    mLoading = false;      mLookUpLodFiles = false;      mLoadState = LLModelLoader::STARTING;      mGroup = 0; @@ -211,6 +214,7 @@ LLModelPreview::~LLModelPreview()      {          mModelLoader->shutdown();          mModelLoader = NULL; +        mLoading = false;      }      if (mPreviewAvatar) @@ -557,10 +561,7 @@ void LLModelPreview::rebuildUploadData()                              texture->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(getHandle()), &mCallbackTextureList, false);                              texture->forceToSaveRawImage(0, F32_MAX);                              texture->updateFetch(); -                            if (mModelLoader) -                            { -                                mModelLoader->mNumOfFetchingTextures++; -                            } +                            mNumOfFetchingTextures++;                          }                      }                  } @@ -691,7 +692,7 @@ void LLModelPreview::saveUploadData(const std::string& filename,                  save_skinweights,                  save_joint_positions,                  lock_scale_if_joint_position, -                false, true, instance.mModel->mSubmodelID); +                LLModel::WRITE_BINARY, true, instance.mModel->mSubmodelID);              data["mesh"][instance.mModel->mLocalID] = str.str();          } @@ -753,6 +754,10 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable          LL_WARNS() << out.str() << LL_ENDL;          LLFloaterModelPreview::addStringToLog(out, true);          assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS); +        if (mModelLoader == nullptr) +        { +            mLoading = false; +        }          return;      } @@ -780,7 +785,7 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable      mLODFile[lod] = filename; -    std::map<std::string, std::string> joint_alias_map; +    std::map<std::string, std::string, std::less<>> joint_alias_map;      getJointAliases(joint_alias_map);      LLHandle<LLModelPreview> preview_handle = getHandle(); @@ -806,10 +811,14 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable              joint_alias_map,              LLSkinningUtil::getMaxJointCount(),              gSavedSettings.getU32("ImporterModelLimit"), +            gSavedSettings.getU32("ImporterDebugMode"),              gSavedSettings.getBOOL("ImporterPreprocessDAE"));      }      else      { +        LLVOAvatar* av = getPreviewAvatar(); +        std::vector<LLJointData> viewer_skeleton; +        av->getJointMatricesAndHierarhy(viewer_skeleton);          mModelLoader = new LLGLTFLoader(              filename,              lod, @@ -822,7 +831,9 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable              mJointsFromNode,              joint_alias_map,              LLSkinningUtil::getMaxJointCount(), -            gSavedSettings.getU32("ImporterModelLimit")); +            gSavedSettings.getU32("ImporterModelLimit"), +            gSavedSettings.getU32("ImporterDebugMode"), +            viewer_skeleton);      }      if (force_disable_slm) @@ -985,7 +996,9 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)      setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());      setLegacyRigFlags(mModelLoader->getLegacyRigFlags()); +    mTexturesNeedScaling |= mModelLoader->mTexturesNeedScaling;      mModelLoader->loadTextures(); +    warnTextureScaling();      if (loaded_lod == -1)      { //populate all LoDs from model loader scene @@ -1807,7 +1820,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target  void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit)  { -    LL_INFOS() << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL; +    LL_DEBUGS("Upload") << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL;      // Allow LoD from -1 to LLModel::LOD_PHYSICS      if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1)      { @@ -1884,6 +1897,12 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d      mMaxTriangleLimit = base_triangle_count; +    // For logging purposes +    S32 meshes_processed = 0; +    S32 meshes_simplified = 0; +    S32 meshes_sloppy_simplified = 0; +    S32 meshes_fail_count = 0; +      // Build models      S32 start = LLModel::LOD_HIGH; @@ -1893,7 +1912,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d      {          start = which_lod;          end = which_lod; -    } +    };      for (S32 lod = start; lod >= end; --lod)      { @@ -1956,6 +1975,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d                          const LLVolumeFace &face = base->getVolumeFace(face_idx);                          LLVolumeFace &new_face = target_model->getVolumeFace(face_idx);                          new_face = face; +                        meshes_fail_count++; +                    } +                    else +                    { +                        meshes_simplified++;                      }                  }              } @@ -1968,7 +1992,18 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d                      if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY) < 0)                      {                          // Sloppy failed and returned an invalid model -                        genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); +                        if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL) < 0) +                        { +                            meshes_fail_count++; +                        } +                        else +                        { +                            meshes_simplified++; +                        } +                    } +                    else +                    { +                        meshes_sloppy_simplified++;                      }                  }              } @@ -2068,25 +2103,28 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d                              precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);                          } -                        LL_INFOS() << "Model " << target_model->getName() +                        LL_DEBUGS("Upload") << "Model " << target_model->getName()                              << " lod " << which_lod                              << " resulting ratio " << precise_ratio                              << " simplified using per model method." << LL_ENDL; +                        meshes_simplified++;                      }                      else                      { -                        LL_INFOS() << "Model " << target_model->getName() +                        LL_DEBUGS("Upload") << "Model " << target_model->getName()                              << " lod " << which_lod                              << " resulting ratio " << sloppy_ratio                              << " sloppily simplified using per model method." << LL_ENDL; +                        meshes_sloppy_simplified++;                      }                  }                  else                  { -                    LL_INFOS() << "Model " << target_model->getName() +                    LL_DEBUGS("Upload") << "Model " << target_model->getName()                          << " lod " << which_lod                          << " resulting ratio " << precise_ratio                          << " simplified using per model method." << LL_ENDL; +                    meshes_simplified++;                  }              } @@ -2100,6 +2138,8 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d              //copy material list              target_model->mMaterialList = base->mMaterialList; +            meshes_processed++; +              if (!validate_model(target_model))              {                  LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL; @@ -2129,6 +2169,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d              }          }      } + +    LL_INFOS("Upload") << "LOD " << which_lod << ", Mesh optimizer processed meshes : " << meshes_processed +        <<" simplified: " << meshes_simplified +        << ", slopily simplified: " << meshes_sloppy_simplified +        << ", failures: " << meshes_fail_count << LL_ENDL;  }  void LLModelPreview::updateStatusMessages() @@ -2466,7 +2511,7 @@ void LLModelPreview::updateStatusMessages()          LLMutexLock lock(this);          if (mModelLoader)          { -            if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) +            if (!areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())              {                  // Some textures are still loading, prevent upload until they are done                  mModelNoErrors = false; @@ -3039,9 +3084,12 @@ void LLModelPreview::loadedCallback(      S32 lod,      void* opaque)  { +    if(LLModelPreview::sIgnoreLoadedCallback) +        return; +      LLModelPreview* pPreview = static_cast<LLModelPreview*>(opaque);      LLMutexLock lock(pPreview); -    if (pPreview && pPreview->mModelLoader && !LLModelPreview::sIgnoreLoadedCallback) +    if (pPreview && pPreview->mModelLoader)      {          // Load loader's warnings into floater's log tab          const LLSD out = pPreview->mModelLoader->logOut(); @@ -3090,25 +3138,48 @@ void LLModelPreview::lookupLODModelFiles(S32 lod)      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 lod_filename_lower(lod_filename);      LLStringUtil::toLower(lod_filename_lower); -    std::string::size_type i = lod_filename_lower.rfind(ext); -    if (i != std::string::npos) + +    // Check for each supported file extension +    std::vector<std::string> supported_exts = { ".dae", ".gltf", ".glb" }; +    std::string found_ext; +    std::string::size_type ext_pos = std::string::npos; + +    for (const auto& ext : supported_exts)      { -        lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext); +        std::string::size_type i = lod_filename_lower.rfind(ext); +        if (i != std::string::npos) +        { +            ext_pos = i; +            found_ext = ext; +            break; +        }      } -    if (gDirUtilp->fileExists(lod_filename)) + +    if (ext_pos != std::string::npos)      { -        LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; -        if (fmp) +        // Replace extension with LOD suffix + original extension +        std::string lod_file_to_check = lod_filename; +        lod_file_to_check.replace(ext_pos, found_ext.size(), getLodSuffix(next_lod) + found_ext); + +        if (gDirUtilp->fileExists(lod_file_to_check)) +        { +            LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; +            if (fmp) +            { +                fmp->setCtrlLoadFromFile(next_lod); +            } +            loadModel(lod_file_to_check, next_lod); +        } +        else          { -            fmp->setCtrlLoadFromFile(next_lod); +            lookupLODModelFiles(next_lod);          } -        loadModel(lod_filename, next_lod);      }      else      { +        // No recognized extension found, continue with next LOD          lookupLODModelFiles(next_lod);      }  } @@ -3149,6 +3220,7 @@ U32 LLModelPreview::loadTextures(LLImportMaterial& material, LLHandle<LLModelPre          tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(handle), &preview->mCallbackTextureList, false);          tex->forceToSaveRawImage(0, F32_MAX);          material.setDiffuseMap(tex->getID()); // record tex ID +        preview->mNumOfFetchingTextures++;          return 1;      } @@ -3992,6 +4064,18 @@ void LLModelPreview::setPreviewLOD(S32 lod)      updateStatusMessages();  } +void LLModelPreview::warnTextureScaling() +{ +    if (areTexturesReady() && mTexturesNeedScaling) +    { +        std::ostringstream out; +        out << "One or more textures in this model were scaled to be within the allowed limits."; +        LL_INFOS() << out.str() << LL_ENDL; +        LLSD args; +        LLFloaterModelPreview::addStringToLog("ModelTextureScaling", args, true, -1); +    } +} +  //static  void LLModelPreview::textureLoadedCallback(      bool success, @@ -4012,11 +4096,19 @@ void LLModelPreview::textureLoadedCallback(          LLModelPreview* preview = static_cast<LLModelPreview*>(handle->get());          preview->refresh(); -        if (final && preview->mModelLoader) +        if (final)          { -            if (preview->mModelLoader->mNumOfFetchingTextures > 0) +            if (src_vi +                && (src_vi->getOriginalWidth() > LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT +                    || src_vi->getOriginalHeight() > LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT)) +            { +                preview->mTexturesNeedScaling = true; +            } + +            if (preview->mNumOfFetchingTextures > 0)              { -                preview->mModelLoader->mNumOfFetchingTextures--; +                preview->mNumOfFetchingTextures--; +                preview->warnTextureScaling();              }          }      } diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index 0873263587..7b3b699b33 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -204,6 +204,7 @@ public:      std::vector<S32> mLodsQuery;      std::vector<S32> mLodsWithParsingError;      bool mHasDegenerate; +    bool areTexturesReady() { return !mNumOfFetchingTextures; }  protected: @@ -213,6 +214,7 @@ protected:      static LLJoint* lookupJointByName(const std::string&, void* opaque);      static U32          loadTextures(LLImportMaterial& material, LLHandle<LLModelPreview> handle); +    void warnTextureScaling();      void lookupLODModelFiles(S32 lod);  private: @@ -242,6 +244,9 @@ private:      /// Not read unless mWarnOfUnmatchedPhyicsMeshes is true.      LLPointer<LLModel> mDefaultPhysicsShapeP; +    S32 mNumOfFetchingTextures; +    bool mTexturesNeedScaling; +      typedef enum      {          MESH_OPTIMIZER_FULL, diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 2d51acc063..f6d635f51f 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -154,7 +154,8 @@ std::string LLMute::getDisplayType() const  // LLMuteList()  //-----------------------------------------------------------------------------  LLMuteList::LLMuteList() : -    mIsLoaded(false) +    mLoadState(ML_INITIAL), +    mRequestStartTime(0.f)  {      gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList); @@ -209,6 +210,23 @@ bool LLMuteList::isLinden(const std::string& name)      return last_name == "linden";  } +bool LLMuteList::getLoadFailed() const +{ +    if (mLoadState == ML_FAILED) +    { +        return true; +    } +    if (mLoadState == ML_REQUESTED) +    { +        constexpr F64 WAIT_SECONDS = 30; +        if (mRequestStartTime + WAIT_SECONDS < LLTimer::getTotalSeconds()) +        { +            return true; +        } +    } +    return false; +} +  static LLVOAvatar* find_avatar(const LLUUID& id)  {      LLViewerObject *obj = gObjectList.findObject(id); @@ -371,11 +389,14 @@ void LLMuteList::updateAdd(const LLMute& mute)      msg->addU32("MuteFlags", mute.mFlags);      gAgent.sendReliableMessage(); -    if (!mIsLoaded) +    if (!isLoaded())      {          LL_WARNS() << "Added elements to non-initialized block list" << LL_ENDL;      } -    mIsLoaded = true; // why is this here? -MG +    // Based of logs and testing, if file doesn't exist server side, +    // viewer will not receive any callback and won't know to set +    // ML_LOADED. As a workaround, set it regardless of current state. +    mLoadState = ML_LOADED;  } @@ -564,6 +585,7 @@ bool LLMuteList::loadFromFile(const std::string& filename)      if(!filename.size())      {          LL_WARNS() << "Mute List Filename is Empty!" << LL_ENDL; +        mLoadState = ML_FAILED;          return false;      } @@ -571,6 +593,7 @@ bool LLMuteList::loadFromFile(const std::string& filename)      if (!fp)      {          LL_WARNS() << "Couldn't open mute list " << filename << LL_ENDL; +        mLoadState = ML_FAILED;          return false;      } @@ -730,13 +753,17 @@ void LLMuteList::requestFromServer(const LLUUID& agent_id)      if (gDisconnected)      {          LL_WARNS() << "Trying to request mute list when disconnected!" << LL_ENDL; +        mLoadState = ML_FAILED;          return;      }      if (!gAgent.getRegion())      {          LL_WARNS() << "No region for agent yet, skipping mute list request!" << LL_ENDL; +        mLoadState = ML_FAILED;          return;      } +    mLoadState = ML_REQUESTED; +    mRequestStartTime = LLTimer::getElapsedSeconds();      // Double amount of retries due to this request happening during busy stage      // Ideally this should be turned into a capability      gMessageSystem->sendReliable(gAgent.getRegionHost(), LL_DEFAULT_RELIABLE_RETRIES * 2, true, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL); @@ -749,7 +776,7 @@ void LLMuteList::requestFromServer(const LLUUID& agent_id)  void LLMuteList::cache(const LLUUID& agent_id)  {      // Write to disk even if empty. -    if(mIsLoaded) +    if(isLoaded())      {          std::string agent_id_string;          std::string filename; @@ -777,6 +804,13 @@ void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**)      msg->getStringFast(_PREHASH_MuteData, _PREHASH_Filename, unclean_filename);      std::string filename = LLDir::getScrubbedFileName(unclean_filename); +    LLMuteList* mute_list = getInstance(); +    mute_list->mLoadState = ML_REQUESTED; +    mute_list->mRequestStartTime = LLTimer::getElapsedSeconds(); + +    // Todo: Based of logs and testing, there is no callback +    // from server if file doesn't exist server side. +    // Once server side gets fixed make sure it gets handled right.      std::string *local_filename_and_path = new std::string(gDirUtilp->getExpandedFilename( LL_PATH_CACHE, filename ));      gXferManager->requestFile(*local_filename_and_path,                                filename, @@ -809,12 +843,16 @@ void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_          LLMuteList::getInstance()->loadFromFile(*local_filename_and_path);          LLFile::remove(*local_filename_and_path);      } +    else +    { +        LLMuteList::getInstance()->mLoadState = ML_FAILED; +    }      delete local_filename_and_path;  }  void LLMuteList::onAccountNameChanged(const LLUUID& id, const std::string& username)  { -    if (mIsLoaded) +    if (isLoaded())      {          LLMute mute(id, username, LLMute::AGENT);          mute_set_t::iterator mute_it = mMutes.find(mute); @@ -866,7 +904,7 @@ void LLMuteList::removeObserver(LLMuteListObserver* observer)  void LLMuteList::setLoaded()  { -    mIsLoaded = true; +    mLoadState = ML_LOADED;      notifyObservers();  } diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index 13d579c61f..b65fd61fcc 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -74,6 +74,14 @@ class LLMuteList : public LLSingleton<LLMuteList>      LLSINGLETON(LLMuteList);      ~LLMuteList();      /*virtual*/ void cleanupSingleton() override; + +    enum EMuteListState +    { +        ML_INITIAL, +        ML_REQUESTED, +        ML_LOADED, +        ML_FAILED, +    };  public:      // reasons for auto-unmuting a resident      enum EAutoReason @@ -107,7 +115,8 @@ public:      static bool isLinden(const std::string& name); -    bool isLoaded() const { return mIsLoaded; } +    bool isLoaded() const { return mLoadState == ML_LOADED; } +    bool getLoadFailed() const;      std::vector<LLMute> getMutes() const; @@ -167,7 +176,8 @@ private:      typedef std::set<LLMuteListObserver*> observer_set_t;      observer_set_t mObservers; -    bool mIsLoaded; +    EMuteListState mLoadState; +    F64 mRequestStartTime;      friend class LLDispatchEmptyMuteList;  }; diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 3a6a6a5ec3..f0555408dd 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -463,6 +463,7 @@ void LLPanelEmojiComplete::updateConstraints()      {          mEmojiHeight = mRenderRect.getHeight();          mRenderRect.stretch((mRenderRect.getWidth() - static_cast<S32>(mVisibleEmojis) * mEmojiWidth) / -2, 0); +        mRenderRect.translate(-mRenderRect.mLeft, 0); // Left align emojis to fix hitboxes      }      updateScrollPos(); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 2086c8f68f..3ab48f69c8 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -162,6 +162,36 @@ void LLPanelFace::updateSelectedGLTFMaterials(std::function<void(LLGLTFMaterial*      LLSelectMgr::getInstance()->getSelection()->applyToTEs(&select_func);  } +void LLPanelFace::updateSelectedGLTFMaterialsWithScale(std::function<void(LLGLTFMaterial*, const F32, const F32)> func) +{ +    struct LLSelectedTEGLTFMaterialFunctor : public LLSelectedTEFunctor +    { +        LLSelectedTEGLTFMaterialFunctor(std::function<void(LLGLTFMaterial*, const F32, const F32)> func) : mFunc(func) {} +        virtual ~LLSelectedTEGLTFMaterialFunctor() {}; +        bool apply(LLViewerObject* object, S32 face) override +        { +            LLGLTFMaterial new_override; +            const LLTextureEntry* tep = object->getTE(face); +            if (tep->getGLTFMaterialOverride()) +            { +                new_override = *tep->getGLTFMaterialOverride(); +            } + +            U32 s_axis = VX; +            U32 t_axis = VY; +            LLPrimitive::getTESTAxes(face, &s_axis, &t_axis); +            mFunc(&new_override, object->getScale().mV[s_axis], object->getScale().mV[t_axis]); +            LLGLTFMaterialList::queueModify(object, face, &new_override); + +            return true; +        } + +        std::function<void(LLGLTFMaterial*, const F32, const F32)> mFunc; +    } select_func(func); + +    LLSelectMgr::getInstance()->getSelection()->applyToTEs(&select_func); +} +  template<typename T>  void readSelectedGLTFMaterial(std::function<T(const LLGLTFMaterial*)> func, T& value, bool& identical, bool has_tolerance, T tolerance)  { @@ -182,6 +212,36 @@ void readSelectedGLTFMaterial(std::function<T(const LLGLTFMaterial*)> func, T& v      identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&select_func, value, has_tolerance, tolerance);  } +void getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo channel, F32& repeats, bool& identical) +{ +    // The All channel should read base color values +    if (channel == LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_COUNT) +        channel = LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_BASE_COLOR; + +    struct LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor : public LLSelectedTEGetFunctor<F32> +    { +        LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor(LLGLTFMaterial::TextureInfo channel) : mChannel(channel) {} +        virtual ~LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor() {}; +        F32 get(LLViewerObject* object, S32 face) override +        { +            const LLTextureEntry* tep = object->getTE(face); +            const LLGLTFMaterial* render_material = tep->getGLTFRenderMaterial(); +            if (!render_material) +                return 0.f; + +            U32 s_axis = VX; +            U32 t_axis = VY; +            LLPrimitive::getTESTAxes(face, &s_axis, &t_axis); +            F32 repeats_u = render_material->mTextureTransform[mChannel].mScale[VX] / object->getScale().mV[s_axis]; +            F32 repeats_v = render_material->mTextureTransform[mChannel].mScale[VY] / object->getScale().mV[t_axis]; +            return llmax(repeats_u, repeats_v); +        } + +        LLGLTFMaterial::TextureInfo mChannel; +    } max_repeats_func(channel); +    identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&max_repeats_func, repeats); +} +  BOOST_STATIC_ASSERT(MATTYPE_DIFFUSE == LLRender::DIFFUSE_MAP && MATTYPE_NORMAL == LLRender::NORMAL_MAP && MATTYPE_SPECULAR == LLRender::SPECULAR_MAP);  // @@ -216,7 +276,7 @@ LLRender::eTexIndex LLPanelFace::getMatTextureChannel()              return LLRender::NORMAL_MAP;          break;      case MATTYPE_SPECULAR: // "Shininess (specular)" -        if (getCurrentNormalMap().notNull()) +        if (getCurrentSpecularMap().notNull())              return LLRender::SPECULAR_MAP;          break;      } @@ -322,6 +382,7 @@ bool LLPanelFace::postBuild()      getChildSetCommitCallback(mPBRScaleU, "gltfTextureScaleU", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureScaleU(); });      getChildSetCommitCallback(mPBRScaleV, "gltfTextureScaleV", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureScaleV(); }); +    getChildSetCommitCallback(mPBRRepeat, "gltfRptctrl", [&](LLUICtrl*, const LLSD&) { onCommitGLTFRepeatsPerMeter(); });      getChildSetCommitCallback(mPBRRotate, "gltfTextureRotation", [&](LLUICtrl*, const LLSD&) { onCommitGLTFRotation(); });      getChildSetCommitCallback(mPBROffsetU, "gltfTextureOffsetU", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureOffsetU(); });      getChildSetCommitCallback(mPBROffsetV, "gltfTextureOffsetV", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureOffsetV(); }); @@ -1394,9 +1455,18 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)              spec_scale_s = editable ? spec_scale_s : 1.0f;              spec_scale_s *= identical_planar_texgen ? 2.0f : 1.0f; -            mTexScaleU->setValue(diff_scale_s); -            mShinyScaleU->setValue(spec_scale_s); -            mBumpyScaleU->setValue(norm_scale_s); +            if (force_set_values) +            { +                mTexScaleU->forceSetValue(diff_scale_s); +                mShinyScaleU->forceSetValue(spec_scale_s); +                mBumpyScaleU->forceSetValue(norm_scale_s); +            } +            else +            { +                mTexScaleU->setValue(diff_scale_s); +                mShinyScaleU->setValue(spec_scale_s); +                mBumpyScaleU->setValue(norm_scale_s); +            }              mTexScaleU->setEnabled(editable && has_material);              mShinyScaleU->setEnabled(editable && has_material && specmap_id.notNull()); @@ -1444,13 +1514,16 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)              if (force_set_values)              {                  mTexScaleV->forceSetValue(diff_scale_t); +                mShinyScaleV->forceSetValue(spec_scale_t); +                mBumpyScaleV->forceSetValue(norm_scale_t);              }              else              {                  mTexScaleV->setValue(diff_scale_t); +                mShinyScaleV->setValue(spec_scale_t); +                mBumpyScaleV->setValue(norm_scale_t);              } -            mShinyScaleV->setValue(spec_scale_t); -            mBumpyScaleV->setValue(norm_scale_t); +              mTexScaleV->setTentative(LLSD(diff_scale_tentative));              mShinyScaleV->setTentative(LLSD(spec_scale_tentative)); @@ -1590,36 +1663,57 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)              F32 repeats_norm = 1.f;              F32 repeats_spec = 1.f; +            F32 repeats_pbr_basecolor = 1.f; +            F32 repeats_pbr_metallic_roughness = 1.f; +            F32 repeats_pbr_normal = 1.f; +            F32 repeats_pbr_emissive = 1.f; +              bool identical_diff_repeats = false;              bool identical_norm_repeats = false;              bool identical_spec_repeats = false; -            LLSelectedTE::getMaxDiffuseRepeats(repeats_diff, identical_diff_repeats); -            LLSelectedTEMaterial::getMaxNormalRepeats(repeats_norm, identical_norm_repeats); -            LLSelectedTEMaterial::getMaxSpecularRepeats(repeats_spec, identical_spec_repeats); +            bool identical_pbr_basecolor_repeats = false; +            bool identical_pbr_metallic_roughness_repeats = false; +            bool identical_pbr_normal_repeats = false; +            bool identical_pbr_emissive_repeats = false;              { +                LLSpinCtrl* repeats_spin_ctrl  = nullptr;                  S32 index = mComboTexGen ? mComboTexGen->getCurrentIndex() : 0;                  bool enabled = editable && (index != 1);                  bool identical_repeats = true;                  S32 material_selection = mComboMatMedia->getCurrentIndex();                  F32 repeats = 1.0f; -                U32 material_type = MATTYPE_DIFFUSE; -                if (material_selection == MATMEDIA_MATERIAL) +                LLRender::eTexIndex material_channel = LLRender::DIFFUSE_MAP; +                if (material_selection != MATMEDIA_PBR)                  { -                    material_type = mRadioMaterialType->getSelectedIndex(); +                    repeats_spin_ctrl = mTexRepeat; +                    material_channel = getMatTextureChannel(); +                    LLSelectedTE::getMaxDiffuseRepeats(repeats_diff, identical_diff_repeats); +                    LLSelectedTEMaterial::getMaxNormalRepeats(repeats_norm, identical_norm_repeats); +                    LLSelectedTEMaterial::getMaxSpecularRepeats(repeats_spec, identical_spec_repeats);                  }                  else if (material_selection == MATMEDIA_PBR)                  { +                    repeats_spin_ctrl = mPBRRepeat;                      enabled = editable && has_pbr_material; -                    material_type = mRadioPbrType->getSelectedIndex(); +                    material_channel = getPBRTextureChannel(); + +                    getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_BASE_COLOR, +                                                      repeats_pbr_basecolor, identical_pbr_basecolor_repeats); +                    getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS, +                                                      repeats_pbr_metallic_roughness, identical_pbr_metallic_roughness_repeats); +                    getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_NORMAL, +                                                      repeats_pbr_normal, identical_pbr_normal_repeats); +                    getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_EMISSIVE, +                                                      repeats_pbr_emissive, identical_pbr_emissive_repeats);                  } -                switch (material_type) +                switch (material_channel)                  {                  default: -                case MATTYPE_DIFFUSE: +                case LLRender::DIFFUSE_MAP:                      if (material_selection != MATMEDIA_PBR)                      {                          enabled = editable && !id.isNull(); @@ -1627,7 +1721,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)                      identical_repeats = identical_diff_repeats;                      repeats = repeats_diff;                      break; -                case MATTYPE_SPECULAR: +                case LLRender::SPECULAR_MAP:                      if (material_selection != MATMEDIA_PBR)                      {                          enabled = (editable && ((shiny == SHINY_TEXTURE) && !specmap_id.isNull())); @@ -1635,7 +1729,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)                      identical_repeats = identical_spec_repeats;                      repeats = repeats_spec;                      break; -                case MATTYPE_NORMAL: +                case LLRender::NORMAL_MAP:                      if (material_selection != MATMEDIA_PBR)                      {                          enabled = (editable && ((bumpy == BUMPY_TEXTURE) && !normmap_id.isNull())); @@ -1643,6 +1737,23 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)                      identical_repeats = identical_norm_repeats;                      repeats = repeats_norm;                      break; +                case LLRender::NUM_TEXTURE_CHANNELS: +                case LLRender::BASECOLOR_MAP: +                    identical_repeats = identical_pbr_basecolor_repeats; +                    repeats = repeats_pbr_basecolor; +                    break; +                case LLRender::METALLIC_ROUGHNESS_MAP: +                    identical_repeats = identical_pbr_metallic_roughness_repeats; +                    repeats = repeats_pbr_metallic_roughness; +                    break; +                case LLRender::GLTF_NORMAL_MAP: +                    identical_repeats = identical_pbr_normal_repeats; +                    repeats = repeats_pbr_normal; +                    break; +                case LLRender::EMISSIVE_MAP: +                    identical_repeats = identical_pbr_emissive_repeats; +                    repeats = repeats_pbr_emissive; +                    break;                  }                  bool repeats_tentative = !identical_repeats; @@ -1650,14 +1761,14 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)                  if (force_set_values)                  {                      // onCommit, previosly edited element updates related ones -                    mTexRepeat->forceSetValue(editable ? repeats : 1.0f); +                    repeats_spin_ctrl->forceSetValue(editable ? repeats : 1.0f);                  }                  else                  { -                    mTexRepeat->setValue(editable ? repeats : 1.0f); +                    repeats_spin_ctrl->setValue(editable ? repeats : 1.0f);                  } -                mTexRepeat->setTentative(LLSD(repeats_tentative)); -                mTexRepeat->setEnabled(has_material && !identical_planar_texgen && enabled); +                repeats_spin_ctrl->setTentative(LLSD(repeats_tentative)); +                repeats_spin_ctrl->setEnabled(!identical_planar_texgen && enabled);              }          } @@ -1803,6 +1914,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)          }          mLabelColorTransp->setEnabled(false);          mTexRepeat->setEnabled(false); +        mPBRRepeat->setEnabled(false);          mLabelTexGen->setEnabled(false);          mLabelShininess->setEnabled(false);          mLabelBumpiness->setEnabled(false); @@ -1998,6 +2110,7 @@ void LLPanelFace::updateVisibilityGLTF(LLViewerObject* objectp /*= nullptr */)      mPBRRotate->setVisible(show_pbr);      mPBROffsetU->setVisible(show_pbr);      mPBROffsetV->setVisible(show_pbr); +    mPBRRepeat->setVisible(show_pbr);  }  void LLPanelFace::updateCopyTexButton() @@ -2092,7 +2205,7 @@ void LLPanelFace::refreshMedia()      // check if all faces have media(or, all dont have media) -    LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue(&func, bool_has_media); +    bool identical_has_media_info = selected_objects->getSelectedTEValue(&func, bool_has_media);      const LLMediaEntry default_media_data; @@ -2114,7 +2227,8 @@ void LLPanelFace::refreshMedia()      } func_media_data(default_media_data);      LLMediaEntry media_data_get; -    LLFloaterMediaSettings::getInstance()->mMultipleMedia = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get)); +    bool multiple_media = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get)); +    bool multiple_valid_media = false;      std::string multi_media_info_str = LLTrans::getString("Multiple Media");      std::string media_title = ""; @@ -2123,12 +2237,12 @@ void LLPanelFace::refreshMedia()      mAddMedia->setEnabled(editable);      // IF all the faces have media (or all dont have media) -    if (LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo) +    if (identical_has_media_info)      {          // TODO: get media title and set it.          mTitleMediaText->clear();          // if identical is set, all faces are same (whether all empty or has the same media) -        if (!(LLFloaterMediaSettings::getInstance()->mMultipleMedia)) +        if (!multiple_media)          {              // Media data is valid              if (media_data_get != default_media_data) @@ -2149,9 +2263,9 @@ void LLPanelFace::refreshMedia()      else // not all face has media but at least one does.      {          // seleted faces have not identical value -        LLFloaterMediaSettings::getInstance()->mMultipleValidMedia = selected_objects->isMultipleTEValue(&func_media_data, default_media_data); +        multiple_valid_media = selected_objects->isMultipleTEValue(&func_media_data, default_media_data); -        if (LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) +        if (multiple_valid_media)          {              media_title = multi_media_info_str;          } @@ -2188,7 +2302,7 @@ void LLPanelFace::refreshMedia()      // load values for media settings      updateMediaSettings(); -    LLFloaterMediaSettings::initValues(mMediaSettings, editable); +    LLFloaterMediaSettings::initValues(mMediaSettings, editable, identical_has_media_info, multiple_media, multiple_valid_media);  }  void LLPanelFace::unloadMedia() @@ -3261,6 +3375,7 @@ void LLPanelFace::onSelectNormalTexture(const LLSD& data)  // TODO: test if there is media on the item and only allow editing if present  void LLPanelFace::onClickBtnEditMedia()  { +    LLFloaterMediaSettings::getInstance(); // make sure floater we are about to open exists before refreshMedia      refreshMedia();      LLFloaterReg::showInstance("media_settings");  } @@ -3279,6 +3394,7 @@ void LLPanelFace::onClickBtnAddMedia()      // check if multiple faces are selected      if (LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected())      { +        LLFloaterMediaSettings::getInstance(); // make sure floater we are about to open exists before refreshMedia          refreshMedia();          LLNotificationsUtil::add("MultipleFacesSelected", LLSD(), LLSD(), multipleFacesSelectedConfirm);      } @@ -3648,18 +3764,8 @@ void LLPanelFace::onCommitRepeatsPerMeter()      if (gSavedSettings.getBOOL("SyncMaterialSettings"))      {          LLSelectMgr::getInstance()->selectionTexScaleAutofit(repeats_per_meter); - -        mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter); -        mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter); - -        LLSelectedTEMaterial::setNormalRepeatX(this, obj_scale_s * repeats_per_meter); -        LLSelectedTEMaterial::setNormalRepeatY(this, obj_scale_t * repeats_per_meter); - -        mShinyScaleU->setValue(obj_scale_s * repeats_per_meter); -        mShinyScaleV->setValue(obj_scale_t * repeats_per_meter); - -        LLSelectedTEMaterial::setSpecularRepeatX(this, obj_scale_s * repeats_per_meter); -        LLSelectedTEMaterial::setSpecularRepeatY(this, obj_scale_t * repeats_per_meter); +        LLSelectedTEMaterial::selectionNormalScaleAutofit(this, repeats_per_meter); +        LLSelectedTEMaterial::selectionSpecularScaleAutofit(this, repeats_per_meter);      }      else      { @@ -3670,18 +3776,10 @@ void LLPanelFace::onCommitRepeatsPerMeter()              LLSelectMgr::getInstance()->selectionTexScaleAutofit(repeats_per_meter);              break;          case MATTYPE_NORMAL: -            mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter); -            mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter); - -            LLSelectedTEMaterial::setNormalRepeatX(this, obj_scale_s * repeats_per_meter); -            LLSelectedTEMaterial::setNormalRepeatY(this, obj_scale_t * repeats_per_meter); +            LLSelectedTEMaterial::selectionNormalScaleAutofit(this, repeats_per_meter);              break;          case MATTYPE_SPECULAR: -            mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter); -            mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter); - -            LLSelectedTEMaterial::setSpecularRepeatX(this, obj_scale_s * repeats_per_meter); -            LLSelectedTEMaterial::setSpecularRepeatY(this, obj_scale_t * repeats_per_meter); +            LLSelectedTEMaterial::selectionSpecularScaleAutofit(this, repeats_per_meter);              break;          default:              llassert(false); @@ -3692,6 +3790,21 @@ void LLPanelFace::onCommitRepeatsPerMeter()      updateUI(true);  } +// Commit the number of GLTF repeats per meter +void LLPanelFace::onCommitGLTFRepeatsPerMeter() +{ +    F32 repeats_per_meter = (F32)mPBRRepeat->getValue().asReal(); + +    LLGLTFMaterial::TextureInfo material_type = getPBRTextureInfo(); +    updateGLTFTextureTransformWithScale(material_type, [&](LLGLTFMaterial::TextureTransform* new_transform, F32 scale_s, F32 scale_t) +    { +        new_transform->mScale.mV[VX] = scale_s * repeats_per_meter; +        new_transform->mScale.mV[VY] = scale_t * repeats_per_meter; +    }); + +    updateUI(true); +} +  struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor  {      virtual bool apply(LLViewerObject* object, S32 te) @@ -4791,6 +4904,29 @@ void LLPanelFace::updateGLTFTextureTransform(std::function<void(LLGLTFMaterial::      }  } +void LLPanelFace::updateGLTFTextureTransformWithScale(const LLGLTFMaterial::TextureInfo texture_info, std::function<void(LLGLTFMaterial::TextureTransform*, const F32, const F32)> edit) +{ +    if (texture_info == LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT) +    { +        updateSelectedGLTFMaterialsWithScale([&](LLGLTFMaterial* new_override, const F32 scale_s, const F32 scale_t) +        { +            for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) +            { +                LLGLTFMaterial::TextureTransform& new_transform = new_override->mTextureTransform[(LLGLTFMaterial::TextureInfo)i]; +                edit(&new_transform, scale_s, scale_t); +            } +        }); +    } +    else +    { +        updateSelectedGLTFMaterialsWithScale([&](LLGLTFMaterial* new_override, const F32 scale_s, const F32 scale_t) +        { +            LLGLTFMaterial::TextureTransform& new_transform = new_override->mTextureTransform[texture_info]; +            edit(&new_transform, scale_s, scale_t); +        }); +    } +} +  void LLPanelFace::setMaterialOverridesFromSelection()  {      const LLGLTFMaterial::TextureInfo texture_info = getPBRTextureInfo(); @@ -4866,8 +5002,9 @@ void LLPanelFace::setMaterialOverridesFromSelection()          }      } -    mPBRScaleU->setValue(transform.mScale[VX]); -    mPBRScaleV->setValue(transform.mScale[VY]); +    // Force set scales just in case they were set by repeats per meter and their spinner is focused +    mPBRScaleU->forceSetValue(transform.mScale[VX]); +    mPBRScaleV->forceSetValue(transform.mScale[VY]);      mPBRRotate->setValue(transform.mRotation * RAD_TO_DEG);      mPBROffsetU->setValue(transform.mOffset[VX]);      mPBROffsetV->setValue(transform.mOffset[VY]); @@ -4877,6 +5014,12 @@ void LLPanelFace::setMaterialOverridesFromSelection()      mPBRRotate->setTentative(!rotation_same);      mPBROffsetU->setTentative(!offset_u_same);      mPBROffsetV->setTentative(!offset_v_same); + +    F32 repeats = 1.f; +    bool identical = false; +    getSelectedGLTFMaterialMaxRepeats(getPBRDropChannel(), repeats, identical); +    mPBRRepeat->forceSetValue(repeats); +    mPBRRepeat->setTentative(!identical || !scale_u_same || !scale_v_same);  }  void LLPanelFace::Selection::connect() @@ -5370,6 +5513,62 @@ void LLPanelFace::LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(U8& diffuse_a      identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &get_diff_mode, diffuse_alpha_mode);  } +void LLPanelFace::LLSelectedTEMaterial::selectionNormalScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter) +{ +    struct f : public LLSelectedTEFunctor +    { +        LLPanelFace* mFacePanel; +        F32 mRepeatsPerMeter; +        f(LLPanelFace* face_panel, const F32& repeats_per_meter) : mFacePanel(face_panel), mRepeatsPerMeter(repeats_per_meter) {} +        bool apply(LLViewerObject* object, S32 te) +        { +            if (object->permModify()) +            { +                // Compute S,T to axis mapping +                U32 s_axis, t_axis; +                if (!LLPrimitive::getTESTAxes(te, &s_axis, &t_axis)) +                    return true; + +                F32 new_s = object->getScale().mV[s_axis] * mRepeatsPerMeter; +                F32 new_t = object->getScale().mV[t_axis] * mRepeatsPerMeter; + +                setNormalRepeatX(mFacePanel, new_s, te); +                setNormalRepeatY(mFacePanel, new_t, te); +            } +            return true; +        } +    } setfunc(panel_face, repeats_per_meter); +    LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc); +} + +void LLPanelFace::LLSelectedTEMaterial::selectionSpecularScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter) +{ +    struct f : public LLSelectedTEFunctor +    { +        LLPanelFace* mFacePanel; +        F32 mRepeatsPerMeter; +        f(LLPanelFace* face_panel, const F32& repeats_per_meter) : mFacePanel(face_panel), mRepeatsPerMeter(repeats_per_meter) {} +        bool apply(LLViewerObject* object, S32 te) +        { +            if (object->permModify()) +            { +                // Compute S,T to axis mapping +                U32 s_axis, t_axis; +                if (!LLPrimitive::getTESTAxes(te, &s_axis, &t_axis)) +                    return true; + +                F32 new_s = object->getScale().mV[s_axis] * mRepeatsPerMeter; +                F32 new_t = object->getScale().mV[t_axis] * mRepeatsPerMeter; + +                setSpecularRepeatX(mFacePanel, new_s, te); +                setSpecularRepeatY(mFacePanel, new_t, te); +            } +            return true; +        } +    } setfunc(panel_face, repeats_per_meter); +    LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc); +} +  void LLPanelFace::LLSelectedTE::getObjectScaleS(F32& scale_s, bool& identical)  {      struct LLSelectedTEGetObjectScaleS : public LLSelectedTEGetFunctor<F32> diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index b59f7434af..8a8460204c 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -250,6 +250,7 @@ protected:      void onCommitGLTFRotation();      void onCommitGLTFTextureOffsetU();      void onCommitGLTFTextureOffsetV(); +    void onCommitGLTFRepeatsPerMeter();      void onClickAutoFix();      void onAlignTexture(); @@ -361,6 +362,7 @@ private:      LLButton* mDelMedia { nullptr };      LLSpinCtrl* mPBRScaleU { nullptr };      LLSpinCtrl* mPBRScaleV { nullptr }; +    LLSpinCtrl* mPBRRepeat { nullptr };      LLSpinCtrl* mPBRRotate { nullptr };      LLSpinCtrl* mPBROffsetU { nullptr };      LLSpinCtrl* mPBROffsetV { nullptr }; @@ -554,7 +556,9 @@ private:      void updateVisibilityGLTF(LLViewerObject* objectp = nullptr);      void updateSelectedGLTFMaterials(std::function<void(LLGLTFMaterial*)> func); +    void updateSelectedGLTFMaterialsWithScale(std::function<void(LLGLTFMaterial*, const F32, const F32)> func);      void updateGLTFTextureTransform(std::function<void(LLGLTFMaterial::TextureTransform*)> edit); +    void updateGLTFTextureTransformWithScale(const LLGLTFMaterial::TextureInfo texture_info, std::function<void(LLGLTFMaterial::TextureTransform*, const F32, const F32)> edit);      void setMaterialOverridesFromSelection(); @@ -649,6 +653,8 @@ public:          static void getMaxSpecularRepeats(F32& repeats, bool& identical);          static void getMaxNormalRepeats(F32& repeats, bool& identical);          static void getCurrentDiffuseAlphaMode(U8& diffuse_alpha_mode, bool& identical, bool diffuse_texture_has_alpha); +        static void selectionNormalScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter); +        static void selectionSpecularScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter);          DEF_GET_MAT_STATE(LLUUID, const LLUUID&, getNormalID, LLUUID::null, false, LLUUID::null);          DEF_GET_MAT_STATE(LLUUID, const LLUUID&, getSpecularID, LLUUID::null, false, LLUUID::null); diff --git a/indra/newview/llpanelgroupbulk.cpp b/indra/newview/llpanelgroupbulk.cpp index 8032e207cd..81c0bd97be 100644 --- a/indra/newview/llpanelgroupbulk.cpp +++ b/indra/newview/llpanelgroupbulk.cpp @@ -54,17 +54,16 @@  //////////////////////////////////////////////////////////////////////////  LLPanelGroupBulkImpl::LLPanelGroupBulkImpl(const LLUUID& group_id) :      mGroupID(group_id), -    mBulkAgentList(NULL), -    mOKButton(NULL), +    mBulkAgentList(nullptr), +    mOKButton(nullptr),      mAddButton(nullptr), -    mRemoveButton(NULL), -    mGroupName(NULL), +    mRemoveButton(nullptr), +    mGroupName(nullptr),      mLoadingText(),      mTooManySelected(), -    mCloseCallback(NULL), -    mCloseCallbackUserData(NULL), -    mAvatarNameCacheConnection(), -    mRoleNames(NULL), +    mCloseCallback(nullptr), +    mCloseCallbackUserData(nullptr), +    mRoleNames(nullptr),      mOwnerWarning(),      mAlreadyInGroup(),      mConfirmedOwnerInvite(false), @@ -74,10 +73,13 @@ LLPanelGroupBulkImpl::LLPanelGroupBulkImpl(const LLUUID& group_id) :  LLPanelGroupBulkImpl::~LLPanelGroupBulkImpl()  { -    if (mAvatarNameCacheConnection.connected()) +    for (auto& [id, connection] : mAvatarNameCacheConnections)      { -        mAvatarNameCacheConnection.disconnect(); +        if (connection.connected()) +            connection.disconnect();      } + +    mAvatarNameCacheConnections.clear();  }  void LLPanelGroupBulkImpl::callbackClickAdd(LLPanelGroupBulk* panelp) @@ -124,43 +126,42 @@ void LLPanelGroupBulkImpl::callbackSelect(LLUICtrl* ctrl, void* userdata)  void LLPanelGroupBulkImpl::addUsers(const uuid_vec_t& agent_ids)  { -    std::vector<std::string> names;      for (const LLUUID& agent_id : agent_ids)      { -        LLAvatarName av_name; -        if (LLAvatarNameCache::get(agent_id, &av_name)) +        if (LLAvatarName av_name; LLAvatarNameCache::get(agent_id, &av_name))          {              onAvatarNameCache(agent_id, av_name);          }          else          { -            if (mAvatarNameCacheConnection.connected()) +            if (auto found = mAvatarNameCacheConnections.find(agent_id); found != mAvatarNameCacheConnections.end())              { -                mAvatarNameCacheConnection.disconnect(); +                if (found->second.connected()) +                    found->second.disconnect(); + +                mAvatarNameCacheConnections.erase(found);              } -            // *TODO : Add a callback per avatar name being fetched. -            mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, + +            mAvatarNameCacheConnections.try_emplace(agent_id, LLAvatarNameCache::get(agent_id,                  [&](const LLUUID& agent_id, const LLAvatarName& av_name)                  {                      onAvatarNameCache(agent_id, av_name); -                }); +                }));          }      }  }  void LLPanelGroupBulkImpl::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)  { -    if (mAvatarNameCacheConnection.connected()) +    if (auto found = mAvatarNameCacheConnections.find(agent_id); found != mAvatarNameCacheConnections.end())      { -        mAvatarNameCacheConnection.disconnect(); -    } +        if (found->second.connected()) +            found->second.disconnect(); -    std::vector<std::string> names; -    uuid_vec_t agent_ids; -    agent_ids.push_back(agent_id); -    names.push_back(av_name.getCompleteName()); +        mAvatarNameCacheConnections.erase(found); +    } -    addUsers(names, agent_ids); +    addUsers({ av_name.getCompleteName() }, { agent_id });  }  void LLPanelGroupBulkImpl::handleRemove() @@ -232,7 +233,7 @@ void LLPanelGroupBulkImpl::addUsers(const std::vector<std::string>& names, const      }  } -void LLPanelGroupBulkImpl::setGroupName(std::string name) +void LLPanelGroupBulkImpl::setGroupName(const std::string& name)  {      if (mGroupName)      { @@ -337,12 +338,7 @@ void LLPanelGroupBulk::updateGroupData()  void LLPanelGroupBulk::addUserCallback(const LLUUID& id, const LLAvatarName& av_name)  { -    std::vector<std::string> names; -    uuid_vec_t agent_ids; -    agent_ids.push_back(id); -    names.push_back(av_name.getAccountName()); - -    mImplementation->addUsers(names, agent_ids); +    mImplementation->addUsers({ av_name.getAccountName() }, { id });  }  void LLPanelGroupBulk::setCloseCallback(void (*close_callback)(void*), void* data) diff --git a/indra/newview/llpanelgroupbulkimpl.h b/indra/newview/llpanelgroupbulkimpl.h index 5515bd6d9a..f186ae5373 100644 --- a/indra/newview/llpanelgroupbulkimpl.h +++ b/indra/newview/llpanelgroupbulkimpl.h @@ -59,7 +59,7 @@ public:      void handleSelection();      void addUsers(const std::vector<std::string>& names, const uuid_vec_t& agent_ids); -    void setGroupName(std::string name); +    void setGroupName(const std::string& name);  public: @@ -84,7 +84,7 @@ public:      void (*mCloseCallback)(void* data);      void* mCloseCallbackUserData; -    boost::signals2::connection mAvatarNameCacheConnection; +    std::map<LLUUID, boost::signals2::connection> mAvatarNameCacheConnections;      // The following are for the LLPanelGroupInvite subclass only.      // These aren't needed for LLPanelGroupBulkBan, but if we have to add another diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index ed80c8b732..59aa375457 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -184,7 +184,6 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,      mCallback(callback),      mCallbackData(cb_data),      mListener(new LLPanelLoginListener(this)), -    mFirstLoginThisInstall(gSavedSettings.getBOOL("FirstLoginThisInstall")),      mUsernameLength(0),      mPasswordLength(0),      mLocationLength(0), @@ -203,14 +202,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,          login_holder->addChild(this);      } -    if (mFirstLoginThisInstall) -    { -        buildFromFile( "panel_login_first.xml"); -    } -    else -    { -        buildFromFile( "panel_login.xml"); -    } +    buildFromFile("panel_login.xml");      reshape(rect.getWidth(), rect.getHeight()); @@ -224,38 +216,36 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,      sendChildToBack(getChildView("sign_up_text"));      std::string current_grid = LLGridManager::getInstance()->getGrid(); -    if (!mFirstLoginThisInstall) -    { -        LLComboBox* favorites_combo = getChild<LLComboBox>("start_location_combo"); -        updateLocationSelectorsVisibility(); // separate so that it can be called from preferences -        favorites_combo->setReturnCallback(boost::bind(&LLPanelLogin::onClickConnect, false)); -        favorites_combo->setFocusLostCallback(boost::bind(&LLPanelLogin::onLocationSLURL, this)); -        LLComboBox* server_choice_combo = getChild<LLComboBox>("server_combo"); -        server_choice_combo->setCommitCallback(boost::bind(&LLPanelLogin::onSelectServer, this)); +    LLComboBox* favorites_combo = getChild<LLComboBox>("start_location_combo"); +    updateLocationSelectorsVisibility(); // separate so that it can be called from preferences +    favorites_combo->setReturnCallback(boost::bind(&LLPanelLogin::onClickConnect, false)); +    favorites_combo->setFocusLostCallback(boost::bind(&LLPanelLogin::onLocationSLURL, this)); + +    LLComboBox* server_choice_combo = getChild<LLComboBox>("server_combo"); +    server_choice_combo->setCommitCallback(boost::bind(&LLPanelLogin::onSelectServer, this)); -        // Load all of the grids, sorted, and then add a bar and the current grid at the top -        server_choice_combo->removeall(); +    // Load all of the grids, sorted, and then add a bar and the current grid at the top +    server_choice_combo->removeall(); -        std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(); -        for (std::map<std::string, std::string>::iterator grid_choice = known_grids.begin(); -            grid_choice != known_grids.end(); -            grid_choice++) +    std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(); +    for (std::map<std::string, std::string>::iterator grid_choice = known_grids.begin(); +        grid_choice != known_grids.end(); +        grid_choice++) +    { +        if (!grid_choice->first.empty() && current_grid != grid_choice->first)          { -            if (!grid_choice->first.empty() && current_grid != grid_choice->first) -            { -                LL_DEBUGS("AppInit") << "adding " << grid_choice->first << LL_ENDL; -                server_choice_combo->add(grid_choice->second, grid_choice->first); -            } +            LL_DEBUGS("AppInit") << "adding " << grid_choice->first << LL_ENDL; +            server_choice_combo->add(grid_choice->second, grid_choice->first);          } -        server_choice_combo->sortByName(); - -        LL_DEBUGS("AppInit") << "adding current " << current_grid << LL_ENDL; -        server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(), -            current_grid, -            ADD_TOP); -        server_choice_combo->selectFirstItem();      } +    server_choice_combo->sortByName(); + +    LL_DEBUGS("AppInit") << "adding current " << current_grid << LL_ENDL; +    server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(), +        current_grid, +        ADD_TOP); +    server_choice_combo->selectFirstItem();      LLSLURL start_slurl(LLStartUp::getStartSLURL());      // The StartSLURL might have been set either by an explicit command-line @@ -331,15 +321,6 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,  void LLPanelLogin::addFavoritesToStartLocation()  { -    if (mFirstLoginThisInstall) -    { -        // first login panel has no favorites, just update name length and buttons -        std::string user_defined_name = getChild<LLComboBox>("username_combo")->getSimple(); -        mUsernameLength = static_cast<unsigned int>(user_defined_name.length()); -        updateLoginButtons(); -        return; -    } -      // Clear the combo.      LLComboBox* combo = getChild<LLComboBox>("start_location_combo");      if (!combo) return; @@ -559,16 +540,9 @@ void LLPanelLogin::resetFields()          // function is used to reset list in case of changes by external sources          return;      } -    if (sInstance->mFirstLoginThisInstall) -    { -        // no list to populate -        LL_WARNS() << "Shouldn't happen, user should have no ability to modify list on first install" << LL_ENDL; -    } -    else -    { -        LLPointer<LLCredential> cred = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); -        sInstance->populateUserList(cred); -    } + +    LLPointer<LLCredential> cred = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); +    sInstance->populateUserList(cred);  }  // static @@ -586,7 +560,6 @@ void LLPanelLogin::setFields(LLPointer<LLCredential> credential)      if(identifier.has("type") && (std::string)identifier["type"] == "agent")      { -        // not nessesary for panel_login.xml, needed for panel_login_first.xml          std::string firstname = identifier["first_name"].asString();          std::string lastname = identifier["last_name"].asString();          std::string login_id = firstname; @@ -1081,8 +1054,7 @@ void LLPanelLogin::onRememberUserCheck(void*)          LLComboBox* user_combo(sInstance->getChild<LLComboBox>("username_combo"));          bool remember = remember_name->getValue().asBoolean(); -        if (!sInstance->mFirstLoginThisInstall -            && user_combo->getCurrentIndex() != -1 +        if (user_combo->getCurrentIndex() != -1              && !remember)          {              remember = true; @@ -1197,17 +1169,14 @@ void LLPanelLogin::updateLoginButtons()      login_btn->setEnabled(mUsernameLength != 0 && mPasswordLength != 0); -    if (!mFirstLoginThisInstall) +    LLComboBox* user_combo = getChild<LLComboBox>("username_combo"); +    LLCheckBoxCtrl* remember_name = getChild<LLCheckBoxCtrl>("remember_name"); +    if (user_combo->getCurrentIndex() != -1)      { -        LLComboBox* user_combo = getChild<LLComboBox>("username_combo"); -        LLCheckBoxCtrl* remember_name = getChild<LLCheckBoxCtrl>("remember_name"); -        if (user_combo->getCurrentIndex() != -1) -        { -            remember_name->setValue(true); -            LLCheckBoxCtrl* remember_pass = getChild<LLCheckBoxCtrl>("remember_password"); -            remember_pass->setEnabled(true); -        } // Note: might be good idea to do "else remember_name->setValue(mRememberedState)" but it might behave 'weird' to user -    } +        remember_name->setValue(true); +        LLCheckBoxCtrl* remember_pass = getChild<LLCheckBoxCtrl>("remember_password"); +        remember_pass->setEnabled(true); +    } // Note: might be good idea to do "else remember_name->setValue(mRememberedState)" but it might behave 'weird' to user  }  void LLPanelLogin::populateUserList(LLPointer<LLCredential> credential) diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index a1bf25fb05..1259bf26d6 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -119,7 +119,6 @@ private:      static LLPanelLogin* sInstance;      static bool     sCapslockDidNotification; -    bool            mFirstLoginThisInstall;      static bool sCredentialSet; diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 3083bffdd4..ad7aa57842 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -79,9 +79,9 @@ static LLPanelInjector<LLPanelMainInventory> t_inventory("panel_main_inventory")  class LLFloaterInventoryFinder : public LLFloater  {  public: -    LLFloaterInventoryFinder( LLPanelMainInventory* inventory_view); -    virtual void draw(); -    /*virtual*/ bool    postBuild(); +    LLFloaterInventoryFinder(LLPanelMainInventory* inventory_view); +    void draw(); +    bool postBuild();      void changeFilter(LLInventoryFilter* filter);      void updateElementsFromFilter();      bool getCheckShowEmpty(); @@ -91,17 +91,35 @@ public:      void onCreatorSelfFilterCommit();      void onCreatorOtherFilterCommit(); -    static void onTimeAgo(LLUICtrl*, void *); -    static void onCloseBtn(void* user_data); -    static void selectAllTypes(void* user_data); -    static void selectNoTypes(void* user_data); +    void onTimeAgo(); +    void onCloseBtn(); +    void selectAllTypes(); +    void selectNoTypes();  private: -    LLPanelMainInventory*   mPanelMainInventory; -    LLSpinCtrl*         mSpinSinceDays; -    LLSpinCtrl*         mSpinSinceHours; -    LLCheckBoxCtrl*     mCreatorSelf; -    LLCheckBoxCtrl*     mCreatorOthers; -    LLInventoryFilter*  mFilter; +    LLPanelMainInventory*   mPanelMainInventory{ nullptr }; +    LLSpinCtrl*         mSpinSinceDays{ nullptr }; +    LLSpinCtrl*         mSpinSinceHours{ nullptr }; +    LLCheckBoxCtrl*     mCreatorSelf{ nullptr }; +    LLCheckBoxCtrl*     mCreatorOthers{ nullptr }; +    LLInventoryFilter*  mFilter{ nullptr }; + +    LLCheckBoxCtrl* mCheckAnimation{ nullptr }; +    LLCheckBoxCtrl* mCheckCallingCard{ nullptr }; +    LLCheckBoxCtrl* mCheckClothing{ nullptr }; +    LLCheckBoxCtrl* mCheckGesture{ nullptr }; +    LLCheckBoxCtrl* mCheckLandmark{ nullptr }; +    LLCheckBoxCtrl* mCheckMaterial{ nullptr }; +    LLCheckBoxCtrl* mCheckNotecard{ nullptr }; +    LLCheckBoxCtrl* mCheckObject{ nullptr }; +    LLCheckBoxCtrl* mCheckScript{ nullptr }; +    LLCheckBoxCtrl* mCheckSounds{ nullptr }; +    LLCheckBoxCtrl* mCheckTexture{ nullptr }; +    LLCheckBoxCtrl* mCheckSnapshot{ nullptr }; +    LLCheckBoxCtrl* mCheckSettings{ nullptr }; +    LLCheckBoxCtrl* mCheckShowEmpty{ nullptr }; +    LLCheckBoxCtrl* mCheckSinceLogoff{ nullptr }; + +    LLRadioGroup* mRadioDateSearchDirection{ nullptr };  };  ///---------------------------------------------------------------------------- @@ -747,7 +765,6 @@ bool LLPanelMainInventory::filtersVisible(void* user_data)  void LLPanelMainInventory::onClearSearch()  {      bool initially_active = false; -    LLFloater *finder = getFinder();      if (mActivePanel && (getActivePanel() != mWornItemsPanel))      {          initially_active = mActivePanel->getFilter().isNotDefault(); @@ -756,9 +773,9 @@ void LLPanelMainInventory::onClearSearch()          mActivePanel->setFilterLinks(LLInventoryFilter::FILTERLINK_INCLUDE_LINKS);      } -    if (finder) +    if (LLFloaterInventoryFinder* finder = getFinder())      { -        LLFloaterInventoryFinder::selectAllTypes(finder); +        finder->selectAllTypes();      }      // re-open folders that were initially open in case filter was active @@ -1158,36 +1175,53 @@ bool LLFloaterInventoryFinder::postBuild()      const LLRect& viewrect = mPanelMainInventory->getRect();      setRect(LLRect(viewrect.mLeft - getRect().getWidth(), viewrect.mTop, viewrect.mLeft, viewrect.mTop - getRect().getHeight())); -    childSetAction("All", selectAllTypes, this); -    childSetAction("None", selectNoTypes, this); +    childSetAction("All", [this](LLUICtrl*, const LLSD&) { selectAllTypes(); }); +    childSetAction("None", [this](LLUICtrl*, const LLSD&) { selectNoTypes(); });      mSpinSinceHours = getChild<LLSpinCtrl>("spin_hours_ago"); -    childSetCommitCallback("spin_hours_ago", onTimeAgo, this); +    mSpinSinceHours->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTimeAgo(); });      mSpinSinceDays = getChild<LLSpinCtrl>("spin_days_ago"); -    childSetCommitCallback("spin_days_ago", onTimeAgo, this); +    mSpinSinceDays->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTimeAgo(); });      mCreatorSelf = getChild<LLCheckBoxCtrl>("check_created_by_me");      mCreatorOthers = getChild<LLCheckBoxCtrl>("check_created_by_others");      mCreatorSelf->setCommitCallback(boost::bind(&LLFloaterInventoryFinder::onCreatorSelfFilterCommit, this));      mCreatorOthers->setCommitCallback(boost::bind(&LLFloaterInventoryFinder::onCreatorOtherFilterCommit, this)); -    childSetAction("Close", onCloseBtn, this); +    mCheckAnimation = getChild<LLCheckBoxCtrl>("check_animation"); +    mCheckCallingCard = getChild<LLCheckBoxCtrl>("check_calling_card"); +    mCheckClothing = getChild<LLCheckBoxCtrl>("check_clothing"); +    mCheckGesture = getChild<LLCheckBoxCtrl>("check_gesture"); +    mCheckLandmark = getChild<LLCheckBoxCtrl>("check_landmark"); +    mCheckMaterial = getChild<LLCheckBoxCtrl>("check_material"); +    mCheckNotecard = getChild<LLCheckBoxCtrl>("check_notecard"); +    mCheckObject = getChild<LLCheckBoxCtrl>("check_object"); +    mCheckScript = getChild<LLCheckBoxCtrl>("check_script"); +    mCheckSounds = getChild<LLCheckBoxCtrl>("check_sound"); +    mCheckTexture = getChild<LLCheckBoxCtrl>("check_texture"); +    mCheckSnapshot = getChild<LLCheckBoxCtrl>("check_snapshot"); +    mCheckSettings = getChild<LLCheckBoxCtrl>("check_settings"); +    mCheckShowEmpty = getChild<LLCheckBoxCtrl>("check_show_empty"); +    mCheckSinceLogoff = getChild<LLCheckBoxCtrl>("check_since_logoff"); + +    mRadioDateSearchDirection = getChild<LLRadioGroup>("date_search_direction"); + +    childSetAction("Close", [this](LLUICtrl*, const LLSD&) { onCloseBtn(); });      updateElementsFromFilter(); +      return true;  } -void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data) -{ -    LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data; -    if (!self) return; -    if ( self->mSpinSinceDays->get() ||  self->mSpinSinceHours->get() ) +void LLFloaterInventoryFinder::onTimeAgo() +{ +    if (mSpinSinceDays->get() || mSpinSinceHours->get())      { -        self->getChild<LLUICtrl>("check_since_logoff")->setValue(false); +        mCheckSinceLogoff->setValue(false); -        U32 days = (U32)self->mSpinSinceDays->get(); -        U32 hours = (U32)self->mSpinSinceHours->get(); +        U32 days = (U32)mSpinSinceDays->get(); +        U32 hours = (U32)mSpinSinceHours->get();          if (hours >= 24)          {              // Try to handle both cases of spinner clicking and text input in a sensible fashion as best as possible. @@ -1203,11 +1237,11 @@ void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data)                  days = hours / 24;              }              hours = (U32)hours % 24; -            self->mSpinSinceHours->setFocus(false); -            self->mSpinSinceDays->setFocus(false); -            self->mSpinSinceDays->set((F32)days); -            self->mSpinSinceHours->set((F32)hours); -            self->mSpinSinceHours->setFocus(true); +            mSpinSinceHours->setFocus(false); +            mSpinSinceDays->setFocus(false); +            mSpinSinceDays->set((F32)days); +            mSpinSinceHours->set((F32)hours); +            mSpinSinceHours->setFocus(true);          }      }  } @@ -1236,29 +1270,28 @@ void LLFloaterInventoryFinder::updateElementsFromFilter()      // update the ui elements      setTitle(mFilter->getName()); -    getChild<LLUICtrl>("check_animation")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION)); - -    getChild<LLUICtrl>("check_calling_card")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_CALLINGCARD)); -    getChild<LLUICtrl>("check_clothing")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE)); -    getChild<LLUICtrl>("check_gesture")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE)); -    getChild<LLUICtrl>("check_landmark")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK)); -    getChild<LLUICtrl>("check_material")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_MATERIAL)); -    getChild<LLUICtrl>("check_notecard")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD)); -    getChild<LLUICtrl>("check_object")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT)); -    getChild<LLUICtrl>("check_script")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LSL)); -    getChild<LLUICtrl>("check_sound")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND)); -    getChild<LLUICtrl>("check_texture")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE)); -    getChild<LLUICtrl>("check_snapshot")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT)); -    getChild<LLUICtrl>("check_settings")->setValue((S32)(filter_types & 0x1 << LLInventoryType::IT_SETTINGS)); -    getChild<LLUICtrl>("check_show_empty")->setValue(show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS); - -    getChild<LLUICtrl>("check_created_by_me")->setValue(show_created_by_me); -    getChild<LLUICtrl>("check_created_by_others")->setValue(show_created_by_others); - -    getChild<LLUICtrl>("check_since_logoff")->setValue(mFilter->isSinceLogoff()); +    mCheckAnimation->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION)); +    mCheckCallingCard->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_CALLINGCARD)); +    mCheckClothing->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE)); +    mCheckGesture->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE)); +    mCheckLandmark->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK)); +    mCheckMaterial->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_MATERIAL)); +    mCheckNotecard->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD)); +    mCheckObject->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT)); +    mCheckScript->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LSL)); +    mCheckSounds->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND)); +    mCheckTexture->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE)); +    mCheckSnapshot->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT)); +    mCheckSettings->setValue((S32)(filter_types & 0x1 << LLInventoryType::IT_SETTINGS)); +    mCheckShowEmpty->setValue(show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS); + +    mCreatorSelf->setValue(show_created_by_me); +    mCreatorOthers->setValue(show_created_by_others); + +    mCheckSinceLogoff->setValue(mFilter->isSinceLogoff());      mSpinSinceHours->set((F32)(hours % 24));      mSpinSinceDays->set((F32)(hours / 24)); -    getChild<LLRadioGroup>("date_search_direction")->setSelectedIndex(date_search_direction); +    mRadioDateSearchDirection->setSelectedIndex(date_search_direction);  }  void LLFloaterInventoryFinder::draw() @@ -1266,80 +1299,80 @@ void LLFloaterInventoryFinder::draw()      U64 filter = 0xffffffffffffffffULL;      bool filtered_by_all_types = true; -    if (!getChild<LLUICtrl>("check_animation")->getValue()) +    if (!mCheckAnimation->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_ANIMATION);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_calling_card")->getValue()) +    if (!mCheckCallingCard->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_CALLINGCARD);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_clothing")->getValue()) +    if (!mCheckClothing->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_WEARABLE);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_gesture")->getValue()) +    if (!mCheckGesture->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_GESTURE);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_landmark")->getValue()) +    if (!mCheckLandmark->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_LANDMARK);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_material")->getValue()) +    if (!mCheckMaterial->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_MATERIAL);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_notecard")->getValue()) +    if (!mCheckNotecard->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_NOTECARD);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_object")->getValue()) +    if (!mCheckObject->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_OBJECT);          filter &= ~(0x1 << LLInventoryType::IT_ATTACHMENT);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_script")->getValue()) +    if (!mCheckScript->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_LSL);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_sound")->getValue()) +    if (!mCheckSounds->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_SOUND);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_texture")->getValue()) +    if (!mCheckTexture->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_TEXTURE);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_snapshot")->getValue()) +    if (!mCheckSnapshot->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_SNAPSHOT);          filtered_by_all_types = false;      } -    if (!getChild<LLUICtrl>("check_settings")->getValue()) +    if (!mCheckSettings->getValue())      {          filter &= ~(0x1 << LLInventoryType::IT_SETTINGS);          filtered_by_all_types = false; @@ -1457,65 +1490,56 @@ void LLFloaterInventoryFinder::onCreatorOtherFilterCommit()  bool LLFloaterInventoryFinder::getCheckShowEmpty()  { -    return getChild<LLUICtrl>("check_show_empty")->getValue(); +    return mCheckShowEmpty->getValue();  }  bool LLFloaterInventoryFinder::getCheckSinceLogoff()  { -    return getChild<LLUICtrl>("check_since_logoff")->getValue(); +    return mCheckSinceLogoff->getValue();  }  U32 LLFloaterInventoryFinder::getDateSearchDirection()  { -    return  getChild<LLRadioGroup>("date_search_direction")->getSelectedIndex(); +    return mRadioDateSearchDirection->getSelectedIndex();  } -void LLFloaterInventoryFinder::onCloseBtn(void* user_data) +void LLFloaterInventoryFinder::onCloseBtn()  { -    LLFloaterInventoryFinder* finderp = (LLFloaterInventoryFinder*)user_data; -    finderp->closeFloater(); +    closeFloater();  } -// static -void LLFloaterInventoryFinder::selectAllTypes(void* user_data) -{ -    LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data; -    if(!self) return; - -    self->getChild<LLUICtrl>("check_animation")->setValue(true); -    self->getChild<LLUICtrl>("check_calling_card")->setValue(true); -    self->getChild<LLUICtrl>("check_clothing")->setValue(true); -    self->getChild<LLUICtrl>("check_gesture")->setValue(true); -    self->getChild<LLUICtrl>("check_landmark")->setValue(true); -    self->getChild<LLUICtrl>("check_material")->setValue(true); -    self->getChild<LLUICtrl>("check_notecard")->setValue(true); -    self->getChild<LLUICtrl>("check_object")->setValue(true); -    self->getChild<LLUICtrl>("check_script")->setValue(true); -    self->getChild<LLUICtrl>("check_sound")->setValue(true); -    self->getChild<LLUICtrl>("check_texture")->setValue(true); -    self->getChild<LLUICtrl>("check_snapshot")->setValue(true); -    self->getChild<LLUICtrl>("check_settings")->setValue(true); +void LLFloaterInventoryFinder::selectAllTypes() +{ +    mCheckAnimation->setValue(true); +    mCheckCallingCard->setValue(true); +    mCheckClothing->setValue(true); +    mCheckGesture->setValue(true); +    mCheckLandmark->setValue(true); +    mCheckMaterial->setValue(true); +    mCheckNotecard->setValue(true); +    mCheckObject->setValue(true); +    mCheckScript->setValue(true); +    mCheckSounds->setValue(true); +    mCheckTexture->setValue(true); +    mCheckSnapshot->setValue(true); +    mCheckSettings->setValue(true);  } -//static -void LLFloaterInventoryFinder::selectNoTypes(void* user_data) -{ -    LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data; -    if(!self) return; - -    self->getChild<LLUICtrl>("check_animation")->setValue(false); -    self->getChild<LLUICtrl>("check_calling_card")->setValue(false); -    self->getChild<LLUICtrl>("check_clothing")->setValue(false); -    self->getChild<LLUICtrl>("check_gesture")->setValue(false); -    self->getChild<LLUICtrl>("check_landmark")->setValue(false); -    self->getChild<LLUICtrl>("check_material")->setValue(false); -    self->getChild<LLUICtrl>("check_notecard")->setValue(false); -    self->getChild<LLUICtrl>("check_object")->setValue(false); -    self->getChild<LLUICtrl>("check_script")->setValue(false); -    self->getChild<LLUICtrl>("check_sound")->setValue(false); -    self->getChild<LLUICtrl>("check_texture")->setValue(false); -    self->getChild<LLUICtrl>("check_snapshot")->setValue(false); -    self->getChild<LLUICtrl>("check_settings")->setValue(false); +void LLFloaterInventoryFinder::selectNoTypes() +{ +    mCheckAnimation->setValue(false); +    mCheckCallingCard->setValue(false); +    mCheckClothing->setValue(false); +    mCheckGesture->setValue(false); +    mCheckLandmark->setValue(false); +    mCheckMaterial->setValue(false); +    mCheckNotecard->setValue(false); +    mCheckObject->setValue(false); +    mCheckScript->setValue(false); +    mCheckSounds->setValue(false); +    mCheckTexture->setValue(false); +    mCheckSnapshot->setValue(false); +    mCheckSettings->setValue(false);  }  ////////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 25672db318..33599357a3 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -843,6 +843,10 @@ void LLPanelPeople::updateNearbyList()      LLWorld::getInstance()->getAvatars(&mNearbyList->getIDs(), &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange"));      mNearbyList->setDirty(); +#ifdef LL_DISCORD +    if (gSavedSettings.getBOOL("EnableDiscord")) +        LLAppViewer::updateDiscordPartyMaxSize((S32)mNearbyList->getIDs().size()); +#endif      DISTANCE_COMPARATOR.updateAvatarsPositions(positions, mNearbyList->getIDs());      LLActiveSpeakerMgr::instance().update(true); diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index 0ef86235d5..62e726d21d 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -154,11 +154,15 @@ void PeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)  bool PeopleContextMenu::enableContextMenuItem(const LLSD& userdata)  { +    std::string item = userdata.asString();      if(gAgent.getID() == mUUIDs.front())      { +        if (item == std::string("can_zoom_in")) +        { +            return true; +        }          return false;      } -    std::string item = userdata.asString();      // Note: can_block and can_delete is used only for one person selected menu      // so we don't need to go over all uuids. diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 891896076a..cbf5819fda 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -1177,6 +1177,7 @@ void LLPanelPermissions::onCommitName(LLUICtrl*, void* data)          {              LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);              new_item->rename(tb->getText()); +            new_item->setComplete(true); // to not err at updateServer              new_item->updateServer(false);              gInventory.updateItem(new_item);              gInventory.notifyObservers(); diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp index a87ef4f0f9..c9626bf9ea 100644 --- a/indra/newview/llpanelprofilepicks.cpp +++ b/indra/newview/llpanelprofilepicks.cpp @@ -248,6 +248,8 @@ void LLPanelProfilePicks::onClickNewBtn()          select_tab(true).          label(pick_panel->getPickName()));      updateButtons(); + +    pick_panel->addLocationChangedCallbacks();  }  void LLPanelProfilePicks::onClickDelete() @@ -607,10 +609,12 @@ void LLPanelProfilePick::setAvatarId(const LLUUID& avatar_id)      {          mPickName->setEnabled(true);          mPickDescription->setEnabled(true); +        mSetCurrentLocationButton->setVisible(true);      }      else      {          mSnapshotCtrl->setEnabled(false); +        mSetCurrentLocationButton->setVisible(false);      }  } @@ -621,6 +625,7 @@ bool LLPanelProfilePick::postBuild()      mSaveButton = getChild<LLButton>("save_changes_btn");      mCreateButton = getChild<LLButton>("create_changes_btn");      mCancelButton = getChild<LLButton>("cancel_changes_btn"); +    mSetCurrentLocationButton = getChild<LLButton>("set_to_curr_location_btn");      mSnapshotCtrl = getChild<LLTextureCtrl>("pick_snapshot");      mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelProfilePick::onSnapshotChanged, this)); @@ -633,6 +638,7 @@ bool LLPanelProfilePick::postBuild()      mSaveButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSave, this));      mCreateButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSave, this));      mCancelButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickCancel, this)); +    mSetCurrentLocationButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSetLocation, this));      mPickName->setKeystrokeCallback(boost::bind(&LLPanelProfilePick::onPickChanged, this, _1), NULL);      mPickName->setEnabled(false); @@ -811,6 +817,32 @@ bool LLPanelProfilePick::isDirty() const      return false;  } +void LLPanelProfilePick::onClickSetLocation() +{ +    // Save location for later use. +    setPosGlobal(gAgent.getPositionGlobal()); + +    std::string parcel_name, region_name; + +    LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); +    if (parcel) +    { +        mParcelId = parcel->getID(); +        parcel_name = parcel->getName(); +    } + +    LLViewerRegion* region = gAgent.getRegion(); +    if (region) +    { +        region_name = region->getName(); +    } + +    setPickLocation(createLocationText(getLocationNotice(), parcel_name, region_name, getPosGlobal())); + +    mLocationChanged = true; +    enableSaveButton(true); +} +  void LLPanelProfilePick::onClickSave()  {      if (mRegionCallbackConnection.connected()) @@ -821,6 +853,10 @@ void LLPanelProfilePick::onClickSave()      {          mParcelCallbackConnection.disconnect();      } +    if (mLocationChanged) +    { +        onClickSetLocation(); +    }      sendUpdate();      mLocationChanged = false; @@ -871,6 +907,12 @@ void LLPanelProfilePick::processParcelInfo(const LLParcelData& parcel_data)      }  } +void LLPanelProfilePick::addLocationChangedCallbacks() +{ +    mRegionCallbackConnection = gAgent.addRegionChangedCallback([this]() { onClickSetLocation(); }); +    mParcelCallbackConnection = gAgent.addParcelChangedCallback([this]() { onClickSetLocation(); }); +} +  void LLPanelProfilePick::setParcelID(const LLUUID& parcel_id)  {      if (mParcelId != parcel_id) diff --git a/indra/newview/llpanelprofilepicks.h b/indra/newview/llpanelprofilepicks.h index b4d3eb010e..847ac57cea 100644 --- a/indra/newview/llpanelprofilepicks.h +++ b/indra/newview/llpanelprofilepicks.h @@ -141,6 +141,8 @@ public:      LLUUID getParcelID() const { return mParcelId; }      void setErrorStatus(S32 status, const std::string& reason) override {}; +    void addLocationChangedCallbacks(); +    protected:      /** @@ -203,6 +205,11 @@ public:      void resetDirty() override;      /** +     * Callback for "Set Location" button click +     */ +    void onClickSetLocation(); + +    /**       * Callback for "Save" and "Create" button click       */      void onClickSave(); @@ -224,6 +231,7 @@ protected:      LLTextureCtrl*      mSnapshotCtrl;      LLLineEditor*       mPickName;      LLTextEditor*       mPickDescription; +    LLButton*           mSetCurrentLocationButton;      LLButton*           mSaveButton;      LLButton*           mCreateButton;      LLButton*           mCancelButton; @@ -241,7 +249,7 @@ protected:      bool mLocationChanged;      bool mNewPick; -    bool                mIsEditing; +    bool mIsEditing;      void onDescriptionFocusReceived();  }; diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp index 910509928d..7f5076bd56 100644 --- a/indra/newview/llreflectionmap.cpp +++ b/indra/newview/llreflectionmap.cpp @@ -177,7 +177,7 @@ void LLReflectionMap::autoAdjustOrigin()          mPriority = 1;          mOrigin.load3(mViewerObject->getPositionAgent().mV); -        if (mViewerObject->getVolume() && ((LLVOVolume*)mViewerObject)->getReflectionProbeIsBox()) +        if (mViewerObject->getVolume() && ((LLVOVolume*)mViewerObject.get())->getReflectionProbeIsBox())          {              LLVector3 s = mViewerObject->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f));              mRadius = s.magVec(); diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h index d20bba7059..a818793550 100644 --- a/indra/newview/llreflectionmap.h +++ b/indra/newview/llreflectionmap.h @@ -124,7 +124,7 @@ public:      LLSpatialGroup* mGroup = nullptr;      // viewer object this probe is tracking (if any) -    LLViewerObject* mViewerObject = nullptr; +    LLPointer<LLViewerObject> mViewerObject = nullptr;      // what priority should this probe have (higher is higher priority)      // currently only 0 or 1 diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp index f2abc7b8b7..3391b7adf7 100644 --- a/indra/newview/llreflectionmapmanager.cpp +++ b/indra/newview/llreflectionmapmanager.cpp @@ -1146,7 +1146,7 @@ void LLReflectionMapManager::updateUniforms()          {              if (refmap->mViewerObject && refmap->mViewerObject->getVolume())              { // have active manual probes live-track the object they're associated with -                LLVOVolume* vobj = (LLVOVolume*)refmap->mViewerObject; +                LLVOVolume* vobj = (LLVOVolume*)refmap->mViewerObject.get();                  refmap->mOrigin.load3(vobj->getPositionAgent().mV); diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp index afa4686d52..223fc2a8f5 100644 --- a/indra/newview/llskinningutil.cpp +++ b/indra/newview/llskinningutil.cpp @@ -135,6 +135,12 @@ void LLSkinningUtil::initSkinningMatrixPalette(      initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar); +    if (skin->mInvBindMatrix.size() < count ) +    { +        // faulty model? mInvBindMatrix.size() should have matched mJointNames.size() +        return; +    } +      LLMatrix4a world[LL_CHARACTER_MAX_ANIMATED_JOINTS];      for (S32 j = 0; j < count; ++j) @@ -354,7 +360,8 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a                              {                                  rig_info_tab[joint_num].setIsRiggedTo(true); -                                const LLMatrix4a& mat = skin->mBindPoseMatrix[joint_index]; +                                size_t bind_poses_size = skin->mBindPoseMatrix.size(); +                                const LLMatrix4a& mat = bind_poses_size > joint_index ? skin->mBindPoseMatrix[joint_index] : LLMatrix4a::identity();                                  LLVector4a pos_joint_space;                                  mat.affineTransform(pos, pos_joint_space); diff --git a/indra/newview/llsky.h b/indra/newview/llsky.h index 32599dcee2..d06f181357 100644 --- a/indra/newview/llsky.h +++ b/indra/newview/llsky.h @@ -28,7 +28,6 @@  #define LL_LLSKY_H  #include "llmath.h" -//#include "vmath.h"  #include "v3math.h"  #include "v4math.h"  #include "v4color.h" diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 4956c188fb..b49c0119ed 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -1026,6 +1026,10 @@ void LLLocalSpeakerMgr::updateSpeakerList()      uuid_vec_t avatar_ids;      std::vector<LLVector3d> positions;      LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS); +#ifdef LL_DISCORD +    if (gSavedSettings.getBOOL("EnableDiscord")) +        LLAppViewer::updateDiscordPartyCurrentSize((S32)avatar_ids.size()); +#endif      for(U32 i=0; i<avatar_ids.size(); i++)      {          setSpeaker(avatar_ids[i]); diff --git a/indra/newview/llsprite.h b/indra/newview/llsprite.h index 44439bd30c..d6e8e37ec9 100644 --- a/indra/newview/llsprite.h +++ b/indra/newview/llsprite.h @@ -27,8 +27,6 @@  #ifndef LL_LLSPRITE_H  #define LL_LLSPRITE_H -////#include "vmath.h" -//#include "llmath.h"  #include "v3math.h"  #include "v4math.h"  #include "v4color.h" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index cc4f49c0b4..2409b71f00 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -724,6 +724,10 @@ bool idle_startup()              LL_WARNS("AppInit") << "Unreliable timers detected (may be bad PCI chipset)!!" << LL_ENDL;          } +#ifdef LL_DISCORD +        LLAppViewer::initDiscordSocial(); +#endif +          //          // Log on to system          // @@ -2103,9 +2107,6 @@ bool idle_startup()          do_startup_frame(); -        // We're successfully logged in. -        gSavedSettings.setBOOL("FirstLoginThisInstall", false); -          LLFloaterReg::showInitialVisibleInstances();          LLFloaterGridStatus::getInstance()->startGridStatusTimer(); @@ -2451,6 +2452,27 @@ bool idle_startup()          LLPerfStats::StatsRecorder::setAutotuneInit(); +        // Display Avatar Welcome Pack the first time a user logs in +        // (or clears their settings....) +        if (gSavedSettings.getBOOL("FirstLoginThisInstall")) +        { +            LLFloater* avatar_welcome_pack_floater = LLFloaterReg::findInstance("avatar_welcome_pack"); +            if (avatar_welcome_pack_floater != nullptr) +            { +                // There is a (very - 1 in ~50 times) hard to repro bug where the login +                // page is not hidden when the AWP floater is presented. This (agressive) +                // approach to always close it seems like the best fix for now. +                LLPanelLogin::closePanel(); + +                avatar_welcome_pack_floater->setVisible(true); +            } +        } + +        //// We're successfully logged in. +        // 2025-06 Moved lower down in the state machine so the Avatar Welcome Pack +        // floater display can be triggered correctly. +        gSavedSettings.setBOOL("FirstLoginThisInstall", false); +          return true;      } diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 8aa2058ae1..bb93e2e79e 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -114,6 +114,8 @@ LLStatusBar::LLStatusBar(const LLRect& rect)      mBtnVolume(NULL),      mBoxBalance(NULL),      mBalance(0), +    mBalanceClicked(false), +    mObscureBalance(false),      mHealth(100),      mSquareMetersCredit(0),      mSquareMetersCommitted(0), @@ -125,20 +127,11 @@ LLStatusBar::LLStatusBar(const LLRect& rect)      // status bar can possible overlay menus?      setMouseOpaque(false); -    mBalanceTimer = new LLFrameTimer(); -    mHealthTimer = new LLFrameTimer(); -      buildFromFile("panel_status_bar.xml");  }  LLStatusBar::~LLStatusBar()  { -    delete mBalanceTimer; -    mBalanceTimer = NULL; - -    delete mHealthTimer; -    mHealthTimer = NULL; -      // LLView destructor cleans up children  } @@ -171,7 +164,8 @@ bool LLStatusBar::postBuild()      getChild<LLUICtrl>("goShop")->setCommitCallback(boost::bind(&LLWeb::loadURL, gSavedSettings.getString("MarketplaceURL"), LLStringUtil::null, LLStringUtil::null));      mBoxBalance = getChild<LLTextBox>("balance"); -    mBoxBalance->setClickedCallback( &LLStatusBar::onClickBalance, this ); +    mBoxBalance->setClickedCallback(&LLStatusBar::onClickRefreshBalance, this); +    mBoxBalance->setDoubleClickCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onClickToggleBalance(); });      mIconPresetsCamera = getChild<LLIconCtrl>( "presets_icon_camera" );      mIconPresetsCamera->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterPresetsCamera, this)); @@ -191,12 +185,14 @@ bool LLStatusBar::postBuild()      gSavedSettings.getControl("MuteAudio")->getSignal()->connect(boost::bind(&LLStatusBar::onVolumeChanged, this, _2));      gSavedSettings.getControl("EnableVoiceChat")->getSignal()->connect(boost::bind(&LLStatusBar::onVoiceChanged, this, _2)); +    gSavedSettings.getControl("ObscureBalanceInStatusBar")->getSignal()->connect(boost::bind(&LLStatusBar::onObscureBalanceChanged, this, _2));      if (!gSavedSettings.getBOOL("EnableVoiceChat") && LLAppViewer::instance()->isSecondInstance())      {          // Indicate that second instance started without sound          mBtnVolume->setImageUnselected(LLUI::getUIImage("VoiceMute_Off"));      } +    mObscureBalance = gSavedSettings.getBOOL("ObscureBalanceInStatusBar");      // Adding Net Stat Graph      S32 x = getRect().getWidth() - 2; @@ -319,6 +315,12 @@ void LLStatusBar::refresh()          mTextTime->setToolTip (dtStr);      } +    if (mBalanceClicked && mBalanceClickTimer.getElapsedTimeF32() > 1.f) +    { +        mBalanceClicked = false; +        sendMoneyBalanceRequest(); +    } +      LLRect r;      const S32 MENU_RIGHT = gMenuBarView->getRightmostMenuEdge(); @@ -384,9 +386,17 @@ void LLStatusBar::setBalance(S32 balance)      std::string money_str = LLResMgr::getInstance()->getMonetaryString( balance );      LLStringUtil::format_map_t string_args; -    string_args["[AMT]"] = llformat("%s", money_str.c_str()); +    if (mObscureBalance) +    { +        string_args["[AMT]"] = "****"; +    } +    else +    { +        string_args["[AMT]"] = llformat("%s", money_str.c_str()); +    }      std::string label_str = getString("buycurrencylabel", string_args);      mBoxBalance->setValue(label_str); +    mBoxBalance->setToolTipArg(LLStringExplicit("[AMT]"), llformat("%s", money_str.c_str()));      updateBalancePanelPosition(); @@ -406,8 +416,6 @@ void LLStatusBar::setBalance(S32 balance)      if( balance != mBalance )      { -        mBalanceTimer->reset(); -        mBalanceTimer->setTimerExpirySec( ICON_TIMER_EXPIRY );          mBalance = balance;      }  } @@ -459,9 +467,6 @@ void LLStatusBar::setHealth(S32 health)                  }              }          } - -        mHealthTimer->reset(); -        mHealthTimer->setTimerExpirySec( ICON_TIMER_EXPIRY );      }      mHealth = health; @@ -621,13 +626,27 @@ static void onClickVolume(void* data)  }  //static -void LLStatusBar::onClickBalance(void* ) +void LLStatusBar::onClickRefreshBalance(void* data)  { -    // Force a balance request message: -    LLStatusBar::sendMoneyBalanceRequest(); +    LLStatusBar* status_bar = (LLStatusBar*)data; + +    if (!status_bar->mBalanceClicked) +    { +        // Schedule a balance request message: +        status_bar->mBalanceClicked = true; +        status_bar->mBalanceClickTimer.reset(); +    }      // The refresh of the display (call to setBalance()) will be done by process_money_balance_reply()  } +void LLStatusBar::onClickToggleBalance() +{ +    mObscureBalance = !mObscureBalance; +    gSavedSettings.setBOOL("ObscureBalanceInStatusBar", mObscureBalance); +    setBalance(mBalance); +    mBalanceClicked = false; // supress click +} +  //static  void LLStatusBar::onClickMediaToggle(void* data)  { @@ -657,6 +676,12 @@ void LLStatusBar::onVoiceChanged(const LLSD& newvalue)      refresh();  } +void LLStatusBar::onObscureBalanceChanged(const LLSD& newvalue) +{ +    mObscureBalance = newvalue.asBoolean(); +    setBalance(mBalance); +} +  void LLStatusBar::onUpdateFilterTerm()  {      LLWString searchValue = utf8str_to_wstring( mFilterEdit->getValue() ); diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h index 45cbda0ef1..a8fc621ff8 100644 --- a/indra/newview/llstatusbar.h +++ b/indra/newview/llstatusbar.h @@ -72,7 +72,8 @@ public:      void        debitBalance(S32 debit);      void        creditBalance(S32 credit); -    // Request the latest currency balance from the server +    // Request the latest currency balance from the server. +    // Reply at process_money_balance_reply()      static void sendMoneyBalanceRequest();      void        setHealth(S32 percent); @@ -102,6 +103,7 @@ private:      void onClickBuyCurrency();      void onVolumeChanged(const LLSD& newvalue);      void onVoiceChanged(const LLSD& newvalue); +    void onObscureBalanceChanged(const LLSD& newvalue);      void onMouseEnterPresetsCamera();      void onMouseEnterPresets(); @@ -109,7 +111,8 @@ private:      void onMouseEnterNearbyMedia();      static void onClickMediaToggle(void* data); -    static void onClickBalance(void* data); +    static void onClickRefreshBalance(void* data); +    void onClickToggleBalance();      LLSearchEditor *mFilterEdit;      LLPanel *mSearchPanel; @@ -135,11 +138,12 @@ private:      LLFrameTimer    mClockUpdateTimer;      S32             mBalance; +    bool            mBalanceClicked; +    bool            mObscureBalance; +    LLTimer         mBalanceClickTimer;      S32             mHealth;      S32             mSquareMetersCredit;      S32             mSquareMetersCommitted; -    LLFrameTimer*   mBalanceTimer; -    LLFrameTimer*   mHealthTimer;      LLPanelPresetsCameraPulldown* mPanelPresetsCameraPulldown;      LLPanelPresetsPulldown* mPanelPresetsPulldown;      LLPanelVolumePulldown* mPanelVolumePulldown; diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h index 10a104730b..fc72ab7db7 100644 --- a/indra/newview/llsurface.h +++ b/indra/newview/llsurface.h @@ -27,7 +27,6 @@  #ifndef LL_LLSURFACE_H  #define LL_LLSURFACE_H -//#include "vmath.h"  #include "v3math.h"  #include "v3dmath.h"  #include "v4math.h" diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 8cdc2e94f4..0fd9faab35 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -72,6 +72,7 @@  #include "llweb.h"  #include "pipeline.h"   // setHighlightObject  #include "lluiusage.h" +#include "llcallingcard.h"  extern bool gDebugClicks; @@ -1501,6 +1502,136 @@ static void handle_click_action_play()      }  } +bool LLToolPie::shouldAllowFirstMediaInteraction(const LLPickInfo& pick, bool moap_flag) +{ +    // Early failure cases +    if(!pick.getObject()) +    { +        LL_WARNS() << "pick.getObject() is NULL" << LL_ENDL; +        return false; +    } + +    static LLCachedControl<S32> FirstClickPref(gSavedSettings, "MediaFirstClickInteract", 1); + +    // Special / early-exit cases first, then checks get more complex and needy as we go down +    // Feature disabled +    if(FirstClickPref == MEDIA_FIRST_CLICK_NONE) +    { +        LL_DEBUGS_ONCE() << "FirstClickPref == MEDIA_FIRST_CLICK_NONE" << LL_ENDL; +        return false; +    } +    // Every check beyond this point requires PRIM_MEDIA_FIRST_CLICK_INTERACT to be TRUE +    if(!moap_flag && !(FirstClickPref & MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG)) +    { +        LL_DEBUGS_ONCE() << "PRIM_MEDIA_FIRST_CLICK_INTERACT not set" << LL_ENDL; +        return false; +    } +    // Any object with PRIM_MEDIA_FIRST_CLICK_INTERACT set to TRUE +    if((FirstClickPref & MEDIA_FIRST_CLICK_ANY) == MEDIA_FIRST_CLICK_ANY) +    { +        LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_ANY" << LL_ENDL; +        return true; +    } + +    // The following checks require some object information so we obtain that +    LLPointer<LLViewerObject> object = pick.getObject(); +    if(object.isNull()) +    { +        LL_WARNS() << "pick.getObject() is NULL" << LL_ENDL; +        return false; +    } + +    // HUD attachments +    if((FirstClickPref & MEDIA_FIRST_CLICK_HUD) && object->isHUDAttachment()) +    { +        LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_HUD" << LL_ENDL; +        return true; +    } + +    // Further object detail required beyond this point +    LLPermissions* perms = LLSelectMgr::getInstance()->getHoverNode()->mPermissions; +    if(perms == nullptr) +    { +        LL_WARNS() << "LLSelectMgr::getInstance()->getHoverNode()->mPermissions is NULL" << LL_ENDL; +        return false; +    } +    LLUUID owner_id = perms->getOwner(); +    LLUUID group_id = perms->getGroup(); +    if(owner_id.isNull() && group_id.isNull()) +    { +        LL_WARNS() << "Owner information was not reliably obtained" << LL_ENDL; +        return false; +    } + +    // Own objects +    if((FirstClickPref & MEDIA_FIRST_CLICK_OWN) && owner_id == gAgent.getID()) +    { +        LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_OWN" << LL_ENDL; +        return true; +    } + +    // Check if the object is owned by a friend of the agent +    if(FirstClickPref & MEDIA_FIRST_CLICK_FRIEND) +    { +        if(LLAvatarTracker::instance().isBuddy(owner_id)) +        { +            LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_FRIEND. id: " << owner_id << LL_ENDL; +            return true; +        } +    } + +    // Check for objects set to or owned by the active group +    if(FirstClickPref & MEDIA_FIRST_CLICK_GROUP) +    { +        if(gAgent.isInGroup(group_id) || gAgent.isInGroup(owner_id)) +        { +            LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_GROUP. group_id:" << group_id << ", owner_id: " << owner_id << LL_ENDL; +            return true; +        } +    } + +    // This check ensures that the following conditions are met: +    // 1. The object is located in the same parcel as the agent. +    // 2. One of the following is true: +    //    a. The object is owned by the same group as the parcel. +    //    b. The object is set to the same group as the parcel. +    //    c. The object is owned by the same owner as the parcel. +    // Conditions 2a and 2b are mutually exclusive, our check is the same for both. +    if(FirstClickPref & MEDIA_FIRST_CLICK_LAND) +    { +        LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); +        if(parcel == nullptr) +        { +            LL_WARNS() << "LLViewerParcelMgr::getInstance()->getAgentParcel() is NULL" << LL_ENDL; +            return false; +        } + +        // Same parcel as the agent only +        if(!LLViewerParcelMgr::getInstance()->inAgentParcel(object->getPositionGlobal())) +        { +            LL_WARNS_ONCE() << "Object is not in the same parcel as the agent" << LL_ENDL; +            return false; +        } + +        LLUUID parcel_owner = parcel->getOwnerID(); +        LLUUID parcel_group = parcel->getGroupID(); + +        // The parcel owner and group can't both be null +        if(parcel_owner.isNull() && parcel_group.isNull()) +        { +            LL_WARNS() << "Parcel owner and group are both null" << LL_ENDL; +            return false; +        } + +        if(owner_id == parcel_owner || group_id == parcel_group) +        { +            LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_LAND. Parcel owner: " << parcel_owner << ", group_id:" << group_id << ", owner_id: " << owner_id << LL_ENDL; +            return true; +        } +    } +    return false; +} +  bool LLToolPie::handleMediaClick(const LLPickInfo& pick)  {      //FIXME: how do we handle object in different parcel than us? @@ -1535,6 +1666,16 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick)          {              // It's okay to give this a null impl              LLViewerMediaFocus::getInstance()->setFocusFace(pick.getObject(), pick.mObjectFace, media_impl, pick.mNormal); +            if (shouldAllowFirstMediaInteraction(pick, mep->getFirstClickInteract())) +            { +                if (media_impl.notNull()) +                { +                    media_impl->mouseDown(pick.mUVCoords, gKeyboard->currentMask(true)); +                    mMediaMouseCaptureID = mep->getMediaID(); +                    setMouseCapture(true); +                    return true; +                } +            }          }          else          { @@ -1647,7 +1788,7 @@ bool LLToolPie::handleMediaHover(const LLPickInfo& pick)              }              // If this is the focused media face, send mouse move events. -            if (LLViewerMediaFocus::getInstance()->isFocusedOnFace(objectp, pick.mObjectFace)) +            if (LLViewerMediaFocus::getInstance()->isFocusedOnFace(objectp, pick.mObjectFace) || (shouldAllowFirstMediaInteraction(pick, mep->getFirstClickInteract())))              {                  media_impl->mouseMove(pick.mUVCoords, gKeyboard->currentMask(true));                  gViewerWindow->setCursor(media_impl->getLastSetCursor()); diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index b3884a6bfc..ec54e0207d 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -89,6 +89,22 @@ private:      void showVisualContextMenuEffect();      ECursorType cursorFromObject(LLViewerObject* object); +    enum MediaFirstClickTypes +    { +        MEDIA_FIRST_CLICK_NONE       = 0,         // Special case: Feature is disabled +        MEDIA_FIRST_CLICK_HUD        = 1 << 0,    // 0b00000001 (1) +        MEDIA_FIRST_CLICK_OWN        = 1 << 1,    // 0b00000010 (2) +        MEDIA_FIRST_CLICK_FRIEND     = 1 << 2,    // 0b00000100 (4) +        MEDIA_FIRST_CLICK_GROUP      = 1 << 3,    // 0b00001000 (8) +        MEDIA_FIRST_CLICK_LAND       = 1 << 4,    // 0b00010000 (16) + +        // Covers any object with PRIM_MEDIA_FIRST_CLICK_INTERACT (combines all previous flags) +        MEDIA_FIRST_CLICK_ANY        = (1 << 15) - 1, // 0b0111111111111111 (32767) + +        // Covers all media regardless of other rules or PRIM_MEDIA_FIRST_CLICK_INTERACT +        MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG = 1 << 15  // 0b10000000000000000 (32768) +    }; +    bool shouldAllowFirstMediaInteraction(const LLPickInfo& info, bool moap_flag);      bool handleMediaClick(const LLPickInfo& info);      bool handleMediaDblClick(const LLPickInfo& info);      bool handleMediaHover(const LLPickInfo& info); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 27718782ee..71830e0948 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -100,13 +100,13 @@  extern LLPointer<LLViewerTexture> gStartTexture;  extern bool gShiftFrame; -LLPointer<LLViewerTexture> gDisconnectedImagep = NULL; +LLPointer<LLViewerTexture> gDisconnectedImagep = nullptr;  // used to toggle renderer back on after teleport  bool         gTeleportDisplay = false;  LLFrameTimer gTeleportDisplayTimer;  LLFrameTimer gTeleportArrivalTimer; -const F32       RESTORE_GL_TIME = 5.f;  // Wait this long while reloading textures before we raise the curtain +constexpr F32 RESTORE_GL_TIME = 5.f;  // Wait this long while reloading textures before we raise the curtain  bool gForceRenderLandFence = false;  bool gDisplaySwapBuffers = false; @@ -120,9 +120,9 @@ bool gSnapshotNoPost = false;  bool gShaderProfileFrame = false;  // This is how long the sim will try to teleport you before giving up. -const F32 TELEPORT_EXPIRY = 15.0f; +constexpr F32 TELEPORT_EXPIRY = 15.0f;  // Additional time (in seconds) to wait per attachment -const F32 TELEPORT_EXPIRY_PER_ATTACHMENT = 3.f; +constexpr F32 TELEPORT_EXPIRY_PER_ATTACHMENT = 3.f;  U32 gRecentFrameCount = 0; // number of 'recent' frames  LLFrameTimer gRecentFPSTime; @@ -130,8 +130,6 @@ LLFrameTimer gRecentMemoryTime;  LLFrameTimer gAssetStorageLogTime;  // Rendering stuff -void pre_show_depth_buffer(); -void post_show_depth_buffer();  void render_ui(F32 zoom_factor = 1.f, int subfield = 0);  void swap();  void render_hud_attachments(); @@ -212,7 +210,8 @@ void display_update_camera()      F32 final_far = gAgentCamera.mDrawDistance;      if (gCubeSnapshot)      { -        final_far = gSavedSettings.getF32("RenderReflectionProbeDrawDistance"); +        static LLCachedControl<F32> reflection_probe_draw_distance(gSavedSettings, "RenderReflectionProbeDrawDistance", 64.f); +        final_far = reflection_probe_draw_distance();      }      else if (CAMERA_MODE_CUSTOMIZE_AVATAR == gAgentCamera.getCameraMode())      { @@ -237,7 +236,7 @@ void display_update_camera()  void display_stats()  {      LL_PROFILE_ZONE_SCOPED; -    const F32 FPS_LOG_FREQUENCY = 10.f; +    constexpr F32 FPS_LOG_FREQUENCY = 10.f;      if (gRecentFPSTime.getElapsedTimeF32() >= FPS_LOG_FREQUENCY)      {          LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("DS - FPS"); @@ -246,7 +245,7 @@ void display_stats()          gRecentFrameCount = 0;          gRecentFPSTime.reset();      } -    F32 mem_log_freq = gSavedSettings.getF32("MemoryLogFrequency"); +    static LLCachedControl<F32> mem_log_freq(gSavedSettings, "MemoryLogFrequency", 600.f);      if (mem_log_freq > 0.f && gRecentMemoryTime.getElapsedTimeF32() >= mem_log_freq)      {          LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("DS - Memory"); @@ -256,7 +255,7 @@ void display_stats()          LLMemory::logMemoryInfo(true) ;          gRecentMemoryTime.reset();      } -    const F32 ASSET_STORAGE_LOG_FREQUENCY = 60.f; +    constexpr F32 ASSET_STORAGE_LOG_FREQUENCY = 60.f;      if (gAssetStorageLogTime.getElapsedTimeF32() >= ASSET_STORAGE_LOG_FREQUENCY)      {          LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("DS - Asset Storage"); @@ -572,8 +571,10 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)      LLImageGL::updateStats(gFrameTimeSeconds); -    LLVOAvatar::sRenderName = gSavedSettings.getS32("AvatarNameTagMode"); -    LLVOAvatar::sRenderGroupTitles = (gSavedSettings.getBOOL("NameTagShowGroupTitles") && gSavedSettings.getS32("AvatarNameTagMode")); +    static LLCachedControl<S32> avatar_name_tag_mode(gSavedSettings, "AvatarNameTagMode", 1); +    static LLCachedControl<bool> name_tag_show_group_titles(gSavedSettings, "NameTagShowGroupTitles", true); +    LLVOAvatar::sRenderName = avatar_name_tag_mode; +    LLVOAvatar::sRenderGroupTitles = name_tag_show_group_titles && avatar_name_tag_mode > 0;      gPipeline.mBackfaceCull = true;      gFrameCount++; @@ -796,7 +797,7 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)              }              gGL.setColorMask(true, true); -            glClearColor(0,0,0,0); +            glClearColor(0.f, 0.f, 0.f, 0.f);              LLGLState::checkStates(); @@ -964,7 +965,7 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)          gPipeline.mRT->deferredScreen.bindTarget();          if (gUseWireframe)          { -            F32 g = 0.5f; +            constexpr F32 g = 0.5f;              glClearColor(g, g, g, 1.f);          }          else @@ -983,11 +984,12 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)              LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("display - 5")              LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; -            if (gSavedSettings.getBOOL("RenderDepthPrePass")) +            static LLCachedControl<bool> render_depth_pre_pass(gSavedSettings, "RenderDepthPrePass", false); +            if (render_depth_pre_pass)              {                  gGL.setColorMask(false, false); -                static const U32 types[] = { +                constexpr U32 types[] = {                      LLRenderPass::PASS_SIMPLE,                      LLRenderPass::PASS_FULLBRIGHT,                      LLRenderPass::PASS_SHINY @@ -1201,7 +1203,7 @@ void display_cube_face()      gGL.setColorMask(true, true); -    glClearColor(0, 0, 0, 0); +    glClearColor(0.f, 0.f, 0.f, 0.f);      gPipeline.generateSunShadow(*LLViewerCamera::getInstance());      glClear(GL_DEPTH_BUFFER_BIT); // | GL_STENCIL_BUFFER_BIT); @@ -1237,7 +1239,7 @@ void display_cube_face()      }      else      { -        glClearColor(1, 0, 1, 1); +        glClearColor(1.f, 0.f, 1.f, 1.f);      }      gPipeline.mRT->deferredScreen.clear(); @@ -1278,11 +1280,12 @@ void render_hud_attachments()      {          LLPipeline::sRenderingHUDs = true;          LLCamera hud_cam = *LLViewerCamera::getInstance(); -        hud_cam.setOrigin(-1.f,0,0); -        hud_cam.setAxes(LLVector3(1,0,0), LLVector3(0,1,0), LLVector3(0,0,1)); +        hud_cam.setOrigin(-1.f, 0.f, 0.f); +        hud_cam.setAxes(LLVector3(1.f, 0.f, 0.f), LLVector3(0.f, 1.f, 0.f), LLVector3(0.f, 0.f, 1.f));          LLViewerCamera::updateFrustumPlanes(hud_cam, true); -        bool render_particles = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) && gSavedSettings.getBOOL("RenderHUDParticles"); +        static LLCachedControl<bool> render_hud_particles(gSavedSettings, "RenderHUDParticles", false); +        bool render_particles = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) && render_hud_particles;          //only render hud objects          gPipeline.pushRenderTypeMask(); @@ -1642,10 +1645,11 @@ void render_ui_3d()      stop_glerror();      gUIProgram.bind(); -    gGL.color4f(1, 1, 1, 1); +    gGL.color4f(1.f, 1.f, 1.f, 1.f);      // Coordinate axes -    if (gSavedSettings.getBOOL("ShowAxes")) +    static LLCachedControl<bool> show_axes(gSavedSettings, "ShowAxes"); +    if (show_axes())      {          draw_axes();      } @@ -1705,7 +1709,7 @@ void render_ui_2d()          gGL.pushMatrix();          S32 half_width = (gViewerWindow->getWorldViewWidthScaled() / 2);          S32 half_height = (gViewerWindow->getWorldViewHeightScaled() / 2); -        gGL.scalef(LLUI::getScaleFactor().mV[0], LLUI::getScaleFactor().mV[1], 1.f); +        gGL.scalef(LLUI::getScaleFactor().mV[VX], LLUI::getScaleFactor().mV[VY], 1.f);          gGL.translatef((F32)half_width, (F32)half_height, 0.f);          F32 zoom = gAgentCamera.mHUDCurZoom;          gGL.scalef(zoom,zoom,1.f); @@ -1727,7 +1731,7 @@ void render_ui_2d()              gPipeline.mUIScreen.bindTarget();              gGL.setColorMask(true, true);              { -                static const S32 pad = 8; +                constexpr S32 pad = 8;                  LLView::sDirtyRect.mLeft -= pad;                  LLView::sDirtyRect.mRight += pad; @@ -1780,8 +1784,6 @@ void render_ui_2d()          gViewerWindow->draw();      } - -      // reset current origin for font rendering, in case of tiling render      LLFontGL::sCurOrigin.set(0, 0);  } @@ -1790,7 +1792,7 @@ void render_disconnected_background()  {      gUIProgram.bind(); -    gGL.color4f(1,1,1,1); +    gGL.color4f(1.f, 1.f, 1.f, 1.f);      if (!gDisconnectedImagep && gDisconnected)      {          LL_INFOS() << "Loading last bitmap..." << LL_ENDL; @@ -1830,7 +1832,7 @@ void render_disconnected_background()          raw->expandToPowerOfTwo(); -        gDisconnectedImagep = LLViewerTextureManager::getLocalTexture(raw.get(), false ); +        gDisconnectedImagep = LLViewerTextureManager::getLocalTexture(raw.get(), false);          gStartTexture = gDisconnectedImagep;          gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);      } @@ -1865,6 +1867,5 @@ void render_disconnected_background()  void display_cleanup()  { -    gDisconnectedImagep = NULL; +    gDisconnectedImagep = nullptr;  } - diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 4d9c2f3281..4b3af6d7e8 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -38,8 +38,8 @@  #include "llfloateraddpaymentmethod.h"  #include "llfloaterauction.h"  #include "llfloaterautoreplacesettings.h" -#include "llfloateravatar.h"  #include "llfloateravatarpicker.h" +#include "llfloateravatarwelcomepack.h"  #include "llfloateravatarrendersettings.h"  #include "llfloateravatartextures.h"  #include "llfloaterbanduration.h" @@ -331,8 +331,8 @@ void LLViewerFloaterReg::registerFloaters()      LLFloaterReg::add("appearance", "floater_my_appearance.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);      LLFloaterReg::add("associate_listing", "floater_associate_listing.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAssociateListing>);      LLFloaterReg::add("auction", "floater_auction.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAuction>); -    LLFloaterReg::add("avatar", "floater_avatar.xml",  (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatar>);      LLFloaterReg::add("avatar_picker", "floater_avatar_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarPicker>); +    LLFloaterReg::add("avatar_welcome_pack", "floater_avatar_welcome_pack.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarWelcomePack>);      LLFloaterReg::add("avatar_render_settings", "floater_avatar_render_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarRenderSettings>);      LLFloaterReg::add("avatar_textures", "floater_avatar_textures.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterAvatarTextures>); diff --git a/indra/newview/llviewermedia_streamingaudio.cpp b/indra/newview/llviewermedia_streamingaudio.cpp index b68ffbe1a2..4a1c433f8e 100644 --- a/indra/newview/llviewermedia_streamingaudio.cpp +++ b/indra/newview/llviewermedia_streamingaudio.cpp @@ -60,13 +60,26 @@ void LLStreamingAudio_MediaPlugins::start(const std::string& url)      if(!mMediaPlugin)          return; -    if (!url.empty()) { +    if (!url.empty()) +    {          LL_INFOS() << "Starting internet stream: " << url << LL_ENDL; -        mURL = url; -        mMediaPlugin->loadURI ( url ); + +        mURL = url; // keep original url here for comparison purposes +        std::string snt_url = url; +        LLStringUtil::trim(snt_url); +        size_t pos = snt_url.find(' '); +        if (pos != std::string::npos) +        { +            // fmod permited having names after the url and people were using it. +            // People label their streams this way, ignore the 'label'. +            snt_url = snt_url.substr(0, pos); +        } +        mMediaPlugin->loadURI(snt_url);          mMediaPlugin->start();          LL_INFOS() << "Playing stream..." << LL_ENDL; -    } else { +    } +    else +    {          LL_INFOS() << "setting stream to NULL"<< LL_ENDL;          mURL.clear();          mMediaPlugin->stop(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 6aa20a0019..d66da27bc7 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -290,6 +290,7 @@ void force_error_coroutine_crash();  void force_error_coroprocedure_crash();  void force_error_work_queue_crash();  void force_error_thread_crash(); +void force_exception_thread_crash();  void handle_force_delete();  void print_object_info(); @@ -2663,6 +2664,15 @@ class LLAdvancedForceErrorThreadCrash : public view_listener_t      }  }; +class LLAdvancedForceExceptionThreadCrash : public view_listener_t +{ +    bool handleEvent(const LLSD& userdata) +    { +        force_exception_thread_crash(); +        return true; +    } +}; +  class LLAdvancedForceErrorDisconnectViewer : public view_listener_t  {      bool handleEvent(const LLSD& userdata) @@ -8791,6 +8801,11 @@ void force_error_thread_crash()      LLAppViewer::instance()->forceErrorThreadCrash();  } +void force_exception_thread_crash() +{ +    LLAppViewer::instance()->forceExceptionThreadCrash(); +} +  class LLToolsUseSelectionForGrid : public view_listener_t  {      bool handleEvent(const LLSD& userdata) @@ -9995,6 +10010,7 @@ void initialize_menus()      view_listener_t::addMenu(new LLAdvancedForceErrorCoroprocedureCrash(), "Advanced.ForceErrorCoroprocedureCrash");      view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");      view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash"); +    view_listener_t::addMenu(new LLAdvancedForceExceptionThreadCrash(), "Advanced.ForceExceptionThreadCrash");      view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer");      // Advanced (toplevel) diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 15f8a6e6f1..3695478061 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -69,6 +69,7 @@  #include "llviewerassetupload.h"  // linden libraries +#include "llfilesystem.h"  #include "llnotificationsutil.h"  #include "llsdserialize.h"  #include "llsdutil.h" @@ -94,7 +95,7 @@ class LLFileEnableUploadModel : public view_listener_t      bool handleEvent(const LLSD& userdata)      {          LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::findInstance("upload_model"); -        if (fmp && fmp->isModelLoading()) +        if (fmp && !fmp->isDead() && fmp->isModelLoading())          {              return false;          } @@ -550,16 +551,9 @@ void do_bulk_upload(std::vector<std::string> filenames, bool allow_2k)          if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec))          {              bool resource_upload = false; -            if (asset_type == LLAssetType::AT_TEXTURE && allow_2k) +            if (asset_type == LLAssetType::AT_TEXTURE)              { -                LLPointer<LLImageFormatted> image_frmted = LLImageFormatted::createFromType(codec); -                if (gDirUtilp->fileExists(filename) && image_frmted && image_frmted->load(filename)) -                { -                    S32 biased_width = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getWidth(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); -                    S32 biased_height = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getHeight(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); -                    expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(biased_width, biased_height); -                    resource_upload = true; -                } +                resource_upload = true;              }              else if (LLAgentBenefitsMgr::current().findUploadCost(asset_type, expected_upload_cost))              { @@ -568,23 +562,115 @@ void do_bulk_upload(std::vector<std::string> filenames, bool allow_2k)              if (resource_upload)              { -                LLNewFileResourceUploadInfo* info_p = new LLNewFileResourceUploadInfo( -                    filename, -                    asset_name, -                    asset_name, 0, -                    LLFolderType::FT_NONE, LLInventoryType::IT_NONE, -                    LLFloaterPerms::getNextOwnerPerms("Uploads"), -                    LLFloaterPerms::getGroupPerms("Uploads"), -                    LLFloaterPerms::getEveryonePerms("Uploads"), -                    expected_upload_cost); - -                if (!allow_2k) +                if (asset_type == LLAssetType::AT_TEXTURE)                  { -                    info_p->setMaxImageSize(1024); -                } -                LLResourceUploadInfo::ptr_t uploadInfo(info_p); +                    std::string exten = gDirUtilp->getExtension(filename); +                    U32         codec = LLImageBase::getCodecFromExtension(exten); + +                    // Load the image +                    LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec); +                    if (image.isNull()) +                    { +                        LL_WARNS() << "Failed to create image container for " << filename << LL_ENDL; +                        continue; +                    } +                    if (!image->load(filename)) +                    { +                        LL_WARNS() << "Failed to load image: " << filename << LL_ENDL; +                        continue; +                    } +                    // Decompress or expand it in a raw image structure +                    LLPointer<LLImageRaw> raw_image = new LLImageRaw; +                    if (!image->decode(raw_image, 0.0f)) +                    { +                        LL_WARNS() << "Failed to decode image: " << filename << LL_ENDL; +                        continue; +                    } +                    // Check the image constraints +                    if ((image->getComponents() != 3) && (image->getComponents() != 4)) +                    { +                        LL_WARNS() << "Attempted to upload a texture that has " << image->getComponents() +                                   << " components, but only 3 (RGB) or 4 (RGBA) are allowed." << LL_ENDL; +                        continue; +                    } +                    // Downscale images to fit the max_texture_dimensions_*, or 1024 if allow_2k is false +                    S32 max_width  = allow_2k ? gSavedSettings.getS32("max_texture_dimension_X") : 1024; +                    S32 max_height = allow_2k ? gSavedSettings.getS32("max_texture_dimension_Y") : 1024; + +                    S32 orig_width  = raw_image->getWidth(); +                    S32 orig_height = raw_image->getHeight(); + +                    if (orig_width > max_width || orig_height > max_height) +                    { +                        // Calculate scale factors +                        F32 width_scale  = (F32)max_width / (F32)orig_width; +                        F32 height_scale = (F32)max_height / (F32)orig_height; +                        F32 scale        = llmin(width_scale, height_scale); + +                        // Calculate new dimensions, preserving aspect ratio +                        S32 new_width  = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_width * scale), 4, max_width)); +                        S32 new_height = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_height * scale), 4, max_height)); + +                        if (!raw_image->scale(new_width, new_height)) +                        { +                            LL_WARNS() << "Failed to scale image from " << orig_width << "x" << orig_height << " to " << new_width << "x" +                                       << new_height << LL_ENDL; +                            continue; +                        } + +                        // Inform the resident about the resized image +                        LLSD subs; +                        subs["[ORIGINAL_WIDTH]"]  = orig_width; +                        subs["[ORIGINAL_HEIGHT]"] = orig_height; +                        subs["[NEW_WIDTH]"]       = new_width; +                        subs["[NEW_HEIGHT]"]      = new_height; +                        subs["[MAX_WIDTH]"]       = max_width; +                        subs["[MAX_HEIGHT]"]      = max_height; +                        LLNotificationsUtil::add("ImageUploadResized", subs); +                    } + +                    raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); + +                    LLTransactionID tid; +                    tid.generate(); +                    LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); -                upload_new_resource(uploadInfo); +                    LLPointer<LLImageJ2C> formatted = new LLImageJ2C; + +                    if (formatted->encode(raw_image, 0.0f)) +                    { +                        LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE); +                        fmt_file.write(formatted->getData(), formatted->getDataSize()); + +                        LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo( +                            tid, LLAssetType::AT_TEXTURE, +                            asset_name, +                            asset_name, 0, +                            LLFolderType::FT_NONE, LLInventoryType::IT_NONE, +                            LLFloaterPerms::getNextOwnerPerms("Uploads"), +                            LLFloaterPerms::getGroupPerms("Uploads"), +                            LLFloaterPerms::getEveryonePerms("Uploads"), +                            LLAgentBenefitsMgr::current().getTextureUploadCost(raw_image->getWidth(), raw_image->getHeight()) +                        )); + +                        upload_new_resource(assetUploadInfo); +                    } +                } +                else +                { +                    LLNewFileResourceUploadInfo* info_p = new LLNewFileResourceUploadInfo( +                        filename, +                        asset_name, +                        asset_name, 0, +                        LLFolderType::FT_NONE, LLInventoryType::IT_NONE, +                        LLFloaterPerms::getNextOwnerPerms("Uploads"), +                        LLFloaterPerms::getGroupPerms("Uploads"), +                        LLFloaterPerms::getEveryonePerms("Uploads"), +                        expected_upload_cost); +                    LLResourceUploadInfo::ptr_t uploadInfo(info_p); + +                    upload_new_resource(uploadInfo); +                }              }          } @@ -653,8 +739,31 @@ bool get_bulk_upload_expected_cost(                  LLPointer<LLImageFormatted> image_frmted = LLImageFormatted::createFromType(codec);                  if (gDirUtilp->fileExists(filename) && image_frmted && image_frmted->load(filename))                  { -                    S32 biased_width = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getWidth(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); -                    S32 biased_height = LLImageRaw::biasedDimToPowerOfTwo(image_frmted->getHeight(), LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); +                    S32 biased_width, biased_height; + +                    S32 max_width  = allow_2k ? gSavedSettings.getS32("max_texture_dimension_X") : 1024; +                    S32 max_height = allow_2k ? gSavedSettings.getS32("max_texture_dimension_Y") : 1024; + +                    S32 orig_width  = image_frmted->getWidth(); +                    S32 orig_height = image_frmted->getHeight(); + +                    if (orig_width > max_width || orig_height > max_height) +                    { +                        // Calculate scale factors +                        F32 width_scale  = (F32)max_width / (F32)orig_width; +                        F32 height_scale = (F32)max_height / (F32)orig_height; +                        F32 scale        = llmin(width_scale, height_scale); + +                        // Calculate new dimensions, preserving aspect ratio +                        biased_width = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_width * scale), 4, max_width)); +                        biased_height = LLImageRaw::contractDimToPowerOfTwo(llclamp((S32)llroundf(orig_height * scale), 4, max_height)); +                    } +                    else +                    { +                        biased_width = LLImageRaw::biasedDimToPowerOfTwo(orig_width, LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); +                        biased_height = LLImageRaw::biasedDimToPowerOfTwo(orig_height, LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); +                    } +                      total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(biased_width, biased_height);                      S32 area = biased_width * biased_height;                      if (area >= LLAgentBenefits::MIN_2K_TEXTURE_AREA) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1501ba41c2..44831aea03 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3052,6 +3052,11 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)          }      } +#ifdef LL_DISCORD +    if (gSavedSettings.getBOOL("EnableDiscord")) +        LLAppViewer::updateDiscordActivity(); +#endif +      if ( LLTracker::isTracking(NULL) )      {          // Check distance to beacon, if < 5m, remove beacon @@ -3189,6 +3194,7 @@ void send_agent_update(bool force_send, bool send_reliable)      static F64          last_send_time = 0.0;      static U32          last_control_flags = 0; +    static bool         control_flags_follow_up = false;      static U8           last_render_state = 0;      static U8           last_flags = AU_FLAGS_NONE;      static LLQuaternion last_body_rot, @@ -3266,6 +3272,20 @@ void send_agent_update(bool force_send, bool send_reliable)                  break;              } +            // example: +            // user taps crouch (control_flags 4128), viewer sends 4128 then immediately 0 +            // server starts crouching motion but does not stop it, only once viewer sends 0 +            // second time will server stop the motion. follow_up exists to make sure all +            // states like 'crouch' motion are properly cleared server side. +            // +            // P.S. Server probably shouldn't require a reminder to stop a motion, +            // but at the moment it does. +            if (control_flags_follow_up) +            { +                send_update = true; +                break; +            } +              // check translation              constexpr F32 TRANSLATE_THRESHOLD = 0.01f;              if ((last_camera_pos_agent - camera_pos_agent).magVec() > TRANSLATE_THRESHOLD) @@ -3399,6 +3419,7 @@ void send_agent_update(bool force_send, bool send_reliable)      // remember last update data      last_send_time = now; +    control_flags_follow_up = last_control_flags != control_flags;      last_control_flags = control_flags;      last_render_state = render_state;      last_flags = flags; diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp index da03d3b015..e36ad0e722 100755 --- a/indra/newview/llviewerparceloverlay.cpp +++ b/indra/newview/llviewerparceloverlay.cpp @@ -33,6 +33,7 @@  #include "llfloaterreg.h"  #include "llgl.h"  #include "llrender.h" +#include "lluicolor.h"  #include "v4color.h"  #include "v2math.h" @@ -50,8 +51,8 @@  #include "pipeline.h" -static const U8  OVERLAY_IMG_COMPONENTS = 4; -static const F32 LINE_WIDTH = 0.0625f; +static constexpr U8  OVERLAY_IMG_COMPONENTS = 4; +static constexpr F32 LINE_WIDTH = 0.0625f;  bool LLViewerParcelOverlay::sColorSetInitialized = false;  LLUIColor LLViewerParcelOverlay::sAvailColor; @@ -91,7 +92,7 @@ LLViewerParcelOverlay::LLViewerParcelOverlay(LLViewerRegion* region, F32 region_      // Initialize the GL texture with empty data.      //      // Create the base texture. -    U8 *raw = mImageRaw->getData(); +    U8* raw = mImageRaw->getData();      const S32 COUNT = mParcelGridsPerEdge * mParcelGridsPerEdge * OVERLAY_IMG_COMPONENTS;      for (S32 i = 0; i < COUNT; i++)      { @@ -158,10 +159,10 @@ bool LLViewerParcelOverlay::encroachesOwned(const std::vector<LLBBox>& boxes) co          LLVector3 min = boxes[i].getMinAgent();          LLVector3 max = boxes[i].getMaxAgent(); -        S32 left   = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 right  = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 top    = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); +        S32 left   = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 right  = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 top    = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));          for (S32 row = top; row <= bottom; row++)          { @@ -186,10 +187,10 @@ bool LLViewerParcelOverlay::encroachesOnUnowned(const std::vector<LLBBox>& boxes          LLVector3 min = boxes[i].getMinAgent();          LLVector3 max = boxes[i].getMaxAgent(); -        S32 left   = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 right  = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 top    = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); +        S32 left   = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 right  = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 top    = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));          for (S32 row = top; row <= bottom; row++)          { @@ -223,10 +224,10 @@ bool LLViewerParcelOverlay::encroachesOnNearbyParcel(const std::vector<LLBBox>&              return true;          } -        S32 left   = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 right  = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 bottom = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); -        S32 top    = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); +        S32 left   = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 right  = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 bottom = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f)); +        S32 top    = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));          const S32 GRIDS_PER_EDGE = mParcelGridsPerEdge; @@ -348,11 +349,11 @@ void LLViewerParcelOverlay::updateOverlayTexture()      const LLColor4U auction = sAuctionColor.get();      // Create the base texture. -    U8 *raw = mImageRaw->getData(); +    U8* raw = mImageRaw->getData();      const S32 COUNT = mParcelGridsPerEdge * mParcelGridsPerEdge;      S32 max = mOverlayTextureIdx + mParcelGridsPerEdge;      if (max > COUNT) max = COUNT; -    S32 pixel_index = mOverlayTextureIdx*OVERLAY_IMG_COMPONENTS; +    S32 pixel_index = mOverlayTextureIdx * OVERLAY_IMG_COMPONENTS;      S32 i;      for (i = mOverlayTextureIdx; i < max; i++)      { @@ -361,7 +362,7 @@ void LLViewerParcelOverlay::updateOverlayTexture()          U8 r,g,b,a;          // Color stored in low three bits -        switch( ownership & 0x7 ) +        switch (ownership & 0x7)          {          case PARCEL_PUBLIC:              r = avail.mV[VRED]; @@ -407,10 +408,10 @@ void LLViewerParcelOverlay::updateOverlayTexture()              break;          } -        raw[pixel_index + 0] = (U8)r; -        raw[pixel_index + 1] = (U8)g; -        raw[pixel_index + 2] = (U8)b; -        raw[pixel_index + 3] = (U8)a; +        raw[pixel_index + VRED]   = (U8)r; +        raw[pixel_index + VGREEN] = (U8)g; +        raw[pixel_index + VBLUE]  = (U8)b; +        raw[pixel_index + VALPHA] = (U8)a;          pixel_index += OVERLAY_IMG_COMPONENTS;      } @@ -431,11 +432,10 @@ void LLViewerParcelOverlay::updateOverlayTexture()      }  } - -void LLViewerParcelOverlay::uncompressLandOverlay(S32 chunk, U8 *packed_overlay) +void LLViewerParcelOverlay::uncompressLandOverlay(S32 chunk, U8* packed_overlay)  {      // Unpack the message data into the ownership array -    S32 size    = mParcelGridsPerEdge * mParcelGridsPerEdge; +    S32 size = mParcelGridsPerEdge * mParcelGridsPerEdge;      S32 chunk_size = size / PARCEL_OVERLAY_CHUNKS;      memcpy(mOwnership + chunk*chunk_size, packed_overlay, chunk_size);      /*Flawfinder: ignore*/ @@ -460,7 +460,7 @@ void LLViewerParcelOverlay::updatePropertyLines()      mEdges.clear(); -    const F32 GRID_STEP = PARCEL_GRID_STEP_METERS; +    constexpr F32 GRID_STEP = PARCEL_GRID_STEP_METERS;      const S32 GRIDS_PER_EDGE = mParcelGridsPerEdge;      for (S32 row = 0; row < GRIDS_PER_EDGE; row++) @@ -537,16 +537,16 @@ void LLViewerParcelOverlay::addPropertyLine(F32 start_x, F32 start_y, F32 dx, F3      auto split = [&](const LLVector3& start, F32 x, F32 y, F32 z, F32 part)          { -            F32 new_x = start.mV[0] + (x - start.mV[0]) * part; -            F32 new_y = start.mV[1] + (y - start.mV[1]) * part; -            F32 new_z = start.mV[2] + (z - start.mV[2]) * part; +            F32 new_x = start.mV[VX] + (x - start.mV[VX]) * part; +            F32 new_y = start.mV[VY] + (y - start.mV[VY]) * part; +            F32 new_z = start.mV[VZ] + (z - start.mV[VZ]) * part;              edge.vertices.emplace_back(new_x, new_y, new_z);          };      auto checkForSplit = [&]()          {              const LLVector3& last_outside = edge.vertices.back(); -            F32 z0 = last_outside.mV[2]; +            F32 z0 = last_outside.mV[VZ];              F32 z1 = outside_z;              if ((z0 >= water_z && z1 >= water_z) || (z0 < water_z && z1 < water_z))                  return; @@ -581,7 +581,7 @@ void LLViewerParcelOverlay::addPropertyLine(F32 start_x, F32 start_y, F32 dx, F3      outside_y += dy * (dy - LINE_WIDTH);      // Middle part, full width -    const S32 GRID_STEP = (S32)PARCEL_GRID_STEP_METERS; +    constexpr S32 GRID_STEP = (S32)PARCEL_GRID_STEP_METERS;      for (S32 i = 1; i < GRID_STEP; i++)      {          inside_z = land.resolveHeightRegion( inside_x, inside_y ); @@ -711,7 +711,7 @@ void LLViewerParcelOverlay::renderPropertyLines()      bool render_hidden = LLSelectMgr::sRenderHiddenSelections && LLFloaterReg::instanceVisible("build"); -    const F32 PROPERTY_LINE_CLIP_DIST_SQUARED = 256.f * 256.f; +    constexpr F32 PROPERTY_LINE_CLIP_DIST_SQUARED = 256.f * 256.f;      for (const Edge& edge : mEdges)      { @@ -744,7 +744,7 @@ void LLViewerParcelOverlay::renderPropertyLines()              else              {                  LLVector3 visible = vertex; -                visible.mV[2] = water_z; +                visible.mV[VZ] = water_z;                  gGL.vertex3fv(visible.mV);              }          } @@ -758,7 +758,7 @@ void LLViewerParcelOverlay::renderPropertyLines()              gGL.begin(LLRender::TRIANGLE_STRIP);              LLColor4U color = edge.color; -            color.mV[3] /= 4; +            color.mV[VALPHA] /= 4;              gGL.color4ubv(color.mV);              for (const LLVector3& vertex : edge.vertices) @@ -792,7 +792,7 @@ void grid_2d_part_lines(const F32 left, const F32 top, const F32 right, const F3      gGL.end();  } -void LLViewerParcelOverlay::renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32 *parcel_outline_color) +void LLViewerParcelOverlay::renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32* parcel_outline_color)  {      static LLCachedControl<bool> show(gSavedSettings, "MiniMapShowPropertyLines"); @@ -803,8 +803,8 @@ void LLViewerParcelOverlay::renderPropertyLinesOnMinimap(F32 scale_pixels_per_me      LLVector3 origin_agent     = mRegion->getOriginAgent();      LLVector3 rel_region_pos   = origin_agent - gAgentCamera.getCameraPositionAgent(); -    F32       region_left      = rel_region_pos.mV[0] * scale_pixels_per_meter; -    F32       region_bottom    = rel_region_pos.mV[1] * scale_pixels_per_meter; +    F32       region_left      = rel_region_pos.mV[VX] * scale_pixels_per_meter; +    F32       region_bottom    = rel_region_pos.mV[VY] * scale_pixels_per_meter;      F32       map_parcel_width = PARCEL_GRID_STEP_METERS * scale_pixels_per_meter;      const S32 GRIDS_PER_EDGE   = mParcelGridsPerEdge; diff --git a/indra/newview/llviewerparceloverlay.h b/indra/newview/llviewerparceloverlay.h index 03ae464cb8..50bef02ddf 100644 --- a/indra/newview/llviewerparceloverlay.h +++ b/indra/newview/llviewerparceloverlay.h @@ -34,12 +34,11 @@  #include "llframetimer.h"  #include "lluuid.h"  #include "llviewertexture.h" -#include "llgl.h" -#include "lluicolor.h"  class LLViewerRegion;  class LLVector3;  class LLColor4U; +class LLUIColor;  class LLVector2;  class LLViewerParcelOverlay : public LLGLUpdate @@ -65,19 +64,18 @@ public:      bool            isSoundLocal(const LLVector3& pos) const; -    bool            isBuildCameraAllowed(const LLVector3& pos) const;      F32             getOwnedRatio() const;      // Returns the number of vertices drawn      void            renderPropertyLines();      void            renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32* parcel_outline_color); -    U8              ownership( const LLVector3& pos) const; -    U8              parcelLineFlags( const LLVector3& pos) const; +    U8              ownership(const LLVector3& pos) const; +    U8              parcelLineFlags(const LLVector3& pos) const;      U8              parcelLineFlags(S32 row, S32 col) const;      // MANIPULATE -    void    uncompressLandOverlay(S32 chunk, U8 *compressed_overlay); +    void    uncompressLandOverlay(S32 chunk, U8* compressed_overlay);      // Indicate property lines and overlay texture need to be rebuilt.      void    setDirty(); @@ -88,8 +86,7 @@ public:  private:      // This is in parcel rows and columns, not grid rows and columns      // Stored in bottom three bits. -    U8      ownership(S32 row, S32 col) const -                { return parcelFlags(row, col, (U8)0x7); } +    U8      ownership(S32 row, S32 col) const { return parcelFlags(row, col, (U8)0x7); }      U8      parcelFlags(S32 row, S32 col, U8 flags) const; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b9f52e11aa..a085bc4d91 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -38,7 +38,6 @@  #include "llregionhandle.h"  #include "llsurface.h"  #include "message.h" -//#include "vmath.h"  #include "v3math.h"  #include "v4math.h" diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 73aabf49d1..a4f308bbf9 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -220,7 +220,20 @@ SimMeasurement<F64Megabytes >   SIM_PHYSICS_MEM("physicsmemoryallocated", "", LL  LLTrace::SampleStatHandle<F64Milliseconds > FRAMETIME_JITTER("frametimejitter", "Average delta between successive frame times"),                                              FRAMETIME("frametime", "Measured frame time"), -                                            SIM_PING("simpingstat"); +                                            SIM_PING("simpingstat"), +                                            FRAMETIME_JITTER_99TH("frametimejitter99", "99th percentile of frametime jitter over the last 5 seconds."), +                                            FRAMETIME_JITTER_95TH("frametimejitter95", "99th percentile of frametime jitter over the last 5 seconds."), +                                            FRAMETIME_99TH("frametime99", "99th percentile of frametime over the last 5 seconds."), +                                            FRAMETIME_95TH("frametime95", "99th percentile of frametime over the last 5 seconds."), +                                            FRAMETIME_JITTER_CUMULATIVE("frametimejitcumulative", "Cumulative frametime jitter over the session."), +                                            FRAMETIME_JITTER_STDDEV("frametimejitterstddev", "Standard deviation of frametime jitter in a 5 second period."), +                                            FRAMETIME_STDDEV("frametimestddev", "Standard deviation of frametime in a 5 second period."); + +LLTrace::SampleStatHandle<U32> FRAMETIME_JITTER_EVENTS("frametimeevents", "Number of frametime events in the session.  Applies when jitter exceeds 10% of the previous frame."), +                                FRAMETIME_JITTER_EVENTS_PER_MINUTE("frametimeeventspm", "Average number of frametime events per minute."), +                                FRAMETIME_JITTER_EVENTS_LAST_MINUTE("frametimeeventslastmin", "Number of frametime events in the last minute."); + +LLTrace::SampleStatHandle<F64> NOTRMALIZED_FRAMETIME_JITTER_SESSION("normalizedframetimejitter", "Normalized frametime jitter over the session.");  LLTrace::EventStatHandle<LLUnit<F64, LLUnits::Meters> > AGENT_POSITION_SNAP("agentpositionsnap", "agent position corrections"); @@ -264,16 +277,105 @@ void LLViewerStats::resetStats()      getRecording().reset();  } +// Helper for calculating Nth percentile with linear interpolation +template<typename T> +T calcPercentile(const std::vector<T>& sorted, double percent) +{ +    if (sorted.empty()) +        return T(0); +    double idx       = percent * (sorted.size() - 1); +    size_t idx_below = static_cast<size_t>(std::floor(idx)); +    size_t idx_above = static_cast<size_t>(std::ceil(idx)); +    if (idx_below == idx_above) +        return sorted[idx_below]; +    double weight_above = idx - idx_below; +    return sorted[idx_below] * (1.0 - weight_above) + sorted[idx_above] * weight_above; +} + +template<typename T> +T calcStddev(const std::vector<T>& values) +{ +    if (values.size() < 2) +        return T(0); +    double sum = 0, sq_sum = 0; +    for (const auto& v : values) +    { +        double d = v.value(); +        sum += d; +        sq_sum += d * d; +    } +    double mean     = sum / values.size(); +    double variance = (sq_sum / values.size()) - (mean * mean); +    return T(std::sqrt(variance)); +} +  void LLViewerStats::updateFrameStats(const F64Seconds time_diff)  {      if (gFrameCount && mLastTimeDiff > (F64Seconds)0.0)      { +        mTotalTime += time_diff;          sample(LLStatViewer::FRAMETIME, time_diff);          // old stats that were never really used          F64Seconds jit = (F64Seconds)std::fabs((mLastTimeDiff - time_diff));          sample(LLStatViewer::FRAMETIME_JITTER, jit); -    } +        mTotalFrametimeJitter += jit; +        sample(LLStatViewer::FRAMETIME_JITTER_CUMULATIVE, mTotalFrametimeJitter); +        sample(LLStatViewer::NOTRMALIZED_FRAMETIME_JITTER_SESSION, mTotalFrametimeJitter / mTotalTime); + +        static LLCachedControl<F32> frameTimeEventThreshold(gSavedSettings, "StatsFrametimeEventThreshold", 0.1f); + +        if (time_diff - mLastTimeDiff > mLastTimeDiff * frameTimeEventThreshold()) +        { +            sample(LLStatViewer::FRAMETIME_JITTER_EVENTS, mFrameJitterEvents++); +            mFrameJitterEventsLastMinute++; +        } +        mFrameTimes.push_back(time_diff); +        mFrameTimesJitter.push_back(jit); + +        mLastFrameTimeSample += time_diff; +        mTimeSinceLastEventSample += time_diff; + +        static LLCachedControl<S32> frameTimeSampleSeconds(gSavedSettings, "StatsFrametimeSampleSeconds", 5); + +        if (mLastFrameTimeSample >= frameTimeSampleSeconds()) +        { +            std::sort(mFrameTimes.begin(), mFrameTimes.end()); +            std::sort(mFrameTimesJitter.begin(), mFrameTimesJitter.end()); + +            // Use new helpers for calculations +            F64Seconds frame_time_stddev = calcStddev(mFrameTimes); +            sample(LLStatViewer::FRAMETIME_STDDEV, frame_time_stddev); + +            F64Seconds ninety_ninth_percentile = calcPercentile(mFrameTimes, 0.99); +            F64Seconds ninety_fifth_percentile = calcPercentile(mFrameTimes, 0.95); +            sample(LLStatViewer::FRAMETIME_99TH, ninety_ninth_percentile); +            sample(LLStatViewer::FRAMETIME_95TH, ninety_fifth_percentile); + +            frame_time_stddev = calcStddev(mFrameTimesJitter); +            sample(LLStatViewer::FRAMETIME_JITTER_STDDEV, frame_time_stddev); + +            ninety_ninth_percentile = calcPercentile(mFrameTimesJitter, 0.99); +            ninety_fifth_percentile = calcPercentile(mFrameTimesJitter, 0.95); +            sample(LLStatViewer::FRAMETIME_JITTER_99TH, ninety_ninth_percentile); +            sample(LLStatViewer::FRAMETIME_JITTER_95TH, ninety_fifth_percentile); + +            mFrameTimes.clear(); +            mFrameTimesJitter.clear(); +            mLastFrameTimeSample = F64Seconds(0); +        } + +        if (mTimeSinceLastEventSample >= 60) +        { +            mEventMinutes++; +            // Calculate average events per minute +            U64 frame_time_events_per_minute = (U64)mFrameJitterEvents / mEventMinutes; +            sample(LLStatViewer::FRAMETIME_JITTER_EVENTS_PER_MINUTE, frame_time_events_per_minute); +            sample(LLStatViewer::FRAMETIME_JITTER_EVENTS_LAST_MINUTE, mFrameJitterEventsLastMinute); +            mFrameJitterEventsLastMinute   = 0; +            mTimeSinceLastEventSample    = F64Seconds(0); +        } +    }      mLastTimeDiff = time_diff;  } diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 8aed1c537e..63fb7d4a17 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -275,6 +275,17 @@ private:      LLTrace::Recording              mRecording;      F64Seconds mLastTimeDiff;  // used for time stat updates +    F64Seconds mTotalFrametimeJitter; + +    U32 mFrameJitterEvents = 0; +    U32 mFrameJitterEventsLastMinute = 0; +    U32 mEventMinutes = 0; +    F64Seconds mTotalTime; + +    F64Seconds              mLastFrameTimeSample; // used for frame time stats +    F64Seconds              mTimeSinceLastEventSample; +    std::vector<F64Seconds>      mFrameTimes;          // used for frame time stats +    std::vector<F64Seconds> mFrameTimesJitter;    // used for frame time jitter stats  };  static const F32 SEND_STATS_PERIOD = 300.0f; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 0d02dc034e..6f79532ec3 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -911,6 +911,7 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag          bool on_screen = false;          U32 face_count = 0; +        U32 max_faces_to_check = 1024;          // get adjusted bias based on image resolution          LLImageGL* img = imagep->getGLTexture(); @@ -923,13 +924,15 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag          LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;          for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)          { -            for (S32 fi = 0; fi < imagep->getNumFaces(i); ++fi) +            face_count += imagep->getNumFaces(i); +            S32 faces_to_check = (face_count > max_faces_to_check) ? 0 : imagep->getNumFaces(i); + +            for (S32 fi = 0; fi < faces_to_check; ++fi)              {                  LLFace* face = (*(imagep->getFaceList(i)))[fi];                  if (face && face->getViewerObject())                  { -                    ++face_count;                      F32 radius;                      F32 cos_angle_to_view_dir; @@ -992,11 +995,10 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag              }          } -        if (face_count > 1024) +        if (face_count > max_faces_to_check)          { // this texture is used in so many places we should just boost it and not bother checking its vsize              // this is especially important because the above is not time sliced and can hit multiple ms for a single texture -            imagep->setBoostLevel(LLViewerFetchedTexture::BOOST_HIGH); -            // Do we ever remove it? This also sets texture nodelete! +            max_vsize = MAX_IMAGE_AREA;          }          if (imagep->getType() == LLViewerTexture::LOD_TEXTURE && imagep->getBoostLevel() == LLViewerTexture::BOOST_NONE) @@ -1195,6 +1197,8 @@ F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time)          enditer = iter;          LLViewerFetchedTexture *imagep = *curiter;          imagep->loadFromFastCache(); +        if (timer.getElapsedTimeF32() > max_time) +            break;      }      mFastCacheList.erase(mFastCacheList.begin(), enditer);      return timer.getElapsedTimeF32(); @@ -1306,7 +1310,7 @@ void LLViewerTextureList::decodeAllImages(F32 max_time)      LLTimer timer;      //loading from fast cache -    updateImagesLoadingFastCache(max_time); +    max_time -= updateImagesLoadingFastCache(max_time);      // Update texture stats and priorities      std::vector<LLPointer<LLViewerFetchedTexture> > image_list; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index e74df0917f..b0408b73ad 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2301,13 +2301,13 @@ void LLViewerWindow::initWorldUI()              url = LLWeb::expandURLSubstitutions(url, LLSD());              destinations->navigateTo(url, HTTP_CONTENT_TEXT_HTML);          } -        LLMediaCtrl* avatar_picker = LLFloaterReg::getInstance("avatar")->findChild<LLMediaCtrl>("avatar_picker_contents"); -        if (avatar_picker) +        LLMediaCtrl* avatar_welcome_pack = LLFloaterReg::getInstance("avatar_welcome_pack")->findChild<LLMediaCtrl>("avatar_picker_contents"); +        if (avatar_welcome_pack)          { -            avatar_picker->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); -            std::string url = gSavedSettings.getString("AvatarPickerURL"); +            avatar_welcome_pack->setErrorPageURL(gSavedSettings.getString("GenericErrorPageURL")); +            std::string url = gSavedSettings.getString("AvatarWelcomePack");              url = LLWeb::expandURLSubstitutions(url, LLSD()); -            avatar_picker->navigateTo(url, HTTP_CONTENT_TEXT_HTML); +            avatar_welcome_pack->navigateTo(url, HTTP_CONTENT_TEXT_HTML);          }      }  } @@ -4790,7 +4790,18 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save  #else      boost::filesystem::path b_path(lastSnapshotDir);  #endif -    if (!boost::filesystem::is_directory(b_path)) +    boost::system::error_code ec; +    if (!boost::filesystem::is_directory(b_path, ec) || ec.failed()) +    { +        LLSD args; +        args["PATH"] = lastSnapshotDir; +        LLNotificationsUtil::add("SnapshotToLocalDirNotExist", args); +        resetSnapshotLoc(); +        failure_cb(); +        return; +    } +    boost::filesystem::space_info b_space = boost::filesystem::space(b_path, ec); +    if (ec.failed())      {          LLSD args;          args["PATH"] = lastSnapshotDir; @@ -4799,7 +4810,6 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save          failure_cb();          return;      } -    boost::filesystem::space_info b_space = boost::filesystem::space(b_path);      if (b_space.free < image->getDataSize())      {          LLSD args; @@ -4816,6 +4826,8 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save          LLNotificationsUtil::add("SnapshotToComputerFailed", args);          failure_cb(); + +        // Shouldn't there be a return here?      }      // Look for an unused file name diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index bee3af4632..903d6a035f 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -678,6 +678,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,      mVisuallyMuteSetting(AV_RENDER_NORMALLY),      mMutedAVColor(LLColor4::white /* used for "uninitialize" */),      mFirstFullyVisible(true), +    mWaitingForMeshes(false),      mFirstDecloudTime(-1.f),      mFullyLoaded(false),      mPreviousFullyLoaded(false), @@ -919,12 +920,12 @@ bool LLVOAvatar::isFullyTextured() const  bool LLVOAvatar::hasGray() const  { -    return !getIsCloud() && !isFullyTextured(); +    return !getHasMissingParts() && !isFullyTextured();  }  S32 LLVOAvatar::getRezzedStatus() const  { -    if (getIsCloud()) return 0; +    if (getHasMissingParts()) return 0;      bool textured = isFullyTextured();      bool all_baked_loaded = allBakedTexturesCompletelyDownloaded();      if (textured && all_baked_loaded && getAttachmentCount() == mSimAttachments.size()) return 4; @@ -971,30 +972,45 @@ bool LLVOAvatar::areAllNearbyInstancesBaked(S32& grey_avatars)  }  // static -void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars) +void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars)  {      counts.clear();      counts.resize(5);      avg_cloud_time = 0;      cloud_avatars = 0; +    pending_meshes = 0; +    control_avatars = 0;      S32 count_avg = 0;      for (LLCharacter* character : LLCharacter::sInstances)      { -        if (LLVOAvatar* inst = (LLVOAvatar*)character) +        LLVOAvatar* inst = (LLVOAvatar*)character; +        if (inst && !inst->isUIAvatar() && !inst->isSelf())          { -            S32 rez_status = inst->getRezzedStatus(); -            counts[rez_status]++; -            F32 time = inst->getFirstDecloudTime(); -            if (time >= 0) +            if (inst->isControlAvatar())              { -                avg_cloud_time+=time; -                count_avg++; +                control_avatars++;              } -            if (!inst->isFullyLoaded() || time < 0) +            else              { -                // still renders as cloud -                cloud_avatars++; +                S32 rez_status = inst->getRezzedStatus(); +                counts[rez_status]++; +                F32 time = inst->getFirstDecloudTime(); +                if (time >= 0) +                { +                    avg_cloud_time += time; +                    count_avg++; +                } +                if (!inst->isFullyLoaded() || time < 0) +                { +                    // still renders as cloud +                    cloud_avatars++; +                    if (rez_status >= 4 +                        && inst->mWaitingForMeshes) +                    { +                        pending_meshes++; +                    } +                }              }          }      } @@ -1011,7 +1027,7 @@ std::string LLVOAvatar::rezStatusToString(S32 rez_status)      switch (rez_status)      {      case 0: -        return "cloud"; +        return "missing parts";      case 1:          return "gray";      case 2: @@ -3473,7 +3489,7 @@ void LLVOAvatar::idleUpdateNameTagText(bool new_name)          is_muted = isInMuteList();      }      bool is_friend = isBuddy(); -    bool is_cloud = getIsCloud(); +    bool is_cloud = getHasMissingParts();      if (is_appearance != mNameAppearance)      { @@ -6325,13 +6341,13 @@ const LLUUID& LLVOAvatar::getID() const  //-----------------------------------------------------------------------------  // RN: avatar joints are multi-rooted to include screen-based attachments  // virtual -LLJoint *LLVOAvatar::getJoint( const std::string &name ) +LLJoint* LLVOAvatar::getJoint(std::string_view name)  {      joint_map_t::iterator iter = mJointMap.find(name); -    LLJoint* jointp = NULL; +    LLJoint* jointp = nullptr; -    if (iter == mJointMap.end() || iter->second == NULL) +    if (iter == mJointMap.end() || iter->second == nullptr)      {   //search for joint and cache found joint in lookup table          if (mJointAliasMap.empty())          { @@ -6348,7 +6364,7 @@ LLJoint *LLVOAvatar::getJoint( const std::string &name )              canonical_name = name;          }          jointp = mRoot->findJoint(canonical_name); -        mJointMap[name] = jointp; +        mJointMap[std::string(name)] = jointp;      }      else      {   //return cached pointer @@ -8223,7 +8239,7 @@ bool LLVOAvatar::isVisible() const  }  // Determine if we have enough avatar data to render -bool LLVOAvatar::getIsCloud() const +bool LLVOAvatar::getHasMissingParts() const  {      if (mIsDummy)      { @@ -8430,8 +8446,12 @@ bool LLVOAvatar::updateIsFullyLoaded()                     || (mLoadedCallbackTextures < mCallbackTextureList.size() && mLastTexCallbackAddedTime.getElapsedTimeF32() < MAX_TEXTURE_WAIT_TIME_SEC)                     || !mPendingAttachment.empty()                     || (rez_status < 3 && !isFullyBaked()) -                   || hasPendingAttachedMeshes()                    ); +        if (!loading) +        { +            mWaitingForMeshes = hasPendingAttachedMeshes(); +            loading = mWaitingForMeshes; +        }          // compare amount of attachments to one reported by simulator          if (!isSelf() && mLastCloudAttachmentCount < mSimAttachments.size() && mSimAttachments.size() > 0) diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 13d42fb02f..1e563c4869 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -202,7 +202,7 @@ public:      void                    startDefaultMotions();      void                    dumpAnimationState(); -    virtual LLJoint*        getJoint(const std::string &name); +    virtual LLJoint*        getJoint(std::string_view name);      LLJoint*                getJoint(S32 num);      void                    initAllJoints(); @@ -401,7 +401,7 @@ public:      bool            isTooComplex() const;      bool            visualParamWeightsAreDefault(); -    virtual bool    getIsCloud() const; +    virtual bool    getHasMissingParts() const;      bool            isFullyTextured() const;      bool            hasGray() const;      S32             getRezzedStatus() const; // 0 = cloud, 1 = gray, 2 = textured, 3 = waiting for attachments, 4 = full. @@ -427,6 +427,7 @@ protected:  private:      bool            mFirstFullyVisible; +    bool            mWaitingForMeshes;      F32             mFirstDecloudTime;      LLFrameTimer    mFirstAppearanceMessageTimer; @@ -723,7 +724,7 @@ public:      bool            isFullyBaked();      static bool     areAllNearbyInstancesBaked(S32& grey_avatars); -    static void     getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars); +    static void     getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars);      static std::string rezStatusToString(S32 status);      //-------------------------------------------------------------------- diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 90ff4067f2..653c7e82eb 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -697,17 +697,17 @@ void LLVOAvatarSelf::idleUpdate(LLAgent &agent, const F64 &time)  }  // virtual -LLJoint *LLVOAvatarSelf::getJoint(const std::string &name) +LLJoint* LLVOAvatarSelf::getJoint(std::string_view name)  {      std::lock_guard lock(mJointMapMutex); -    LLJoint *jointp = NULL; +    LLJoint* jointp = nullptr;      jointp = LLVOAvatar::getJoint(name);      if (!jointp && mScreenp)      {          jointp = mScreenp->findJoint(name);          if (jointp)          { -            mJointMap[name] = jointp; +            mJointMap[std::string(name)] = jointp;          }      }      if (jointp && jointp != mScreenp && jointp != mRoot) @@ -1927,7 +1927,7 @@ void LLVOAvatarSelf::dumpTotalLocalTextureByteCount()      LL_INFOS() << "Total Avatar LocTex GL:" << (gl_bytes/1024) << "KB" << LL_ENDL;  } -bool LLVOAvatarSelf::getIsCloud() const +bool LLVOAvatarSelf::getHasMissingParts() const  {      // Let people know why they're clouded without spamming them into oblivion.      bool do_warn = false; @@ -2237,14 +2237,18 @@ void LLVOAvatarSelf::appearanceChangeMetricsCoro(std::string url)      std::vector<S32> rez_counts;      F32 avg_time;      S32 total_cloud_avatars; -    LLVOAvatar::getNearbyRezzedStats(rez_counts, avg_time, total_cloud_avatars); +    S32 waiting_for_meshes; +    S32 control_avatars; +    LLVOAvatar::getNearbyRezzedStats(rez_counts, avg_time, total_cloud_avatars, waiting_for_meshes, control_avatars);      for (S32 rez_stat = 0; rez_stat < rez_counts.size(); ++rez_stat)      {          std::string rez_status_name = LLVOAvatar::rezStatusToString(rez_stat);          msg["nearby"][rez_status_name] = rez_counts[rez_stat];      } +    msg["nearby"]["waiting_for_meshes"] = waiting_for_meshes;      msg["nearby"]["avg_decloud_time"] = avg_time;      msg["nearby"]["cloud_total"] = total_cloud_avatars; +    msg["nearby"]["animeshes"] = control_avatars;      //  std::vector<std::string> bucket_fields("timer_name","is_self","grid_x","grid_y","is_using_server_bake");      std::vector<std::string> by_fields; @@ -2826,6 +2830,12 @@ void LLVOAvatarSelf::setHoverOffset(const LLVector3& hover_offset, bool send_upd  //------------------------------------------------------------------------  bool LLVOAvatarSelf::needsRenderBeam()  { +    static LLCachedControl<bool> enable_selection_hints(gSavedSettings, "EnableSelectionHints", true); +    if (!enable_selection_hints) +    { +        return false; +    } +      LLTool *tool = LLToolMgr::getInstance()->getCurrentTool();      bool is_touching_or_grabbing = (tool == LLToolGrab::getInstance() && LLToolGrab::getInstance()->isEditing()); diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index f9bea41b1d..45985b2a80 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -90,7 +90,7 @@ public:      /*virtual*/ bool        hasMotionFromSource(const LLUUID& source_id);      /*virtual*/ void        stopMotionFromSource(const LLUUID& source_id);      /*virtual*/ void        requestStopMotion(LLMotion* motion); -    /*virtual*/ LLJoint*    getJoint(const std::string &name); +    /*virtual*/ LLJoint*    getJoint(std::string_view name);      /*virtual*/ void renderJoints(); @@ -129,7 +129,7 @@ public:      // Loading state      //--------------------------------------------------------------------  public: -    /*virtual*/ bool    getIsCloud() const; +    /*virtual*/ bool    getHasMissingParts() const;      //--------------------------------------------------------------------      // Region state diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 9835a69e4e..627d759df4 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -336,35 +336,37 @@ void LLWebRTCVoiceClient::updateSettings()      LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE;      setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled()); -    static LLCachedControl<S32> sVoiceEarLocation(gSavedSettings, "VoiceEarLocation"); -    setEarLocation(sVoiceEarLocation); - -    static LLCachedControl<std::string> sInputDevice(gSavedSettings, "VoiceInputAudioDevice"); -    setCaptureDevice(sInputDevice); +    if (mVoiceEnabled) +    { +        static LLCachedControl<S32> sVoiceEarLocation(gSavedSettings, "VoiceEarLocation"); +        setEarLocation(sVoiceEarLocation); -    static LLCachedControl<std::string> sOutputDevice(gSavedSettings, "VoiceOutputAudioDevice"); -    setRenderDevice(sOutputDevice); +        static LLCachedControl<std::string> sInputDevice(gSavedSettings, "VoiceInputAudioDevice"); +        setCaptureDevice(sInputDevice); -    LL_INFOS("Voice") << "Input device: " << std::quoted(sInputDevice()) << ", output device: " << std::quoted(sOutputDevice()) << LL_ENDL; +        static LLCachedControl<std::string> sOutputDevice(gSavedSettings, "VoiceOutputAudioDevice"); +        setRenderDevice(sOutputDevice); -    static LLCachedControl<F32> sMicLevel(gSavedSettings, "AudioLevelMic"); -    setMicGain(sMicLevel); +        LL_INFOS("Voice") << "Input device: " << std::quoted(sInputDevice()) << ", output device: " << std::quoted(sOutputDevice()) << LL_ENDL; -    llwebrtc::LLWebRTCDeviceInterface::AudioConfig config; +        static LLCachedControl<F32> sMicLevel(gSavedSettings, "AudioLevelMic"); +        setMicGain(sMicLevel); -    static LLCachedControl<bool> sEchoCancellation(gSavedSettings, "VoiceEchoCancellation", true); -    config.mEchoCancellation = sEchoCancellation; +        llwebrtc::LLWebRTCDeviceInterface::AudioConfig config; -    static LLCachedControl<bool> sAGC(gSavedSettings, "VoiceAutomaticGainControl", true); -    config.mAGC = sAGC; +        static LLCachedControl<bool> sEchoCancellation(gSavedSettings, "VoiceEchoCancellation", true); +        config.mEchoCancellation = sEchoCancellation; -    static LLCachedControl<U32> sNoiseSuppressionLevel(gSavedSettings, -                                                       "VoiceNoiseSuppressionLevel", -                                                       llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel::NOISE_SUPPRESSION_LEVEL_VERY_HIGH); -    config.mNoiseSuppressionLevel = (llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel) (U32)sNoiseSuppressionLevel; +        static LLCachedControl<bool> sAGC(gSavedSettings, "VoiceAutomaticGainControl", true); +        config.mAGC = sAGC; -    mWebRTCDeviceInterface->setAudioConfig(config); +        static LLCachedControl<U32> sNoiseSuppressionLevel(gSavedSettings, +            "VoiceNoiseSuppressionLevel", +            llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel::NOISE_SUPPRESSION_LEVEL_VERY_HIGH); +        config.mNoiseSuppressionLevel = (llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel)(U32)sNoiseSuppressionLevel; +        mWebRTCDeviceInterface->setAudioConfig(config); +    }  }  // Observers diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 4931473551..80fc486a10 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -2656,6 +2656,17 @@ void LLVOVolume::syncMediaData(S32 texture_index, const LLSD &media_data, bool m          }          viewer_media_t media_impl = LLViewerMedia::getInstance()->updateMediaImpl(mep, previous_url, update_from_self); +        static LLCachedControl<bool> media_autoplay_huds(gSavedSettings, "MediaAutoPlayHuds", true); +        bool was_loaded = media_impl->hasMedia(); +        if (isHUDAttachment() && media_autoplay_huds && !was_loaded) +        { +            std::string url = mep->getCurrentURL(); +            if (media_impl->getCurrentMediaURL() != url) +            { +                media_impl->navigateTo(url, "", false, true); +            } +        } +          addMediaImpl(media_impl, texture_index) ;      }      else @@ -3799,7 +3810,12 @@ bool LLVOVolume::canBeAnimatedObject() const  bool LLVOVolume::isAnimatedObject() const  { -    LLVOVolume *root_vol = (LLVOVolume*)getRootEdit(); +    LLViewerObject *root_obj = getRootEdit(); +    if (root_obj->getPCode() != LL_PCODE_VOLUME) +    { +        return false; // at the moment only volumes can be animated +    } +    LLVOVolume* root_vol = (LLVOVolume*)root_obj;      mIsAnimatedObject = root_vol->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;      return mIsAnimatedObject;  } diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp index 0a1d346266..bf171fe954 100644 --- a/indra/newview/llwatchdog.cpp +++ b/indra/newview/llwatchdog.cpp @@ -29,7 +29,7 @@  #include "llwatchdog.h"  #include "llthread.h" -const U32 WATCHDOG_SLEEP_TIME_USEC = 1000000; +constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U;  // This class runs the watchdog timing thread.  class LLWatchdogTimerThread : public LLThread @@ -51,7 +51,7 @@ public:          mSleepMsecs = 1;      } -    /* virtual */ void run() +    void run() override      {          while(!mStopping)          { @@ -83,7 +83,7 @@ void LLWatchdogEntry::start()  void LLWatchdogEntry::stop()  {      // this can happen very late in the shutdown sequence -    if (! LLWatchdog::wasDeleted()) +    if (!LLWatchdog::wasDeleted())      {          LLWatchdog::getInstance()->remove(this);      } @@ -117,7 +117,7 @@ void LLWatchdogTimeout::setTimeout(F32 d)      mTimeout = d;  } -void LLWatchdogTimeout::start(const std::string& state) +void LLWatchdogTimeout::start(std::string_view state)  {      if (mTimeout == 0)      { @@ -139,9 +139,9 @@ void LLWatchdogTimeout::stop()      mTimer.stop();  } -void LLWatchdogTimeout::ping(const std::string& state) +void LLWatchdogTimeout::ping(std::string_view state)  { -    if(!state.empty()) +    if (!state.empty())      {          mPingState = state;      } @@ -151,7 +151,7 @@ void LLWatchdogTimeout::ping(const std::string& state)  // LLWatchdog  LLWatchdog::LLWatchdog()      :mSuspectsAccessMutex() -    ,mTimer(NULL) +    ,mTimer(nullptr)      ,mLastClockCount(0)  {  } @@ -176,7 +176,7 @@ void LLWatchdog::remove(LLWatchdogEntry* e)  void LLWatchdog::init()  { -    if(!mSuspectsAccessMutex && !mTimer) +    if (!mSuspectsAccessMutex && !mTimer)      {          mSuspectsAccessMutex = new LLMutex();          mTimer = new LLWatchdogTimerThread(); @@ -191,17 +191,17 @@ void LLWatchdog::init()  void LLWatchdog::cleanup()  { -    if(mTimer) +    if (mTimer)      {          mTimer->stop();          delete mTimer; -        mTimer = NULL; +        mTimer = nullptr;      } -    if(mSuspectsAccessMutex) +    if (mSuspectsAccessMutex)      {          delete mSuspectsAccessMutex; -        mSuspectsAccessMutex = NULL; +        mSuspectsAccessMutex = nullptr;      }      mLastClockCount = 0; @@ -214,12 +214,12 @@ void LLWatchdog::run()      // Check the time since the last call to run...      // If the time elapsed is two times greater than the regualr sleep time      // reset the active timeouts. -    const U32 TIME_ELAPSED_MULTIPLIER = 2; +    constexpr U32 TIME_ELAPSED_MULTIPLIER = 2;      U64 current_time = LLTimer::getTotalTime();      U64 current_run_delta = current_time - mLastClockCount;      mLastClockCount = current_time; -    if(current_run_delta > (WATCHDOG_SLEEP_TIME_USEC * TIME_ELAPSED_MULTIPLIER)) +    if (current_run_delta > (WATCHDOG_SLEEP_TIME_USEC * TIME_ELAPSED_MULTIPLIER))      {          LL_INFOS() << "Watchdog thread delayed: resetting entries." << LL_ENDL;          for (const auto& suspect : mSuspects) @@ -233,7 +233,7 @@ void LLWatchdog::run()              std::find_if(mSuspects.begin(),                  mSuspects.end(),                  [](const LLWatchdogEntry* suspect){ return ! suspect->isAlive(); }); -        if(result != mSuspects.end()) +        if (result != mSuspects.end())          {              // error!!!              if(mTimer) @@ -251,7 +251,7 @@ void LLWatchdog::run()  void LLWatchdog::lockThread()  { -    if(mSuspectsAccessMutex != NULL) +    if (mSuspectsAccessMutex)      {          mSuspectsAccessMutex->lock();      } @@ -259,7 +259,7 @@ void LLWatchdog::lockThread()  void LLWatchdog::unlockThread()  { -    if(mSuspectsAccessMutex != NULL) +    if (mSuspectsAccessMutex)      {          mSuspectsAccessMutex->unlock();      } diff --git a/indra/newview/llwatchdog.h b/indra/newview/llwatchdog.h index fe8932e298..1931c582b0 100644 --- a/indra/newview/llwatchdog.h +++ b/indra/newview/llwatchdog.h @@ -56,14 +56,14 @@ public:      LLWatchdogTimeout();      virtual ~LLWatchdogTimeout(); -    /* virtual */ bool isAlive() const; -    /* virtual */ void reset(); -    /* virtual */ void start() { start(""); } -    /* virtual */ void stop(); +    bool isAlive() const override; +    void reset() override; +    void start() override { start(""); } +    void stop() override; -    void start(const std::string& state); +    void start(std::string_view state);      void setTimeout(F32 d); -    void ping(const std::string& state); +    void ping(std::string_view state);      const std::string& getState() {return mPingState; }  private: diff --git a/indra/newview/res/ll_icon_small.ico b/indra/newview/res/ll_icon_small.ico Binary files differnew file mode 100644 index 0000000000..a3f6877935 --- /dev/null +++ b/indra/newview/res/ll_icon_small.ico diff --git a/indra/newview/res/resource.h b/indra/newview/res/resource.h index e904f4a1a8..1d3289d784 100644 --- a/indra/newview/res/resource.h +++ b/indra/newview/res/resource.h @@ -30,6 +30,7 @@  #define IDREMOVE                        3  #define IDI_LL_ICON                     103  #define IDC_GRABHAND                    104 +#define IDI_LL_ICON_SMALL               105  #define IDC_CURSOR1                     134  #define IDC_CURSOR2                     136  #define IDC_CURSOR3                     147 diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc index 4ee26a312a..dc2ba5f171 100755 --- a/indra/newview/res/viewerRes.rc +++ b/indra/newview/res/viewerRes.rc @@ -56,6 +56,7 @@ END  // remains consistent on all systems.  IDI_LL_ICON             ICON                    "ll_icon.ico"  IDI_LCD_LL_ICON         ICON                    "icon1.ico" +IDI_LL_ICON_SMALL       ICON                    "ll_icon_small.ico"  /////////////////////////////////////////////////////////////////////////////  // diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index d650e7e791..b18d151ab7 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -657,7 +657,6 @@ with the same filename but different name    <texture name="login_sl_logo"  file_name="windows/login_sl_logo.png" preload="true" />    <texture name="login_sl_logo_small"  file_name="windows/login_sl_logo_small.png" preload="true" /> -  <texture name="first_login_image"  file_name="windows/first_login_image.jpg" preload="true" />    <texture name="Stepper_Down_Off" file_name="widgets/Stepper_Down_Off.png" preload="false" />    <texture name="Stepper_Down_Press" file_name="widgets/Stepper_Down_Press.png" preload="false" /> diff --git a/indra/newview/skins/default/textures/windows/first_login_image.jpg b/indra/newview/skins/default/textures/windows/first_login_image.jpg Binary files differdeleted file mode 100644 index 30f31341ed..0000000000 --- a/indra/newview/skins/default/textures/windows/first_login_image.jpg +++ /dev/null diff --git a/indra/newview/skins/default/xui/de/panel_login_first.xml b/indra/newview/skins/default/xui/de/panel_login_first.xml deleted file mode 100644 index 038001157e..0000000000 --- a/indra/newview/skins/default/xui/de/panel_login_first.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		http://secondlife.com/account/request.php?lang=de -	</panel.string> -	<panel.string name="sign_up_url"> -		https://join.secondlife.com/ -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="Benutzername" name="username_combo" tool_tip="Bei der Registrierung gewählter Benutzername wie „berndschmidt12“ oder „Liebe Sonne“"/> -					<line_editor label="Kennwort" name="password_edit"/> -					<button label="Anmelden" name="connect_btn"/> -					<check_box label="Details speichern" name="remember_check"/> -					<text name="forgot_password_text"> -						Kennwort vergessen -					</text> -					<text name="sign_up_text"> -						Registrieren -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						Ihr erster Schritt ist Learning Island. Suchen Sie die Pforte! -					</text> -					<text name="image_caption_right"> -						Erkunden Sie dann Social Island und lernen Sie andere Einwohner kennen! -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/floater_avatar_welcome_pack.xml b/indra/newview/skins/default/xui/en/floater_avatar_welcome_pack.xml new file mode 100644 index 0000000000..795d642755 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_avatar_welcome_pack.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater + positioning="cascading" + legacy_header_height="225" + can_minimize="true" + can_close="true" + can_resize="false" + min_height="438" + min_width="530" + height="438" + layout="topleft" + name="Avatar Welcome Pack" + single_instance="true" + save_rect="true" + save_visibility="true" + title="AVATAR WELCOME PACK" + width="530"> +    <web_browser +      top="25" +      height="438" +      width="530" +      follows="all" +      name="avatar_picker_contents" +      trusted_content="true"/> +</floater> 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 90223fcda8..f11d687840 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -14,7 +14,7 @@   legacy_header_height="25">    <string name="status_idle"></string> -  <string name="status_parse_error">Error: Dae parsing issue - see log for details.</string> +  <string name="status_parse_error">Error: Model parsing issue - see log for details.</string>    <string name="status_bind_shape_orientation">Warning: bind shape matrix is not in standard X-forward orientation.</string>    <string name="status_material_mismatch">Error: Material of model is not a subset of reference model.</string>    <string name="status_reading_file">Loading...</string> @@ -39,12 +39,14 @@    <string name="decomposing">Analyzing...</string>    <string name="simplifying">Simplifying...</string>    <string name="tbd">TBD</string> +  <string name="ModelTextureScaling">One or more textures in this model were scaled to be within the allowed limits.</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="InvBindCountMismatch">Bind matrices count mismatch joints count</string>    <string name="IncompleteTC">Texture coordinates data is not complete.</string>    <string name="PositionNaN">Found NaN while loading position data from DAE-Model, invalid model.</string> @@ -60,6 +62,27 @@    <string name="ParsingErrorNoRoot">Document has no root</string>    <string name="ParsingErrorNoScene">Document has no visual_scene</string>    <string name="ParsingErrorPositionInvalidModel">Unable to process mesh without position data. Invalid model.</string> +  <string name="UnknownException">Importer crashed while processing [FILENAME], if you encounter this and file is valid, please report the issue to Second Life Support. Exception: [EXCEPTION].</string> + + <!-- GLTF specific messages --> +  <string name="NoScenesFound">No scenes defined in GLTF file</string> +  <string name="InvalidMeshReference">Node [NODE_NAME] references invalid mesh [MESH_INDEX] (total meshes: [TOTAL_MESHES])</string> +  <string name="InvalidGeometryNonTriangulated">Mesh [MESH_NAME] primitive [PRIMITIVE_INDEX]: Invalid geometry with [INDEX_COUNT] indices (must be triangulated)</string> +  <string name="EmptyVertexArray">Mesh [MESH_NAME] primitive [PRIMITIVE_INDEX]: Empty vertex array</string> +  <string name="ErrorIndexLimit">Unable to process mesh [MESH_NAME] due to 65,534 vertex limit. Vertex count: [VERTEX_COUNT]</string> +  <string name="TextureFound">Found texture: [TEXTURE_NAME] for material: [MATERIAL_NAME]</string> +  <string name="IgnoredExtension">Model uses unsupported extension: [EXT], related material properties are ignored</string> +  <string name="UnsupportedExtension">Unable to load model, unsupported extension: [EXT]</string> +  <string name="FailedToCreateTempFile">Failed to create temporary file for embedded [TEXTURE_TYPE] texture [TEXTURE_INDEX]: [TEMP_FILE]</string> +  <string name="SkinJointsOverLimit">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, maximum is: [MAX]. Unused joints will be stripped on per model basis.</string> +  <string name="SkinUsupportedJoints">Skin [SKIN_INDEX] defines [JOINT_COUNT] joints, but only [LEGAL_COUNT] were recognized and are compatible</string> +  <string name="SkinUnusedJoints">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, of them only [USED_COUNT] were used</string> +  <string name="ModelTooManyJoints">Model [MODEL_NAME] uses [JOINT_COUNT], maximum: [MAX], upload might fail</string> +  <string name="ModelSplitPrimitive">Too many vertices in primitive [MODEL_NAME], it was split into [FACE_COUNT] faces</string> +  <string name="ModelTooManySubmodels">Model [MODEL_NAME] contains [SUBMODEL_COUNT] generated mesh parts, parts were trimmed to [SUBMODEL_LIMIT]</string> +  <string name="ParsingErrorMissingBuffer">Buffer is either missing or empty [BUFFER_NAME].</string> +  <string name="ParsingErrorMissingBufferBin">Buffer is either missing or empty. Check presence of [BUFFER_URI] file.</string> +  <string name="ParsingErrorException">Parser failed to process [FILENAME], file might be corrupt, incomplete or protected from reading. Exception: [EXCEPTION].</string>    <panel      follows="top|left" @@ -1404,7 +1427,7 @@           word_wrap="true">          </text_editor>          <check_box -         control_name="ImporterDebug" +         control_name="ImporterDebugVerboseLogging"           follows="top|left"           top_pad="9"           left="6" @@ -1706,7 +1729,6 @@ Analysed:     height="408"/>    <panel      follows="right|bottom" -    can_resize="false"      height="140"      layout="topleft"      name="right_panel" diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml index 6633e25099..f2309eb817 100644 --- a/indra/newview/skins/default/xui/en/floater_stats.xml +++ b/indra/newview/skins/default/xui/en/floater_stats.xml @@ -54,6 +54,19 @@                    label="jitter"                    decimal_digits="1"                    stat="frametimejitter"/> +          <stat_bar name="normalized_cumulative_frametime" +                    label="normalized sess. jitter" +                    decimal_digits="4" +                    stat="normalizedframetimejitter"/> +          <stat_bar name="frame_events_per_minute" +                    label="frame events/minute" +                    decimal_digits="2" +                    stat="frametimeeventspm"/> +          <stat_bar name="frame_events_last_minute" +                    label="frame events last min." +                    decimal_digits="0" +                    stat="frametimeeventslastmin"/> +                    <stat_bar name="bandwidth"                    label="UDP Data Received"                    stat="activemessagedatareceived" @@ -74,6 +87,38 @@          <stat_view name="render"                     label="Render"                     setting="OpenDebugStatRender"> +            <stat_bar name="framet_cumulative" +                    label="jitter cumulative" +                    decimal_digits="1" +                    stat="frametimejitcumulative"/> +            <stat_bar name="framet_jitter_99th" +                      label="jitter 99th percentile" +                      decimal_digits="1" +                      stat="frametimejitter99"/> +            <stat_bar name="framet_jitter_95th" +                      label="jitter 95th percentile" +                      decimal_digits="1" +                      stat="frametimejitter95"/> +            <stat_bar name="framet_jitter_stddev" +                      label="frametime jitter std dev" +                      decimal_digits="1" +                      stat="frametimejitterstddev"/> +            <stat_bar name="framet_99th" +                      label="frametime 99th percentile" +                      decimal_digits="1" +                      stat="frametime99"/> +            <stat_bar name="framet_95th" +                      label="frametime 95th percentile" +                      decimal_digits="1" +                      stat="frametime95"/> +            <stat_bar name="framet_stddev" +                      label="frametime std dev" +                      decimal_digits="1" +                      stat="frametimestddev"/> +            <stat_bar name="framet_events" +                      label="frametime events" +                      decimal_digits="0" +                      stat="frametimeevents"/>            <stat_bar name="ktrisframe"                      label="KTris per Frame"                      unit_label="ktris/fr" diff --git a/indra/newview/skins/default/xui/en/floater_test_slapp.xml b/indra/newview/skins/default/xui/en/floater_test_slapp.xml index dd2720816a..5a13a0147e 100644 --- a/indra/newview/skins/default/xui/en/floater_test_slapp.xml +++ b/indra/newview/skins/default/xui/en/floater_test_slapp.xml @@ -8,7 +8,7 @@   width="500">     <floater.string      name="remove_folder_slapp"> -    secondlife://app/remove_folder/?folder_id= +    secondlife:///app/remove_folder/?folder_id=     </floater.string>     <text      type="string" @@ -42,7 +42,7 @@      width="450"      layout="topleft"      left="16"> -        secondlife://app/wear_folder/?folder_name=Daisy +        secondlife:///app/wear_folder/?folder_name=Daisy     </text>     <text      type="string" @@ -63,7 +63,7 @@      width="450"      layout="topleft"      left="16"> -        secondlife://app/add_folder/?folder_name=Cardboard%20Boxbot +        secondlife:///app/add_folder/?folder_name=Cardboard%20Boxbot     </text>     <text      type="string" @@ -73,7 +73,7 @@      height="16"      width="450"      layout="topleft"> -        secondlife://app/add_folder/?folder_id=59219db2-c260-87d3-213d-bb3bc298a3d8 +        secondlife:///app/add_folder/?folder_id=59219db2-c260-87d3-213d-bb3bc298a3d8     </text>     <text      type="string" @@ -118,6 +118,6 @@      name="remove_folder_txt"      layout="topleft"      left="16"> -        secondlife://app/remove_folder/?folder_id= +        secondlife:///app/remove_folder/?folder_id=     </text>  </floater> diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index 5ab0177de6..0ce64b7a83 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -455,7 +455,7 @@      <panel       follows="right|top|bottom" -	 height="330" +	 height="235"  	 top_pad="0"  	 width="238"  	 name="layout_panel_4"> @@ -532,9 +532,9 @@       width="16" />      <search_editor       follows="top|right" -  search_button_visible="false" +     search_button_visible="false"       height="22" -  text_readonly_color="DkGray" +     text_readonly_color="DkGray"       label="Regions by Name"       layout="topleft"       top_delta="-2" @@ -542,10 +542,7 @@       name="location"       select_on_focus="true"       tool_tip="Type the name of a region" -     width="152"> -               <search_editor.commit_callback -                function="WMap.Location" /> -  </search_editor> +     width="152"/>     <button       follows="top|right"       height="23" @@ -594,6 +591,13 @@  		<scroll_list.commit_callback  		function="WMap.SearchResult" />      </scroll_list> +    </panel> +    <panel +     follows="right|bottom" +	 height="95" +	 top_pad="0" +	 width="238" +	 name="layout_panel_7">        <text        type="string"        length="1" diff --git a/indra/newview/skins/default/xui/en/menu_url_parcel.xml b/indra/newview/skins/default/xui/en/menu_url_parcel.xml index e0f1fcf9c3..95752dab66 100644 --- a/indra/newview/skins/default/xui/en/menu_url_parcel.xml +++ b/indra/newview/skins/default/xui/en/menu_url_parcel.xml @@ -16,7 +16,7 @@       layout="topleft"       name="show_on_map">          <menu_item_call.on_click -         function="Url.ShowOnMap" /> +         function="Url.ShowParcelOnMap" />      </menu_item_call>      <menu_item_separator       layout="topleft" /> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 10b15bfb93..081e5be014 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -411,11 +411,11 @@        </menu_item_call>        <menu_item_separator/>        <menu_item_call -        label="Complete avatars..." -        name="Avatar Picker"> +        label="Avatar Welcome Pack..." +        name="Avatar Welcome Pack">          <menu_item_call.on_click           function="Floater.ToggleOrBringToFront" -         parameter="avatar" /> +         parameter="avatar_welcome_pack" />        </menu_item_call>        <menu_item_separator/> @@ -2866,12 +2866,18 @@ function="World.EnvPreset"                   function="Advanced.ForceErrorWorkQueueCrash" />              </menu_item_call>              <menu_item_call -             label="Force a Crash in a Thread" -             name="Force a Crash in a Thread"> +             label="Force an LLError Crash in a Thread" +             name="Force an LLError Crash in a Thread">                  <menu_item_call.on_click                   function="Advanced.ForceErrorThreadCrash" />              </menu_item_call>              <menu_item_call +             label="Force an Exception Crash in a Thread" +             name="Force an Exception Crash in a Thread"> +                <menu_item_call.on_click +                 function="Advanced.ForceExceptionThreadCrash" /> +            </menu_item_call> +            <menu_item_call               label="Force Disconnect Viewer"               name="Force Disconnect Viewer">                  <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index cd888d4448..773e7c311b 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -7162,6 +7162,20 @@ You don't have permission to view this notecard.    </notification>    <notification +   icon="alertmodal.tga" +   name="MaterialImagesWereScaled" +   type="alertmodal"> +One or more textures in this material were scaled to be within the allowed limits. +Textures must have power of two dimensions and must not exceed [MAX_SIZE]x[MAX_SIZE] pixels. +    <unique/> +    <tag>confirm</tag> +    <usetemplate +     ignoretext="Warn if textures will be scaled during upload." +     name="okignore" +     yestext="OK"/> +  </notification> + +  <notification     icon="notifytip.tga"     name="RezItemNoPermissions"     type="notifytip"> @@ -9479,8 +9493,11 @@ Unable to upload texture: '[NAME]'     icon="alertmodal.tga"     name="CannotUploadMaterial"     type="alertmodal"> -There was a problem uploading the file +Unable to upload material file. The file may be corrupted, in an unsupported format, or contain invalid data. Please check that you're using a valid GLTF/GLB file with proper material definitions.      <tag>fail</tag> +    <usetemplate +     name="okbutton" +     yestext="OK"/>    </notification>    <notification @@ -12645,4 +12662,15 @@ are wearing now.      Unable to apply material to the water exclusion surface.      <tag>fail</tag>    </notification> + +  <notification +   icon="notify.tga" +   name="ImageUploadResized" +   type="alertmodal"> +      The texture you are uploading has been resized from [ORIGINAL_WIDTH]x[ORIGINAL_HEIGHT] to [NEW_WIDTH]x[NEW_HEIGHT] in order to to fit the maximum size of [MAX_WIDTH]x[MAX_HEIGHT] pixels. +      <usetemplate +          ignoretext="Image Upload Resized" +          name="okignore" +          yestext="OK"/> +  </notification>  </notifications> diff --git a/indra/newview/skins/default/xui/en/panel_login_first.xml b/indra/newview/skins/default/xui/en/panel_login_first.xml deleted file mode 100644 index d6ac71db94..0000000000 --- a/indra/newview/skins/default/xui/en/panel_login_first.xml +++ /dev/null @@ -1,262 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel -  follows="all" -  height="768" -  layout="topleft" -  name="panel_login" -  focus_root="true" -  background_visible="true" -  bg_opaque_color="0.16 0.16 0.16 1" -  background_opaque="true" -  width="1024"> -  <panel.string -    name="forgot_password_url"> -    http://secondlife.com/account/request.php -  </panel.string> -  <panel.string -    name="sign_up_url"> -    https://join.secondlife.com/ -  </panel.string> -  <layout_stack -    follows="left|right|top|bottom" -    width="1024" -    height="768" -    left="0" -    name="logo_stack" -    orientation="vertical" -    top="0"> -    <layout_panel -      height="18" -      auto_resize="false" -      name="page_top" -      width="1024" /> -    <!-- start of logo stack --> -    <layout_panel -      height="130" -      min_height="10" -      auto_resize="false" -      name="parent_panel" -      width="1024"> -      <layout_stack -        follows="left|right|top|bottom" -        height="100" -        left="0" -        name="logo_stack" -        orientation="horizontal" -        top="0" -        width="1024"> -        <layout_panel -          height="110" -          min_height="10" -          auto_resize="true" -          name="logo_left" -          width="300" /> -        <layout_panel -          auto_resize="false" -          follows="left|right|top" -          name="logo_container" -          width="225" -          left="0" -          top="0" -          height="105"> -          <icon -            height="94" -            image_name="login_sl_logo" -            left="0" -            name="sl_logo" -            top="0" /> -        </layout_panel> -        <layout_panel -          height="100" -          name="logo_right" -          auto_resize="true" -          width="300" /> -      </layout_stack> -    </layout_panel> -    <!-- end of logo stack --> -    <!-- start of widget stack --> -    <layout_panel -      height="100" -      min_height="10" -      auto_resize="false" -      name="parent_panel2" -      width="1024"> -      <layout_stack -        follows="left|right|top|bottom" -        height="80" -        left="0" -        name="widget_stack" -        orientation="horizontal" -        top="0" -        width="1024"> -        <layout_panel -          height="80" -          min_height="10" -          auto_resize="true" -          name="widget_left" -          width="200" /> -        <layout_panel -          auto_resize="false" -          follows="left|right|top" -          name="widget_container" -          width="730" -          left="0" -          top="0" -          height="80"> -          <combo_box -            allow_text_entry="true" -            follows="left|bottom" -            height="32" -            left="42" -            label="Username" -            combo_editor.font="SansSerifLarge" -            max_chars="128" -            top="0" -            combo_editor.prevalidator="ascii" -            tool_tip="The username you chose when you registered, like bobsmith12 or Steller Sunshine" -            name="username_combo" -            width="232"> -            <combo_box.combo_editor -                text_pad_left="8" /> -            <combo_box.combo_button -                visible ="false"/> -              <combo_box.drop_down_button -                visible ="false"/> -          </combo_box> -          <line_editor -            follows="left|top" -            width="200" -            height="32" -            left="262" -            max_length_chars="16" -            name="password_edit" -            label="Password" -            text_pad_left="8" -            font="SansSerifLarge" -            is_password="true" -            select_on_focus="true" -            commit_on_focus_lost="false" -            top="0" /> -          <button -            follows="left|top" -            image_unselected="PushButton_Login" -            image_pressed="PushButton_Login_Pressed" -            image_hover_unselected="PushButton_Login_Over" -            label="Log In" -            label_color="White" -            font="SansSerifLarge" -            name="connect_btn" -  	        left_pad="15" -            width="120" -            height="32" -            top="0" /> -          <text -            follows="left|top" -            font="SansSerifLarge" -            font.style="BOLD" -            text_color="EmphasisColor" -            height="34" -            name="sign_up_text" -            left_pad="10" -            top="0" -            width="200" -            valign="center"> -            Sign up -          </text> -          <check_box -            follows="left|top" -            font="SansSerifLarge" -            left="42" -            top="32" -            height="24" -            label="Remember me" -            word_wrap="down" -            check_button.bottom="3" -            name="remember_name" -            tool_tip="Already remembered user can be forgotten from Me > Preferences > Advanced > Remembered Usernames." -            width="198" /> -          <check_box -            control_name="RememberPassword" -            follows="left|top" -            font="SansSerifLarge" -            height="24" -            left="262" -            bottom_delta="0" -            label="Remember password" -            word_wrap="down" -            check_button.bottom="3" -            name="remember_password" -            width="198" /> -          <text -            follows="left|top" -            font="SansSerifLarge" -            text_color="EmphasisColor" -            height="16" -            name="forgot_password_text" -            left="492" -            top="34" -            width="200"> -            Forgotten password -          </text> -        </layout_panel> -        <layout_panel -          height="100" -          name="widget_right" -          auto_resize="true" -          width="200" /> -      </layout_stack> -    </layout_panel> -    <!-- end of widget stack --> -    <!-- start of images stack --> -    <layout_panel -      height="500" -      min_height="10" -      auto_resize="false" -      name="parent_panel3" -      width="1024"> -      <layout_stack -        follows="left|right|top|bottom" -        height="500" -        left="0" -        name="images_stack" -        orientation="horizontal" -        top="0" -        width="1024"> -        <layout_panel -          height="500" -          min_height="10" -          auto_resize="true" -          name="images_left" -          width="96" /> -        <layout_panel -          auto_resize="false" -          follows="left|right|top" -          name="images_container" -          width="675" -          left="0" -          top="0" -          height="500"> -          <icon -            height="450" -            width="675" -            image_name="first_login_image" -            left="0" -            name="image_left" -            top="0" /> -        </layout_panel> -        <layout_panel -          height="100" -          name="images_right" -          auto_resize="true" -          width="96" /> -      </layout_stack> -    </layout_panel> -    <!-- end of images stack --> -    <layout_panel -      height="400" -      min_height="10" -      auto_resize="true" -      name="page_bottom" -      width="1024" /> -  </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml index 8051ffa8ec..86999b1afb 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml @@ -32,15 +32,15 @@     height="23"     increment="64"     initial_value="1024" -   label="Cache size (256 - 9984MB)" +   label="Cache size (896 - 32768MB)"     label_width="150"     layout="topleft"     left="80" -   max_val="9984" -   min_val="256" +   max_val="32768" +   min_val="896"     top_pad="10"     name="cachesizespinner" -   width="200" /> +   width="210" />    <text     type="string"     length="1" @@ -59,7 +59,7 @@     label="Clear Cache"     label_selected="Clear Cache"     layout="topleft" -   left_pad="30" +   left_pad="20"     name="clear_cache"     top_delta="0"     width="100"> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml index 5041fb4878..1c00837073 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml @@ -1,15 +1,31 @@  <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel +  <panel      border="true"      follows="left|top|right|bottom" -    height="408" -    label="Communication" +    height="438" +    label="Privacy"      layout="topleft"      left="102" -    name="im" +    name="Privacy panel"      top="1"      width="517"> +  <tab_container +    top_pad="0" +    enabled="true" +    follows="left|top" +    height="430" +    width="517" +    left_delta="0" +    name="privacy_tab_container" +    tab_position="top" +    tab_stop="false"> +  <panel +    label="General" +    name="privacy_preferences_general" +    layout="topleft" +    follows="top|left"> +    <panel.string        name="log_in_to_change">      log in to change @@ -63,7 +79,7 @@        name="online_visibility"        top_pad="30"        width="350" /> -     +      <check_box       enabled_control="EnableVoiceChat"       control_name="AutoDisengageMic" @@ -74,6 +90,37 @@       name="auto_disengage_mic_check"       top_pad="10"       width="350" /> +    <check_box +     control_name="EnableLookAtTarget" +     height="16" +     label="Enable LookAt" +     tool_tip="Enable tracking cursor position with avatar head's rotation" +     layout="topleft" +     left="30" +     name="enable_lookat_target" +     top_pad="10" +     width="350" /> +    <check_box +     enabled_control="EnableLookAtTarget" +     control_name="LimitLookAtTarget" +     height="16" +     label="Limit LookAt Distance" +     tool_tip="Limit the look at target's distance by restricting it around the avatar's head" +     layout="topleft" +     left="50" +     name="limit_lookat_distance" +     top_pad="10" +     width="350" /> +    <check_box +     control_name="EnableSelectionHints" +     height="16" +     label="Enable Selection Hints" +     tool_tip="Enable reporting and tracking current selection using 'beam' particles and character animations" +     layout="topleft" +     left="30" +     name="enable_selection_hints" +     top_pad="10" +     width="350" />      <button       follows="left|top"       height="23" @@ -103,3 +150,48 @@         (People and/or Objects you have blocked)      </text>      </panel> + +    <panel +      label="Discord" +      name="privacy_preferences_discord" +      layout="topleft" +      follows="top|left"> + +      <check_box +        control_name="EnableDiscord" +        height="16" +        enabled="true" +        label="Enable Discord integration" +        layout="topleft" +        left="30" +        name="enable_discord" +        top_pad="20" +        width="350" /> + +      <check_box +        enabled_control="EnableDiscord" +        control_name="ShowDiscordActivityDetails" +        height="16" +        enabled="true" +        label="Show avatar name" +        layout="topleft" +        left="30" +        name="show_name" +        top_pad="20" +        width="350" /> + +      <check_box +        enabled_control="EnableDiscord" +        control_name="ShowDiscordActivityState" +        height="16" +        enabled="false" +        label="Show location" +        layout="topleft" +        left="30" +        name="show_location" +        top_pad="20" +        width="350" /> +      </panel> +  </tab_container> + +</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 d909a56733..52413abe74 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml @@ -62,20 +62,9 @@       name="mute_when_minimized"  	   top_delta="3"       left_pad="5" -     width="20" /> -     <!-- *HACK  -     	After storm-1109 will be fixed: instead of using this text_box, word_wrap should be applied for "mute_when_minimized" check_box's label.--> -     <text -      follows="top|left" -      height="15" -      layout="topleft" -      left_pad="0" -      name="mute_chb_label" -      top_delta="-1" -      width="150" -      wrap="true"> -      Mute when minimized -     </text> +     width="20" +     label="Mute when minimized" +     word_wrap="true"/>  	<slider  		control_name="AudioLevelUI"  		disabled_control="MuteAudio" @@ -321,66 +310,45 @@  		left_pad="5"  		name="enable_voice_check"  		width="110"/> -	<!-- -->    <text     type="string"     length="1"     follows="left|top"     layout="topleft"     left="23" -   top_delta="22" +   top_delta="25"     name="Listen media from"     height="15" -   word_wrap="true" -   width="112"> -      Hear media and sounds from: +   width="165" +   halign="right"> +      Hear media and sounds from    </text> -  <radio_group +  <combo_box     control_name="MediaSoundsEarLocation"     follows="left|top" -   top_delta="-6"     layout="topleft" -   left_pad="10" -   width="360" -   height="40" -   name="media_ear_location"> -      <radio_item -       height="19" -       label="Camera position" -       follows="left|top" -       layout="topleft" -       name="0" -       width="200"/> -      <radio_item -       height="19" -       follows="left|top" -       label="Avatar position" -       layout="topleft" -       left_delta="0" -       name="1" -       top_delta ="18" -       width="200" /> -  </radio_group>   - 	<check_box -    name="media_show_on_others_btn" -    control_name="MediaShowOnOthers" -    value="true" -    follows="left|top" -    layout="topleft"  -    height="15" -    top_pad="8" -    tool_tip="Uncheck this to hide media attached to other avatars nearby" -    label="Play media attached to other avatars" -    left="20" -    width="230"/> +   left_pad="5" +   width="130" +   height="23" +   top_delta="-4" +   name="media_ear_location_combo"> +   <item +      label="Camera position" +      name="camera_position" +      value="0" /> +   <item +      label="Avatar position" +      name="avatar_position" +      value="1" /> +</combo_box>    <text     follows="left|top"     layout="topleft"     height="15"     left="23" -   top_pad="8" -   width="120" -   name="media_autoplay_label"> +   width="165" +   name="media_autoplay_label" +   halign="right">      Auto-play media    </text>    <combo_box @@ -389,10 +357,10 @@          follows="left|top"          layout="topleft"          height="23" -        left_pad="-15" +        left_delta="170"          top_delta="-4"          name="media_auto_play_combo" -        width="115"> +        width="130">      <item          label="Never"          name="autoplay_disabled" @@ -406,23 +374,106 @@          name="autoplay_ask"          value="2"/>    </combo_box> +    <text +        follows="left|top" +        layout="topleft" +        height="15" +        left="23" +        width="165" +        name="media_firstinteract_label" +        halign="right"> +            Media first-interact +    </text> +    <combo_box +    control_name="MediaFirstClickInteract" +    enabled_control="AudioStreamingMedia" +    follows="left|top" +    layout="topleft" +    height="23" +    left_delta="170" +    top_delta="-4" +    width="130" +    name="media_first_interact_combo" +    tool_tip="This setting controls which media (once loaded) does not require a first click to focus before interaction can begin. This allows clicks to be passed directly to media bypassing the focus click requirement. Each option also inherits the previous ones."> +        <item +            label="Disabled" +            name="media_first_click_none" +            value="0"/> +        <item +            label="Worn HUDs only" +            name="media_first_click_hud" +            value="1"/> +        <item +            label="Owned objects" +            name="media_first_click_own" +            value="3"/> +        <item +            label="Friends' objects" +            name="media_first_click_friend" +            value="7"/> +        <item +            label="Group objects" +            name="media_first_click_group" +            value="15"/> +        <item +            label="Landowner objects" +            name="media_first_click_land" +            value="31"/> +        <item +            label="Anyone's objects" +            name="media_first_interact_any" +            value="32767"/> +        <item +            label="All MOAP" +            name="media_first_click_all" +            value="65535"/> +    </combo_box> +   <check_box +    name="media_show_on_others_btn" +    control_name="MediaShowOnOthers" +    enabled_control="AudioStreamingMedia" +    value="true" +    follows="left|top" +    tool_tip="Uncheck this to hide media attached to other avatars nearby" +    label="Play media attached to other avatars" +    left="23" +    width="15" +    top_delta="30" +    height="15"/> +  <check_box +    name="media_huds_autoplay" +    control_name="MediaAutoPlayHuds" +    enabled_control="AudioStreamingMedia" +    value="true" +    follows="left|top" +    layout="topleft" +    tool_tip="Uncheck this to make HUDs follow the standard media auto-play setting" +    label="Auto-play media attached to your HUD" +    left="260" +    top_pad="-15" +    width="15" +    height="15"/>    <text     layout="topleft" +   follows="left"     height="15" -   left="260" -   top_pad="-18" -   width="100" -   name="noise_suppression_label"> -     Noise Suppression +   width="165" +   name="noise_suppression_label" +   left="23" +   top_delta="22" +   halign="right"> +     Microphone Noise Suppression    </text>    <combo_box     control_name="VoiceNoiseSuppressionLevel" +   enabled_control="EnableVoiceChat" +   follows="left|top"     layout="topleft" +   left_delta="170" +   top_delta="-6" +   width="130"     height="23" -   left_pad="10" -   top_pad="-20" -   name="noise_suppression_combo" -   width="80"> +   name="noise_suppression_combo">      <item       label="Off"       name="noise_suppression_none" @@ -452,48 +503,44 @@     left="23"     top_delta="30"     name="Listen from" -   width="112"> -     Hear voice from: +   width="165" +   height="15" +   halign="right"> +     Hear voice from    </text> -  <radio_group +  <combo_box     enabled_control="EnableVoiceChat"     control_name="VoiceEarLocation"     follows="left|top"     layout="topleft" -   left_pad="10" +   left_delta="170"     top_delta="-6" -   width="360" -   height="40" -   name="ear_location"> -   <radio_item -    height="19" -    label="Camera position" -    follows="left|top" -    layout="topleft" -    name="0" -    width="200"/> -   <radio_item -    height="19" -    follows="left|top" -    label="Avatar position" -    layout="topleft" -    left_delta="0" -    name="1" -    top_delta ="18"  -    width="200" /> -  </radio_group> +   width="130" +   height="23" +   name="ear_location_combo"> +   <item +      label="Camera position" +      name="camera_position" +      value="0" /> +   <item +      label="Avatar position" +      name="avatar_position" +      value="1" /> +</combo_box>    <check_box     control_name="LipSyncEnabled" +   enabled_control="EnableVoiceChat"     follows="left|top"     height="15"     label="Move avatar lips when speaking"     layout="topleft"     left="20"     name="enable_lip_sync" -   top_pad="10"  +   top_pad="8"     width="237"/>    <check_box     control_name="VoiceEchoCancellation" +   enabled_control="EnableVoiceChat"     height="15"     tool_tip="Check to enable voice echo cancellation"     label="Echo Cancellation" @@ -516,6 +563,7 @@     top_pad="5"/>    <check_box     control_name="VoiceAutomaticGainControl" +   enabled_control="EnableVoiceChat"     height="15"     tool_tip="Check to enable automatic gain control"     label="Automatic Gain Control" @@ -537,6 +585,7 @@     left="20"/>    <check_box     control_name="VoiceVisualizerEnabled" +   enabled_control="EnableVoiceChat"     height="15"     tool_tip="Check to show voice dot indicator above avatars"     label="Show voice dot above avatars" @@ -547,6 +596,7 @@     width="200"/>    <button     control_name="ShowDeviceSettings" +   enabled_control="EnableVoiceChat"     follows="left|top"     height="23"     is_toggle="true" diff --git a/indra/newview/skins/default/xui/en/panel_profile_pick.xml b/indra/newview/skins/default/xui/en/panel_profile_pick.xml index 024120931f..4f441b9b49 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_pick.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_pick.xml @@ -200,6 +200,26 @@          <layout_panel           follows="all" +         layout="bottomleft" +         left_pad="2" +         name="set_to_curr_location_btn_lp" +         auto_resize="false" +         width="100"> +          <button +           name="set_to_curr_location_btn" +           label="Set Location" +           tool_tip="Set to Current Location" +           left="0" +           top="0" +           height="23" +           width="100" +           follows="left|top" +           layout="topleft" +          /> +        </layout_panel> + +        <layout_panel +         follows="all"           layout="topleft"           name="util_resizer_right"           auto_resize="true" diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml index 4501e0df3a..cf52916484 100644 --- a/indra/newview/skins/default/xui/en/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml @@ -86,7 +86,7 @@       height="18"       left="0"        name="balance" -     tool_tip="Click to refresh your L$ balance" +     tool_tip="L$ [AMT]
Click to refresh your L$ balance.
Double-click to display or hide your L$ balance."       v_pad="4"       top="0"       wrap="false"  diff --git a/indra/newview/skins/default/xui/en/panel_tools_texture.xml b/indra/newview/skins/default/xui/en/panel_tools_texture.xml index b7db9dec96..1c70383bf9 100644 --- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml +++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml @@ -986,6 +986,19 @@               name="gltfTextureScaleV"               width="265" />              <spinner +             decimal_digits="1" +             follows="left|top" +             height="19" +             initial_value="" +			 label="Repeats per meter" +             layout="topleft" +			 label_width="205" +             left="10" +             max_val="100" +             min_val="-100" +             name="gltfRptctrl" +             width="265" /> +            <spinner               follows="left|top"               height="19"               initial_value="0" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index bb8c8d6baf..21db7eba47 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -4187,7 +4187,7 @@ Try enclosing path to the editor with double quotes.  name="Command_360_Capture_Label">360 snapshot</string>    <string name="Command_AboutLand_Label">About land</string>    <string name="Command_Appearance_Label">Outfits</string> -  <string name="Command_Avatar_Label">Complete avatars</string> +  <string name="Command_Avatar_Label">Avatar Welcome Pack</string>    <string name="Command_Build_Label">Build</string>    <string name="Command_Chat_Label">Chat</string>    <string name="Command_Conversations_Label">Conversations</string> diff --git a/indra/newview/skins/default/xui/es/panel_login_first.xml b/indra/newview/skins/default/xui/es/panel_login_first.xml deleted file mode 100644 index ccb6858351..0000000000 --- a/indra/newview/skins/default/xui/es/panel_login_first.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		http://secondlife.com/account/request.php?lang=es -	</panel.string> -	<panel.string name="sign_up_url"> -		https://join.secondlife.com/ -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="Nombre de usuario" name="username_combo" tool_tip="El nombre de usuario que elegiste al registrarte, como bobsmith12 o Steller Sunshine"/> -					<line_editor label="Contraseña" name="password_edit"/> -					<button label="Iniciar sesión" name="connect_btn"/> -					<check_box label="Recordarme" name="remember_check"/> -					<text name="forgot_password_text"> -						Contraseña olvidada -					</text> -					<text name="sign_up_text"> -						Regístrate -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						Tu primer destino es la Isla de aprendizaje. ¡Encuentra el portal de salida! -					</text> -					<text name="image_caption_right"> -						A continuación, puedes explorar la Isla social y hablar con otros residentes nuevos. -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/fr/panel_login_first.xml b/indra/newview/skins/default/xui/fr/panel_login_first.xml deleted file mode 100644 index 8f40d0230c..0000000000 --- a/indra/newview/skins/default/xui/fr/panel_login_first.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		http://secondlife.com/account/request.php?lang=fr -	</panel.string> -	<panel.string name="sign_up_url"> -		https://join.secondlife.com/ -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="Nom d'utilisateur" name="username_combo" tool_tip="Nom d'utilisateur que vous avez choisi lors de votre inscription (par exemple, bobsmith12 ou Steller Sunshine)."/> -					<line_editor label="Mot de passe" name="password_edit"/> -					<button label="Connexion" name="connect_btn"/> -					<check_box font="SansSerifSmall" label="Mémoriser mes informations" name="remember_check"/> -					<text name="forgot_password_text"> -						Mot de passe oublié -					</text> -					<text name="sign_up_text"> -						S'inscrire -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						Votre première étape est Learning Island. Trouvez le portail de sortie. -					</text> -					<text name="image_caption_right"> -						Puis explorez Social Island et faites la connaissance d'autres résidents. -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/it/panel_login_first.xml b/indra/newview/skins/default/xui/it/panel_login_first.xml deleted file mode 100644 index 5b04fd411a..0000000000 --- a/indra/newview/skins/default/xui/it/panel_login_first.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		http://secondlife.com/account/request.php?lang=it -	</panel.string> -	<panel.string name="sign_up_url"> -		http://join.secondlife.com/ -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="Nome utente" name="username_combo" tool_tip="Il nome utente che hai scelto durante la registrazione, come roby12 o Stella Solare"/> -					<line_editor label="Password" name="password_edit"/> -					<button label="Accedi" name="connect_btn"/> -					<check_box label="Ricordami" name="remember_check"/> -					<text name="forgot_password_text"> -						Password dimenticata -					</text> -					<text name="sign_up_text"> -						Registrati -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						Il primo passo è a Learning Island. Trova il portale di uscita! -					</text> -					<text name="image_caption_right"> -						Quindi esplora Social Island e incontra altri nuovi residenti. -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/ja/panel_login_first.xml b/indra/newview/skins/default/xui/ja/panel_login_first.xml deleted file mode 100644 index 0f987fc816..0000000000 --- a/indra/newview/skins/default/xui/ja/panel_login_first.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		https://secondlife.com/my/account/request.php?lang=ja-JP -	</panel.string> -	<panel.string name="sign_up_url"> -		https://join.secondlife.com/?lang=ja -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="page_top"/> -		<layout_panel name="parent_panel"> -			<layout_stack name="logo_stack"> -				<layout_panel name="logo_left"/> -				<layout_panel name="logo_container"> -					<icon name="sl_logo"/> -				</layout_panel> -				<layout_panel auto_resize="true"/> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_left"/> -				<layout_panel name="widget_container"> -					<combo_box label="ユーザ名" tool_tip="登録時に自分で選んだユーザー名(例:bobsmith12、Steller Sunshineなど)" name="username_combo"> -						<combo_box.combo_editor/> -						<combo_box.combo_button/> -						<combo_box.drop_down_button/> -					</combo_box> -					<line_editor name="password_edit" label="パスワード"/> -					<button label="ログイン" name="connect_btn"/> -					<text name="sign_up_text" valign="center"> -						サインアップ -					</text> -					<check_box label="ユーザ名を記憶" name="remember_name" tool_tip="すでに記憶されているユーザーは、「私」>「初期設定」>「詳細設定」>「記憶されたユーザー名」から削除できます。"/> -					<check_box label="パスワード記憶" name="remember_password"/> -					<text name="forgot_password_text"> -						パスワードを忘れましたか? -					</text> -				</layout_panel> -				<layout_panel name="widget_right"/> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_left"/> -				<layout_panel name="images_container"> -					<icon name="image_left"/> -				</layout_panel> -				<layout_panel name="images_right"/> -			</layout_stack> -		</layout_panel> -		<layout_panel name="page_bottom"/> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/pl/panel_login_first.xml b/indra/newview/skins/default/xui/pl/panel_login_first.xml deleted file mode 100644 index 0604ecbcff..0000000000 --- a/indra/newview/skins/default/xui/pl/panel_login_first.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel name="panel_login"> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="Użytkownik" tool_tip="Nazwa użytkownika wybrana przy rejestracji, np. bobsmith12 lub Steller Sunshine" name="username_combo" /> -					<line_editor name="password_edit" label="Hasło" /> -					<button label="Zaloguj" name="connect_btn" /> -					<check_box label="Zapamiętaj mnie" name="remember_check" /> -					<text name="forgot_password_text"> -						Zapomniałem/am hasła -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						Wyspa Nauki to Twój pierwszy krok. Znajdź portal z wyjściem! -					</text> -					<text name="image_caption_right"> -						Potem zwiedź Wyspę Towarzyską i poznaj innych nowych rezydentów! -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/pt/panel_login_first.xml b/indra/newview/skins/default/xui/pt/panel_login_first.xml deleted file mode 100644 index 86c61163bc..0000000000 --- a/indra/newview/skins/default/xui/pt/panel_login_first.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		http://secondlife.com/account/request.php?lang=pt -	</panel.string> -	<panel.string name="sign_up_url"> -		https://join.secondlife.com/ -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="Nome de usuário" name="username_combo" tool_tip="O nome de usuário que você escolheu ao fazer seu cadastro, como zecazc12 ou Magia Solar"/> -					<line_editor label="Senha" name="password_edit"/> -					<button label="Login" name="connect_btn"/> -					<check_box label="Lembrar-me" name="remember_check"/> -					<text name="forgot_password_text"> -						Senha esquecida -					</text> -					<text name="sign_up_text"> -						Cadastre-se -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						Sua primeira parada é a Ilha da Educação. Encontre o portal de saída! -					</text> -					<text name="image_caption_right"> -						Em seguida, explore a Ilha Social e encontre novos residentes! -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/ru/panel_login_first.xml b/indra/newview/skins/default/xui/ru/panel_login_first.xml deleted file mode 100644 index 5db81ea7ca..0000000000 --- a/indra/newview/skins/default/xui/ru/panel_login_first.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		http://secondlife.com/account/request.php -	</panel.string> -	<panel.string name="sign_up_url"> -		https://join.secondlife.com/ -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="Имя пользователя" name="username_combo" tool_tip="Имя пользователя, которое вы выбрали при регистрации, например, «bobsmith12» или «Steller Sunshine»"/> -					<line_editor label="Пароль" name="password_edit"/> -					<button label="Войти" name="connect_btn"/> -					<check_box label="Запомнить меня" name="remember_check"/> -					<text name="forgot_password_text"> -						Забытый пароль -					</text> -					<text name="sign_up_text"> -						Регистрация -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						Ваш первый шаг – Учебный остров. Найдите портал выхода! -					</text> -					<text name="image_caption_right"> -						Затем исследуйте Социальный остров и познакомьтесь с другими новичками! -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/tr/panel_login_first.xml b/indra/newview/skins/default/xui/tr/panel_login_first.xml deleted file mode 100644 index 1fc80c2b97..0000000000 --- a/indra/newview/skins/default/xui/tr/panel_login_first.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		http://secondlife.com/account/request.php -	</panel.string> -	<panel.string name="sign_up_url"> -		https://join.secondlife.com/ -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="Kullanıcı Adı" name="username_combo" tool_tip="Kaydolduğunuzda seçtiğiniz kullanıcı adı, örn. mustafayalcin12 veya Faruk Gungoren"/> -					<line_editor label="Parola" name="password_edit"/> -					<button label="Oturum Aç" name="connect_btn"/> -					<check_box label="Beni hatırla" name="remember_check"/> -					<text name="forgot_password_text"> -						Parolamı unuttum -					</text> -					<text name="sign_up_text"> -						Kaydol -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						Başlangıç yeriniz Eğitim Adası. Haydi çıkış portalını bulun! -					</text> -					<text name="image_caption_right"> -						Sonra da Sosyal Ada'yı keşfe çıkın ve diğer LS sakinleriyle tanışın! -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_login_first.xml b/indra/newview/skins/default/xui/zh/panel_login_first.xml deleted file mode 100644 index 4d72fcdd03..0000000000 --- a/indra/newview/skins/default/xui/zh/panel_login_first.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel name="panel_login"> -	<panel.string name="forgot_password_url"> -		http://secondlife.com/account/request.php -	</panel.string> -	<panel.string name="sign_up_url"> -		http://join.secondlife.com/ -	</panel.string> -	<layout_stack name="logo_stack"> -		<layout_panel name="parent_panel2"> -			<layout_stack name="widget_stack"> -				<layout_panel name="widget_container"> -					<combo_box label="使用者名稱" name="username_combo" tool_tip="使用者名稱是你註冊時所挑選的,例如 bobsmith12 或 Steller Sunshine"/> -					<line_editor label="密碼" name="password_edit"/> -					<button label="登入" name="connect_btn"/> -					<check_box label="記得我" name="remember_check"/> -					<text name="forgot_password_text"> -						忘記密碼 -					</text> -					<text name="sign_up_text"> -						註冊 -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -		<layout_panel name="parent_panel3"> -			<layout_stack name="images_stack"> -				<layout_panel name="images_container"> -					<text name="image_caption_left"> -						你在「學習島」的第一步。 找到離開的傳送門! -					</text> -					<text name="image_caption_right"> -						接著,到「社交島」探索,認識新的居民朋友! -					</text> -				</layout_panel> -			</layout_stack> -		</layout_panel> -	</layout_stack> -</panel> diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp index d5e281bba8..10c68432a1 100644 --- a/indra/newview/tests/llviewerassetstats_test.cpp +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -43,12 +43,15 @@ namespace LLStatViewer      LLTrace::SampleStatHandle<>     FPS_SAMPLE("fpssample");  } -void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars) +void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars)  {      counts.resize(3);      counts[0] = 0;      counts[1] = 0;      counts[2] = 1; +    cloud_avatars = 0; +    pending_meshes = 0; +    control_avatars = 0;  }  // static diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index cada4a2cd6..04c3fea93a 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -149,7 +149,6 @@ class ViewerManifest(LLManifest):              with self.prefix(src_dst="skins"):                      # include the entire textures directory recursively                      with self.prefix(src_dst="*/textures"): -                            self.path("*/*.jpg")                              self.path("*/*.png")                              self.path("*.tga")                              self.path("*.j2c") @@ -557,6 +556,9 @@ class Windows_x86_64_Manifest(ViewerManifest):              ):                  self.path(libfile) +            if self.args['discord'] == 'ON': +                self.path("discord_partner_sdk.dll") +              if self.args['openal'] == 'ON':                  # Get openal dll                  self.path("OpenAL32.dll") @@ -1019,6 +1021,13 @@ class Darwin_x86_64_Manifest(ViewerManifest):                                  ):                      self.path2basename(relpkgdir, libfile) +                # Discord social SDK +                if self.args['discord'] == 'ON': +                    for libfile in ( +                                "libdiscord_partner_sdk.dylib", +                                ): +                        self.path2basename(relpkgdir, libfile) +                  # OpenAL dylibs                  if self.args['openal'] == 'ON':                      for libfile in ( @@ -1385,6 +1394,7 @@ if __name__ == "__main__":      extra_arguments = [          dict(name='bugsplat', description="""BugSplat database to which to post crashes,               if BugSplat crash reporting is desired""", default=''), +        dict(name='discord', description="""Indication discord social sdk libraries are needed""", default='OFF'),          dict(name='openal', description="""Indication openal libraries are needed""", default='OFF'),          dict(name='tracy', description="""Indication tracy profiler is enabled""", default='OFF'),          ]  | 
