summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/cmake/CMakeLists.txt1
-rw-r--r--indra/cmake/Copy3rdPartyLibs.cmake11
-rw-r--r--indra/cmake/Discord.cmake11
-rw-r--r--indra/cmake/Linking.cmake2
-rw-r--r--indra/llappearance/CMakeLists.txt1
-rw-r--r--indra/llappearance/llavatarappearance.cpp66
-rw-r--r--indra/llappearance/llavatarappearance.h10
-rw-r--r--indra/llappearance/lljointdata.h66
-rw-r--r--indra/llappearance/lltexlayer.cpp29
-rw-r--r--indra/llcharacter/llbvhloader.cpp6
-rw-r--r--indra/llcharacter/llbvhloader.h2
-rw-r--r--indra/llcharacter/llcharacter.cpp7
-rw-r--r--indra/llcharacter/llcharacter.h2
-rw-r--r--indra/llcharacter/lljoint.cpp7
-rw-r--r--indra/llcharacter/lljoint.h2
-rw-r--r--indra/llcommon/llthread.cpp105
-rw-r--r--indra/llcommon/llthread.h7
-rw-r--r--indra/llcommon/workqueue.cpp24
-rw-r--r--indra/llfilesystem/lldir.cpp5
-rw-r--r--indra/llimage/llimagedimensionsinfo.cpp20
-rw-r--r--indra/llimage/llimagedimensionsinfo.h4
-rw-r--r--indra/llimage/llimagej2c.cpp9
-rw-r--r--indra/llimagej2coj/llimagej2coj.cpp217
-rw-r--r--indra/llimagej2coj/llimagej2coj.h2
-rw-r--r--indra/llinventory/llparcel.h102
-rw-r--r--indra/llinventory/llsettingsbase.cpp6
-rw-r--r--indra/llinventory/llsettingssky.cpp7
-rw-r--r--indra/llmath/raytrace.cpp1
-rw-r--r--indra/llmessage/patch_code.cpp1
-rw-r--r--indra/llmessage/patch_dct.cpp1
-rw-r--r--indra/llmessage/patch_idct.cpp1
-rw-r--r--indra/llplugin/llpluginprocessparent.cpp2
-rw-r--r--indra/llprimitive/CMakeLists.txt2
-rw-r--r--indra/llprimitive/lldaeloader.cpp18
-rw-r--r--indra/llprimitive/lldaeloader.h30
-rw-r--r--indra/llprimitive/llgltfloader.cpp404
-rw-r--r--indra/llprimitive/llgltfloader.h206
-rw-r--r--indra/llprimitive/llmodel.cpp194
-rw-r--r--indra/llprimitive/llmodel.h11
-rw-r--r--indra/llprimitive/llmodelloader.cpp196
-rw-r--r--indra/llprimitive/llmodelloader.h19
-rw-r--r--indra/llrender/llimagegl.cpp27
-rw-r--r--indra/llui/lllineeditor.cpp3
-rw-r--r--indra/llui/llscrolllistctrl.cpp2
-rw-r--r--indra/llui/lltextbase.cpp1
-rw-r--r--indra/llui/llurlaction.cpp18
-rw-r--r--indra/llui/llurlaction.h2
-rw-r--r--indra/llui/llurlentry.cpp16
-rw-r--r--indra/llui/llurlentry.h4
-rw-r--r--indra/llwebrtc/llwebrtc.cpp56
-rw-r--r--indra/llwebrtc/llwebrtc_impl.h7
-rw-r--r--indra/llwindow/llopenglview-objc.mm4
-rw-r--r--indra/llwindow/llwindow.cpp1
-rw-r--r--indra/llwindow/llwindow.h1
-rw-r--r--indra/llwindow/llwindowwin32.cpp122
-rw-r--r--indra/llwindow/llwindowwin32.h9
-rw-r--r--indra/newview/CMakeLists.txt27
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/commands.xml4
-rw-r--r--indra/newview/app_settings/settings.xml173
-rw-r--r--indra/newview/gltf/accessor.cpp2
-rw-r--r--indra/newview/gltf/asset.cpp200
-rw-r--r--indra/newview/gltf/asset.h13
-rw-r--r--indra/newview/gltf/buffer_util.h11
-rw-r--r--indra/newview/gltf/llgltfloader.cpp1822
-rw-r--r--indra/newview/gltf/llgltfloader.h216
-rw-r--r--indra/newview/gltfscenemanager.cpp2
-rw-r--r--indra/newview/llagent.cpp13
-rw-r--r--indra/newview/llagentcamera.cpp10
-rw-r--r--indra/newview/llagentwearables.cpp10
-rw-r--r--indra/newview/llappearancemgr.cpp2
-rw-r--r--indra/newview/llappviewer.cpp265
-rw-r--r--indra/newview/llappviewer.h15
-rw-r--r--indra/newview/llappviewerwin32.cpp24
-rw-r--r--indra/newview/llappviewerwin32.h1
-rw-r--r--indra/newview/llfilepicker.cpp6
-rw-r--r--indra/newview/llfloateravatarwelcomepack.cpp (renamed from indra/newview/llfloateravatar.cpp)19
-rw-r--r--indra/newview/llfloateravatarwelcomepack.h (renamed from indra/newview/llfloateravatar.h)25
-rw-r--r--indra/newview/llfloaterbvhpreview.cpp4
-rw-r--r--indra/newview/llfloaterbvhpreview.h2
-rw-r--r--indra/newview/llfloaterimagepreview.cpp114
-rw-r--r--indra/newview/llfloaterimagepreview.h2
-rw-r--r--indra/newview/llfloatermediasettings.cpp9
-rw-r--r--indra/newview/llfloatermediasettings.h2
-rw-r--r--indra/newview/llfloatermodelpreview.cpp13
-rw-r--r--indra/newview/llfloaterpreference.cpp9
-rw-r--r--indra/newview/llfloatersettingsdebug.cpp5
-rw-r--r--indra/newview/llfloatersettingsdebug.h1
-rw-r--r--indra/newview/llfloateruipreview.cpp5
-rwxr-xr-xindra/newview/llfloaterworldmap.cpp335
-rw-r--r--indra/newview/llfloaterworldmap.h17
-rw-r--r--indra/newview/llhudeffectlookat.cpp39
-rw-r--r--indra/newview/llhudeffectpointat.cpp14
-rw-r--r--indra/newview/llhudtext.cpp4
-rw-r--r--indra/newview/llimprocessing.cpp4
-rw-r--r--indra/newview/llinventoryfilter.cpp6
-rw-r--r--indra/newview/llinventoryfilter.h2
-rw-r--r--indra/newview/llinventoryfunctions.cpp4
-rw-r--r--indra/newview/llmaterialeditor.cpp84
-rw-r--r--indra/newview/llmeshrepository.cpp44
-rw-r--r--indra/newview/llmeshrepository.h17
-rw-r--r--indra/newview/llmodelpreview.cpp154
-rw-r--r--indra/newview/llmodelpreview.h5
-rw-r--r--indra/newview/llmutelist.cpp50
-rw-r--r--indra/newview/llmutelist.h14
-rw-r--r--indra/newview/llpanelemojicomplete.cpp1
-rw-r--r--indra/newview/llpanelface.cpp303
-rw-r--r--indra/newview/llpanelface.h6
-rw-r--r--indra/newview/llpanelgroupbulk.cpp62
-rw-r--r--indra/newview/llpanelgroupbulkimpl.h4
-rw-r--r--indra/newview/llpanellogin.cpp103
-rw-r--r--indra/newview/llpanellogin.h1
-rw-r--r--indra/newview/llpanelmaininventory.cpp248
-rw-r--r--indra/newview/llpanelpeople.cpp4
-rw-r--r--indra/newview/llpanelpeoplemenus.cpp6
-rw-r--r--indra/newview/llpanelpermissions.cpp1
-rw-r--r--indra/newview/llpanelprofilepicks.cpp42
-rw-r--r--indra/newview/llpanelprofilepicks.h10
-rw-r--r--indra/newview/llreflectionmap.cpp2
-rw-r--r--indra/newview/llreflectionmap.h2
-rw-r--r--indra/newview/llreflectionmapmanager.cpp2
-rw-r--r--indra/newview/llskinningutil.cpp9
-rw-r--r--indra/newview/llsky.h1
-rw-r--r--indra/newview/llspeakers.cpp4
-rw-r--r--indra/newview/llsprite.h2
-rw-r--r--indra/newview/llstartup.cpp28
-rw-r--r--indra/newview/llstatusbar.cpp63
-rw-r--r--indra/newview/llstatusbar.h12
-rw-r--r--indra/newview/llsurface.h1
-rw-r--r--indra/newview/lltoolpie.cpp143
-rw-r--r--indra/newview/lltoolpie.h16
-rw-r--r--indra/newview/llviewerdisplay.cpp63
-rw-r--r--indra/newview/llviewerfloaterreg.cpp4
-rw-r--r--indra/newview/llviewermedia_streamingaudio.cpp21
-rw-r--r--indra/newview/llviewermenu.cpp16
-rw-r--r--indra/newview/llviewermenufile.cpp163
-rw-r--r--indra/newview/llviewermessage.cpp21
-rwxr-xr-xindra/newview/llviewerparceloverlay.cpp74
-rw-r--r--indra/newview/llviewerparceloverlay.h13
-rwxr-xr-xindra/newview/llviewerregion.cpp1
-rw-r--r--indra/newview/llviewerstats.cpp106
-rw-r--r--indra/newview/llviewerstats.h11
-rw-r--r--indra/newview/llviewertexturelist.cpp16
-rw-r--r--indra/newview/llviewerwindow.cpp26
-rw-r--r--indra/newview/llvoavatar.cpp62
-rw-r--r--indra/newview/llvoavatar.h7
-rw-r--r--indra/newview/llvoavatarself.cpp20
-rw-r--r--indra/newview/llvoavatarself.h4
-rw-r--r--indra/newview/llvoicewebrtc.cpp42
-rw-r--r--indra/newview/llvovolume.cpp18
-rw-r--r--indra/newview/llwatchdog.cpp34
-rw-r--r--indra/newview/llwatchdog.h12
-rw-r--r--indra/newview/res/ll_icon_small.icobin0 -> 91010 bytes
-rw-r--r--indra/newview/res/resource.h1
-rwxr-xr-xindra/newview/res/viewerRes.rc1
-rw-r--r--indra/newview/skins/default/textures/textures.xml1
-rw-r--r--indra/newview/skins/default/textures/windows/first_login_image.jpgbin104529 -> 0 bytes
-rw-r--r--indra/newview/skins/default/xui/de/panel_login_first.xml39
-rw-r--r--indra/newview/skins/default/xui/en/floater_avatar_welcome_pack.xml25
-rw-r--r--indra/newview/skins/default/xui/en/floater_model_preview.xml28
-rw-r--r--indra/newview/skins/default/xui/en/floater_stats.xml45
-rw-r--r--indra/newview/skins/default/xui/en/floater_test_slapp.xml10
-rw-r--r--indra/newview/skins/default/xui/en/floater_world_map.xml18
-rw-r--r--indra/newview/skins/default/xui/en/menu_url_parcel.xml2
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml16
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml30
-rw-r--r--indra/newview/skins/default/xui/en/panel_login_first.xml262
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_advanced.xml10
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_privacy.xml102
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_sound.xml236
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile_pick.xml20
-rw-r--r--indra/newview/skins/default/xui/en/panel_status_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_tools_texture.xml13
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml2
-rw-r--r--indra/newview/skins/default/xui/es/panel_login_first.xml39
-rw-r--r--indra/newview/skins/default/xui/fr/panel_login_first.xml39
-rw-r--r--indra/newview/skins/default/xui/it/panel_login_first.xml39
-rw-r--r--indra/newview/skins/default/xui/ja/panel_login_first.xml54
-rw-r--r--indra/newview/skins/default/xui/pl/panel_login_first.xml30
-rw-r--r--indra/newview/skins/default/xui/pt/panel_login_first.xml39
-rw-r--r--indra/newview/skins/default/xui/ru/panel_login_first.xml39
-rw-r--r--indra/newview/skins/default/xui/tr/panel_login_first.xml39
-rw-r--r--indra/newview/skins/default/xui/zh/panel_login_first.xml39
-rw-r--r--indra/newview/tests/llviewerassetstats_test.cpp5
-rwxr-xr-xindra/newview/viewer_manifest.py12
185 files changed, 6293 insertions, 2665 deletions
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index cc217b0563..0a00ccbb5b 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -20,6 +20,7 @@ set(cmake_SOURCE_FILES
Copy3rdPartyLibs.cmake
DBusGlib.cmake
DeploySharedLibs.cmake
+ Discord.cmake
DragDrop.cmake
EXPAT.cmake
FindAutobuild.cmake
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index 6ac00fd131..0153e69d5b 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -6,6 +6,9 @@
include(CMakeCopyIfDifferent)
include(Linking)
+if (USE_DISCORD)
+ include(Discord)
+endif ()
include(OPENAL)
# When we copy our dependent libraries, we almost always want to copy them to
@@ -75,6 +78,10 @@ if(WINDOWS)
endif(ADDRESS_SIZE EQUAL 32)
endif (USE_BUGSPLAT)
+ if (TARGET ll::discord_sdk)
+ list(APPEND release_files discord_partner_sdk.dll)
+ endif ()
+
if (TARGET ll::openal)
list(APPEND release_files openal32.dll alut.dll)
endif ()
@@ -180,6 +187,10 @@ elseif(DARWIN)
)
endif()
+ if (TARGET ll::discord_sdk)
+ list(APPEND release_files libdiscord_partner_sdk.dylib)
+ endif ()
+
if (TARGET ll::openal)
list(APPEND release_files libalut.dylib libopenal.dylib)
endif ()
diff --git a/indra/cmake/Discord.cmake b/indra/cmake/Discord.cmake
new file mode 100644
index 0000000000..95cfaacf5b
--- /dev/null
+++ b/indra/cmake/Discord.cmake
@@ -0,0 +1,11 @@
+include(Prebuilt)
+
+include_guard()
+
+add_library(ll::discord_sdk INTERFACE IMPORTED)
+target_compile_definitions(ll::discord_sdk INTERFACE LL_DISCORD=1)
+
+use_prebuilt_binary(discord_sdk)
+
+target_include_directories(ll::discord_sdk SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/discord_sdk)
+target_link_libraries(ll::discord_sdk INTERFACE discord_partner_sdk)
diff --git a/indra/cmake/Linking.cmake b/indra/cmake/Linking.cmake
index 1093fc7f71..8451659c34 100644
--- a/indra/cmake/Linking.cmake
+++ b/indra/cmake/Linking.cmake
@@ -72,7 +72,6 @@ else()
find_library(COCOA_LIBRARY Cocoa)
find_library(IOKIT_LIBRARY IOKit)
- find_library(AGL_LIBRARY AGL)
find_library(APPKIT_LIBRARY AppKit)
find_library(COREAUDIO_LIBRARY CoreAudio)
@@ -81,7 +80,6 @@ else()
${IOKIT_LIBRARY}
${COREFOUNDATION_LIBRARY}
${CARBON_LIBRARY}
- ${AGL_LIBRARY}
${APPKIT_LIBRARY}
${COREAUDIO_LIBRARY}
)
diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt
index c3be8bc78e..6744c8d8a4 100644
--- a/indra/llappearance/CMakeLists.txt
+++ b/indra/llappearance/CMakeLists.txt
@@ -14,6 +14,7 @@ set(llappearance_SOURCE_FILES
llavatarjoint.cpp
llavatarjointmesh.cpp
lldriverparam.cpp
+ lljointdata.h
lllocaltextureobject.cpp
llpolyskeletaldistortion.cpp
llpolymesh.cpp
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index 3d66809ed6..dab18c240d 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -29,16 +29,17 @@
#include "llavatarappearance.h"
#include "llavatarappearancedefines.h"
#include "llavatarjointmesh.h"
+#include "lljointdata.h"
#include "llstl.h"
#include "lldir.h"
#include "llpolymorph.h"
#include "llpolymesh.h"
#include "llpolyskeletaldistortion.h"
-#include "llstl.h"
#include "lltexglobalcolor.h"
#include "llwearabledata.h"
#include "boost/bind.hpp"
#include "boost/tokenizer.hpp"
+#include "v4math.h"
using namespace LLAvatarAppearanceDefines;
@@ -71,11 +72,13 @@ public:
mChildren.clear();
}
bool parseXml(LLXmlTreeNode* node);
+ glm::mat4 getJointMatrix();
private:
std::string mName;
std::string mSupport;
std::string mAliases;
+ std::string mGroup;
bool mIsJoint;
LLVector3 mPos;
LLVector3 mEnd;
@@ -106,10 +109,16 @@ public:
S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
private:
+ typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
+ static void getJointMatricesAndHierarhy(
+ LLAvatarBoneInfo* bone_info,
+ LLJointData& data,
+ const glm::mat4& parent_mat);
+
+private:
S32 mNumBones;
S32 mNumCollisionVolumes;
LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
- typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
bone_info_list_t mBoneInfoList;
};
@@ -1598,6 +1607,15 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
mSupport = "base";
}
+ // Skeleton has 133 bones, but shader only allows 110 (LL_MAX_JOINTS_PER_MESH_OBJECT)
+ // Groups can be used by importer to cut out unused groups of joints
+ static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
+ if (!node->getFastAttributeString(group_string, mGroup))
+ {
+ LL_WARNS() << "Bone without group " << mName << LL_ENDL;
+ mGroup = "global";
+ }
+
if (mIsJoint)
{
static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
@@ -1623,6 +1641,21 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
return true;
}
+
+glm::mat4 LLAvatarBoneInfo::getJointMatrix()
+{
+ glm::mat4 mat(1.0f);
+ // 1. Scaling
+ mat = glm::scale(mat, glm::vec3(mScale[0], mScale[1], mScale[2]));
+ // 2. Rotation (Euler angles rad)
+ mat = glm::rotate(mat, mRot[0], glm::vec3(1, 0, 0));
+ mat = glm::rotate(mat, mRot[1], glm::vec3(0, 1, 0));
+ mat = glm::rotate(mat, mRot[2], glm::vec3(0, 0, 1));
+ // 3. Position
+ mat = glm::translate(mat, glm::vec3(mPos[0], mPos[1], mPos[2]));
+ return mat;
+}
+
//-----------------------------------------------------------------------------
// LLAvatarSkeletonInfo::parseXml()
//-----------------------------------------------------------------------------
@@ -1653,6 +1686,25 @@ bool LLAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
return true;
}
+void LLAvatarSkeletonInfo::getJointMatricesAndHierarhy(
+ LLAvatarBoneInfo* bone_info,
+ LLJointData& data,
+ const glm::mat4& parent_mat)
+{
+ data.mName = bone_info->mName;
+ data.mJointMatrix = bone_info->getJointMatrix();
+ data.mScale = glm::vec3(bone_info->mScale[0], bone_info->mScale[1], bone_info->mScale[2]);
+ data.mRotation = bone_info->mRot;
+ data.mRestMatrix = parent_mat * data.mJointMatrix;
+ data.mIsJoint = bone_info->mIsJoint;
+ data.mGroup = bone_info->mGroup;
+ for (LLAvatarBoneInfo* child_info : bone_info->mChildren)
+ {
+ LLJointData& child_data = data.mChildren.emplace_back();
+ getJointMatricesAndHierarhy(child_info, child_data, data.mRestMatrix);
+ }
+}
+
//Make aliases for joint and push to map.
void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
{
@@ -1714,6 +1766,16 @@ const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases
return mJointAliasMap;
}
+void LLAvatarAppearance::getJointMatricesAndHierarhy(std::vector<LLJointData> &data) const
+{
+ glm::mat4 identity(1.f);
+ for (LLAvatarBoneInfo* bone_info : sAvatarSkeletonInfo->mBoneInfoList)
+ {
+ LLJointData& child_data = data.emplace_back();
+ LLAvatarSkeletonInfo::getJointMatricesAndHierarhy(bone_info, child_data, identity);
+ }
+}
+
//-----------------------------------------------------------------------------
// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h
index 13e504e639..84cb42056a 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -34,6 +34,7 @@
#include "lltexlayer.h"
#include "llviewervisualparam.h"
#include "llxmltree.h"
+#include "v4math.h"
class LLTexLayerSet;
class LLTexGlobalColor;
@@ -41,6 +42,7 @@ class LLTexGlobalColorInfo;
class LLWearableData;
class LLAvatarBoneInfo;
class LLAvatarSkeletonInfo;
+class LLJointData;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// LLAvatarAppearance
@@ -138,7 +140,7 @@ public:
LLVector3 mHeadOffset{}; // current head position
LLAvatarJoint* mRoot{ nullptr };
- typedef std::map<std::string, LLJoint*> joint_map_t;
+ typedef std::map<std::string, LLJoint*, std::less<>> joint_map_t;
joint_map_t mJointMap;
typedef std::map<std::string, LLVector3> joint_state_map_t;
@@ -151,9 +153,11 @@ public:
public:
typedef std::vector<LLAvatarJoint*> avatar_joint_list_t;
const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
- typedef std::map<std::string, std::string> joint_alias_map_t;
+ typedef std::map<std::string, std::string, std::less<>> joint_alias_map_t;
const joint_alias_map_t& getJointAliases();
-
+ typedef std::map<std::string, std::string> joint_parent_map_t; // matrix plus parent
+ typedef std::map<std::string, glm::mat4> joint_rest_map_t;
+ void getJointMatricesAndHierarhy(std::vector<LLJointData> &data) const;
protected:
static bool parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree);
diff --git a/indra/llappearance/lljointdata.h b/indra/llappearance/lljointdata.h
new file mode 100644
index 0000000000..2fc26198ee
--- /dev/null
+++ b/indra/llappearance/lljointdata.h
@@ -0,0 +1,66 @@
+/**
+ * @file lljointdata.h
+ * @brief LLJointData class for holding individual joint data and skeleton
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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_LLJOINTDATA_H
+#define LL_LLJOINTDATA_H
+
+#include "v4math.h"
+
+// may be just move LLAvatarBoneInfo
+class LLJointData
+{
+public:
+ std::string mName;
+ std::string mGroup;
+ glm::mat4 mJointMatrix;
+ glm::mat4 mRestMatrix;
+ glm::vec3 mScale;
+ LLVector3 mRotation;
+
+ typedef std::vector<LLJointData> bones_t;
+ bones_t mChildren;
+
+ bool mIsJoint; // if not, collision_volume
+ enum SupportCategory
+ {
+ SUPPORT_BASE,
+ SUPPORT_EXTENDED
+ };
+ SupportCategory mSupport;
+ void setSupport(const std::string& support)
+ {
+ if (support == "extended")
+ {
+ mSupport = SUPPORT_EXTENDED;
+ }
+ else
+ {
+ mSupport = SUPPORT_BASE;
+ }
+ }
+};
+
+#endif //LL_LLJOINTDATA_H
diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp
index aa48a2d621..b3800e6981 100644
--- a/indra/llappearance/lltexlayer.cpp
+++ b/indra/llappearance/lltexlayer.cpp
@@ -1293,7 +1293,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
{
if (!force_render && !hasMorph())
{
- LL_DEBUGS() << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
+ LL_DEBUGS("Morph") << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
return;
}
LL_PROFILE_ZONE_SCOPED;
@@ -1325,7 +1325,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
success &= param->render( x, y, width, height );
if (!success && !force_render)
{
- LL_DEBUGS() << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
+ LL_DEBUGS("Morph") << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
return;
}
}
@@ -1365,7 +1365,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
}
else
{
- LL_WARNS() << "Skipping rendering of " << getInfo()->mStaticImageFileName
+ LL_WARNS("Morph") << "Skipping rendering of " << getInfo()->mStaticImageFileName
<< "; expected 1 or 4 components." << LL_ENDL;
}
}
@@ -1404,8 +1404,8 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
// We can get bad morph masks during login, on minimize, and occasional gl errors.
// We should only be doing this when we believe something has changed with respect to the user's appearance.
{
- LL_DEBUGS("Avatar") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
- // clear out a slot if we have filled our cache
+ LL_DEBUGS("Morph") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
+ // clear out a slot if we have filled our cache
S32 max_cache_entries = getTexLayerSet()->getAvatarAppearance()->isSelf() ? 4 : 1;
while ((S32)mAlphaCache.size() >= max_cache_entries)
{
@@ -1444,13 +1444,20 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
}
glGetTexImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
-
- U8* alpha_cursor = alpha_data;
- U8* pixel = temp;
- for (int i = 0; i < pixels; i++)
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ LL_INFOS("Morph") << "GL Error while reading back morph texture. Error code: " << error << LL_ENDL;
+ }
+ else
{
- *alpha_cursor++ = pixel[3];
- pixel += 4;
+ U8* alpha_cursor = alpha_data;
+ U8* pixel = temp;
+ for (int i = 0; i < pixels; i++)
+ {
+ *alpha_cursor++ = pixel[3];
+ pixel += 4;
+ }
}
gGL.getTexUnit(0)->disable();
diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp
index 9dace08e6f..581e9f62d5 100644
--- a/indra/llcharacter/llbvhloader.cpp
+++ b/indra/llcharacter/llbvhloader.cpp
@@ -131,7 +131,7 @@ LLQuaternion::Order bvhStringToOrder( char *str )
// LLBVHLoader()
//-----------------------------------------------------------------------------
-LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string>& joint_alias_map )
+LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string, std::less<>>& joint_alias_map )
{
reset();
errorLine = 0;
@@ -156,9 +156,9 @@ LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &error
}
// Recognize all names we've been told are legal.
- for (std::map<std::string, std::string>::value_type& alias_pair : joint_alias_map)
+ for (const auto& [alias, joint] : joint_alias_map)
{
- makeTranslation( alias_pair.first , alias_pair.second );
+ makeTranslation(alias, joint);
}
char error_text[128]; /* Flawfinder: ignore */
diff --git a/indra/llcharacter/llbvhloader.h b/indra/llcharacter/llbvhloader.h
index de31f76dd3..ae2e347ba1 100644
--- a/indra/llcharacter/llbvhloader.h
+++ b/indra/llcharacter/llbvhloader.h
@@ -227,7 +227,7 @@ class LLBVHLoader
friend class LLKeyframeMotion;
public:
// Constructor
- LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string>& joint_alias_map );
+ LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string, std::less<>>& joint_alias_map );
~LLBVHLoader();
/*
diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp
index ecbcdb3bf5..8efcd9dd29 100644
--- a/indra/llcharacter/llcharacter.cpp
+++ b/indra/llcharacter/llcharacter.cpp
@@ -77,12 +77,11 @@ LLCharacter::~LLCharacter()
//-----------------------------------------------------------------------------
// getJoint()
//-----------------------------------------------------------------------------
-LLJoint *LLCharacter::getJoint( const std::string &name )
+LLJoint* LLCharacter::getJoint(std::string_view name)
{
- LLJoint* joint = NULL;
+ LLJoint* joint = nullptr;
- LLJoint *root = getRootJoint();
- if (root)
+ if (LLJoint* root = getRootJoint())
{
joint = root->findJoint(name);
}
diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
index 0b9b148463..079bcd132a 100644
--- a/indra/llcharacter/llcharacter.h
+++ b/indra/llcharacter/llcharacter.h
@@ -76,7 +76,7 @@ public:
// get the specified joint
// default implementation does recursive search,
// subclasses may optimize/cache results.
- virtual LLJoint *getJoint( const std::string &name );
+ virtual LLJoint* getJoint(std::string_view name);
// get the position of the character
virtual LLVector3 getCharacterPosition() = 0;
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index f31aa5d4c9..405e62a38b 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -242,21 +242,20 @@ LLJoint *LLJoint::getRoot()
//-----------------------------------------------------------------------------
// findJoint()
//-----------------------------------------------------------------------------
-LLJoint *LLJoint::findJoint( const std::string &name )
+LLJoint* LLJoint::findJoint(std::string_view name)
{
if (name == getName())
return this;
for (LLJoint* joint : mChildren)
{
- LLJoint *found = joint->findJoint(name);
- if (found)
+ if (LLJoint* found = joint->findJoint(name))
{
return found;
}
}
- return NULL;
+ return nullptr;
}
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 763c1e3865..b58dc797f8 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -222,7 +222,7 @@ public:
LLJoint *getRoot();
// search for child joints by name
- LLJoint *findJoint( const std::string &name );
+ LLJoint* findJoint(std::string_view name);
// add/remove children
void addChild( LLJoint *joint );
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index e5d25b52f0..692941a892 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -28,6 +28,7 @@
#include "apr_portable.h"
+#include "llapp.h"
#include "llthread.h"
#include "llmutex.h"
@@ -35,6 +36,7 @@
#include "lltrace.h"
#include "lltracethreadrecorder.h"
#include "llexception.h"
+#include "workqueue.h"
#if LL_LINUX
#include <sched.h>
@@ -106,6 +108,27 @@ namespace
return s_thread_id;
}
+#if LL_WINDOWS
+
+ static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
+
+ U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
+ {
+ if (LLApp::instance()->reportCrashToBugsplat((void*)exception_infop))
+ {
+ // Handled
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else if (code == STATUS_MSC_EXCEPTION)
+ {
+ // C++ exception, go on
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // handle it, convert to std::exception
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+#endif // LL_WINDOWS
} // anonymous namespace
LL_COMMON_API bool on_main_thread()
@@ -157,20 +180,11 @@ void LLThread::threadRun()
// Run the user supplied function
do
{
- try
- {
- run();
- }
- catch (const LLContinueError &e)
- {
- LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
- "' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL;
- //output possible call stacks to log file.
- LLError::LLCallStacks::print();
-
- LOG_UNHANDLED_EXCEPTION("LLThread");
- continue;
- }
+#ifdef LL_WINDOWS
+ sehHandle(); // Structured Exception Handling
+#else
+ tryRun();
+#endif
break;
} while (true);
@@ -188,6 +202,69 @@ void LLThread::threadRun()
mStatus = STOPPED;
}
+void LLThread::tryRun()
+{
+ try
+ {
+ run();
+ }
+ catch (const LLContinueError& e)
+ {
+ LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
+ "'. Error what is: '" << e.what() << "'" << LL_ENDL;
+ LLError::LLCallStacks::print();
+
+ LOG_UNHANDLED_EXCEPTION("LLThread");
+ }
+ catch (std::bad_alloc&)
+ {
+ // Todo: improve this, this is going to have a different callstack
+ // instead of showing where it crashed
+ LL_WARNS("THREAD") << "Out of memory in a thread: " << mName << LL_ENDL;
+
+ LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
+ main_queue->post(
+ // Bind the current exception, rethrow it in main loop.
+ []() {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS("THREAD") << "Out of memory in a thread" << LL_ENDL;
+ });
+ }
+#ifndef LL_WINDOWS
+ catch (...)
+ {
+ // Stash any other kind of uncaught exception to be rethrown by main thread.
+ LL_WARNS("THREAD") << "Capturing and rethrowing uncaught exception in LLThread "
+ << mName << LL_ENDL;
+
+ LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
+ main_queue->post(
+ // Bind the current exception, rethrow it in main loop.
+ [exc = std::current_exception()]() { std::rethrow_exception(exc); });
+ }
+#endif // else LL_WINDOWS
+}
+
+#ifdef LL_WINDOWS
+void LLThread::sehHandle()
+{
+ __try
+ {
+ // handle stop and continue exceptions first
+ tryRun();
+ }
+ __except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ // convert to C++ styled exception
+ // Note: it might be better to use _se_set_translator
+ // if you want exception to inherit full callstack
+ char integer_string[512];
+ sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
+ throw std::exception(integer_string);
+ }
+}
+#endif
+
LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
mPaused(false),
mName(name),
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index 4194e0014d..8794ac93aa 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -68,7 +68,7 @@ public:
// Called from MAIN THREAD.
void pause();
void unpause();
- bool isPaused() { return isStopped() || mPaused; }
+ bool isPaused() const { return isStopped() || mPaused; }
// Cause the thread to wake up and check its condition
void wake();
@@ -97,6 +97,11 @@ private:
// static function passed to APR thread creation routine
void threadRun();
+ void tryRun();
+
+#ifdef LL_WINDOWS
+ void sehHandle();
+#endif
protected:
std::string mName;
diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp
index 0eb20323ad..7efaebd569 100644
--- a/indra/llcommon/workqueue.cpp
+++ b/indra/llcommon/workqueue.cpp
@@ -210,14 +210,22 @@ void LL::WorkQueueBase::callWork(const Work& work)
}
catch (...)
{
- // Stash any other kind of uncaught exception to be rethrown by main thread.
- LL_WARNS("LLCoros") << "Capturing and rethrowing uncaught exception in WorkQueueBase "
- << getKey() << LL_ENDL;
-
- LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
- main_queue->post(
- // Bind the current exception, rethrow it in main loop.
- [exc = std::current_exception()]() { std::rethrow_exception(exc); });
+ if (getKey() != "mainloop")
+ {
+ // Stash any other kind of uncaught exception to be rethrown by main thread.
+ LL_WARNS("LLCoros") << "Capturing and rethrowing uncaught exception in WorkQueueBase "
+ << getKey() << LL_ENDL;
+
+ LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
+ main_queue->post(
+ // Bind the current exception, rethrow it in main loop.
+ [exc = std::current_exception()]() { std::rethrow_exception(exc); });
+ }
+ else
+ {
+ // let main loop crash
+ throw;
+ }
}
#endif // else LL_WINDOWS
}
diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp
index 99d4850610..b80243c22e 100644
--- a/indra/llfilesystem/lldir.cpp
+++ b/indra/llfilesystem/lldir.cpp
@@ -110,9 +110,10 @@ std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
std::vector<std::string> v;
- if (exists(p))
+ boost::system::error_code ec;
+ if (exists(p, ec) && !ec.failed())
{
- if (is_directory(p))
+ if (is_directory(p, ec) && !ec.failed())
{
boost::filesystem::directory_iterator end_iter;
for (boost::filesystem::directory_iterator dir_itr(p);
diff --git a/indra/llimage/llimagedimensionsinfo.cpp b/indra/llimage/llimagedimensionsinfo.cpp
index d4efbcfad2..c896d60c85 100644
--- a/indra/llimage/llimagedimensionsinfo.cpp
+++ b/indra/llimage/llimagedimensionsinfo.cpp
@@ -75,7 +75,7 @@ bool LLImageDimensionsInfo::load(const std::string& src_filename,U32 codec)
bool LLImageDimensionsInfo::getImageDimensionsBmp()
{
// Make sure the file is long enough.
- const S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4)
+ constexpr S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4)
if (!checkFileLength(DATA_LEN))
{
LL_WARNS() << "Premature end of file" << LL_ENDL;
@@ -105,7 +105,7 @@ bool LLImageDimensionsInfo::getImageDimensionsBmp()
bool LLImageDimensionsInfo::getImageDimensionsTga()
{
- const S32 TGA_FILE_HEADER_SIZE = 12;
+ constexpr S32 TGA_FILE_HEADER_SIZE = 12;
// Make sure the file is long enough.
if (!checkFileLength(TGA_FILE_HEADER_SIZE + 1 /* width */ + 1 /* height */))
@@ -124,7 +124,7 @@ bool LLImageDimensionsInfo::getImageDimensionsTga()
bool LLImageDimensionsInfo::getImageDimensionsPng()
{
- const S32 PNG_MAGIC_SIZE = 8;
+ constexpr S32 PNG_MAGIC_SIZE = 8;
// Make sure the file is long enough.
if (!checkFileLength(PNG_MAGIC_SIZE + 8 + sizeof(S32) * 2 /* width, height */))
@@ -134,7 +134,7 @@ bool LLImageDimensionsInfo::getImageDimensionsPng()
}
// Read PNG signature.
- const U8 png_magic[PNG_MAGIC_SIZE] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
+ constexpr U8 png_magic[PNG_MAGIC_SIZE] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
U8 signature[PNG_MAGIC_SIZE];
mInfile.read((void*)signature, PNG_MAGIC_SIZE);
@@ -166,34 +166,36 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg()
{
sJpegErrorEncountered = false;
clean();
- FILE *fp = LLFile::fopen(mSrcFilename, "rb");
- if (fp == NULL)
+ FILE* fp = LLFile::fopen(mSrcFilename, "rb");
+ if (!fp)
{
setLastError("Unable to open file for reading", mSrcFilename);
return false;
}
/* Make sure this is a JPEG file. */
- const size_t JPEG_MAGIC_SIZE = 2;
- const U8 jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8};
+ constexpr size_t JPEG_MAGIC_SIZE = 2;
+ constexpr U8 jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8};
U8 signature[JPEG_MAGIC_SIZE];
if (fread(signature, sizeof(signature), 1, fp) != 1)
{
LL_WARNS() << "Premature end of file" << LL_ENDL;
+ fclose(fp);
return false;
}
if (memcmp(signature, jpeg_magic, JPEG_MAGIC_SIZE) != 0)
{
LL_WARNS() << "Not a JPEG" << LL_ENDL;
mWarning = "texture_load_format_error";
+ fclose(fp);
return false;
}
fseek(fp, 0, SEEK_SET); // go back to start of the file
/* Init jpeg */
jpeg_error_mgr jerr;
- jpeg_decompress_struct cinfo;
+ jpeg_decompress_struct cinfo{};
cinfo.err = jpeg_std_error(&jerr);
// Call our function instead of exit() if Libjpeg encounters an error.
// This is done to avoid crash in this case (STORM-472).
diff --git a/indra/llimage/llimagedimensionsinfo.h b/indra/llimage/llimagedimensionsinfo.h
index 681d66ae4e..4870f2e815 100644
--- a/indra/llimage/llimagedimensionsinfo.h
+++ b/indra/llimage/llimagedimensionsinfo.h
@@ -38,7 +38,7 @@ class LLImageDimensionsInfo
{
public:
LLImageDimensionsInfo():
- mData(NULL)
+ mData(nullptr)
,mHeight(0)
,mWidth(0)
{}
@@ -67,7 +67,7 @@ protected:
{
mInfile.close();
delete[] mData;
- mData = NULL;
+ mData = nullptr;
mWidth = 0;
mHeight = 0;
}
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index aa161709a1..5a941dc958 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -281,10 +281,11 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r
S32 height = (h > 0) ? h : 2048;
S32 max_dimension = llmax(width, height); // Find largest dimension
S32 block_area = MAX_BLOCK_SIZE * MAX_BLOCK_SIZE; // Calculated initial block area from established max block size (currently 64)
- block_area *= llmax((max_dimension / MAX_BLOCK_SIZE / max_components), 1); // Adjust initial block area by ratio of largest dimension to block size per component
- S32 totalbytes = (S32) (block_area * max_components * precision); // First block layer computed before loop without compression rate
- S32 block_layers = 1; // Start at layer 1 since first block layer is computed outside loop
- while (block_layers < 6) // Walk five layers for the five discards in JPEG2000
+ S32 max_layers = (S32)llmax(llround(log2f((float)max_dimension) - log2f((float)MAX_BLOCK_SIZE)), 4); // Find number of powers of two between extents and block size to a minimum of 4
+ block_area *= llmax(max_layers, 1); // Adjust initial block area by max number of layers
+ S32 totalbytes = (S32) (MIN_LAYER_SIZE * max_components * precision); // Start estimation with a minimum reasonable size
+ S32 block_layers = 0;
+ while (block_layers <= max_layers) // Walk the layers
{
if (block_layers <= (5 - discard_level)) // Walk backwards from discard 5 to required discard layer.
totalbytes += (S32) (block_area * max_components * precision * rate); // Add each block layer reduced by assumed compression rate
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index f4bcb97a5d..c56e94aaa4 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -32,8 +32,6 @@
#include "event.h"
#include "cio.h"
-#define MAX_ENCODED_DISCARD_LEVELS 5
-
// Factory function: see declaration in llimagej2c.cpp
LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()
{
@@ -132,73 +130,96 @@ static void opj_error(const char* msg, void* user_data)
static OPJ_SIZE_T opj_read(void * buffer, OPJ_SIZE_T bytes, void* user_data)
{
- llassert(user_data);
+ llassert(user_data && buffer);
+
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
- OPJ_SIZE_T remainder = (jpeg_codec->size - jpeg_codec->offset);
- if (remainder <= 0)
+
+ if (jpeg_codec->offset < 0 || static_cast<OPJ_SIZE_T>(jpeg_codec->offset) >= jpeg_codec->size)
{
jpeg_codec->offset = jpeg_codec->size;
- // Indicate end of stream (hacky?)
- return (OPJ_OFF_T)-1;
+ return static_cast<OPJ_SIZE_T>(-1); // Indicate EOF
}
- OPJ_SIZE_T to_read = llclamp(U32(bytes), U32(0), U32(remainder));
+
+ OPJ_SIZE_T remainder = jpeg_codec->size - static_cast<OPJ_SIZE_T>(jpeg_codec->offset);
+ OPJ_SIZE_T to_read = (bytes < remainder) ? bytes : remainder;
+
memcpy(buffer, jpeg_codec->buffer + jpeg_codec->offset, to_read);
jpeg_codec->offset += to_read;
+
return to_read;
}
static OPJ_SIZE_T opj_write(void * buffer, OPJ_SIZE_T bytes, void* user_data)
{
- llassert(user_data);
+ llassert(user_data && buffer);
+
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
- OPJ_SIZE_T remainder = jpeg_codec->size - jpeg_codec->offset;
- if (remainder < bytes)
+ OPJ_OFF_T required_offset = jpeg_codec->offset + static_cast<OPJ_OFF_T>(bytes);
+
+ // Overflow check
+ if (required_offset < jpeg_codec->offset)
+ return 0; // Overflow detected
+
+ // Resize if needed (exponential growth)
+ if (required_offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
{
- OPJ_SIZE_T new_size = jpeg_codec->size + (bytes - remainder);
+ OPJ_SIZE_T new_size = jpeg_codec->size ? jpeg_codec->size : 1024;
+ while (required_offset > static_cast<OPJ_OFF_T>(new_size))
+ new_size *= 2;
+
+ const OPJ_SIZE_T MAX_BUFFER_SIZE = 512 * 1024 * 1024; // 512 MB, increase if needed
+ if (new_size > MAX_BUFFER_SIZE) return 0;
+
U8* new_buffer = (U8*)ll_aligned_malloc_16(new_size);
- memcpy(new_buffer, jpeg_codec->buffer, jpeg_codec->offset);
- U8* old_buffer = jpeg_codec->buffer;
+ if (!new_buffer) return 0; // Allocation failed
+
+ if (jpeg_codec->offset > 0)
+ memcpy(new_buffer, jpeg_codec->buffer, static_cast<size_t>(jpeg_codec->offset));
+
+ ll_aligned_free_16(jpeg_codec->buffer);
jpeg_codec->buffer = new_buffer;
- ll_aligned_free_16(old_buffer);
jpeg_codec->size = new_size;
}
- memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, bytes);
- jpeg_codec->offset += bytes;
+
+ memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, static_cast<size_t>(bytes));
+ jpeg_codec->offset = required_offset;
return bytes;
}
static OPJ_OFF_T opj_skip(OPJ_OFF_T bytes, void* user_data)
{
+ llassert(user_data);
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
- jpeg_codec->offset += bytes;
- if (jpeg_codec->offset > (OPJ_OFF_T)jpeg_codec->size)
- {
- jpeg_codec->offset = jpeg_codec->size;
- // Indicate end of stream
- return (OPJ_OFF_T)-1;
- }
+ OPJ_OFF_T new_offset = jpeg_codec->offset + bytes;
- if (jpeg_codec->offset < 0)
+ if (new_offset < 0 || new_offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
{
- // Shouldn't be possible?
- jpeg_codec->offset = 0;
+ // Clamp and indicate EOF or error
+ jpeg_codec->offset = llclamp<OPJ_OFF_T>(new_offset, 0, static_cast<OPJ_OFF_T>(jpeg_codec->size));
return (OPJ_OFF_T)-1;
}
+ jpeg_codec->offset = new_offset;
return bytes;
}
-static OPJ_BOOL opj_seek(OPJ_OFF_T bytes, void * user_data)
+static OPJ_BOOL opj_seek(OPJ_OFF_T offset, void * user_data)
{
+ llassert(user_data);
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
- jpeg_codec->offset = bytes;
- jpeg_codec->offset = llclamp(U32(jpeg_codec->offset), U32(0), U32(jpeg_codec->size));
+
+ if (offset < 0 || offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
+ return OPJ_FALSE;
+
+ jpeg_codec->offset = offset;
return OPJ_TRUE;
}
static void opj_free_user_data(void * user_data)
{
+ llassert(user_data);
+
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
// Don't free, data is managed externally
jpeg_codec->buffer = nullptr;
@@ -208,14 +229,54 @@ static void opj_free_user_data(void * user_data)
static void opj_free_user_data_write(void * user_data)
{
+ llassert(user_data);
+
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
// Free, data was allocated here
- ll_aligned_free_16(jpeg_codec->buffer);
- jpeg_codec->buffer = nullptr;
+ if (jpeg_codec->buffer)
+ {
+ ll_aligned_free_16(jpeg_codec->buffer);
+ jpeg_codec->buffer = nullptr;
+ }
jpeg_codec->size = 0;
jpeg_codec->offset = 0;
}
+/**
+ * Estimates the number of layers necessary depending on the image surface (w x h)
+ */
+static U32 estimate_num_layers(U32 surface)
+{
+ if (surface <= 1024) return 2; // Tiny (≤32×32)
+ else if (surface <= 16384) return 3; // Small (≤128×128)
+ else if (surface <= 262144) return 4; // Medium (≤512×512)
+ else if (surface <= 1048576) return 5; // Up to ~1MP
+ else return 6; // Up to ~1.5–2MP
+}
+
+/**
+ * Sets the parameters.tcp_rates according to the number of layers and a last tcp_rate value (which equals to the final compression ratio).
+ *
+ * Example for 6 layers:
+ *
+ * i = 5, parameters.tcp_rates[6 - 1 - 5] = 8.0f * (1 << (5 << 1)) = 8192 // Layer 5 (lowest quality)
+ * i = 4, parameters.tcp_rates[6 - 1 - 4] = 8.0f * (1 << (4 << 1)) = 2048 // Layer 4
+ * i = 3, parameters.tcp_rates[6 - 1 - 3] = 8.0f * (1 << (3 << 1)) = 512 // Layer 3
+ * i = 2, parameters.tcp_rates[6 - 1 - 2] = 8.0f * (1 << (2 << 1)) = 128 // Layer 2
+ * i = 1, parameters.tcp_rates[6 - 1 - 1] = 8.0f * (1 << (1 << 1)) = 32 // Layer 1
+ * i = 0, parameters.tcp_rates[6 - 1 - 0] = 8.0f * (1 << (0 << 1)) = 8 // Layer 0 (highest quality)
+ *
+ */
+static void set_tcp_rates(opj_cparameters_t* parameters, U32 num_layers = 1, F32 last_tcp_rate = LAST_TCP_RATE)
+{
+ parameters->tcp_numlayers = num_layers;
+
+ for (int i = num_layers - 1; i >= 0; i--)
+ {
+ parameters->tcp_rates[num_layers - 1 - i] = last_tcp_rate * static_cast<F32>(1 << (i << 1));
+ }
+}
+
class JPEG2KDecode : public JPEG2KBase
{
public:
@@ -430,15 +491,16 @@ public:
opj_set_default_encoder_parameters(&parameters);
parameters.cod_format = OPJ_CODEC_J2K;
- parameters.cp_disto_alloc = 1;
+ parameters.prog_order = OPJ_RLCP; // should be the default, but, just in case
+ parameters.cp_disto_alloc = 1; // enable rate allocation by distortion
+ parameters.max_cs_size = 0; // do not cap max size because we're using tcp_rates and also irrelevant with lossless.
if (reversible)
{
- parameters.max_cs_size = 0; // do not limit size for reversible compression
parameters.irreversible = 0; // should be the default, but, just in case
parameters.tcp_numlayers = 1;
/* documentation seems to be wrong, should be 0.0f for lossless, not 1.0f
- see https://github.com/uclouvain/openjpeg/blob/39e8c50a2f9bdcf36810ee3d41bcbf1cc78968ae/src/lib/openjp2/j2k.c#L7755
+ see https://github.com/uclouvain/openjpeg/blob/e7453e398b110891778d8da19209792c69ca7169/src/lib/openjp2/j2k.c#L7817
*/
parameters.tcp_rates[0] = 0.0f;
}
@@ -493,53 +555,22 @@ public:
encoder = opj_create_compress(OPJ_CODEC_J2K);
- parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0;
- parameters.cod_format = OPJ_CODEC_J2K;
- parameters.prog_order = OPJ_RLCP;
- parameters.cp_disto_alloc = 1;
+ parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; // no color transform for RGBA images
+
// if not lossless compression, computes tcp_numlayers and max_cs_size depending on the image dimensions
- if( parameters.irreversible ) {
+ if( parameters.irreversible )
+ {
// computes a number of layers
U32 surface = rawImageIn.getWidth() * rawImageIn.getHeight();
- U32 nb_layers = 1;
- U32 s = 64*64;
- while (surface > s)
- {
- nb_layers++;
- s *= 4;
- }
- nb_layers = llclamp(nb_layers, 1, 6);
- parameters.tcp_numlayers = nb_layers;
- parameters.tcp_rates[nb_layers - 1] = (U32)(1.f / DEFAULT_COMPRESSION_RATE); // 1:8 by default
+ // gets the necessary number of layers
+ U32 nb_layers = estimate_num_layers(surface);
- // for each subsequent layer, computes its rate and adds surface * numcomps * 1/rate to the max_cs_size
- U32 max_cs_size = (U32)(surface * image->numcomps * DEFAULT_COMPRESSION_RATE);
- U32 multiplier;
- for (int i = nb_layers - 2; i >= 0; i--)
- {
- if( i == nb_layers - 2 )
- {
- multiplier = 15;
- }
- else if( i == nb_layers - 3 )
- {
- multiplier = 4;
- }
- else
- {
- multiplier = 2;
- }
- parameters.tcp_rates[i] = parameters.tcp_rates[i + 1] * multiplier;
- max_cs_size += (U32)(surface * image->numcomps * (1 / parameters.tcp_rates[i]));
- }
-
- //ensure that we have at least a minimal size
- max_cs_size = llmax(max_cs_size, (U32)FIRST_PACKET_SIZE);
+ // fills parameters.tcp_rates and updates parameters.tcp_numlayers
+ set_tcp_rates(&parameters, nb_layers, LAST_TCP_RATE);
- parameters.max_cs_size = max_cs_size;
}
if (!opj_setup_encoder(encoder, &parameters, image))
@@ -551,7 +582,20 @@ public:
opj_set_warning_handler(encoder, opj_warn, this);
opj_set_error_handler(encoder, opj_error, this);
- U32 tile_count = (rawImageIn.getWidth() >> 6) * (rawImageIn.getHeight() >> 6);
+ U32 width_tiles = (rawImageIn.getWidth() >> 6);
+ U32 height_tiles = (rawImageIn.getHeight() >> 6);
+
+ // Allow images with a width or height that are MIN_IMAGE_SIZE <= x < 64
+ if (width_tiles == 0 && (rawImageIn.getWidth() >= MIN_IMAGE_SIZE))
+ {
+ width_tiles = 1;
+ }
+ if (height_tiles == 0 && (rawImageIn.getHeight() >= MIN_IMAGE_SIZE))
+ {
+ height_tiles = 1;
+ }
+
+ U32 tile_count = width_tiles * height_tiles;
U32 data_size_guess = tile_count * TILE_SIZE;
// will be freed in opj_free_user_data_write
@@ -566,7 +610,7 @@ public:
opj_stream_destroy(stream);
}
- stream = opj_stream_create(data_size_guess, false);
+ stream = opj_stream_create(data_size_guess, OPJ_FALSE);
if (!stream)
{
return false;
@@ -607,17 +651,15 @@ public:
void setImage(const LLImageRaw& raw)
{
- opj_image_cmptparm_t cmptparm[MAX_ENCODED_DISCARD_LEVELS];
- memset(&cmptparm[0], 0, MAX_ENCODED_DISCARD_LEVELS * sizeof(opj_image_cmptparm_t));
-
S32 numcomps = raw.getComponents();
- S32 width = raw.getWidth();
- S32 height = raw.getHeight();
+ S32 width = raw.getWidth();
+ S32 height = raw.getHeight();
+
+ std::vector<opj_image_cmptparm_t> cmptparm(numcomps);
for (S32 c = 0; c < numcomps; c++)
{
- cmptparm[c].prec = 8;
- cmptparm[c].bpp = 8;
+ cmptparm[c].prec = 8; // replaces .bpp
cmptparm[c].sgnd = 0;
cmptparm[c].dx = parameters.subsampling_dx;
cmptparm[c].dy = parameters.subsampling_dy;
@@ -625,7 +667,7 @@ public:
cmptparm[c].h = height;
}
- image = opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB);
+ image = opj_image_create(numcomps, cmptparm.data(), OPJ_CLRSPC_SRGB);
image->x1 = width;
image->y1 = height;
@@ -637,7 +679,7 @@ public:
{
for (S32 x = 0; x < width; x++)
{
- const U8 *pixel = src_datap + (y*width + x) * numcomps;
+ const U8 *pixel = src_datap + (y * width + x) * numcomps;
for (S32 c = 0; c < numcomps; c++)
{
image->comps[c].data[i] = *pixel;
@@ -857,12 +899,11 @@ bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, con
{
JPEG2KEncode encode(comment_text, reversible);
bool encoded = encode.encode(raw_image, base);
- if (encoded)
+ if (!encoded)
{
- LL_WARNS() << "Openjpeg encoding implementation isn't complete, returning false" << LL_ENDL;
+ LL_WARNS() << "Openjpeg encoding was unsuccessful, returning false" << LL_ENDL;
}
return encoded;
- //return false;
}
bool LLImageJ2COJ::getMetadata(LLImageJ2C &base)
diff --git a/indra/llimagej2coj/llimagej2coj.h b/indra/llimagej2coj/llimagej2coj.h
index 498502451a..da49597302 100644
--- a/indra/llimagej2coj/llimagej2coj.h
+++ b/indra/llimagej2coj/llimagej2coj.h
@@ -29,6 +29,8 @@
#include "llimagej2c.h"
+const F32 LAST_TCP_RATE = 1.f/DEFAULT_COMPRESSION_RATE; // should be 8, giving a 1:8 ratio
+
class LLImageJ2COJ : public LLImageJ2CImpl
{
public:
diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h
index 759638b956..ac50b428bf 100644
--- a/indra/llinventory/llparcel.h
+++ b/indra/llinventory/llparcel.h
@@ -37,98 +37,98 @@
#include "llsettingsdaycycle.h"
// Grid out of which parcels taken is stepped every 4 meters.
-const F32 PARCEL_GRID_STEP_METERS = 4.f;
+constexpr F32 PARCEL_GRID_STEP_METERS = 4.f;
// Area of one "square" of parcel
-const S32 PARCEL_UNIT_AREA = 16;
+constexpr S32 PARCEL_UNIT_AREA = 16;
// Height _above_ground_ that parcel boundary ends
-const F32 PARCEL_HEIGHT = 50.f;
+constexpr F32 PARCEL_HEIGHT = 50.f;
//Height above ground which parcel boundries exist for explicitly banned avatars
-const F32 BAN_HEIGHT = 5000.f;
+constexpr F32 BAN_HEIGHT = 5000.f;
// Maximum number of entries in an access list
-const S32 PARCEL_MAX_ACCESS_LIST = 300;
+constexpr S32 PARCEL_MAX_ACCESS_LIST = 300;
//Maximum number of entires in an update packet
//for access/ban lists.
-const F32 PARCEL_MAX_ENTRIES_PER_PACKET = 48.f;
+constexpr F32 PARCEL_MAX_ENTRIES_PER_PACKET = 48.f;
// Maximum number of experiences
-const S32 PARCEL_MAX_EXPERIENCE_LIST = 24;
+constexpr S32 PARCEL_MAX_EXPERIENCE_LIST = 24;
// Weekly charge for listing a parcel in the directory
-const S32 PARCEL_DIRECTORY_FEE = 30;
+constexpr S32 PARCEL_DIRECTORY_FEE = 30;
-const S32 PARCEL_PASS_PRICE_DEFAULT = 10;
-const F32 PARCEL_PASS_HOURS_DEFAULT = 1.f;
+constexpr S32 PARCEL_PASS_PRICE_DEFAULT = 10;
+constexpr F32 PARCEL_PASS_HOURS_DEFAULT = 1.f;
// Number of "chunks" in which parcel overlay data is sent
// Chunk 0 = southern rows, entire width
-const S32 PARCEL_OVERLAY_CHUNKS = 4;
+constexpr S32 PARCEL_OVERLAY_CHUNKS = 4;
// Bottom three bits are a color index for the land overlay
-const U8 PARCEL_COLOR_MASK = 0x07;
-const U8 PARCEL_PUBLIC = 0x00;
-const U8 PARCEL_OWNED = 0x01;
-const U8 PARCEL_GROUP = 0x02;
-const U8 PARCEL_SELF = 0x03;
-const U8 PARCEL_FOR_SALE = 0x04;
-const U8 PARCEL_AUCTION = 0x05;
+constexpr U8 PARCEL_COLOR_MASK = 0x07;
+constexpr U8 PARCEL_PUBLIC = 0x00;
+constexpr U8 PARCEL_OWNED = 0x01;
+constexpr U8 PARCEL_GROUP = 0x02;
+constexpr U8 PARCEL_SELF = 0x03;
+constexpr U8 PARCEL_FOR_SALE = 0x04;
+constexpr U8 PARCEL_AUCTION = 0x05;
// unused 0x06
// unused 0x07
// flag, unused 0x08
-const U8 PARCEL_HIDDENAVS = 0x10; // avatars not visible outside of parcel. Used for 'see avs' feature, but must be off for compatibility
-const U8 PARCEL_SOUND_LOCAL = 0x20;
-const U8 PARCEL_WEST_LINE = 0x40; // flag, property line on west edge
-const U8 PARCEL_SOUTH_LINE = 0x80; // flag, property line on south edge
+constexpr U8 PARCEL_HIDDENAVS = 0x10; // avatars not visible outside of parcel. Used for 'see avs' feature, but must be off for compatibility
+constexpr U8 PARCEL_SOUND_LOCAL = 0x20;
+constexpr U8 PARCEL_WEST_LINE = 0x40; // flag, property line on west edge
+constexpr U8 PARCEL_SOUTH_LINE = 0x80; // flag, property line on south edge
// Transmission results for parcel properties
-const S32 PARCEL_RESULT_NO_DATA = -1;
-const S32 PARCEL_RESULT_SUCCESS = 0; // got exactly one parcel
-const S32 PARCEL_RESULT_MULTIPLE = 1; // got multiple parcels
-
-const S32 SELECTED_PARCEL_SEQ_ID = -10000;
-const S32 COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID = -20000;
-const S32 COLLISION_BANNED_PARCEL_SEQ_ID = -30000;
-const S32 COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID = -40000;
-const S32 HOVERED_PARCEL_SEQ_ID = -50000;
-
-const U32 RT_NONE = 0x1 << 0;
-const U32 RT_OWNER = 0x1 << 1;
-const U32 RT_GROUP = 0x1 << 2;
-const U32 RT_OTHER = 0x1 << 3;
-const U32 RT_LIST = 0x1 << 4;
-const U32 RT_SELL = 0x1 << 5;
-
-const S32 INVALID_PARCEL_ID = -1;
-
-const S32 INVALID_PARCEL_ENVIRONMENT_VERSION = -2;
+constexpr S32 PARCEL_RESULT_NO_DATA = -1;
+constexpr S32 PARCEL_RESULT_SUCCESS = 0; // got exactly one parcel
+constexpr S32 PARCEL_RESULT_MULTIPLE = 1; // got multiple parcels
+
+constexpr S32 SELECTED_PARCEL_SEQ_ID = -10000;
+constexpr S32 COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID = -20000;
+constexpr S32 COLLISION_BANNED_PARCEL_SEQ_ID = -30000;
+constexpr S32 COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID = -40000;
+constexpr S32 HOVERED_PARCEL_SEQ_ID = -50000;
+
+constexpr U32 RT_NONE = 0x1 << 0;
+constexpr U32 RT_OWNER = 0x1 << 1;
+constexpr U32 RT_GROUP = 0x1 << 2;
+constexpr U32 RT_OTHER = 0x1 << 3;
+constexpr U32 RT_LIST = 0x1 << 4;
+constexpr U32 RT_SELL = 0x1 << 5;
+
+constexpr S32 INVALID_PARCEL_ID = -1;
+
+constexpr S32 INVALID_PARCEL_ENVIRONMENT_VERSION = -2;
// if Region settings are used, parcel env. version is -1
-const S32 UNSET_PARCEL_ENVIRONMENT_VERSION = -1;
+constexpr S32 UNSET_PARCEL_ENVIRONMENT_VERSION = -1;
// Timeouts for parcels
// default is 21 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 1814400000000
-const U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(1814400000000);
+constexpr U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(1814400000000);
// ***** TESTING is 10 minutes
//const U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(600000000);
// group is 60 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 5184000000000
-const U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(5184000000000);
+constexpr U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(5184000000000);
// ***** TESTING is 10 minutes
//const U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(600000000);
// default sale timeout is 2 days -> 172800000000
-const U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(172800000000);
+constexpr U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(172800000000);
// ***** TESTING is 10 minutes
//const U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(600000000);
// more grace period extensions.
-const U64 SEVEN_DAYS_IN_USEC = U64L(604800000000);
+constexpr U64 SEVEN_DAYS_IN_USEC = U64L(604800000000);
// if more than 100,000s before sale revert, and no extra extension
// has been given, go ahead and extend it more. That's about 1.2 days.
-const S32 EXTEND_GRACE_IF_MORE_THAN_SEC = 100000;
+constexpr S32 EXTEND_GRACE_IF_MORE_THAN_SEC = 100000;
@@ -250,9 +250,9 @@ public:
void setMediaURL(const std::string& url);
void setMediaType(const std::string& type);
void setMediaDesc(const std::string& desc);
- void setMediaID(const LLUUID& id) { mMediaID = id; }
- void setMediaAutoScale ( U8 flagIn ) { mMediaAutoScale = flagIn; }
- void setMediaLoop (U8 loop) { mMediaLoop = loop; }
+ void setMediaID(const LLUUID& id) { mMediaID = id; }
+ void setMediaAutoScale ( U8 flagIn ) { mMediaAutoScale = flagIn; }
+ void setMediaLoop(U8 loop) { mMediaLoop = loop; }
void setMediaWidth(S32 width);
void setMediaHeight(S32 height);
void setMediaCurrentURL(const std::string& url);
diff --git a/indra/llinventory/llsettingsbase.cpp b/indra/llinventory/llsettingsbase.cpp
index d483b33288..d7a94d61a5 100644
--- a/indra/llinventory/llsettingsbase.cpp
+++ b/indra/llinventory/llsettingsbase.cpp
@@ -361,14 +361,12 @@ LLSD LLSettingsBase::interpolateSDValue(const std::string& key_name, const LLSD
new_array = q.getValue();
}
else
- { // TODO: We could expand this to inspect the type and do a deep lerp based on type.
- // for now assume a heterogeneous array of reals.
+ {
size_t len = std::max(value.size(), other_value.size());
for (size_t i = 0; i < len; ++i)
{
-
- new_array[i] = lerp((F32)value[i].asReal(), (F32)other_value[i].asReal(), (F32)mix);
+ new_array[i] = interpolateSDValue(key_name, value[i], other_value[i], defaults, mix, skip, slerps);
}
}
diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp
index e7d2887fdb..4957cf3c02 100644
--- a/indra/llinventory/llsettingssky.cpp
+++ b/indra/llinventory/llsettingssky.cpp
@@ -657,15 +657,16 @@ void LLSettingsSky::blend(LLSettingsBase::ptr_t &end, F64 blendf)
mHasLegacyHaze |= lerp_legacy_float(mHazeDensity, mLegacyHazeDensity, other->mHazeDensity, other->mLegacyHazeDensity, 0.7f, (F32)blendf);
mHasLegacyHaze |= lerp_legacy_float(mDistanceMultiplier, mLegacyDistanceMultiplier, other->mDistanceMultiplier, other->mLegacyDistanceMultiplier, 0.8f, (F32)blendf);
mHasLegacyHaze |= lerp_legacy_float(mDensityMultiplier, mLegacyDensityMultiplier, other->mDensityMultiplier, other->mLegacyDensityMultiplier, 0.0001f, (F32)blendf);
+ mHasLegacyHaze |= lerp_legacy_color(mAmbientColor, mLegacyAmbientColor, other->mAmbientColor, other->mLegacyAmbientColor, LLColor3(0.25f, 0.25f, 0.25f), (F32)blendf);
mHasLegacyHaze |= lerp_legacy_color(mBlueHorizon, mLegacyBlueHorizon, other->mBlueHorizon, other->mLegacyBlueHorizon, LLColor3(0.4954f, 0.4954f, 0.6399f), (F32)blendf);
mHasLegacyHaze |= lerp_legacy_color(mBlueDensity, mLegacyBlueDensity, other->mBlueDensity, other->mLegacyBlueDensity, LLColor3(0.2447f, 0.4487f, 0.7599f), (F32)blendf);
parammapping_t defaults = other->getParameterMap();
stringset_t skip = getSkipInterpolateKeys();
stringset_t slerps = getSlerpKeys();
- mAbsorptionConfigs = interpolateSDMap(mAbsorptionConfigs, other->mAbsorptionConfigs, defaults, blendf, skip, slerps);
- mMieConfigs = interpolateSDMap(mMieConfigs, other->mMieConfigs, defaults, blendf, skip, slerps);
- mRayleighConfigs = interpolateSDMap(mRayleighConfigs, other->mRayleighConfigs, defaults, blendf, skip, slerps);
+ mAbsorptionConfigs = interpolateSDValue("absorption_config", mAbsorptionConfigs, other->mAbsorptionConfigs, defaults, blendf, skip, slerps);
+ mMieConfigs = interpolateSDValue("mie_config", mMieConfigs, other->mMieConfigs, defaults, blendf, skip, slerps);
+ mRayleighConfigs = interpolateSDValue("rayleigh_config", mRayleighConfigs, other->mRayleighConfigs, defaults, blendf, skip, slerps);
setDirtyFlag(true);
setReplaced();
diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp
index 893bf1fc70..c0b5f48f2d 100644
--- a/indra/llmath/raytrace.cpp
+++ b/indra/llmath/raytrace.cpp
@@ -27,7 +27,6 @@
#include "linden_common.h"
#include "math.h"
-//#include "vmath.h"
#include "v3math.h"
#include "llquaternion.h"
#include "m3math.h"
diff --git a/indra/llmessage/patch_code.cpp b/indra/llmessage/patch_code.cpp
index 489b6ce6a6..9f9f4c852a 100644
--- a/indra/llmessage/patch_code.cpp
+++ b/indra/llmessage/patch_code.cpp
@@ -27,7 +27,6 @@
#include "linden_common.h"
#include "llmath.h"
-//#include "vmath.h"
#include "v3math.h"
#include "patch_dct.h"
#include "patch_code.h"
diff --git a/indra/llmessage/patch_dct.cpp b/indra/llmessage/patch_dct.cpp
index 728fe84537..e74e5fd459 100644
--- a/indra/llmessage/patch_dct.cpp
+++ b/indra/llmessage/patch_dct.cpp
@@ -27,7 +27,6 @@
#include "linden_common.h"
#include "llmath.h"
-//#include "vmath.h"
#include "v3math.h"
#include "patch_dct.h"
diff --git a/indra/llmessage/patch_idct.cpp b/indra/llmessage/patch_idct.cpp
index 5483cf98c0..4bcc439917 100644
--- a/indra/llmessage/patch_idct.cpp
+++ b/indra/llmessage/patch_idct.cpp
@@ -27,7 +27,6 @@
#include "linden_common.h"
#include "llmath.h"
-//#include "vmath.h"
#include "v3math.h"
#include "patch_dct.h"
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index 19a0ce639a..afee099697 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -575,7 +575,7 @@ void LLPluginProcessParent::idle(void)
params.args.add("-e");
params.args.add("tell application \"Terminal\"");
params.args.add("-e");
- params.args.add(STRINGIZE("set win to do script \"lldb -pid "
+ params.args.add(STRINGIZE("set win to do script \"lldb -p "
<< mProcess->getProcessID() << "\""));
params.args.add("-e");
params.args.add("do script \"continue\" in win");
diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt
index 3d8e02cb16..e13f0bbd96 100644
--- a/indra/llprimitive/CMakeLists.txt
+++ b/indra/llprimitive/CMakeLists.txt
@@ -12,7 +12,6 @@ include(TinyGLTF)
set(llprimitive_SOURCE_FILES
lldaeloader.cpp
- llgltfloader.cpp
llgltfmaterial.cpp
llmaterialid.cpp
llmaterial.cpp
@@ -32,7 +31,6 @@ set(llprimitive_SOURCE_FILES
set(llprimitive_HEADER_FILES
CMakeLists.txt
lldaeloader.h
- llgltfloader.h
llgltfmaterial.h
llgltfmaterial_templates.h
legacy_object_types.h
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 0759447902..bfcd84a43d 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -204,12 +204,15 @@ LLModel::EModelStatus load_face_from_dom_triangles(
if (idx_stride <= 0
|| (pos_source && pos_offset >= idx_stride)
+ || (pos_source && pos_offset < 0)
|| (tc_source && tc_offset >= idx_stride)
- || (norm_source && norm_offset >= idx_stride))
+ || (tc_source && tc_offset < 0)
+ || (norm_source && norm_offset >= idx_stride)
+ || (norm_source && norm_offset < 0))
{
// Looks like these offsets should fit inside idx_stride
// Might be good idea to also check idx.getCount()%idx_stride != 0
- LL_WARNS() << "Invalid pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL;
+ LL_WARNS() << "Invalid idx_stride " << idx_stride << ", pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL;
return LLModel::BAD_ELEMENT;
}
@@ -880,9 +883,10 @@ LLDAELoader::LLDAELoader(
void* opaque_userdata,
JointTransformMap& jointTransformMap,
JointNameSet& jointsFromNodes,
- std::map<std::string, std::string>& jointAliasMap,
+ std::map<std::string, std::string, std::less<>>& jointAliasMap,
U32 maxJointsPerMesh,
U32 modelLimit,
+ U32 debugMode,
bool preprocess)
: LLModelLoader(
filename,
@@ -895,8 +899,9 @@ LLDAELoader::LLDAELoader(
jointTransformMap,
jointsFromNodes,
jointAliasMap,
- maxJointsPerMesh),
- mGeneratedModelLimit(modelLimit),
+ maxJointsPerMesh,
+ modelLimit,
+ debugMode),
mPreprocessDAE(preprocess)
{
}
@@ -1680,6 +1685,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
{
materials[model->mMaterialList[i]] = LLImportMaterial();
}
+ // todo: likely a bug here, shouldn't be using suffixed label, see how it gets used in other places.
mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials));
stretch_extents(model, transformation);
}
@@ -2412,7 +2418,7 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
}
// static
-size_t LLDAELoader::getSuffixPosition(std::string label)
+size_t LLDAELoader::getSuffixPosition(const std::string &label)
{
if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
{
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
index 4531e03474..0335011a56 100644
--- a/indra/llprimitive/lldaeloader.h
+++ b/indra/llprimitive/lldaeloader.h
@@ -47,19 +47,20 @@ public:
dae_model_map mModelsMap;
LLDAELoader(
- 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>& jointAliasMap,
- U32 maxJointsPerMesh,
- U32 modelLimit,
- bool preprocess);
+ 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,
+ bool preprocess);
virtual ~LLDAELoader() ;
virtual bool OpenFile(const std::string& filename);
@@ -97,13 +98,12 @@ protected:
bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit);
static std::string getElementLabel(daeElement *element);
- static size_t getSuffixPosition(std::string label);
+ static size_t getSuffixPosition(const std::string& label);
static std::string getLodlessLabel(daeElement *element);
static std::string preprocessDAE(std::string filename);
private:
- U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
bool mPreprocessDAE;
};
diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp
deleted file mode 100644
index 480012699a..0000000000
--- a/indra/llprimitive/llgltfloader.cpp
+++ /dev/null
@@ -1,404 +0,0 @@
-/**
- * @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"
-
-// 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 "llmatrix4a.h"
-
-#include <boost/regex.hpp>
-#include <boost/algorithm/string/replace.hpp>
-
-static const std::string lod_suffix[LLModel::NUM_LODS] =
-{
- "_LOD0",
- "_LOD1",
- "_LOD2",
- "",
- "_PHYS",
-};
-
-
-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> &jointAliasMap,
- U32 maxJointsPerMesh,
- U32 modelLimit) //,
- //bool preprocess)
- : LLModelLoader( filename,
- lod,
- load_cb,
- joint_lookup_func,
- texture_load_func,
- state_cb,
- opaque_userdata,
- jointTransformMap,
- jointsFromNodes,
- jointAliasMap,
- maxJointsPerMesh ),
- //mPreprocessGLTF(preprocess),
- mMeshesLoaded(false),
- mMaterialsLoaded(false)
-{
-}
-
-LLGLTFLoader::~LLGLTFLoader() {}
-
-bool LLGLTFLoader::OpenFile(const std::string &filename)
-{
- tinygltf::TinyGLTF loader;
- std::string error_msg;
- std::string warn_msg;
- std::string filename_lc(filename);
- LLStringUtil::toLower(filename_lc);
-
- // Load a tinygltf model fom a file. Assumes that the input filename has already been
- // been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish.
- if (std::string::npos == filename_lc.rfind(".gltf"))
- { // file is binary
- mGltfLoaded = loader.LoadBinaryFromFile(&mGltfModel, &error_msg, &warn_msg, filename);
- }
- else
- { // file is ascii
- mGltfLoaded = loader.LoadASCIIFromFile(&mGltfModel, &error_msg, &warn_msg, filename);
- }
-
- if (!mGltfLoaded)
- {
- if (!warn_msg.empty())
- LL_WARNS("GLTF_IMPORT") << "gltf load warning: " << warn_msg.c_str() << LL_ENDL;
- if (!error_msg.empty())
- LL_WARNS("GLTF_IMPORT") << "gltf load error: " << error_msg.c_str() << LL_ENDL;
- return false;
- }
-
- mMeshesLoaded = parseMeshes();
- if (mMeshesLoaded) uploadMeshes();
-
- mMaterialsLoaded = parseMaterials();
- if (mMaterialsLoaded) uploadMaterials();
-
- return (mMeshesLoaded || mMaterialsLoaded);
-}
-
-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);
-
- for (tinygltf::Mesh mesh : mGltfModel.meshes)
- {
- LLModel *pModel = new LLModel(volume_params, 0.f);
-
- if (populateModelFromMesh(pModel, mesh) &&
- (LLModel::NO_ERRORS == pModel->getStatus()) &&
- validate_model(pModel))
- {
- mModelList.push_back(pModel);
- }
- else
- {
- setLoadState(ERROR_MODEL + pModel->getStatus());
- delete(pModel);
- return false;
- }
- }
- return true;
-}
-
-bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh)
-{
- pModel->mLabel = mesh.name;
- int pos_idx;
- tinygltf::Accessor indices_a, positions_a, normals_a, uv0_a, color0_a;
-
- auto prims = mesh.primitives;
- for (auto prim : prims)
- {
- if (prim.indices >= 0) indices_a = mGltfModel.accessors[prim.indices];
-
- pos_idx = (prim.attributes.count("POSITION") > 0) ? prim.attributes.at("POSITION") : -1;
- if (pos_idx >= 0)
- {
- positions_a = mGltfModel.accessors[pos_idx];
- if (TINYGLTF_COMPONENT_TYPE_FLOAT != positions_a.componentType)
- continue;
- auto positions_bv = mGltfModel.bufferViews[positions_a.bufferView];
- auto positions_buf = mGltfModel.buffers[positions_bv.buffer];
- //auto type = positions_vb.
- //if (positions_buf.name
- }
-
-#if 0
- int norm_idx, tan_idx, uv0_idx, uv1_idx, color0_idx, color1_idx;
- norm_idx = (prim.attributes.count("NORMAL") > 0) ? prim.attributes.at("NORMAL") : -1;
- tan_idx = (prim.attributes.count("TANGENT") > 0) ? prim.attributes.at("TANGENT") : -1;
- uv0_idx = (prim.attributes.count("TEXCOORDS_0") > 0) ? prim.attributes.at("TEXCOORDS_0") : -1;
- uv1_idx = (prim.attributes.count("TEXCOORDS_1") > 0) ? prim.attributes.at("TEXCOORDS_1") : -1;
- color0_idx = (prim.attributes.count("COLOR_0") > 0) ? prim.attributes.at("COLOR_0") : -1;
- color1_idx = (prim.attributes.count("COLOR_1") > 0) ? prim.attributes.at("COLOR_1") : -1;
-#endif
-
- if (prim.mode == TINYGLTF_MODE_TRIANGLES)
- {
- //auto pos = mesh. TODO resume here DJH 2022-04
- }
- }
-
- //pModel->addFace()
- return false;
-}
-
-bool LLGLTFLoader::parseMaterials()
-{
- if (!mGltfLoaded) return false;
-
- // fill local texture data structures
- mSamplers.clear();
- for (auto in_sampler : mGltfModel.samplers)
- {
- gltf_sampler sampler;
- sampler.magFilter = in_sampler.magFilter > 0 ? in_sampler.magFilter : GL_LINEAR;
- sampler.minFilter = in_sampler.minFilter > 0 ? in_sampler.minFilter : GL_LINEAR;;
- sampler.wrapS = in_sampler.wrapS;
- sampler.wrapT = in_sampler.wrapT;
- sampler.name = in_sampler.name; // unused
- mSamplers.push_back(sampler);
- }
-
- mImages.clear();
- for (auto in_image : mGltfModel.images)
- {
- gltf_image image;
- image.numChannels = in_image.component;
- image.bytesPerChannel = in_image.bits >> 3; // Convert bits to bytes
- image.pixelType = in_image.pixel_type; // Maps exactly, i.e. TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE == GL_UNSIGNED_BYTE, etc
- image.size = static_cast<U32>(in_image.image.size());
- image.height = in_image.height;
- image.width = in_image.width;
- image.data = in_image.image.data();
-
- if (in_image.as_is)
- {
- LL_WARNS("GLTF_IMPORT") << "Unsupported image encoding" << LL_ENDL;
- return false;
- }
-
- if (image.size != image.height * image.width * image.numChannels * image.bytesPerChannel)
- {
- LL_WARNS("GLTF_IMPORT") << "Image size error" << LL_ENDL;
- return false;
- }
-
- mImages.push_back(image);
- }
-
- mTextures.clear();
- for (auto in_tex : mGltfModel.textures)
- {
- gltf_texture tex;
- tex.imageIdx = in_tex.source;
- tex.samplerIdx = in_tex.sampler;
- tex.imageUuid.setNull();
-
- if (tex.imageIdx >= mImages.size() || tex.samplerIdx >= mSamplers.size())
- {
- LL_WARNS("GLTF_IMPORT") << "Texture sampler/image index error" << LL_ENDL;
- return false;
- }
-
- mTextures.push_back(tex);
- }
-
- // parse each material
- for (tinygltf::Material gltf_material : mGltfModel.materials)
- {
- gltf_render_material mat;
- mat.name = gltf_material.name;
-
- tinygltf::PbrMetallicRoughness& pbr = gltf_material.pbrMetallicRoughness;
- mat.hasPBR = true; // Always true, for now
-
- mat.baseColor.set(pbr.baseColorFactor.data());
- mat.hasBaseTex = pbr.baseColorTexture.index >= 0;
- mat.baseColorTexIdx = pbr.baseColorTexture.index;
- mat.baseColorTexCoords = pbr.baseColorTexture.texCoord;
-
- mat.metalness = pbr.metallicFactor;
- mat.roughness = pbr.roughnessFactor;
- mat.hasMRTex = pbr.metallicRoughnessTexture.index >= 0;
- mat.metalRoughTexIdx = pbr.metallicRoughnessTexture.index;
- mat.metalRoughTexCoords = pbr.metallicRoughnessTexture.texCoord;
-
- mat.normalScale = gltf_material.normalTexture.scale;
- mat.hasNormalTex = gltf_material.normalTexture.index >= 0;
- mat.normalTexIdx = gltf_material.normalTexture.index;
- mat.normalTexCoords = gltf_material.normalTexture.texCoord;
-
- mat.occlusionScale = gltf_material.occlusionTexture.strength;
- mat.hasOcclusionTex = gltf_material.occlusionTexture.index >= 0;
- mat.occlusionTexIdx = gltf_material.occlusionTexture.index;
- mat.occlusionTexCoords = gltf_material.occlusionTexture.texCoord;
-
- mat.emissiveColor.set(gltf_material.emissiveFactor.data());
- mat.hasEmissiveTex = gltf_material.emissiveTexture.index >= 0;
- mat.emissiveTexIdx = gltf_material.emissiveTexture.index;
- mat.emissiveTexCoords = gltf_material.emissiveTexture.texCoord;
-
- mat.alphaMode = gltf_material.alphaMode;
- mat.alphaMask = gltf_material.alphaCutoff;
-
- if ((mat.hasNormalTex && (mat.normalTexIdx >= mTextures.size())) ||
- (mat.hasOcclusionTex && (mat.occlusionTexIdx >= mTextures.size())) ||
- (mat.hasEmissiveTex && (mat.emissiveTexIdx >= mTextures.size())) ||
- (mat.hasBaseTex && (mat.baseColorTexIdx >= mTextures.size())) ||
- (mat.hasMRTex && (mat.metalRoughTexIdx >= mTextures.size())))
- {
- LL_WARNS("GLTF_IMPORT") << "Texture resource index error" << LL_ENDL;
- return false;
- }
-
- if ((mat.hasNormalTex && (mat.normalTexCoords > 2)) || // mesh can have up to 3 sets of UV
- (mat.hasOcclusionTex && (mat.occlusionTexCoords > 2)) ||
- (mat.hasEmissiveTex && (mat.emissiveTexCoords > 2)) ||
- (mat.hasBaseTex && (mat.baseColorTexCoords > 2)) ||
- (mat.hasMRTex && (mat.metalRoughTexCoords > 2)))
- {
- LL_WARNS("GLTF_IMPORT") << "Image texcoord index error" << LL_ENDL;
- return false;
- }
-
- mMaterials.push_back(mat);
- }
-
- return true;
-}
-
-// TODO: convert raw vertex buffers to UUIDs
-void LLGLTFLoader::uploadMeshes()
-{
- llassert(0);
-}
-
-// convert raw image buffers to texture UUIDs & assemble into a render material
-void LLGLTFLoader::uploadMaterials()
-{
- for (gltf_render_material mat : mMaterials) // Initially 1 material per gltf file, but design for multiple
- {
- if (mat.hasBaseTex)
- {
- gltf_texture& gtex = mTextures[mat.baseColorTexIdx];
- if (gtex.imageUuid.isNull())
- {
- gtex.imageUuid = imageBufferToTextureUUID(gtex);
- }
- }
-
- if (mat.hasMRTex)
- {
- gltf_texture& gtex = mTextures[mat.metalRoughTexIdx];
- if (gtex.imageUuid.isNull())
- {
- gtex.imageUuid = imageBufferToTextureUUID(gtex);
- }
- }
-
- if (mat.hasNormalTex)
- {
- gltf_texture& gtex = mTextures[mat.normalTexIdx];
- if (gtex.imageUuid.isNull())
- {
- gtex.imageUuid = imageBufferToTextureUUID(gtex);
- }
- }
-
- if (mat.hasOcclusionTex)
- {
- gltf_texture& gtex = mTextures[mat.occlusionTexIdx];
- if (gtex.imageUuid.isNull())
- {
- gtex.imageUuid = imageBufferToTextureUUID(gtex);
- }
- }
-
- if (mat.hasEmissiveTex)
- {
- gltf_texture& gtex = mTextures[mat.emissiveTexIdx];
- if (gtex.imageUuid.isNull())
- {
- gtex.imageUuid = imageBufferToTextureUUID(gtex);
- }
- }
- }
-}
-
-LLUUID LLGLTFLoader::imageBufferToTextureUUID(const gltf_texture& tex)
-{
- //gltf_image& image = mImages[tex.imageIdx];
- //gltf_sampler& sampler = mSamplers[tex.samplerIdx];
-
- // fill an LLSD container with image+sampler data
-
- // upload texture
-
- // retrieve UUID
-
- return LLUUID::null;
-}
diff --git a/indra/llprimitive/llgltfloader.h b/indra/llprimitive/llgltfloader.h
deleted file mode 100644
index 66671d1c5a..0000000000
--- a/indra/llprimitive/llgltfloader.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/**
- * @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 "llglheaders.h"
-#include "llmodelloader.h"
-
-// gltf_* structs are temporary, used to organize the subset of data that eventually goes into the material LLSD
-
-class gltf_sampler
-{
-public:
- // Uses GL enums
- S32 minFilter; // GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR or GL_LINEAR_MIPMAP_LINEAR
- S32 magFilter; // GL_NEAREST or GL_LINEAR
- S32 wrapS; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT
- S32 wrapT; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT
- //S32 wrapR; // Found in some sample files, but not part of glTF 2.0 spec. Ignored.
- std::string name; // optional, currently unused
- // extensions and extras are sampler optional fields that we don't support - at least initially
-};
-
-class gltf_image
-{
-public:// Note that glTF images are defined with row 0 at the top (opposite of OpenGL)
- U8* data; // ptr to decoded image data
- U32 size; // in bytes, regardless of channel width
- U32 width;
- U32 height;
- U32 numChannels; // range 1..4
- U32 bytesPerChannel; // converted from gltf "bits", expects only 8, 16 or 32 as input
- U32 pixelType; // one of (TINYGLTF_COMPONENT_TYPE)_UNSIGNED_BYTE, _UNSIGNED_SHORT, _UNSIGNED_INT, or _FLOAT
-};
-
-class gltf_texture
-{
-public:
- U32 imageIdx;
- U32 samplerIdx;
- LLUUID imageUuid = LLUUID::null;
-};
-
-class gltf_render_material
-{
-public:
- std::string name;
-
- // scalar values
- LLColor4 baseColor; // linear encoding. Multiplied with vertex color, if present.
- double metalness;
- double roughness;
- double normalScale; // scale applies only to X,Y components of normal
- double occlusionScale; // strength multiplier for occlusion
- LLColor4 emissiveColor; // emissive mulitiplier, assumed linear encoding (spec 2.0 is silent)
- std::string alphaMode; // "OPAQUE", "MASK" or "BLEND"
- double alphaMask; // alpha cut-off
-
- // textures
- U32 baseColorTexIdx; // always sRGB encoded
- U32 metalRoughTexIdx; // always linear, roughness in G channel, metalness in B channel
- U32 normalTexIdx; // linear, valid range R[0-1], G[0-1], B[0.5-1]. Normal = texel * 2 - vec3(1.0)
- U32 occlusionTexIdx; // linear, occlusion in R channel, 0 meaning fully occluded, 1 meaning not occluded
- U32 emissiveTexIdx; // always stored as sRGB, in nits (candela / meter^2)
-
- // texture coordinates
- U32 baseColorTexCoords;
- U32 metalRoughTexCoords;
- U32 normalTexCoords;
- U32 occlusionTexCoords;
- U32 emissiveTexCoords;
-
- // TODO: Add traditional (diffuse, normal, specular) UUIDs here, or add this struct to LL_TextureEntry??
-
- bool hasPBR;
- bool hasBaseTex, hasMRTex, hasNormalTex, hasOcclusionTex, hasEmissiveTex;
-
- // This field is populated after upload
- LLUUID material_uuid = LLUUID::null;
-
-};
-
-class gltf_mesh
-{
-public:
- std::string name;
-
- // TODO add mesh import DJH 2022-04
-
-};
-
-class LLGLTFLoader : public LLModelLoader
-{
- public:
- typedef std::map<std::string, LLImportMaterial> material_map;
-
- 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> &jointAliasMap,
- U32 maxJointsPerMesh,
- U32 modelLimit); //,
- //bool preprocess );
- virtual ~LLGLTFLoader();
-
- virtual bool OpenFile(const std::string &filename);
-
-protected:
- tinygltf::Model mGltfModel;
- bool mGltfLoaded;
- bool mMeshesLoaded;
- bool mMaterialsLoaded;
-
- std::vector<gltf_mesh> mMeshes;
- std::vector<gltf_render_material> mMaterials;
-
- std::vector<gltf_texture> mTextures;
- std::vector<gltf_image> mImages;
- std::vector<gltf_sampler> mSamplers;
-
-private:
- bool parseMeshes();
- void uploadMeshes();
- bool parseMaterials();
- void uploadMaterials();
- bool populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh);
- LLUUID imageBufferToTextureUUID(const gltf_texture& tex);
-
- // 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 getElementLabel(gltfElement *element);
- static size_t getSuffixPosition(std::string label);
- static std::string getLodlessLabel(gltfElement *element);
-
- static std::string preprocessGLTF(std::string filename);
- */
-
-};
-#endif // LL_LLGLTFLLOADER_H
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 2fb1956301..00ef79ce7f 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -339,6 +339,162 @@ void LLModel::normalizeVolumeFaces()
}
}
+void LLModel::normalizeVolumeFacesAndWeights()
+{
+ if (!mVolumeFaces.empty())
+ {
+ LLVector4a min, max;
+
+ // For all of the volume faces
+ // in the model, loop over
+ // them and see what the extents
+ // of the volume along each axis.
+ min = mVolumeFaces[0].mExtents[0];
+ max = mVolumeFaces[0].mExtents[1];
+
+ for (U32 i = 1; i < mVolumeFaces.size(); ++i)
+ {
+ LLVolumeFace& face = mVolumeFaces[i];
+
+ update_min_max(min, max, face.mExtents[0]);
+ update_min_max(min, max, face.mExtents[1]);
+
+ if (face.mTexCoords)
+ {
+ LLVector2& min_tc = face.mTexCoordExtents[0];
+ LLVector2& max_tc = face.mTexCoordExtents[1];
+
+ min_tc = face.mTexCoords[0];
+ max_tc = face.mTexCoords[0];
+
+ for (S32 j = 1; j < face.mNumVertices; ++j)
+ {
+ update_min_max(min_tc, max_tc, face.mTexCoords[j]);
+ }
+ }
+ else
+ {
+ face.mTexCoordExtents[0].set(0, 0);
+ face.mTexCoordExtents[1].set(1, 1);
+ }
+ }
+
+ // Now that we have the extents of the model
+ // we can compute the offset needed to center
+ // the model at the origin.
+
+ // Compute center of the model
+ // and make it negative to get translation
+ // needed to center at origin.
+ LLVector4a trans;
+ trans.setAdd(min, max);
+ trans.mul(-0.5f);
+
+ // Compute the total size along all
+ // axes of the model.
+ LLVector4a size;
+ size.setSub(max, min);
+
+ // Prevent division by zero.
+ F32 x = size[0];
+ F32 y = size[1];
+ F32 z = size[2];
+ F32 w = size[3];
+ if (fabs(x) < F_APPROXIMATELY_ZERO)
+ {
+ x = 1.0;
+ }
+ if (fabs(y) < F_APPROXIMATELY_ZERO)
+ {
+ y = 1.0;
+ }
+ if (fabs(z) < F_APPROXIMATELY_ZERO)
+ {
+ z = 1.0;
+ }
+ size.set(x, y, z, w);
+
+ // Compute scale as reciprocal of size
+ LLVector4a scale;
+ scale.splat(1.f);
+ scale.div(size);
+
+ LLVector4a inv_scale(1.f);
+ inv_scale.div(scale);
+
+ for (U32 i = 0; i < mVolumeFaces.size(); ++i)
+ {
+ LLVolumeFace& face = mVolumeFaces[i];
+
+ // We shrink the extents so
+ // that they fall within
+ // the unit cube.
+ // VFExtents change
+ face.mExtents[0].add(trans);
+ face.mExtents[0].mul(scale);
+
+ face.mExtents[1].add(trans);
+ face.mExtents[1].mul(scale);
+
+ // For all the positions, we scale
+ // the positions to fit within the unit cube.
+ LLVector4a* pos = (LLVector4a*)face.mPositions;
+ LLVector4a* norm = (LLVector4a*)face.mNormals;
+ LLVector4a* t = (LLVector4a*)face.mTangents;
+
+ for (S32 j = 0; j < face.mNumVertices; ++j)
+ {
+ pos[j].add(trans);
+ pos[j].mul(scale);
+ if (norm && !norm[j].equals3(LLVector4a::getZero()))
+ {
+ norm[j].mul(inv_scale);
+ norm[j].normalize3();
+ }
+
+ if (t)
+ {
+ F32 w = t[j].getF32ptr()[3];
+ t[j].mul(inv_scale);
+ t[j].normalize3();
+ t[j].getF32ptr()[3] = w;
+ }
+ }
+ }
+
+ weight_map old_weights = mSkinWeights;
+ mSkinWeights.clear();
+ mPosition.clear();
+
+ for (auto& weights : old_weights)
+ {
+ LLVector4a pos(weights.first.mV[VX], weights.first.mV[VY], weights.first.mV[VZ]);
+ pos.add(trans);
+ pos.mul(scale);
+ LLVector3 scaled_pos(pos.getF32ptr());
+ mPosition.push_back(scaled_pos);
+ mSkinWeights[scaled_pos] = weights.second;
+ }
+
+ // mNormalizedScale is the scale at which
+ // we would need to multiply the model
+ // by to get the original size of the
+ // model instead of the normalized size.
+ LLVector4a normalized_scale;
+ normalized_scale.splat(1.f);
+ normalized_scale.div(scale);
+ mNormalizedScale.set(normalized_scale.getF32ptr());
+ mNormalizedTranslation.set(trans.getF32ptr());
+ mNormalizedTranslation *= -1.f;
+
+ // remember normalized scale so original dimensions can be recovered for mesh processing (i.e. tangent generation)
+ for (auto& face : mVolumeFaces)
+ {
+ face.mNormalizedScale = mNormalizedScale;
+ }
+ }
+}
+
void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const
{
scale_out = mNormalizedScale;
@@ -667,7 +823,7 @@ LLSD LLModel::writeModel(
bool upload_skin,
bool upload_joints,
bool lock_scale_if_joint_position,
- bool nowrite,
+ EWriteModelMode write_mode,
bool as_slm,
int submodel_id)
{
@@ -946,10 +1102,10 @@ LLSD LLModel::writeModel(
}
}
- return writeModelToStream(ostr, mdl, nowrite, as_slm);
+ return writeModelToStream(ostr, mdl, write_mode, as_slm);
}
-LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, bool nowrite, bool as_slm)
+LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, EWriteModelMode write_mode, bool as_slm)
{
std::string::size_type cur_offset = 0;
@@ -1011,7 +1167,11 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, bool nowrite, bo
}
}
- if (!nowrite)
+ if (write_mode == WRITE_HUMAN)
+ {
+ ostr << mdl;
+ }
+ else if (write_mode == WRITE_BINARY)
{
LLSDSerialize::toBinary(header, ostr);
@@ -1566,11 +1726,21 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
{
ret["joint_names"][i] = mJointNames[i];
+ // For model to work at all there must be a matching bind matrix,
+ // so supply an indentity one if it isn't true
+ // Note: can build an actual bind matrix from joints
+ const LLMatrix4a& inv_bind = mInvBindMatrix.size() > i ? mInvBindMatrix[i] : LLMatrix4a::identity();
+ if (i >= mInvBindMatrix.size())
+ {
+ LL_WARNS("MESHSKININFO") << "Joint index " << i << " (" << mJointNames[i] << ") exceeds inverse bind matrix size "
+ << mInvBindMatrix.size() << LL_ENDL;
+ }
+
for (U32 j = 0; j < 4; j++)
{
for (U32 k = 0; k < 4; k++)
{
- ret["inverse_bind_matrix"][i][j*4+k] = mInvBindMatrix[i].mMatrix[j][k];
+ ret["inverse_bind_matrix"][i][j * 4 + k] = inv_bind.mMatrix[j][k];
}
}
}
@@ -1583,15 +1753,25 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
}
}
- if ( include_joints && mAlternateBindMatrix.size() > 0 )
+ // optional 'joint overrides'
+ if (include_joints && mAlternateBindMatrix.size() > 0)
{
for (U32 i = 0; i < mJointNames.size(); ++i)
{
+ // If there is not enough to match mJointNames,
+ // either supply no alternate matrixes at all or supply
+ // replacements
+ const LLMatrix4a& alt_bind = mAlternateBindMatrix.size() > i ? mAlternateBindMatrix[i] : LLMatrix4a::identity();
+ if (i >= mAlternateBindMatrix.size())
+ {
+ LL_WARNS("MESHSKININFO") << "Joint index " << i << " (" << mJointNames[i] << ") exceeds alternate bind matrix size "
+ << mAlternateBindMatrix.size() << LL_ENDL;
+ }
for (U32 j = 0; j < 4; j++)
{
for (U32 k = 0; k < 4; k++)
{
- ret["alt_inverse_bind_matrix"][i][j*4+k] = mAlternateBindMatrix[i].mMatrix[j][k];
+ ret["alt_inverse_bind_matrix"][i][j * 4 + k] = alt_bind.mMatrix[j][k];
}
}
}
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index fe28926720..6501b3dc50 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -160,6 +160,12 @@ public:
bool loadSkinInfo(LLSD& header, std::istream& is);
bool loadDecomposition(LLSD& header, std::istream& is);
+ enum EWriteModelMode
+ {
+ WRITE_NO = 0,
+ WRITE_BINARY,
+ WRITE_HUMAN,
+ };
static LLSD writeModel(
std::ostream& ostr,
LLModel* physics,
@@ -171,14 +177,14 @@ public:
bool upload_skin,
bool upload_joints,
bool lock_scale_if_joint_position,
- bool nowrite = false,
+ EWriteModelMode write_mode = WRITE_BINARY,
bool as_slm = false,
int submodel_id = 0);
static LLSD writeModelToStream(
std::ostream& ostr,
LLSD& mdl,
- bool nowrite = false, bool as_slm = false);
+ EWriteModelMode write_mode = WRITE_BINARY, bool as_slm = false);
void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); }
@@ -202,6 +208,7 @@ public:
void sortVolumeFacesByMaterialName();
void normalizeVolumeFaces();
+ void normalizeVolumeFacesAndWeights();
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
void remapVolumeFaces();
void optimizeVolumeFaces();
diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
index 7facd53a72..0383659f62 100644
--- a/indra/llprimitive/llmodelloader.cpp
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -33,6 +33,7 @@
#include "llmatrix4a.h"
#include <boost/bind.hpp>
+#include <boost/exception/diagnostic_information.hpp>
std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
@@ -113,7 +114,9 @@ LLModelLoader::LLModelLoader(
JointTransformMap& jointTransformMap,
JointNameSet& jointsFromNodes,
JointMap& legalJointNamesMap,
- U32 maxJointsPerMesh)
+ U32 maxJointsPerMesh,
+ U32 modelLimit,
+ U32 debugMode)
: mJointList( jointTransformMap )
, mJointsFromNode( jointsFromNodes )
, LLThread("Model Loader")
@@ -121,7 +124,6 @@ LLModelLoader::LLModelLoader(
, mLod(lod)
, mTrySLM(false)
, mFirstTransform(true)
-, mNumOfFetchingTextures(0)
, mLoadCallback(load_cb)
, mJointLookupFunc(joint_lookup_func)
, mTextureLoadFunc(texture_load_func)
@@ -132,7 +134,10 @@ LLModelLoader::LLModelLoader(
, mNoNormalize(false)
, mNoOptimize(false)
, mCacheOnlyHitIfRigged(false)
+, mTexturesNeedScaling(false)
, mMaxJointsPerMesh(maxJointsPerMesh)
+, mGeneratedModelLimit(modelLimit)
+, mDebugMode(debugMode)
, mJointMap(legalJointNamesMap)
{
assert_main_thread();
@@ -149,7 +154,44 @@ LLModelLoader::~LLModelLoader()
void LLModelLoader::run()
{
mWarningsArray.clear();
- doLoadModel();
+ try
+ {
+ doLoadModel();
+ }
+ // Model loader isn't mission critical, so we just log all exceptions
+ catch (const LLException& e)
+ {
+ LL_WARNS("THREAD") << "LLException in model loader: " << e.what() << "" << LL_ENDL;
+ LLSD args;
+ args["Message"] = "UnknownException";
+ args["FILENAME"] = mFilename;
+ args["EXCEPTION"] = e.what();
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL;
+ LLSD args;
+ args["Message"] = "UnknownException";
+ args["FILENAME"] = mFilename;
+ args["EXCEPTION"] = e.what();
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ }
+ catch (...)
+ {
+ LOG_UNHANDLED_EXCEPTION("LLModelLoader");
+ LLSD args;
+ args["Message"] = "UnknownException";
+ args["FILENAME"] = mFilename;
+ args["EXCEPTION"] = boost::current_exception_diagnostic_information();
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ }
+
+ // todo: we are inside of a thread, push this into main thread worker,
+ // not into doOnIdleOneTime that laks tread safety
doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
}
@@ -201,7 +243,9 @@ bool LLModelLoader::doLoadModel()
}
}
- return OpenFile(mFilename);
+ bool res = OpenFile(mFilename);
+ dumpDebugData(); // conditional on mDebugMode
+ return res;
}
void LLModelLoader::setLoadState(U32 state)
@@ -466,6 +510,148 @@ bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::
return true;
}
+void LLModelLoader::dumpDebugData()
+{
+ if (mDebugMode == 0)
+ {
+ return;
+ }
+
+ std::string log_file = mFilename + "_importer.txt";
+ LLStringUtil::toLower(log_file);
+ llofstream file;
+ file.open(log_file.c_str());
+ if (!file)
+ {
+ LL_WARNS() << "dumpDebugData failed to open file " << log_file << LL_ENDL;
+ return;
+ }
+ file << "Importing: " << mFilename << "\n";
+
+ std::map<std::string, LLMatrix4a> inv_bind;
+ std::map<std::string, LLMatrix4a> alt_bind;
+ for (LLPointer<LLModel>& mdl : mModelList)
+ {
+
+ file << "Model name: " << mdl->mLabel << "\n";
+ const LLMeshSkinInfo& skin_info = mdl->mSkinInfo;
+ file << "Shape Bind matrix: " << skin_info.mBindShapeMatrix << "\n";
+ file << "Skin Weights count: " << (S32)mdl->mSkinWeights.size() << "\n";
+
+ // some objects might have individual bind matrices,
+ // but for now it isn't accounted for
+ size_t joint_count = skin_info.mJointNames.size();
+ for (size_t i = 0; i< joint_count;i++)
+ {
+ const std::string& joint = skin_info.mJointNames[i];
+ if (skin_info.mInvBindMatrix.size() > i)
+ {
+ inv_bind[joint] = skin_info.mInvBindMatrix[i];
+ }
+ if (skin_info.mAlternateBindMatrix.size() > i)
+ {
+ alt_bind[joint] = skin_info.mAlternateBindMatrix[i];
+ }
+ }
+ }
+
+ file << "\nInv Bind matrices.\n";
+ for (auto& bind : inv_bind)
+ {
+ file << "Joint: " << bind.first << " Matrix: " << bind.second << "\n";
+ }
+
+ file << "\nAlt Bind matrices.\n";
+ for (auto& bind : alt_bind)
+ {
+ file << "Joint: " << bind.first << " Matrix: " << bind.second << "\n";
+ }
+
+ if (mDebugMode == 2)
+ {
+ S32 model_count = 0;
+ for (LLPointer<LLModel>& mdl : mModelList)
+ {
+ const LLVolume::face_list_t &face_list = mdl->getVolumeFaces();
+ for (S32 face = 0; face < face_list.size(); face++)
+ {
+ const LLVolumeFace& vf = face_list[face];
+ file << "\nModel: " << mdl->mLabel
+ << " face " << face
+ << " has " << vf.mNumVertices
+ << " vertices and " << vf.mNumIndices
+ << " indices " << "\n";
+
+ file << "\nPositions for model: " << mdl->mLabel << " face " << face << "\n";
+
+ for (S32 pos = 0; pos < vf.mNumVertices; ++pos)
+ {
+ file << vf.mPositions[pos] << " ";
+ }
+
+ file << "\n\nIndices for model: " << mdl->mLabel << " face " << face << "\n";
+
+ for (S32 ind = 0; ind < vf.mNumIndices; ++ind)
+ {
+ file << vf.mIndices[ind] << " ";
+ }
+ }
+
+ file << "\n\nWeights for model: " << mdl->mLabel;
+ for (auto& weights : mdl->mSkinWeights)
+ {
+ file << "\nVertex: " << weights.first << " Weights: ";
+ for (auto& weight : weights.second)
+ {
+ file << weight.mJointIdx << ":" << weight.mWeight << " ";
+ }
+ }
+
+ file << "\n";
+ model_count++;
+ if (model_count == 5)
+ {
+ file << "Too many models, stopping at 5.\n";
+ break;
+ }
+ }
+ }
+ else if (mDebugMode > 2)
+ {
+ file << "\nModel LLSDs\n";
+ S32 model_count = 0;
+ // some files contain too many models, so stop at 5.
+ for (LLPointer<LLModel>& mdl : mModelList)
+ {
+ const LLMeshSkinInfo& skin_info = mdl->mSkinInfo;
+ size_t joint_count = skin_info.mJointNames.size();
+ size_t alt_count = skin_info.mAlternateBindMatrix.size();
+
+ LLModel::writeModel(
+ file,
+ nullptr,
+ mdl,
+ nullptr,
+ nullptr,
+ nullptr,
+ mdl->mPhysics,
+ joint_count > 0,
+ alt_count > 0,
+ false,
+ LLModel::WRITE_HUMAN,
+ false,
+ mdl->mSubmodelID);
+
+ file << "\n";
+ model_count++;
+ if (model_count == 5)
+ {
+ file << "Too many models, stopping at 5.\n";
+ break;
+ }
+ }
+ }
+}
//called in the main thread
void LLModelLoader::loadTextures()
@@ -484,7 +670,7 @@ void LLModelLoader::loadTextures()
if(!material.mDiffuseMapFilename.empty())
{
- mNumOfFetchingTextures += mTextureLoadFunc(material, mOpaqueData);
+ mTextureLoadFunc(material, mOpaqueData);
}
}
}
diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h
index 530e61e2b8..335d809386 100644
--- a/indra/llprimitive/llmodelloader.h
+++ b/indra/llprimitive/llmodelloader.h
@@ -36,7 +36,7 @@ class LLJoint;
typedef std::map<std::string, LLMatrix4> JointTransformMap;
typedef std::map<std::string, LLMatrix4>::iterator JointTransformMapIt;
-typedef std::map<std::string, std::string> JointMap;
+typedef std::map<std::string, std::string, std::less<>> JointMap;
typedef std::deque<std::string> JointNameSet;
const S32 SLM_SUPPORTED_VERSION = 3;
@@ -109,8 +109,10 @@ public:
bool mTrySLM;
bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info)
+ bool mTexturesNeedScaling;
model_list mModelList;
+ // The scene is pretty much what ends up getting loaded for upload. Basically assign things to this guy if you want something uploaded.
scene mScene;
typedef std::queue<LLPointer<LLModel> > model_queue;
@@ -119,10 +121,16 @@ public:
model_queue mPhysicsQ;
//map of avatar joints as named in COLLADA assets to internal joint names
+ // Do not use this for anything other than looking up the name of a joint. This is populated elsewhere.
JointMap mJointMap;
+
+ // The joint list is what you want to use to actually setup the specific joint transformations.
JointTransformMap& mJointList;
JointNameSet& mJointsFromNode;
+
+
U32 mMaxJointsPerMesh;
+ U32 mDebugMode; // see dumDebugData() for details
LLModelLoader(
std::string filename,
@@ -135,7 +143,9 @@ public:
JointTransformMap& jointTransformMap,
JointNameSet& jointsFromNodes,
JointMap& legalJointNamesMap,
- U32 maxJointsPerMesh);
+ U32 maxJointsPerMesh,
+ U32 modelLimit,
+ U32 debugMode);
virtual ~LLModelLoader();
virtual void setNoNormalize() { mNoNormalize = true; }
@@ -161,9 +171,6 @@ public:
void stretch_extents(const LLModel* model, const LLMatrix4& mat);
- S32 mNumOfFetchingTextures ; // updated in the main thread
- bool areTexturesReady() { return !mNumOfFetchingTextures; } // called in the main thread.
-
bool verifyCount( int expected, int result );
//Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps)
@@ -192,6 +199,7 @@ public:
const LLSD logOut() const { return mWarningsArray; }
void clearLog() { mWarningsArray.clear(); }
+ void dumpDebugData();
protected:
@@ -203,6 +211,7 @@ protected:
bool mRigValidJointUpload;
U32 mLegacyRigFlags;
+ U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
bool mNoNormalize;
bool mNoOptimize;
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 3f8903ca09..1db36d91f9 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1870,8 +1870,17 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint*)&glbytes);
if(!imageraw->allocateDataSize(width, height, ncomponents, glbytes))
{
- LL_WARNS() << "Memory allocation failed for reading back texture. Size is: " << glbytes << LL_ENDL ;
- LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
+ constexpr S64 MAX_GL_BYTES = 2048 * 2048;
+ if (glbytes > 0 && glbytes <= MAX_GL_BYTES)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS() << "Memory allocation failed for reading back texture. Data size: " << glbytes << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << "Memory allocation failed for reading back texture. Data size is: " << glbytes << LL_ENDL;
+ LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL;
+ }
return false ;
}
@@ -1882,8 +1891,18 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
{
if(!imageraw->allocateDataSize(width, height, ncomponents))
{
- LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL ;
- LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
+ constexpr F32 MAX_IMAGE_SIZE = 2048 * 2048;
+ F32 size = (F32)width * (F32)height * (F32)ncomponents;
+ if (size > 0 && size <= MAX_IMAGE_SIZE)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS() << "Memory allocation failed for reading back texture. Data size: " << size << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL;
+ LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL;
+ }
return false ;
}
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 45dab88e87..b534c8d4e8 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -2230,6 +2230,9 @@ void LLLineEditor::clear()
{
mText.clear();
setCursor(0);
+ mFontBufferPreSelection.reset();
+ mFontBufferSelection.reset();
+ mFontBufferPostSelection.reset();
}
//virtual
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 245339b107..ff77b4d482 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -1007,7 +1007,7 @@ void LLScrollListCtrl::deleteItems(const LLSD& sd)
void LLScrollListCtrl::deleteSelectedItems()
{
item_list::iterator iter;
- for (iter = mItemList.begin(); iter < mItemList.end(); )
+ for (iter = mItemList.begin(); iter != mItemList.end(); )
{
LLScrollListItem* itemp = *iter;
if (itemp->getSelected())
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 778b253c3c..7007049e1c 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -2228,6 +2228,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
registrar.add("Url.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, url));
registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, url));
registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url));
+ registrar.add("Url.ShowParcelOnMap", boost::bind(&LLUrlAction::showParcelOnMap, url));
registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url));
registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url));
diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp
index d017f536f0..fdae22cfa1 100644
--- a/indra/llui/llurlaction.cpp
+++ b/indra/llui/llurlaction.cpp
@@ -30,6 +30,7 @@
#include "llview.h"
#include "llwindow.h"
#include "llurlregistry.h"
+#include "v3dmath.h"
// global state for the callback functions
@@ -128,6 +129,23 @@ void LLUrlAction::showLocationOnMap(std::string url)
}
}
+void LLUrlAction::showParcelOnMap(std::string url)
+{
+ LLSD path_array = LLURI(url).pathArray();
+ auto path_parts = path_array.size();
+
+ if (path_parts < 3) // no parcel id
+ {
+ LL_WARNS() << "Global coordinates are missing in url: [" << url << "]" << LL_ENDL;
+ return;
+ }
+
+ LLVector3d parcel_pos = LLUrlEntryParcel::getParcelPos(LLUUID(LLURI::unescape(path_array[2])));
+ std::ostringstream pos;
+ pos << parcel_pos.mdV[VX] << '/' << parcel_pos.mdV[VY] << '/' << parcel_pos.mdV[VZ];
+ executeSLURL("secondlife:///app/worldmap_global/" + pos.str());
+}
+
void LLUrlAction::copyURLToClipboard(std::string url)
{
LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(url));
diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h
index 6fb14f26b4..8b7ab0e7c5 100644
--- a/indra/llui/llurlaction.h
+++ b/indra/llui/llurlaction.h
@@ -63,6 +63,8 @@ public:
/// if the Url specifies an SL location, show it on a map
static void showLocationOnMap(std::string url);
+ static void showParcelOnMap(std::string url);
+
/// perform the appropriate action for left-clicking on a Url
static void clickAction(std::string url, bool trusted_content);
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index ce6595965a..95603d7ed5 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -41,6 +41,7 @@
#include "lluicolortable.h"
#include "message.h"
#include "llexperiencecache.h"
+#include "v3dmath.h"
// Utility functions
std::string localize_slapp_label(const std::string& url, const std::string& full_name);
@@ -1082,6 +1083,7 @@ LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null);
LLHost LLUrlEntryParcel::sRegionHost;
bool LLUrlEntryParcel::sDisconnected(false);
std::set<LLUrlEntryParcel*> LLUrlEntryParcel::sParcelInfoObservers;
+std::map<LLUUID, LLVector3d> LLUrlEntryParcel::sParcelPos;
///
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
@@ -1174,6 +1176,20 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label);
}
}
+ if (sParcelPos.find(parcel_data.parcel_id) == sParcelPos.end())
+ {
+ sParcelPos[parcel_data.parcel_id] = LLVector3d(parcel_data.global_x, parcel_data.global_y, parcel_data.global_z);
+ }
+}
+
+// static
+LLVector3d LLUrlEntryParcel::getParcelPos(const LLUUID& parcel_id)
+{
+ if (sParcelPos.find(parcel_id) != sParcelPos.end())
+ {
+ return sParcelPos[parcel_id];
+ }
+ return LLVector3d();
}
//
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 36128b0391..efb5081103 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -41,6 +41,7 @@
#include <map>
class LLAvatarName;
+class LLVector3d;
#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
@@ -436,6 +437,8 @@ public:
// Processes parcel label and triggers notifying observers.
static void processParcelInfo(const LLParcelData& parcel_data);
+ static LLVector3d getParcelPos(const LLUUID& parcel_id);
+
// Next setters are used to update agent and viewer connection information
// upon events like user login, viewer disconnect and user changing region host.
// These setters are made public to be accessible from newview and should not be
@@ -449,6 +452,7 @@ private:
static LLHost sRegionHost;
static bool sDisconnected;
static std::set<LLUrlEntryParcel*> sParcelInfoObservers;
+ static std::map<LLUUID, LLVector3d> sParcelPos;
};
///
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp
index a306600f85..28639b9af0 100644
--- a/indra/llwebrtc/llwebrtc.cpp
+++ b/indra/llwebrtc/llwebrtc.cpp
@@ -670,7 +670,10 @@ LLWebRTCPeerConnectionInterface *LLWebRTCImpl::newPeerConnection()
peerConnection->init(this);
mPeerConnections.emplace_back(peerConnection);
- peerConnection->enableSenderTracks(!mMute);
+ // Should it really start disabled?
+ // Seems like something doesn't get the memo and senders need to be reset later
+ // to remove the voice indicator from taskbar
+ peerConnection->enableSenderTracks(false);
if (mPeerConnections.empty())
{
setRecording(true);
@@ -704,7 +707,7 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_conn
LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() :
mWebRTCImpl(nullptr),
mPeerConnection(nullptr),
- mMute(true),
+ mMute(MUTE_INITIAL),
mAnswerReceived(false)
{
}
@@ -739,6 +742,19 @@ void LLWebRTCPeerConnectionImpl::terminate()
}
}
+ // to remove 'Secondlife is recording' icon from taskbar
+ // if user was speaking
+ auto senders = mPeerConnection->GetSenders();
+ for (auto& sender : senders)
+ {
+ auto track = sender->track();
+ if (track)
+ {
+ track->set_enabled(false);
+ }
+ }
+ mPeerConnection->SetAudioRecording(false);
+
mPeerConnection->Close();
if (mLocalStream)
{
@@ -828,6 +844,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
audioOptions.auto_gain_control = true;
audioOptions.echo_cancellation = true;
audioOptions.noise_suppression = true;
+ audioOptions.init_recording_on_send = false;
mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream");
@@ -892,6 +909,7 @@ void LLWebRTCPeerConnectionImpl::enableSenderTracks(bool enable)
{
sender->track()->set_enabled(enable);
}
+ mPeerConnection->SetAudioRecording(enable);
}
}
@@ -932,9 +950,17 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
void LLWebRTCPeerConnectionImpl::setMute(bool mute)
{
- mMute = mute;
+ EMicMuteState new_state = mute ? MUTE_MUTED : MUTE_UNMUTED;
+ if (new_state == mMute)
+ {
+ return; // no change
+ }
+ bool force_reset = mMute == MUTE_INITIAL && mute;
+ bool enable = !mute;
+ mMute = new_state;
+
mWebRTCImpl->PostSignalingTask(
- [this]()
+ [this, force_reset, enable]()
{
if (mPeerConnection)
{
@@ -946,16 +972,34 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
auto track = sender->track();
if (track)
{
- track->set_enabled(!mMute);
+ if (force_reset)
+ {
+ // Force notify observers
+ // Was it disabled too early?
+ // Without this microphone icon in Win's taskbar will stay
+ track->set_enabled(true);
+ }
+ track->set_enabled(enable);
}
}
+ mPeerConnection->SetAudioRecording(enable);
}
});
}
void LLWebRTCPeerConnectionImpl::resetMute()
{
- setMute(mMute);
+ switch(mMute)
+ {
+ case MUTE_MUTED:
+ setMute(true);
+ break;
+ case MUTE_UNMUTED:
+ setMute(false);
+ break;
+ default:
+ break;
+ }
}
void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h
index b93a1fdb01..b6294dbd4a 100644
--- a/indra/llwebrtc/llwebrtc_impl.h
+++ b/indra/llwebrtc/llwebrtc_impl.h
@@ -417,7 +417,12 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;
- bool mMute;
+ typedef enum {
+ MUTE_INITIAL,
+ MUTE_MUTED,
+ MUTE_UNMUTED,
+ } EMicMuteState;
+ EMicMuteState mMute;
// signaling
std::vector<LLWebRTCSignalingObserver *> mSignalingObserverList;
diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm
index 0bd4e506a2..937c3c7a6e 100644
--- a/indra/llwindow/llopenglview-objc.mm
+++ b/indra/llwindow/llopenglview-objc.mm
@@ -657,7 +657,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
};
int string_length = [aString length];
- unichar text[string_length];
+ unichar *text = new unichar[string_length];
attributedStringInfo segments;
// I used 'respondsToSelector:@selector(string)'
// to judge aString is an attributed string or not.
@@ -685,6 +685,8 @@ attributedStringInfo getSegments(NSAttributedString *str)
// we must clear the marked text when aString is null.
[self unmarkText];
}
+
+ delete [] text;
} else {
if (mHasMarkedText)
{
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
index 378e633cd2..eb11a28360 100644
--- a/indra/llwindow/llwindow.cpp
+++ b/indra/llwindow/llwindow.cpp
@@ -103,7 +103,6 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags)
mFullscreen(fullscreen),
mFullscreenWidth(0),
mFullscreenHeight(0),
- mFullscreenBits(0),
mFullscreenRefresh(0),
mSupportedResolutions(NULL),
mNumSupportedResolutions(0),
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index 5e06e665f3..151028113a 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -223,7 +223,6 @@ protected:
bool mFullscreen;
S32 mFullscreenWidth;
S32 mFullscreenHeight;
- S32 mFullscreenBits;
S32 mFullscreenRefresh;
LLWindowResolution* mSupportedResolutions;
S32 mNumSupportedResolutions;
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index f2c4931525..d46357629a 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -76,6 +76,11 @@
#pragma comment(lib, "dxguid.lib") // needed for llurlentry test to build on some systems
#pragma comment(lib, "dinput8")
+#pragma comment(lib, "UxTheme.lib")
+#pragma comment(lib, "Dwmapi.lib")
+#include <Uxtheme.h>
+#include <dwmapi.h> // needed for DwmSetWindowAttribute to set window theme
+
const S32 MAX_MESSAGE_PER_UPDATE = 20;
const S32 BITS_PER_PIXEL = 32;
const S32 MAX_NUM_RESOLUTIONS = 32;
@@ -85,6 +90,10 @@ const F32 ICON_FLASH_TIME = 0.5f;
#define USER_DEFAULT_SCREEN_DPI 96 // Win7
#endif
+#ifndef WM_DWMCOLORIZATIONCOLORCHANGED
+#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320
+#endif
+
// Claim a couple unused GetMessage() message IDs
const UINT WM_DUMMY_(WM_USER + 0x0017);
const UINT WM_POST_FUNCTION_(WM_USER + 0x0018);
@@ -104,6 +113,7 @@ static std::thread::id sMainThreadId;
LPWSTR gIconResource = IDI_APPLICATION;
+LPWSTR gIconSmallResource = IDI_APPLICATION;
LPDIRECTINPUT8 gDirectInput8;
LLW32MsgCallback gAsyncMsgCallback = NULL;
@@ -137,6 +147,17 @@ typedef HRESULT(STDAPICALLTYPE *GetDpiForMonitorType)(
_Out_ UINT *dpiX,
_Out_ UINT *dpiY);
+typedef enum PREFERRED_APP_MODE
+{
+ DEFAULT,
+ ALLOW_DARK,
+ FORCE_DARK,
+ FORCE_LIGHT,
+ MAX
+} PREFERRED_APP_MODE;
+
+typedef PREFERRED_APP_MODE(WINAPI* fnSetPreferredAppMode)(PREFERRED_APP_MODE mode);
+
//
// LLWindowWin32
//
@@ -512,6 +533,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
mFSAASamples = fsaa_samples;
mIconResource = gIconResource;
+ mIconSmallResource = gIconSmallResource;
mOverrideAspectRatio = 0.f;
mNativeAspectRatio = 0.f;
mInputProcessingPaused = false;
@@ -700,8 +722,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
}
if (dev_mode.dmPelsWidth == width &&
- dev_mode.dmPelsHeight == height &&
- dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
+ dev_mode.dmPelsHeight == height)
{
success = true;
if ((dev_mode.dmDisplayFrequency - current_refresh)
@@ -741,7 +762,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
// If we found a good resolution, use it.
if (success)
{
- success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
+ success = setDisplayResolution(width, height, closest_refresh);
}
// Keep a copy of the actual current device mode in case we minimize
@@ -754,7 +775,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
mFullscreen = true;
mFullscreenWidth = dev_mode.dmPelsWidth;
mFullscreenHeight = dev_mode.dmPelsHeight;
- mFullscreenBits = dev_mode.dmBitsPerPel;
mFullscreenRefresh = dev_mode.dmDisplayFrequency;
LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth
@@ -768,7 +788,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
mFullscreen = false;
mFullscreenWidth = -1;
mFullscreenHeight = -1;
- mFullscreenBits = -1;
mFullscreenRefresh = -1;
std::map<std::string,std::string> args;
@@ -847,6 +866,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
// Initialize (boot strap) the Language text input management,
// based on the system's (or user's) default settings.
allowLanguageTextInput(NULL, false);
+ updateWindowTheme();
+ setCustomIcon();
}
@@ -1196,7 +1217,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
// If we found a good resolution, use it.
if (success)
{
- success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
+ success = setDisplayResolution(width, height, closest_refresh);
}
// Keep a copy of the actual current device mode in case we minimize
@@ -1208,7 +1229,6 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
mFullscreen = true;
mFullscreenWidth = dev_mode.dmPelsWidth;
mFullscreenHeight = dev_mode.dmPelsHeight;
- mFullscreenBits = dev_mode.dmBitsPerPel;
mFullscreenRefresh = dev_mode.dmDisplayFrequency;
LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth
@@ -1234,7 +1254,6 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
mFullscreen = false;
mFullscreenWidth = -1;
mFullscreenHeight = -1;
- mFullscreenBits = -1;
mFullscreenRefresh = -1;
LL_INFOS("Window") << "Unable to run fullscreen at " << width << "x" << height << LL_ENDL;
@@ -3023,6 +3042,17 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
WINDOW_IMP_POST(window_imp->mMouseVanish = true);
}
}
+ // Check if theme-related settings changed
+ else if (l_param && (wcscmp((LPCWSTR)l_param, L"ImmersiveColorSet") == 0))
+ {
+ WINDOW_IMP_POST(window_imp->updateWindowTheme());
+ }
+ }
+ break;
+
+ case WM_DWMCOLORIZATIONCOLORCHANGED:
+ {
+ WINDOW_IMP_POST(window_imp->updateWindowTheme());
}
break;
@@ -3532,7 +3562,7 @@ F32 LLWindowWin32::getPixelAspectRatio()
// Change display resolution. Returns true if successful.
// protected
-bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh)
+bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 refresh)
{
DEVMODE dev_mode;
::ZeroMemory(&dev_mode, sizeof(DEVMODE));
@@ -3544,7 +3574,6 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
{
if (dev_mode.dmPelsWidth == width &&
dev_mode.dmPelsHeight == height &&
- dev_mode.dmBitsPerPel == bits &&
dev_mode.dmDisplayFrequency == refresh )
{
// ...display mode identical, do nothing
@@ -3556,9 +3585,8 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
dev_mode.dmSize = sizeof(dev_mode);
dev_mode.dmPelsWidth = width;
dev_mode.dmPelsHeight = height;
- dev_mode.dmBitsPerPel = bits;
dev_mode.dmDisplayFrequency = refresh;
- dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
+ dev_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
// CDS_FULLSCREEN indicates that this is a temporary change to the device mode.
LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN);
@@ -3568,7 +3596,7 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
if (!success)
{
LL_WARNS("Window") << "setDisplayResolution failed, "
- << width << "x" << height << "x" << bits << " @ " << refresh << LL_ENDL;
+ << width << "x" << height << " @ " << refresh << LL_ENDL;
}
return success;
@@ -3579,7 +3607,7 @@ bool LLWindowWin32::setFullscreenResolution()
{
if (mFullscreen)
{
- return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh);
+ return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenRefresh);
}
else
{
@@ -4967,3 +4995,69 @@ void LLWindowWin32::updateWindowRect()
});
}
}
+
+bool LLWindowWin32::isSystemAppDarkMode()
+{
+ HKEY hKey;
+ DWORD dwValue = 1; // Default to light theme
+ DWORD dwSize = sizeof(DWORD);
+
+ // Check registry for system theme preference
+ LSTATUS ret_code =
+ RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey);
+ if (ERROR_SUCCESS == ret_code)
+ {
+ if (RegQueryValueExW(hKey, L"AppsUseLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize) != ERROR_SUCCESS)
+ {
+ // If AppsUseLightTheme is not found, check SystemUsesLightTheme
+ dwSize = sizeof(DWORD);
+ RegQueryValueExW(hKey, L"SystemUsesLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize);
+ }
+ RegCloseKey(hKey);
+ }
+
+ // Return true if dark mode
+ return dwValue == 0;
+}
+
+void LLWindowWin32::updateWindowTheme()
+{
+ bool use_dark_mode = isSystemAppDarkMode();
+ if (use_dark_mode == mCurrentDarkMode)
+ {
+ return;
+ }
+ mCurrentDarkMode = use_dark_mode;
+
+ HMODULE hUxTheme = LoadLibraryExW(L"uxtheme.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (hUxTheme)
+ {
+ auto SetPreferredAppMode = (fnSetPreferredAppMode)GetProcAddress(hUxTheme, "SetPreferredAppMode");
+ if (SetPreferredAppMode)
+ {
+ SetPreferredAppMode(use_dark_mode ? ALLOW_DARK : FORCE_LIGHT);
+ }
+ FreeLibrary(hUxTheme);
+ }
+ BOOL dark_mode(use_dark_mode);
+ DwmSetWindowAttribute(mWindowHandle, DWMWA_USE_IMMERSIVE_DARK_MODE, &dark_mode, sizeof(dark_mode));
+
+ LL_INFOS("Window") << "Viewer window theme is set to " << (use_dark_mode ? "dark" : "light") << " mode" << LL_ENDL;
+}
+
+void LLWindowWin32::setCustomIcon()
+{
+ if (mWindowHandle)
+ {
+ HICON hDefaultIcon = LoadIcon(mhInstance, mIconResource);
+ HICON hSmallIcon = LoadIcon(mhInstance, mIconSmallResource);
+ mWindowThread->post([=]()
+ {
+ SendMessage(mWindowHandle, WM_SETICON, ICON_BIG, (LPARAM)hDefaultIcon);
+ SendMessage(mWindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon);
+
+ SetClassLongPtr(mWindowHandle, GCLP_HICON, (LONG_PTR)hDefaultIcon);
+ SetClassLongPtr(mWindowHandle, GCLP_HICONSM, (LONG_PTR)hSmallIcon);
+ });
+ }
+}
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index 36e89e4586..7196706f87 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -150,7 +150,7 @@ protected:
virtual LLSD getNativeKeyData();
// Changes display resolution. Returns true if successful
- bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
+ bool setDisplayResolution(S32 width, S32 height, S32 refresh);
// Go back to last fullscreen display resolution.
bool setFullscreenResolution();
@@ -214,6 +214,7 @@ protected:
bool mCustomGammaSet;
LPWSTR mIconResource;
+ LPWSTR mIconSmallResource;
bool mInputProcessingPaused;
// The following variables are for Language Text Input control.
@@ -246,6 +247,11 @@ protected:
RECT mRect;
RECT mClientRect;
+ void updateWindowTheme();
+ bool isSystemAppDarkMode();
+ void setCustomIcon();
+ bool mCurrentDarkMode { false };
+
struct LLWindowWin32Thread;
LLWindowWin32Thread* mWindowThread = nullptr;
LLThreadSafeQueue<std::function<void()>> mFunctionQueue;
@@ -281,6 +287,7 @@ private:
extern LLW32MsgCallback gAsyncMsgCallback;
extern LPWSTR gIconResource;
+extern LPWSTR gIconSmallResource;
S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 type);
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
new file mode 100644
index 0000000000..a3f6877935
--- /dev/null
+++ b/indra/newview/res/ll_icon_small.ico
Binary files differ
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
deleted file mode 100644
index 30f31341ed..0000000000
--- a/indra/newview/skins/default/textures/windows/first_login_image.jpg
+++ /dev/null
Binary files differ
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&apos;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: &apos;[NAME]&apos;
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 &gt; Preferences &gt; Advanced &gt; 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]&#10;Click to refresh your L$ balance.&#10;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&apos;utilisateur" name="username_combo" tool_tip="Nom d&apos;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&apos;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&apos;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&apos;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'),
]