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'), ] |