summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/app_settings/settings.xml43
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl44
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl39
-rw-r--r--indra/newview/featuretable.txt2
-rw-r--r--indra/newview/featuretable_mac.txt2
-rw-r--r--indra/newview/gltfscenemanager.cpp2
-rw-r--r--indra/newview/llagentlistener.cpp155
-rw-r--r--indra/newview/llagentlistener.h5
-rw-r--r--indra/newview/llavataractions.cpp23
-rw-r--r--indra/newview/llavataractions.h9
-rw-r--r--indra/newview/llcylinder.cpp2
-rw-r--r--indra/newview/lldrawpoolterrain.cpp8
-rw-r--r--indra/newview/llfloaterbump.cpp7
-rw-r--r--indra/newview/llfloaterbump.h1
-rw-r--r--indra/newview/llfloaterimcontainer.cpp10
-rw-r--r--indra/newview/llfloaterluadebug.cpp4
-rw-r--r--indra/newview/llfloaterluascripts.cpp16
-rw-r--r--indra/newview/llfloaterregioninfo.cpp15
-rwxr-xr-xindra/newview/llfloaterworldmap.cpp7
-rw-r--r--indra/newview/llimprocessing.cpp49
-rw-r--r--indra/newview/llimprocessing.h1
-rw-r--r--indra/newview/llimview.cpp13
-rw-r--r--indra/newview/llimview.h4
-rw-r--r--indra/newview/llinventorylistener.cpp6
-rw-r--r--indra/newview/llluamanager.cpp17
-rw-r--r--indra/newview/llmeshrepository.cpp6
-rw-r--r--indra/newview/llmeshrepository.h4
-rw-r--r--indra/newview/llmodelpreview.cpp14
-rw-r--r--indra/newview/llpanelemojicomplete.cpp2
-rw-r--r--indra/newview/llpanelgrouproles.cpp3
-rw-r--r--indra/newview/llpaneloutfitedit.cpp2
-rw-r--r--indra/newview/llpanelprimmediacontrols.cpp2
-rw-r--r--indra/newview/llpanelprofile.cpp16
-rw-r--r--indra/newview/llpanelprofile.h7
-rw-r--r--indra/newview/llpanelvoicedevicesettings.cpp83
-rw-r--r--indra/newview/llpanelvoicedevicesettings.h1
-rw-r--r--indra/newview/llsettingsvo.cpp3
-rw-r--r--indra/newview/llsky.cpp2
-rw-r--r--indra/newview/llsprite.cpp2
-rw-r--r--indra/newview/llterrainpaintmap.cpp712
-rw-r--r--indra/newview/llterrainpaintmap.h128
-rw-r--r--indra/newview/lltexturefetch.cpp10
-rw-r--r--indra/newview/llurldispatcher.cpp2
-rw-r--r--indra/newview/llviewercontrol.cpp1
-rw-r--r--indra/newview/llviewerdisplay.cpp4
-rw-r--r--indra/newview/llviewermedia.cpp4
-rw-r--r--indra/newview/llviewermenu.cpp117
-rw-r--r--indra/newview/llviewermessage.cpp32
-rw-r--r--indra/newview/llviewershadermgr.cpp29
-rw-r--r--indra/newview/llviewershadermgr.h1
-rw-r--r--indra/newview/llviewerstats.cpp19
-rw-r--r--indra/newview/llviewerstats.h1
-rw-r--r--indra/newview/llviewerwindow.cpp7
-rw-r--r--indra/newview/llviewerwindowlistener.cpp20
-rw-r--r--indra/newview/llvlcomposition.cpp4
-rw-r--r--indra/newview/llvlcomposition.h16
-rw-r--r--indra/newview/llvoiceclient.cpp11
-rw-r--r--indra/newview/llvoiceclient.h5
-rw-r--r--indra/newview/llvoicevisualizer.cpp2
-rw-r--r--indra/newview/llvoicevivox.cpp9
-rw-r--r--indra/newview/llvoicevivox.h3
-rw-r--r--indra/newview/llvoicewebrtc.cpp248
-rw-r--r--indra/newview/llvoicewebrtc.h17
-rw-r--r--indra/newview/llwatchdog.cpp1
-rw-r--r--indra/newview/llworldmap.cpp1
-rwxr-xr-xindra/newview/llworldmapview.cpp40
-rw-r--r--indra/newview/llworldmipmap.cpp2
-rw-r--r--indra/newview/pipeline.cpp25
-rw-r--r--indra/newview/scripts/lua/require/LLAgent.lua45
-rw-r--r--indra/newview/scripts/lua/require/UI.lua6
-rw-r--r--indra/newview/scripts/lua/test_LLChatListener.lua9
-rw-r--r--indra/newview/scripts/lua/test_animation.lua18
-rw-r--r--indra/newview/scripts/lua/test_luafloater_demo.lua2
-rw-r--r--indra/newview/scripts/lua/test_nearby_avatars.lua25
-rw-r--r--indra/newview/scripts/lua/test_screen_position.lua8
-rw-r--r--indra/newview/scripts/lua/test_toolbars.lua4
-rw-r--r--indra/newview/skins/default/xui/da/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/de/floater_about_land.xml3
-rw-r--r--indra/newview/skins/default/xui/de/floater_inspect.xml5
-rw-r--r--indra/newview/skins/default/xui/de/panel_classified_info.xml2
-rw-r--r--indra/newview/skins/default/xui/de/panel_landmark_info.xml5
-rw-r--r--indra/newview/skins/default/xui/de/panel_place_profile.xml5
-rw-r--r--indra/newview/skins/default/xui/de/panel_profile_classified.xml2
-rw-r--r--indra/newview/skins/default/xui/de/panel_profile_secondlife.xml6
-rw-r--r--indra/newview/skins/default/xui/de/panel_status_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/de/sidepanel_item_info.xml8
-rw-r--r--indra/newview/skins/default/xui/de/strings.xml12
-rw-r--r--indra/newview/skins/default/xui/en/floater_lua_debug.xml11
-rw-r--r--indra/newview/skins/default/xui/en/floater_lua_scripts.xml1
-rw-r--r--indra/newview/skins/default/xui/en/floater_world_map.xml29
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml7
-rw-r--r--indra/newview/skins/default/xui/en/panel_avatar_list_item.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_conversation_list_item.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_region_general.xml55
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml3
-rw-r--r--indra/newview/skins/default/xui/en/widgets/output_monitor.xml1
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_view.xml1
-rw-r--r--indra/newview/skins/default/xui/es/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/fr/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/it/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/ja/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/pl/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/pt/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/ru/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/tr/strings.xml4
-rw-r--r--indra/newview/skins/default/xui/zh/strings.xml4
-rw-r--r--indra/newview/tests/llluamanager_test.cpp6
-rwxr-xr-xindra/newview/viewer_manifest.py6
112 files changed, 2051 insertions, 389 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index ef97fd2c59..a810e72aac 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -2480,6 +2480,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>UseGroupMemberPagination</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable pagination of group memeber list 50 members at a time.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>DisplayTimecode</key>
<map>
<key>Comment</key>
@@ -4133,6 +4144,17 @@
<key>Value</key>
<array />
</map>
+ <key>LuaDebugShowSource</key>
+ <map>
+ <key>Comment</key>
+ <string>Show source info in Lua Debug Console output</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>GridStatusRSS</key>
<map>
<key>Comment</key>
@@ -4641,6 +4663,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>MapShowGridCoords</key>
+ <map>
+ <key>Comment</key>
+ <string>Shows/hides the grid coordinates of each region on the world map.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>MiniMapAutoCenter</key>
<map>
<key>Comment</key>
@@ -9267,7 +9300,7 @@
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>1</real>
+ <real>1.5</real>
</map>
<key>RenderReflectionProbeDrawDistance</key>
@@ -10078,7 +10111,7 @@
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>1.0</real>
+ <real>0.7</real>
</map>
<key>RenderTonemapType</key>
<map>
@@ -10089,7 +10122,7 @@
<key>Type</key>
<string>U32</string>
<key>Value</key>
- <integer>0</integer>
+ <integer>1</integer>
</map>
<key>ReplaySession</key>
<map>
@@ -13734,7 +13767,7 @@
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
- <integer>0</integer>
+ <integer>1</integer>
</map>
<key>WarningsAsChat</key>
<map>
@@ -15400,7 +15433,7 @@
<key>Type</key>
<string>U32</string>
<key>Value</key>
- <integer>5</integer>
+ <integer>8</integer>
</map>
<key>TerrainPaintResolution</key>
<map>
diff --git a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl
index cf20653a0f..a79a56d725 100644
--- a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl
+++ b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl
@@ -1,5 +1,5 @@
/**
- * @file terrainBakeF.glsl
+ * @file pbrTerrainBakeF.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
diff --git a/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl
new file mode 100644
index 0000000000..e79e9010e6
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl
@@ -0,0 +1,44 @@
+/**
+ * @file terrainStampF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+/*[EXTRA_CODE_HERE]*/
+
+out vec4 frag_color;
+
+// Paint texture to stamp into the "paintmap"
+uniform sampler2D diffuseMap;
+
+in vec2 vary_texcoord0;
+
+void main()
+{
+ vec4 col = texture(diffuseMap, vary_texcoord0);
+ if (col.a <= 0.0f)
+ {
+ discard;
+ }
+
+ frag_color = max(col, vec4(0));
+}
diff --git a/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl
new file mode 100644
index 0000000000..294fa6be26
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl
@@ -0,0 +1,39 @@
+/**
+ * @file terrainStampV.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+uniform mat4 modelview_projection_matrix;
+uniform vec2 terrain_stamp_scale;
+
+in vec3 position;
+
+out vec2 vary_texcoord0;
+
+void main()
+{
+ gl_Position = modelview_projection_matrix * vec4(position, 1.0);
+ // Positions without transforms are treated as UVs for the purpose of this shader.
+ vary_texcoord0.xy = terrain_stamp_scale * position.xy;
+}
+
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
index 24fd7928a6..553d6c1d32 100644
--- a/indra/newview/featuretable.txt
+++ b/indra/newview/featuretable.txt
@@ -77,7 +77,7 @@ RenderScreenSpaceReflections 1 1
RenderMirrors 1 1
RenderHeroProbeResolution 1 2048
RenderHeroProbeDistance 1 16
-RenderHeroProbeUpdateRate 1 4
+RenderHeroProbeUpdateRate 1 6
RenderHeroProbeConservativeUpdateMultiplier 1 16
RenderDownScaleMethod 1 1
RenderCASSharpness 1 1
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
index 06ad730a40..b1359f8b91 100644
--- a/indra/newview/featuretable_mac.txt
+++ b/indra/newview/featuretable_mac.txt
@@ -77,7 +77,7 @@ RenderReflectionProbeLevel 1 3
RenderMirrors 1 1
RenderHeroProbeResolution 1 2048
RenderHeroProbeDistance 1 16
-RenderHeroProbeUpdateRate 1 4
+RenderHeroProbeUpdateRate 1 6
RenderHeroProbeConservativeUpdateMultiplier 1 16
RenderCASSharpness 1 1
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index 29a82416ab..335dbf820a 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -502,7 +502,7 @@ void GLTFSceneManager::update()
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response)
{
LLAppViewer::instance()->postToMainCoro(
- [=]()
+ [=, this]()
{
if (mUploadingAsset)
{
diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp
index 14e443ec4e..2810cd6706 100644
--- a/indra/newview/llagentlistener.cpp
+++ b/indra/newview/llagentlistener.cpp
@@ -32,11 +32,14 @@
#include "llagent.h"
#include "llagentcamera.h"
+#include "llavatarname.h"
+#include "llavatarnamecache.h"
#include "llvoavatar.h"
#include "llcommandhandler.h"
#include "llinventorymodel.h"
#include "llslurl.h"
#include "llurldispatcher.h"
+#include "llviewercontrol.h"
#include "llviewernetwork.h"
#include "llviewerobject.h"
#include "llviewerobjectlist.h"
@@ -46,7 +49,8 @@
#include "llsdutil_math.h"
#include "lltoolgrab.h"
#include "llhudeffectlookat.h"
-#include "llagentcamera.h"
+#include "llviewercamera.h"
+#include "resultset.h"
#include <functional>
static const F64 PLAY_ANIM_THROTTLE_PERIOD = 1.f;
@@ -176,6 +180,32 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
"Return information about [\"item_id\"] animation",
&LLAgentListener::getAnimationInfo,
llsd::map("item_id", LLSD(), "reply", LLSD()));
+
+ add("getID",
+ "Return your own avatar ID",
+ &LLAgentListener::getID,
+ llsd::map("reply", LLSD()));
+
+ add("getNearbyAvatarsList",
+ "Return result set key [\"result\"] for nearby avatars in a range of [\"dist\"]\n"
+ "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n"
+ "reply contains \"result\" table with \"id\", \"name\", \"global_pos\", \"region_pos\", \"region_id\" fields",
+ &LLAgentListener::getNearbyAvatarsList,
+ llsd::map("reply", LLSD()));
+
+ add("getNearbyObjectsList",
+ "Return result set key [\"result\"] for nearby objects in a range of [\"dist\"]\n"
+ "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n"
+ "reply contains \"result\" table with \"id\", \"global_pos\", \"region_pos\", \"region_id\" fields",
+ &LLAgentListener::getNearbyObjectsList,
+ llsd::map("reply", LLSD()));
+
+ add("getAgentScreenPos",
+ "Return screen position of the [\"avatar_id\"] avatar or own avatar if not specified\n"
+ "reply contains \"x\", \"y\" coordinates and \"onscreen\" flag to indicate if it's actually in within the current window\n"
+ "avatar render position is used as the point",
+ &LLAgentListener::getAgentScreenPos,
+ llsd::map("reply", LLSD()));
}
void LLAgentListener::requestTeleport(LLSD const & event_data) const
@@ -693,3 +723,126 @@ void LLAgentListener::getAnimationInfo(LLSD const &event_data)
"priority", motion->getPriority());
}
}
+
+void LLAgentListener::getID(LLSD const& event_data)
+{
+ Response response(llsd::map("id", gAgentID), event_data);
+}
+
+struct AvResultSet : public LL::ResultSet
+{
+ AvResultSet() : LL::ResultSet("nearby_avatars") {}
+ std::vector<LLVOAvatar*> mAvatars;
+
+ int getLength() const override { return narrow(mAvatars.size()); }
+ LLSD getSingle(int index) const override
+ {
+ auto av = mAvatars[index];
+ LLAvatarName av_name;
+ LLAvatarNameCache::get(av->getID(), &av_name);
+ LLVector3 region_pos = av->getCharacterPosition();
+ return llsd::map("id", av->getID(),
+ "global_pos", ll_sd_from_vector3d(av->getPosGlobalFromAgent(region_pos)),
+ "region_pos", ll_sd_from_vector3(region_pos),
+ "name", av_name.getUserName(),
+ "region_id", av->getRegion()->getRegionID());
+ }
+};
+
+struct ObjResultSet : public LL::ResultSet
+{
+ ObjResultSet() : LL::ResultSet("nearby_objects") {}
+ std::vector<LLViewerObject*> mObjects;
+
+ int getLength() const override { return narrow(mObjects.size()); }
+ LLSD getSingle(int index) const override
+ {
+ auto obj = mObjects[index];
+ return llsd::map("id", obj->getID(),
+ "global_pos", ll_sd_from_vector3d(obj->getPositionGlobal()),
+ "region_pos", ll_sd_from_vector3(obj->getPositionRegion()),
+ "region_id", obj->getRegion()->getRegionID());
+ }
+};
+
+
+F32 get_search_radius(LLSD const& event_data)
+{
+ static LLCachedControl<F32> render_far_clip(gSavedSettings, "RenderFarClip", 64);
+ F32 dist = render_far_clip;
+ if (event_data.has("dist"))
+ {
+ dist = llclamp((F32)event_data["dist"].asReal(), 1, 512);
+ }
+ return dist * dist;
+}
+
+void LLAgentListener::getNearbyAvatarsList(LLSD const& event_data)
+{
+ Response response(LLSD(), event_data);
+ auto avresult = new AvResultSet;
+
+ F32 radius = get_search_radius(event_data);
+ LLVector3d agent_pos = gAgent.getPositionGlobal();
+ for (LLCharacter* character : LLCharacter::sInstances)
+ {
+ LLVOAvatar* avatar = (LLVOAvatar*)character;
+ if (!avatar->isDead() && !avatar->isControlAvatar() && !avatar->isSelf())
+ {
+ if ((dist_vec_squared(avatar->getPositionGlobal(), agent_pos) <= radius))
+ {
+ avresult->mAvatars.push_back(avatar);
+ }
+ }
+ }
+ response["result"] = avresult->getKeyLength();
+}
+
+void LLAgentListener::getNearbyObjectsList(LLSD const& event_data)
+{
+ Response response(LLSD(), event_data);
+ auto objresult = new ObjResultSet;
+
+ F32 radius = get_search_radius(event_data);
+ S32 num_objects = gObjectList.getNumObjects();
+ LLVector3d agent_pos = gAgent.getPositionGlobal();
+ for (S32 i = 0; i < num_objects; ++i)
+ {
+ LLViewerObject* object = gObjectList.getObject(i);
+ if (object && object->getVolume() && !object->isAttachment())
+ {
+ if ((dist_vec_squared(object->getPositionGlobal(), agent_pos) <= radius))
+ {
+ objresult->mObjects.push_back(object);
+ }
+ }
+ }
+ response["result"] = objresult->getKeyLength();
+}
+
+void LLAgentListener::getAgentScreenPos(LLSD const& event_data)
+{
+ Response response(LLSD(), event_data);
+ LLVector3 render_pos;
+ if (event_data.has("avatar_id") && (event_data["avatar_id"].asUUID() != gAgentID))
+ {
+ LLUUID avatar_id(event_data["avatar_id"]);
+ for (LLCharacter* character : LLCharacter::sInstances)
+ {
+ LLVOAvatar* avatar = (LLVOAvatar*)character;
+ if (!avatar->isDead() && (avatar->getID() == avatar_id))
+ {
+ render_pos = avatar->getRenderPosition();
+ break;
+ }
+ }
+ }
+ else if (gAgentAvatarp.notNull() && gAgentAvatarp->isValid())
+ {
+ render_pos = gAgentAvatarp->getRenderPosition();
+ }
+ LLCoordGL screen_pos;
+ response["onscreen"] = LLViewerCamera::getInstance()->projectPosAgentToScreen(render_pos, screen_pos, false);
+ response["x"] = screen_pos.mX;
+ response["y"] = screen_pos.mY;
+}
diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h
index 05724ff443..8801c9f7ea 100644
--- a/indra/newview/llagentlistener.h
+++ b/indra/newview/llagentlistener.h
@@ -67,6 +67,11 @@ private:
void stopAnimation(LLSD const &event_data);
void getAnimationInfo(LLSD const &event_data);
+ void getID(LLSD const& event_data);
+ void getNearbyAvatarsList(LLSD const& event_data);
+ void getNearbyObjectsList(LLSD const& event_data);
+ void getAgentScreenPos(LLSD const& event_data);
+
LLViewerObject * findObjectClosestTo( const LLVector3 & position, bool sit_target = false ) const;
private:
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index 6f6b89ea81..aff959d279 100644
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -285,25 +285,18 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floate
make_ui_sound("UISndStartIM");
}
-/* AD *TODO: Is this function needed any more?
- I fixed it a bit(added check for canCall), but it appears that it is not used
- anywhere. Maybe it should be removed?
// static
-bool LLAvatarActions::isCalling(const LLUUID &id)
+bool LLAvatarActions::canCall()
{
- if (id.isNull() || !canCall())
- {
- return false;
- }
-
- LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id);
- return (LLIMModel::getInstance()->findIMSession(session_id) != NULL);
-}*/
+ LLVoiceClient* voice_client = LLVoiceClient::getInstance();
+ return voice_client->voiceEnabled() && voice_client->isVoiceWorking();
+}
-//static
-bool LLAvatarActions::canCall()
+// static
+bool LLAvatarActions::canCallTo(const LLUUID& id)
{
- return LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
+ LLVoiceClient* voice_client = LLVoiceClient::getInstance();
+ return voice_client->voiceEnabled() && voice_client->isVoiceWorking() && voice_client->getVoiceEnabled(id);
}
// static
diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h
index 1f5a42ed22..6f469e96ce 100644
--- a/indra/newview/llavataractions.h
+++ b/indra/newview/llavataractions.h
@@ -172,18 +172,15 @@ public:
static bool canBlock(const LLUUID& id);
/**
- * Return true if the avatar is in a P2P voice call with a given user
+ * @return true if voice calls are available
*/
- /* AD *TODO: Is this function needed any more?
- I fixed it a bit(added check for canCall), but it appears that it is not used
- anywhere. Maybe it should be removed?
- static bool isCalling(const LLUUID &id);*/
+ static bool canCall();
/**
* @return true if call to the resident can be made
*/
+ static bool canCallTo(const LLUUID& id);
- static bool canCall();
/**
* Invite avatar to a group.
*/
diff --git a/indra/newview/llcylinder.cpp b/indra/newview/llcylinder.cpp
index c347d3e1be..d5f1e25151 100644
--- a/indra/newview/llcylinder.cpp
+++ b/indra/newview/llcylinder.cpp
@@ -29,7 +29,7 @@
#include "llcylinder.h"
#include "llerror.h"
-#include "math.h"
+#include <cmath>
#include "llmath.h"
#include "noise.h"
#include "v3math.h"
diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp
index 5e676bc5b3..57def49539 100644
--- a/indra/newview/lldrawpoolterrain.cpp
+++ b/indra/newview/lldrawpoolterrain.cpp
@@ -296,7 +296,7 @@ void LLDrawPoolTerrain::renderFullShaderTextures()
// GL_BLEND disabled by default
drawLoop();
- // Disable multitexture
+ // Disable textures
sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0);
sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL1);
@@ -557,7 +557,7 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool use_local_materials)
// GL_BLEND disabled by default
drawLoop();
- // Disable multitexture
+ // Disable textures
if (paint_type == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE)
{
@@ -769,7 +769,7 @@ void LLDrawPoolTerrain::renderFull4TU()
}
LLVertexBuffer::unbind();
- // Disable multitexture
+ // Disable textures
gGL.getTexUnit(3)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(3)->disable();
gGL.getTexUnit(3)->activate();
@@ -949,7 +949,7 @@ void LLDrawPoolTerrain::renderFull2TU()
// Restore blend state
gGL.setSceneBlendType(LLRender::BT_ALPHA);
- // Disable multitexture
+ // Disable textures
gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE);
gGL.getTexUnit(1)->disable();
diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp
index d56e6cdf20..2a4f1ddd12 100644
--- a/indra/newview/llfloaterbump.cpp
+++ b/indra/newview/llfloaterbump.cpp
@@ -56,7 +56,7 @@ LLFloaterBump::LLFloaterBump(const LLSD& key)
mCommitCallbackRegistrar.add("ShowAgentProfile", { boost::bind(&LLFloaterBump::showProfile, this), cb_info::UNTRUSTED_THROTTLE });
mCommitCallbackRegistrar.add("Avatar.InviteToGroup", { boost::bind(&LLFloaterBump::inviteToGroup, this), cb_info::UNTRUSTED_THROTTLE });
mCommitCallbackRegistrar.add("Avatar.Call", { boost::bind(&LLFloaterBump::startCall, this), cb_info::UNTRUSTED_BLOCK });
- mEnableCallbackRegistrar.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall));
+ mEnableCallbackRegistrar.add("Avatar.EnableCall", { boost::bind(&LLFloaterBump::canCall, this), cb_info::UNTRUSTED_BLOCK });
mCommitCallbackRegistrar.add("Avatar.AddFriend", { boost::bind(&LLFloaterBump::addFriend, this), cb_info::UNTRUSTED_THROTTLE });
mEnableCallbackRegistrar.add("Avatar.EnableAddFriend", boost::bind(&LLFloaterBump::enableAddFriend, this));
mCommitCallbackRegistrar.add("Avatar.Mute", { boost::bind(&LLFloaterBump::muteAvatar, this), cb_info::UNTRUSTED_BLOCK });
@@ -214,6 +214,11 @@ void LLFloaterBump::startCall()
LLAvatarActions::startCall(mItemUUID);
}
+bool LLFloaterBump::canCall()
+{
+ return LLAvatarActions::canCallTo(mItemUUID);
+}
+
void LLFloaterBump::reportAbuse()
{
LLFloaterReporter::showFromAvatar(mItemUUID, "av_name");
diff --git a/indra/newview/llfloaterbump.h b/indra/newview/llfloaterbump.h
index 53e730d73f..48d6f7e33e 100644
--- a/indra/newview/llfloaterbump.h
+++ b/indra/newview/llfloaterbump.h
@@ -52,6 +52,7 @@ public:
void startIM();
void startCall();
+ bool canCall();
void reportAbuse();
void showProfile();
void addFriend();
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index 1e2d790cfc..ed24a8af57 100644
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -1592,15 +1592,15 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v
}
else if ("can_call" == item)
{
+ if (is_single_select)
+ {
+ return LLAvatarActions::canCallTo(single_id);
+ }
return LLAvatarActions::canCall();
}
else if ("can_open_voice_conversation" == item)
{
- return is_single_select && LLAvatarActions::canCall();
- }
- else if ("can_open_voice_conversation" == item)
- {
- return is_single_select && LLAvatarActions::canCall();
+ return is_single_select && LLAvatarActions::canCallTo(single_id);
}
else if ("can_zoom_in" == item)
{
diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp
index 06877df816..362a02a642 100644
--- a/indra/newview/llfloaterluadebug.cpp
+++ b/indra/newview/llfloaterluadebug.cpp
@@ -58,7 +58,9 @@ bool LLFloaterLUADebug::postBuild()
.listen("LLFloaterLUADebug",
[mResultOutput=mResultOutput](const LLSD& data)
{
- mResultOutput->pasteTextWithLinebreaks(data.asString());
+ LLCachedControl<bool> show_source_info(gSavedSettings, "LuaDebugShowSource", false);
+ std::string source_info = show_source_info ? data["source_info"].asString() : "";
+ mResultOutput->pasteTextWithLinebreaks(stringize(data["level"].asString(), source_info, data["msg"].asString()), true);
mResultOutput->addLineBreakChar(true);
return false;
});
diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp
index 0eba45ec29..6b3d87543a 100644
--- a/indra/newview/llfloaterluascripts.cpp
+++ b/indra/newview/llfloaterluascripts.cpp
@@ -51,9 +51,9 @@ LLFloaterLUAScripts::LLFloaterLUAScripts(const LLSD &key)
}, cb_info::UNTRUSTED_BLOCK });
mCommitCallbackRegistrar.add("Script.Terminate", {[this](LLUICtrl*, const LLSD &userdata)
{
- if (mScriptList->hasSelectedItem())
+ std::vector<LLSD> coros = mScriptList->getAllSelectedValues();
+ for (auto coro_name : coros)
{
- std::string coro_name = mScriptList->getSelectedValue();
LLCoros::instance().killreq(coro_name);
}
}, cb_info::UNTRUSTED_BLOCK });
@@ -97,7 +97,7 @@ void LLFloaterLUAScripts::draw()
void LLFloaterLUAScripts::populateScriptList()
{
S32 prev_pos = mScriptList->getScrollPos();
- LLSD prev_selected = mScriptList->getSelectedValue();
+ std::vector<LLSD> prev_selected = mScriptList->getAllSelectedValues();
mScriptList->clearRows();
mScriptList->updateColumns(true);
std::map<std::string, std::string> scripts = LLLUAmanager::getScriptNames();
@@ -112,7 +112,10 @@ void LLFloaterLUAScripts::populateScriptList()
mScriptList->addElement(row);
}
mScriptList->setScrollPos(prev_pos);
- mScriptList->setSelectedByValue(prev_selected, true);
+ for (auto value : prev_selected)
+ {
+ mScriptList->setSelectedByValue(value, true);
+ }
}
void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y)
@@ -120,10 +123,13 @@ void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y)
LLScrollListItem *item = mScriptList->hitItem(x, y);
if (item)
{
- mScriptList->selectItemAt(x, y, MASK_NONE);
+ if (!item->getSelected())
+ mScriptList->selectItemAt(x, y, MASK_NONE);
+
auto menu = mContextMenuHandle.get();
if (menu)
{
+ menu->setItemEnabled(std::string("open_folder"), (mScriptList->getNumSelected() == 1));
menu->show(x, y);
LLMenuGL::showPopup(this, menu, x, y);
}
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index df4acfac5e..ac3e942e15 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -504,6 +504,18 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg)
panel->getChildView("access_combo")->setEnabled(gAgent.isGodlike() || (region && region->canManageEstate() && !teen_grid));
panel->setCtrlsEnabled(allow_modify);
+ panel->getChild<LLLineEditor>("estate_id")->setValue((S32)region_info.mEstateID);
+
+ if (region)
+ {
+ panel->getChild<LLLineEditor>("grid_position_x")->setValue((S32)(region->getOriginGlobal()[VX] / 256));
+ panel->getChild<LLLineEditor>("grid_position_y")->setValue((S32)(region->getOriginGlobal()[VY] / 256));
+ }
+ else
+ {
+ panel->getChild<LLLineEditor>("grid_position_x")->setDefaultText();
+ panel->getChild<LLLineEditor>("grid_position_y")->setDefaultText();
+ }
// DEBUG PANEL
panel = tab->getChild<LLPanel>("Debug");
@@ -863,6 +875,9 @@ bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region, ERefres
getChildView("apply_btn")->setEnabled(false);
getChildView("access_text")->setEnabled(allow_modify);
// getChildView("access_combo")->setEnabled(allow_modify);
+ getChildView("estate_id")->setEnabled(false);
+ getChildView("grid_position_x")->setEnabled(false);
+ getChildView("grid_position_y")->setEnabled(false);
// now set in processRegionInfo for teen grid detection
getChildView("kick_btn")->setEnabled(allow_modify);
getChildView("kick_all_btn")->setEnabled(allow_modify);
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
index dda7266220..4674cd68b6 100755
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -486,8 +486,11 @@ void LLFloaterWorldMap::onOpen(const LLSD& key)
const LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
LLInventoryModelBackgroundFetch::instance().start(landmark_folder_id);
- mLocationEditor->setFocus( true);
- gFocusMgr.triggerFocusFlash();
+ if (hasFocus())
+ {
+ mLocationEditor->setFocus( true);
+ gFocusMgr.triggerFocusFlash();
+ }
buildAvatarIDList();
buildLandmarkIDLists();
diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp
index 590cd09a31..e050fb77e0 100644
--- a/indra/newview/llimprocessing.cpp
+++ b/indra/newview/llimprocessing.cpp
@@ -422,6 +422,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
U8 *binary_bucket,
S32 binary_bucket_size,
LLHost &sender,
+ LLSD metadata,
LLUUID aux_id)
{
LLChat chat;
@@ -451,6 +452,28 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
bool is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT &&
LLMuteList::isLinden(name);
+ /***
+ * The simulator may have flagged this sender as a bot, if the viewer would like to display
+ * the chat text in a different color or font, the below code is how the viewer can
+ * tell if the sender is a bot.
+ *-----------------------------------------------------
+ bool is_bot = false;
+ if (metadata.has("sender"))
+ { // The server has identified this sender as a bot.
+ is_bot = metadata["sender"]["bot"].asBoolean();
+ }
+ *-----------------------------------------------------
+ */
+
+ std::string notice_name;
+ LLSD notice_args;
+ if (metadata.has("notice"))
+ { // The server has injected a notice into the IM conversation.
+ // These will be things like bot notifications, etc.
+ notice_name = metadata["notice"]["id"].asString();
+ notice_args = metadata["notice"]["data"];
+ }
+
chat.mMuted = is_muted;
chat.mFromID = from_id;
chat.mFromName = name;
@@ -544,7 +567,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
}
else
{
- // standard message, not from system
+ // standard message, server may have injected a notice into the conversation.
std::string saved;
if (offline == IM_OFFLINE)
{
@@ -579,8 +602,17 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
region_message = true;
}
}
- gIMMgr->addMessage(
- session_id,
+
+ std::string real_name;
+
+ if (!notice_name.empty())
+ { // The simulator has injected some sort of notice into the conversation.
+ // findString will only replace the contents of buffer if the notice_id is found.
+ LLTrans::findString(buffer, notice_name, notice_args);
+ real_name = SYSTEM_FROM;
+ }
+
+ gIMMgr->addMessage(session_id,
from_id,
name,
buffer,
@@ -591,7 +623,9 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
region_id,
position,
region_message,
- timestamp);
+ timestamp,
+ LLUUID::null,
+ real_name);
}
else
{
@@ -1627,6 +1661,12 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url)
from_group = message_data["from_group"].asString() == "Y";
}
+ LLSD metadata;
+ if (message_data.has("metadata"))
+ {
+ metadata = message_data["metadata"];
+ }
+
EInstantMessage dialog = static_cast<EInstantMessage>(message_data["dialog"].asInteger());
LLUUID session_id = message_data["transaction-id"].asUUID();
if (session_id.isNull() && dialog == IM_FROM_TASK)
@@ -1654,6 +1694,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url)
local_bin_bucket.data(),
S32(local_bin_bucket.size()),
local_sender,
+ metadata,
message_data["asset_id"].asUUID());
});
diff --git a/indra/newview/llimprocessing.h b/indra/newview/llimprocessing.h
index 030d28b198..66ffc59ae0 100644
--- a/indra/newview/llimprocessing.h
+++ b/indra/newview/llimprocessing.h
@@ -48,6 +48,7 @@ public:
U8 *binary_bucket,
S32 binary_bucket_size,
LLHost &sender,
+ LLSD metadata,
LLUUID aux_id = LLUUID::null);
// Either receives list of offline messages from 'ReadOfflineMsgs' capability
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 529deed511..4fcbc91ffd 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -3145,9 +3145,16 @@ void LLIMMgr::addMessage(
const LLUUID& region_id,
const LLVector3& position,
bool is_region_msg,
- U32 timestamp) // May be zero
+ U32 timestamp, // May be zero
+ LLUUID display_id,
+ std::string_view display_name)
{
LLUUID other_participant_id = target_id;
+ std::string message_display_name = (display_name.empty()) ? from : std::string(display_name);
+ if (display_id.isNull() && (display_name.empty()))
+ {
+ display_id = other_participant_id;
+ }
LLUUID new_session_id = session_id;
if (new_session_id.isNull())
@@ -3243,7 +3250,7 @@ void LLIMMgr::addMessage(
}
//Play sound for new conversations
- if (!skip_message & !gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation")))
+ if (!skip_message && !gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation")))
{
make_ui_sound("UISndNewIncomingIMSession");
}
@@ -3257,7 +3264,7 @@ void LLIMMgr::addMessage(
if (!LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !skip_message)
{
- LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg, true, is_region_msg, timestamp);
+ LLIMModel::instance().addMessage(new_session_id, message_display_name, display_id, msg, true, is_region_msg, timestamp);
}
// Open conversation floater if offline messages are present
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index 233fb075e8..162a8d93dd 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -368,7 +368,9 @@ public:
const LLUUID& region_id = LLUUID::null,
const LLVector3& position = LLVector3::zero,
bool is_region_msg = false,
- U32 timestamp = 0);
+ U32 timestamp = 0,
+ LLUUID display_id = LLUUID::null,
+ std::string_view display_name = "");
void addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args);
diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp
index 79726c3e0b..88b07c0b0b 100644
--- a/indra/newview/llinventorylistener.cpp
+++ b/indra/newview/llinventorylistener.cpp
@@ -116,7 +116,8 @@ struct CatResultSet: public LL::ResultSet
LLSD getSingle(int index) const override
{
auto cat = mCategories[index];
- return llsd::map("name", cat->getName(),
+ return llsd::map("id", cat->getUUID(),
+ "name", cat->getName(),
"parent_id", cat->getParentUUID(),
"type", LLFolderType::lookup(cat->getPreferredType()));
}
@@ -133,7 +134,8 @@ struct ItemResultSet: public LL::ResultSet
LLSD getSingle(int index) const override
{
auto item = mItems[index];
- return llsd::map("name", item->getName(),
+ return llsd::map("id", item->getUUID(),
+ "name", item->getName(),
"parent_id", item->getParentUUID(),
"desc", item->getDescription(),
"inv_type", LLInventoryType::lookup(item->getInventoryType()),
diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp
index 7fe5c1ece0..9b6dcc5277 100644
--- a/indra/newview/llluamanager.cpp
+++ b/indra/newview/llluamanager.cpp
@@ -72,9 +72,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level)
lluau_checkstack(L, 2);
luaL_where(L, 1);
// start with the 'where' info at the top of the stack
- std::ostringstream out;
- out << lua_tostring(L, -1);
+ std::string source_info{ lua_tostring(L, -1) };
lua_pop(L, 1);
+
+ std::ostringstream out;
const char* sep = ""; // 'where' info ends with ": "
// now iterate over arbitrary args, calling Lua tostring() on each and
// concatenating with separators
@@ -101,10 +102,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level)
// capture message string
std::string msg{ out.str() };
// put message out there for any interested party (*koff* LLFloaterLUADebug *koff*)
- LLEventPumps::instance().obtain("lua output").post(stringize(level, ": ", msg));
+ LLEventPumps::instance().obtain("lua output").post(llsd::map("msg", msg, "level", stringize(level, ": "), "source_info", source_info));
llcoro::suspend();
- return msg;
+ return source_info + msg;
}
lua_function(print_debug, "print_debug(args...): DEBUG level logging")
@@ -317,7 +318,7 @@ LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) :
void LLRequireResolver::findModule()
{
// If mPathToResolve is absolute, this replaces mSourceDir.
- auto absolutePath = (mSourceDir / mPathToResolve).u8string();
+ fsyspath absolutePath(mSourceDir / mPathToResolve);
// Push _MODULES table on stack for checking and saving to the cache
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
@@ -333,7 +334,7 @@ void LLRequireResolver::findModule()
// not already cached - prep error message just in case
auto fail{
- [L=L, path=mPathToResolve.u8string()]()
+ [L=L, path=mPathToResolve.string()]()
{ luaL_error(L, "could not find require('%s')", path.data()); }};
if (mPathToResolve.is_absolute())
@@ -348,10 +349,10 @@ void LLRequireResolver::findModule()
{
// if path is already absolute, operator/() preserves it
auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / path.asString());
- std::string absolutePathOpt = (abspath / mPathToResolve).u8string();
+ fsyspath absolutePathOpt = (abspath / mPathToResolve);
if (absolutePathOpt.empty())
- luaL_error(L, "error requiring module '%s'", mPathToResolve.u8string().data());
+ luaL_error(L, "error requiring module '%s'", mPathToResolve.string().data());
if (findModuleImpl(absolutePathOpt))
return;
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index e2471ca76a..f4b6f9bee9 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -548,8 +548,8 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate
return ppTex ? (*ppTex).get() : NULL;
}
-volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
-volatile S32 LLMeshRepoThread::sActiveLODRequests = 0;
+std::atomic<S32> LLMeshRepoThread::sActiveHeaderRequests = 0;
+std::atomic<S32> LLMeshRepoThread::sActiveLODRequests = 0;
U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;
S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN;
S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN;
@@ -3990,7 +3990,7 @@ void LLMeshRepository::notifyLoadedMeshes()
}
// erase from background thread
- mThread->mWorkQueue.post([=]()
+ mThread->mWorkQueue.post([=, this]()
{
mThread->mSkinMap.erase(id);
});
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index e13ad10b37..51b19207d9 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -256,8 +256,8 @@ class LLMeshRepoThread : public LLThread
{
public:
- volatile static S32 sActiveHeaderRequests;
- volatile static S32 sActiveLODRequests;
+ static std::atomic<S32> sActiveHeaderRequests;
+ static std::atomic<S32> sActiveLODRequests;
static U32 sMaxConcurrentRequests;
static S32 sRequestLowWater;
static S32 sRequestHighWater;
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 363e176fc8..6ef7712000 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -3364,7 +3364,7 @@ bool LLModelPreview::render()
mFMP->childSetEnabled("upload_joints", upload_skin);
}
- F32 explode = (F32)mFMP->childGetValue("physics_explode").asReal();
+ F32 physics_explode = (F32)mFMP->childGetValue("physics_explode").asReal();
LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview
@@ -3589,12 +3589,12 @@ bool LLModelPreview::render()
for (U32 i = 0; i < physics.mMesh.size(); ++i)
{
- if (explode > 0.f)
+ if (physics_explode > 0.f)
{
gGL.pushMatrix();
LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters;
- offset *= explode;
+ offset *= physics_explode;
gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
}
@@ -3609,7 +3609,7 @@ bool LLModelPreview::render()
gGL.diffuseColor4ubv(hull_colors[i].mV);
LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions);
- if (explode > 0.f)
+ if (physics_explode > 0.f)
{
gGL.popMatrix();
}
@@ -3624,7 +3624,8 @@ bool LLModelPreview::render()
if (render_mesh)
{
auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size();
- if (pass > 0){
+ if (pass > 0)
+ {
for (size_t i = 0; i < num_models; ++i)
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
@@ -3804,7 +3805,8 @@ bool LLModelPreview::render()
}
}
- for (U32 i = 0, e = static_cast<U32>(mVertexBuffer[mPreviewLOD][model].size()); i < e; ++i)
+ std::size_t size = mVertexBuffer[mPreviewLOD][model].size();
+ for (U32 i = 0; i < size; ++i)
{
model->mSkinInfo.updateHash();
LLRenderPass::uploadMatrixPalette(mPreviewAvatar, &model->mSkinInfo);
diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp
index 0d6ca1cb94..2d659cd19d 100644
--- a/indra/newview/llpanelemojicomplete.cpp
+++ b/indra/newview/llpanelemojicomplete.cpp
@@ -440,7 +440,7 @@ void LLPanelEmojiComplete::updateConstraints()
{
mRenderRect = getLocalRect();
- mEmojiWidth = (U16)(mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2);
+ mEmojiWidth = (U16)(mIconFont->getWidthF32(LLWString(1, 0x1F431).c_str()) + mPadding * 2);
if (mVertical)
{
mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2;
diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp
index 4404efff98..e1f2d7588c 100644
--- a/indra/newview/llpanelgrouproles.cpp
+++ b/indra/newview/llpanelgrouproles.cpp
@@ -1360,7 +1360,8 @@ void LLPanelGroupMembersSubTab::activate()
{
if (!gdatap || !gdatap->isMemberDataComplete())
{
- const U32 page_size = 50;
+ static LLCachedControl<bool> enable_pagination(gSavedSettings, "UseGroupMemberPagination", false);
+ const U32 page_size = enable_pagination() ? 50 : 0;
std::string sort_column_name = mMembersList->getSortColumnName();
bool sort_descending = !mMembersList->getSortAscending();
LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID, page_size, 0, sort_column_name, sort_descending);
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp
index 73ade2d902..241cb97f06 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -1309,7 +1309,7 @@ void LLPanelOutfitEdit::showFilteredWearablesListView(LLWearableType::EType type
showWearablesListView();
//e_list_view_item_type implicitly contains LLWearableType::EType starting from LVIT_SHAPE
- applyListViewFilter(static_cast<EListViewItemType>(LVIT_SHAPE + type));
+ applyListViewFilter(EListViewItemType(LVIT_SHAPE + EListViewItemType(type)));
mWearableItemsList->setMenuWearableType(type);
}
diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp
index 1af585708e..955b5e7730 100644
--- a/indra/newview/llpanelprimmediacontrols.cpp
+++ b/indra/newview/llpanelprimmediacontrols.cpp
@@ -777,7 +777,7 @@ void LLPanelPrimMediaControls::draw()
else if(mFadeTimer.getStarted())
{
F32 time = mFadeTimer.getElapsedTimeF32();
- alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f);
+ alpha *= llmax(lerp(1.0f, 0.0f, time / mControlFadeTime), 0.0f);
if(time >= mControlFadeTime)
{
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 46cb65b1ac..b0aadff277 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -754,8 +754,6 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key)
resetData();
- LLUUID avatar_id = getAvatarId();
-
bool own_profile = getSelfProfile();
mGroupList->setShowNone(!own_profile);
@@ -793,7 +791,6 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key)
if (!own_profile)
{
- mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(avatar_id) ? LLAvatarTracker::instance().isBuddyOnline(avatar_id) : true);
updateOnlineStatus();
fillRightsData();
}
@@ -1214,17 +1211,6 @@ void LLPanelProfileSecondLife::changed(U32 mask)
}
}
-// virtual, called by LLVoiceClient
-void LLPanelProfileSecondLife::onChange(EStatusType status, const LLSD& channelInfo, bool proximal)
-{
- if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL)
- {
- return;
- }
-
- mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(getAvatarId()) ? LLAvatarTracker::instance().isBuddyOnline(getAvatarId()) : true);
-}
-
void LLPanelProfileSecondLife::setAvatarId(const LLUUID& avatar_id)
{
if (avatar_id.notNull())
@@ -1502,7 +1488,7 @@ bool LLPanelProfileSecondLife::onEnableMenu(const LLSD& userdata)
}
else if (item_name == "voice_call")
{
- return mVoiceStatus;
+ return LLAvatarActions::canCallTo(agent_id);
}
else if (item_name == "chat_history")
{
diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h
index c207a4162a..ba00e12441 100644
--- a/indra/newview/llpanelprofile.h
+++ b/indra/newview/llpanelprofile.h
@@ -33,7 +33,6 @@
#include "llpanel.h"
#include "llpanelavatar.h"
#include "llmediactrl.h"
-#include "llvoiceclient.h"
// class LLPanelProfileClassifieds;
// class LLTabContainer;
@@ -70,7 +69,6 @@ class LLViewerFetchedTexture;
class LLPanelProfileSecondLife
: public LLPanelProfilePropertiesProcessorTab
, public LLFriendObserver
- , public LLVoiceClientStatusObserver
{
public:
LLPanelProfileSecondLife();
@@ -89,10 +87,6 @@ public:
*/
void changed(U32 mask) override;
- // Implements LLVoiceClientStatusObserver::onChange() to enable the call
- // button when voice is available
- void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) override;
-
void setAvatarId(const LLUUID& avatar_id) override;
bool postBuild() override;
@@ -203,7 +197,6 @@ private:
LLHandle<LLFloater> mFloaterTexturePickerHandle;
bool mHasUnsavedDescriptionChanges;
- bool mVoiceStatus;
bool mWaitingForImageUpload;
bool mAllowPublish;
bool mHideAge;
diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp
index 60877494e7..e65ba523f0 100644
--- a/indra/newview/llpanelvoicedevicesettings.cpp
+++ b/indra/newview/llpanelvoicedevicesettings.cpp
@@ -42,7 +42,7 @@
static LLPanelInjector<LLPanelVoiceDeviceSettings> t_panel_group_general("panel_voice_device_settings");
static const std::string DEFAULT_DEVICE("Default");
-
+static const std::string NO_DEVICE("No Device");
LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings()
: LLPanel()
@@ -51,12 +51,10 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings()
mCtrlOutputDevices = NULL;
mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
- mDevicesUpdated = false; //obsolete
mUseTuningMode = true;
// grab "live" mic volume level
mMicVolume = gSavedSettings.getF32("AudioLevelMic");
-
}
LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings()
@@ -81,7 +79,7 @@ bool LLPanelVoiceDeviceSettings::postBuild()
boost::bind(&LLPanelVoiceDeviceSettings::onCommitUnmute, this));
mLocalizedDeviceNames[DEFAULT_DEVICE] = getString("default_text");
- mLocalizedDeviceNames["No Device"] = getString("name_no_device");
+ mLocalizedDeviceNames[NO_DEVICE] = getString("name_no_device");
mLocalizedDeviceNames["Default System Device"] = getString("name_default_system_device");
mCtrlOutputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onOutputDevicesClicked, this));
@@ -115,7 +113,7 @@ void LLPanelVoiceDeviceSettings::draw()
bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled();
if (voice_enabled)
{
- getChildView("wait_text")->setVisible( !is_in_tuning_mode && mUseTuningMode);
+ getChildView("wait_text")->setVisible(!is_in_tuning_mode && mUseTuningMode);
getChildView("disabled_text")->setVisible(false);
mUnmuteBtn->setVisible(false);
}
@@ -212,56 +210,29 @@ void LLPanelVoiceDeviceSettings::cancel()
void LLPanelVoiceDeviceSettings::refresh()
{
- //grab current volume
+ LLVoiceClient* voice_client = LLVoiceClient::getInstance();
+
+ // grab current volume
LLSlider* volume_slider = getChild<LLSlider>("mic_volume_slider");
+
// set mic volume tuning slider based on last mic volume setting
F32 current_volume = (F32)volume_slider->getValue().asReal();
- LLVoiceClient::getInstance()->tuningSetMicVolume(current_volume);
+ voice_client->tuningSetMicVolume(current_volume);
// Fill in popup menus
- bool device_settings_available = LLVoiceClient::getInstance()->deviceSettingsAvailable();
+ bool device_settings_available = voice_client->deviceSettingsAvailable();
+ bool device_settings_updated = voice_client->deviceSettingsUpdated();
if (mCtrlInputDevices)
{
- mCtrlInputDevices->setEnabled(device_settings_available);
- }
-
- if (mCtrlOutputDevices)
- {
- mCtrlOutputDevices->setEnabled(device_settings_available);
- }
-
- getChild<LLSlider>("mic_volume_slider")->setEnabled(device_settings_available);
-
- if(!device_settings_available)
- {
- // The combo boxes are disabled, since we can't get the device settings from the daemon just now.
- // Put the currently set default (ONLY) in the box, and select it.
- if(mCtrlInputDevices)
- {
- mCtrlInputDevices->removeall();
- mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM);
- mCtrlInputDevices->setValue(mInputDevice);
- }
- if(mCtrlOutputDevices)
- {
- mCtrlOutputDevices->removeall();
- mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM);
- mCtrlOutputDevices->setValue(mOutputDevice);
- }
- }
- else if (LLVoiceClient::getInstance()->deviceSettingsUpdated())
- {
- LLVoiceDeviceList::const_iterator device;
-
- if(mCtrlInputDevices)
+ if (device_settings_available && !voice_client->getCaptureDevices().empty())
{
- LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices();
- if (devices.size() > 0) // if zero, we've not received our devices yet
+ mCtrlInputDevices->setEnabled(true);
+ if (device_settings_updated)
{
mCtrlInputDevices->removeall();
mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
- for (auto& device : devices)
+ for (auto& device : voice_client->getCaptureDevices())
{
mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM);
}
@@ -275,16 +246,24 @@ void LLPanelVoiceDeviceSettings::refresh()
}
}
}
+ else
+ {
+ mCtrlInputDevices->setEnabled(false);
+ mCtrlInputDevices->removeall();
+ mCtrlInputDevices->setLabel(getLocalizedDeviceName(NO_DEVICE));
+ }
+ }
- if(mCtrlOutputDevices)
+ if (mCtrlOutputDevices)
+ {
+ if (device_settings_available && !voice_client->getRenderDevices().empty())
{
- LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices();
- if (devices.size() > 0) // if zero, we've not received our devices yet
+ mCtrlOutputDevices->setEnabled(true);
+ if (device_settings_updated)
{
mCtrlOutputDevices->removeall();
mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
-
- for (auto& device : devices)
+ for (auto& device : voice_client->getRenderDevices())
{
mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM);
}
@@ -298,7 +277,15 @@ void LLPanelVoiceDeviceSettings::refresh()
}
}
}
+ else
+ {
+ mCtrlOutputDevices->setEnabled(false);
+ mCtrlOutputDevices->removeall();
+ mCtrlOutputDevices->setLabel(getLocalizedDeviceName(NO_DEVICE));
+ }
}
+
+ getChild<LLSlider>("mic_volume_slider")->setEnabled(device_settings_available && !voice_client->getCaptureDevices().empty());
}
void LLPanelVoiceDeviceSettings::initialize()
diff --git a/indra/newview/llpanelvoicedevicesettings.h b/indra/newview/llpanelvoicedevicesettings.h
index 815396cbd1..d0d14c212c 100644
--- a/indra/newview/llpanelvoicedevicesettings.h
+++ b/indra/newview/llpanelvoicedevicesettings.h
@@ -63,7 +63,6 @@ protected:
class LLComboBox *mCtrlInputDevices;
class LLComboBox *mCtrlOutputDevices;
class LLButton *mUnmuteBtn;
- bool mDevicesUpdated;
bool mUseTuningMode;
std::map<std::string, std::string> mLocalizedDeviceNames;
};
diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp
index 34b8535c84..fee0bbcda4 100644
--- a/indra/newview/llsettingsvo.cpp
+++ b/indra/newview/llsettingsvo.cpp
@@ -671,7 +671,8 @@ void LLSettingsVOSky::updateSettings()
// After some A/B comparison of relesae vs EEP, tweak to allow strength to fall below 2
// at night, for better match. (mSceneLightStrength is a divisor, so lower value means brighter
// local lights)
- F32 sun_dynamic_range = llmax(gSavedSettings.getF32("RenderSunDynamicRange"), 0.0001f);
+ LLCachedControl<F32> sdr(gSavedSettings, "RenderSunDynamicRange", 1.f);
+ F32 sun_dynamic_range = llmax(sdr(), 0.0001f);
mSceneLightStrength = 2.0f * (0.75f + sun_dynamic_range * dp);
gSky.setSunAndMoonDirectionsCFR(sun_direction, moon_direction);
diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp
index 82caa14433..66fbe0029a 100644
--- a/indra/newview/llsky.cpp
+++ b/indra/newview/llsky.cpp
@@ -39,7 +39,7 @@
// linden library includes
#include "llerror.h"
#include "llmath.h"
-#include "math.h"
+#include <cmath>
#include "v4color.h"
#include "llviewerobjectlist.h"
diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp
index e51aeb6080..09c57eaf1d 100644
--- a/indra/newview/llsprite.cpp
+++ b/indra/newview/llsprite.cpp
@@ -37,7 +37,7 @@
#include "llsprite.h"
-#include "math.h"
+#include <cmath>
#include "lldrawable.h"
#include "llface.h"
diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp
index 8ccde74c93..a858f92d8e 100644
--- a/indra/newview/llterrainpaintmap.cpp
+++ b/indra/newview/llterrainpaintmap.cpp
@@ -31,6 +31,8 @@
// library includes
#include "llglslshader.h"
#include "llrendertarget.h"
+#include "llrender2dutils.h"
+#include "llshadermgr.h"
#include "llvertexbuffer.h"
// newview includes
@@ -38,18 +40,31 @@
#include "llsurface.h"
#include "llsurfacepatch.h"
#include "llviewercamera.h"
+#include "llviewercontrol.h"
#include "llviewerregion.h"
#include "llviewershadermgr.h"
#include "llviewertexture.h"
-// static
-bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex)
+namespace
+{
+#ifdef SHOW_ASSERT
+void check_tex(const LLViewerTexture& tex)
{
llassert(tex.getComponents() == 3);
llassert(tex.getWidth() > 0 && tex.getHeight() > 0);
llassert(tex.getWidth() == tex.getHeight());
llassert(tex.getPrimaryFormat() == GL_RGB);
llassert(tex.getGLTexture());
+}
+#else
+#define check_tex(tex)
+#endif
+} // namespace
+
+// static
+bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex)
+{
+ check_tex(tex);
const LLSurface& surface = region.getLand();
const U32 patch_count = surface.getPatchesPerEdge();
@@ -76,8 +91,8 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion&
// Bind the debug shader and render terrain to tex
// Use a scratch render target because its dimensions may exceed the standard bake target, and this is a one-off bake
LLRenderTarget scratch_target;
- const S32 dim = llmin(tex.getWidth(), tex.getHeight());
- scratch_target.allocate(dim, dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE,
+ const S32 max_dim = llmax(tex.getWidth(), tex.getHeight());
+ scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE,
LLTexUnit::eTextureMipGeneration::TMG_NONE);
if (!scratch_target.isComplete())
{
@@ -104,6 +119,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion&
const F32 region_half_width = region_width / 2.0f;
const F32 region_camera_height = surface.getMaxZ() + DEFAULT_NEAR_PLANE;
LLViewerCamera camera;
+ // TODO: Huh... I just realized this view vector is not completely vertical
const LLVector3 region_center = LLVector3(region_half_width, region_half_width, 0.0) + region.getOriginAgent();
const LLVector3 camera_origin = LLVector3(0.0f, 0.0f, region_camera_height) + region_center;
camera.lookAt(camera_origin, region_center, LLVector3::y_axis);
@@ -237,6 +253,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion&
S32 alpha_ramp = shader.enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
LLPointer<LLViewerTexture> alpha_ramp_texture = LLViewerTextureManager::getFetchedTexture(IMG_ALPHA_GRAD_2D);
+ // TODO: Consider using LLGLSLShader::bindTexture
gGL.getTexUnit(alpha_ramp)->bind(alpha_ramp_texture);
gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
@@ -250,6 +267,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion&
const U32 vertex_offset = n * patch_index;
llassert(index_offset + ni <= region_indices);
llassert(vertex_offset + n <= region_vertices);
+ // TODO: Try a single big drawRange and see if that still works
buf->drawRange(LLRender::TRIANGLES, vertex_offset, vertex_offset + n - 1, ni, index_offset);
}
}
@@ -269,7 +287,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion&
gGL.flush();
LLVertexBuffer::unbind();
// Final step: Copy the output to the terrain paintmap
- const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, dim, dim);
+ const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, tex.getWidth(), tex.getHeight());
if (!success)
{
LL_WARNS() << "Failed to copy framebuffer to paintmap" << LL_ENDL;
@@ -283,3 +301,687 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion&
return success;
}
+
+// *TODO: Decide when to apply the paint queue - ideally once per frame per region
+// Applies paints and then clears the paint queue
+// *NOTE The paint queue is also cleared when setting the paintmap texture
+void LLTerrainPaintMap::applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue)
+{
+ if (queue.empty()) { return; }
+
+ check_tex(tex);
+
+ gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true);
+
+ // glTexSubImage2D replaces all pixels in the rectangular region. That
+ // makes it unsuitable for alpha.
+ llassert(queue.getComponents() == LLTerrainPaint::RGB);
+ constexpr GLenum pixformat = GL_RGB;
+
+ const std::vector<LLTerrainPaint::ptr_t>& queue_list = queue.get();
+ for (size_t i = 0; i < queue_list.size(); ++i)
+ {
+ // It is currently the responsibility of the paint queue to convert
+ // incoming bits to the right bit depth for the paintmap (this could
+ // change in the future).
+ queue.convertBitDepths(i, 8);
+ const LLTerrainPaint::ptr_t& paint = queue_list[i];
+
+ if (paint->mData.empty()) { continue; }
+ constexpr GLint level = 0;
+ if ((paint->mStartX >= tex.getWidth() - 1) || (paint->mStartY >= tex.getHeight() - 1)) { continue; }
+ constexpr GLint miplevel = 0;
+ const S32 x_offset = paint->mStartX;
+ const S32 y_offset = paint->mStartY;
+ const S32 width = llmin(paint->mWidthX, tex.getWidth() - x_offset);
+ const S32 height = llmin(paint->mWidthY, tex.getHeight() - y_offset);
+ const U8* pixels = paint->mData.data();
+ constexpr GLenum pixtype = GL_UNSIGNED_BYTE;
+ // *TODO: Performance suggestion: Use the sub-image utility function
+ // that LLImageGL::setSubImage uses to split texture updates into
+ // lines, if that's faster.
+ glTexSubImage2D(GL_TEXTURE_2D, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, pixels);
+ stop_glerror();
+ }
+
+ // Generating mipmaps at the end...
+ glGenerateMipmap(GL_TEXTURE_2D);
+ stop_glerror();
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ queue.clear();
+}
+
+namespace
+{
+
+// A general-purpose vertex buffer of a quad for stamping textures on the z=0
+// plane.
+// *NOTE: Because we know the vertex XY coordinates go from 0 to 1
+// pre-transform, UVs can be calculated from the vertices
+LLVertexBuffer* get_paint_triangle_buffer()
+{
+ static LLPointer<LLVertexBuffer> buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX);
+ static bool initialized = false;
+ if (!initialized)
+ {
+ // Two triangles forming a square from (0,0) to (1,1)
+ buf->allocateBuffer(/*vertices =*/ 4, /*indices =*/ 6);
+ LLStrider<U16> indices;
+ LLStrider<LLVector3> vertices;
+ buf->getVertexStrider(vertices);
+ buf->getIndexStrider(indices);
+ // y
+ // 2....3
+ // ^ . .
+ // | 0....1
+ // |
+ // -------> x
+ //
+ // triangle 1: 0,1,2
+ // triangle 2: 1,3,2
+ (*(vertices++)).set(0.0f, 0.0f, 0.0f);
+ (*(vertices++)).set(1.0f, 0.0f, 0.0f);
+ (*(vertices++)).set(0.0f, 1.0f, 0.0f);
+ (*(vertices++)).set(1.0f, 1.0f, 0.0f);
+ *(indices++) = 0;
+ *(indices++) = 1;
+ *(indices++) = 2;
+ *(indices++) = 1;
+ *(indices++) = 3;
+ *(indices++) = 2;
+ buf->unmapBuffer();
+ }
+ return buf.get();
+}
+
+};
+
+// static
+LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in)
+{
+ check_tex(tex);
+ llassert(queue_in.getComponents() == LLTerrainPaint::RGBA);
+
+ // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead
+ // TODO: even if it means performing extra render operations to apply the paints, in rare cases where the paints can't all fit within an area that can be represented by the buffer
+ LLRenderTarget scratch_target;
+ const S32 max_dim = llmax(tex.getWidth(), tex.getHeight());
+ scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE,
+ LLTexUnit::eTextureMipGeneration::TMG_NONE);
+ if (!scratch_target.isComplete())
+ {
+ llassert(false);
+ LL_WARNS() << "Failed to allocate render target" << LL_ENDL;
+ return false;
+ }
+ gGL.getTexUnit(0)->disable();
+ stop_glerror();
+
+ scratch_target.bindTarget();
+ glClearColor(0, 0, 0, 0);
+ scratch_target.clear();
+ const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f;
+ const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f;
+
+ LLVertexBuffer* buf = get_paint_triangle_buffer();
+
+ // Update projection matrix and viewport
+ // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later.
+ {
+ stop_glerror();
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+ gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f);
+ stop_glerror();
+ const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0);
+ glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight());
+ }
+
+ // View matrix
+ // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer.
+ // Camera is centered in the middle of the framebuffer.
+ glm::mat4 view(glm::make_mat4((GLfloat*) OGL_TO_CFR_ROTATION));
+ {
+ LLViewerCamera camera;
+ const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f);
+ const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f);
+ camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis);
+ camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth()));
+ GLfloat ogl_matrix[16];
+ camera.getOpenGLTransform(ogl_matrix);
+ view *= glm::make_mat4(ogl_matrix);
+ }
+
+ LLGLDisable stencil(GL_STENCIL_TEST);
+ LLGLDisable scissor(GL_SCISSOR_TEST);
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS);
+ LLGLEnable blend(GL_BLEND);
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+ LLGLSLShader& shader = gTerrainStampProgram;
+ shader.bind();
+
+ // First, apply the paint map as the background
+ {
+ glm::mat4 model;
+ {
+ model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f));
+ model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));
+ }
+ glm::mat4 modelview = view * model;
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.loadMatrix(glm::value_ptr(modelview));
+
+ shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex);
+ // We care about the whole paintmap, which is already a power of two.
+ // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0)
+ shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f);
+ buf->setBuffer();
+ buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0);
+ }
+
+ LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB);
+
+ // Incrementally apply each RGBA paint to the render target, then extract
+ // the result back into memory as an RGB paint.
+ // Put each result in queue_out.
+ const std::vector<LLTerrainPaint::ptr_t>& queue_in_list = queue_in.get();
+ for (size_t i = 0; i < queue_in_list.size(); ++i)
+ {
+ // It is currently the responsibility of the paint queue to convert
+ // incoming bits to the right bit depth for paint operations (this
+ // could change in the future).
+ queue_in.convertBitDepths(i, 8);
+ const LLTerrainPaint::ptr_t& paint_in = queue_in_list[i];
+
+ // Modelview matrix for the current paint
+ // View matrix is already computed. Just need the model matrix.
+ // Orthographic projection matrix is already updated
+ glm::mat4 model;
+ {
+ model = glm::scale(model, glm::vec3(paint_in->mWidthX, paint_in->mWidthY, 1.0f));
+ model = glm::translate(model, glm::vec3(paint_in->mStartX, paint_in->mStartY, 0.0f));
+ }
+ glm::mat4 modelview = view * model;
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.loadMatrix(glm::value_ptr(modelview));
+
+ // Generate temporary stamp texture from paint contents.
+ // Our stamp image needs to be a power of two.
+ // Because the paint data may not cover a whole power-of-two region,
+ // allocate a bigger 2x2 image if needed, but set the image data later
+ // for a subset of the image.
+ // Pixel data outside this subset is left undefined. We will use
+ // TERRAIN_STAMP_SCALE in the stamp shader to define the subset of the
+ // image we care about.
+ const U32 width_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthX))));
+ const U32 height_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthY))));
+ LLPointer<LLImageGL> stamp_image;
+ {
+ // Create image object (dimensions not yet initialized in GL)
+ U32 stamp_tex_name;
+ LLImageGL::generateTextures(1, &stamp_tex_name);
+ const U32 components = paint_in->mComponents;
+ constexpr LLGLenum target = GL_TEXTURE_2D;
+ const LLGLenum internal_format = paint_in->mComponents == 4 ? GL_RGBA8 : GL_RGB8;
+ const LLGLenum format = paint_in->mComponents == 4 ? GL_RGBA : GL_RGB;
+ constexpr LLGLenum type = GL_UNSIGNED_BYTE;
+ stamp_image = new LLImageGL(stamp_tex_name, components, target, internal_format, format, type, LLTexUnit::TAM_WRAP);
+ // Nearest-neighbor filtering to reduce surprises
+ stamp_image->setFilteringOption(LLTexUnit::TFO_POINT);
+
+ // Initialize the image dimensions in GL
+ constexpr U8* undefined_data_for_now = nullptr;
+ gGL.getTexUnit(0)->bind(stamp_image, false, true);
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_rounded, height_rounded, 0, format, type, undefined_data_for_now);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ stamp_image->setSize(width_rounded, height_rounded, components);
+ stamp_image->setDiscardLevel(0);
+
+ // Manually set a subset of the image in GL
+ const U8* data = paint_in->mData.data();
+ const S32 data_width = paint_in->mWidthX;
+ const S32 data_height = paint_in->mWidthY;
+ constexpr S32 origin = 0;
+ // width = data_width; height = data_height. i.e.: Copy the full
+ // contents of data into the image.
+ stamp_image->setSubImage(data, data_width, data_height, origin, origin, /*width=*/data_width, /*height=*/data_height);
+ }
+
+ // Apply ("stamp") the paint to the render target
+ {
+ shader.bindTextureImageGL(LLShaderMgr::DIFFUSE_MAP, stamp_image);
+ const F32 width_fraction = F32(paint_in->mWidthX) / F32(width_rounded);
+ const F32 height_fraction = F32(paint_in->mWidthY) / F32(height_rounded);
+ shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, width_fraction, height_fraction);
+ buf->setBuffer();
+ buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0);
+ }
+
+ // Extract the result back into memory as an RGB paint
+ LLTerrainPaint::ptr_t paint_out = std::make_shared<LLTerrainPaint>();
+ {
+ paint_out->mStartX = paint_in->mStartX;
+ paint_out->mStartY = paint_in->mStartY;
+ paint_out->mWidthX = paint_in->mWidthX;
+ paint_out->mWidthY = paint_in->mWidthY;
+ paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later
+ paint_out->mComponents = LLTerrainPaint::RGB;
+#ifdef SHOW_ASSERT
+ paint_out->assert_confined_to(tex);
+#endif
+ paint_out->confine_to(tex);
+ paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY);
+ constexpr GLint miplevel = 0;
+ const S32 x_offset = paint_out->mStartX;
+ const S32 y_offset = paint_out->mStartY;
+ const S32 width = paint_out->mWidthX;
+ const S32 height = paint_out->mWidthY;
+ constexpr GLenum pixformat = GL_RGB;
+ constexpr GLenum pixtype = GL_UNSIGNED_BYTE;
+ llassert(paint_out->mData.size() <= std::numeric_limits<GLsizei>::max());
+ const GLsizei buf_size = (GLsizei)paint_out->mData.size();
+ U8* pixels = paint_out->mData.data();
+ glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels);
+ }
+
+ // Enqueue the result to the new paint queue, with bit depths per color
+ // channel reduced from 8 to 5, and reduced from RGBA (paintmap
+ // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle
+ // update without alpha mask). This format is suitable for sending
+ // over the network.
+ // *TODO: At some point, queue_out will pass through a network
+ // round-trip which will reduce the bit depth, making the
+ // pre-conversion step not necessary.
+ queue_out.enqueue(paint_out);
+ LLCachedControl<U32> bit_depth(gSavedSettings, "TerrainPaintBitDepth");
+ queue_out.convertBitDepths(queue_out.size()-1, bit_depth);
+ }
+
+ queue_in.clear();
+
+ scratch_target.flush();
+
+ LLGLSLShader::unbind();
+
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.popMatrix();
+
+ return queue_out;
+}
+
+// static
+LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in)
+{
+ check_tex(tex);
+
+ // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead
+ // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer
+ LLRenderTarget scratch_target;
+ const S32 max_dim = llmax(tex.getWidth(), tex.getHeight());
+ scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE,
+ LLTexUnit::eTextureMipGeneration::TMG_NONE);
+ if (!scratch_target.isComplete())
+ {
+ llassert(false);
+ LL_WARNS() << "Failed to allocate render target" << LL_ENDL;
+ return false;
+ }
+ gGL.getTexUnit(0)->disable();
+ stop_glerror();
+
+ scratch_target.bindTarget();
+ glClearColor(0, 0, 0, 0);
+ scratch_target.clear();
+ const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f;
+ const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f;
+
+ LLVertexBuffer* buf = get_paint_triangle_buffer();
+
+ // Update projection matrix and viewport
+ // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later.
+ {
+ stop_glerror();
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+ gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f);
+ stop_glerror();
+ const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0);
+ glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight());
+ }
+
+ // View matrix
+ // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer.
+ // Camera is centered in the middle of the framebuffer.
+ glm::mat4 view(glm::make_mat4((GLfloat*)OGL_TO_CFR_ROTATION));
+ {
+ LLViewerCamera camera;
+ const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f);
+ const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f);
+ camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis);
+ camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth()));
+ GLfloat ogl_matrix[16];
+ camera.getOpenGLTransform(ogl_matrix);
+ view *= glm::make_mat4(ogl_matrix);
+ }
+
+ LLGLDisable stencil(GL_STENCIL_TEST);
+ LLGLDisable scissor(GL_SCISSOR_TEST);
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS);
+ LLGLEnable blend(GL_BLEND);
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
+ LLGLSLShader& shader = gTerrainStampProgram;
+ shader.bind();
+
+ // First, apply the paint map as the background
+ {
+ glm::mat4 model;
+ {
+ model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f));
+ model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));
+ }
+ glm::mat4 modelview = view * model;
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.loadMatrix(glm::value_ptr(modelview));
+
+ shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex);
+ // We care about the whole paintmap, which is already a power of two.
+ // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0)
+ shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f);
+ buf->setBuffer();
+ buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0);
+ }
+
+ LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB);
+
+ // Incrementally apply each brush stroke to the render target, then extract
+ // the result back into memory as an RGB paint.
+ // Put each result in queue_out.
+ const std::vector<LLTerrainBrush::ptr_t>& brush_list = queue_in.get();
+ for (size_t i = 0; i < brush_list.size(); ++i)
+ {
+ const LLTerrainBrush::ptr_t& brush_in = brush_list[i];
+
+ // Modelview matrix for the current brush
+ // View matrix is already computed. Just need the model matrix.
+ // Orthographic projection matrix is already updated
+ // *NOTE: Brush path information is in region space. It will need to be
+ // converted to paintmap pixel space before it makes sense.
+ F32 brush_width_x;
+ F32 brush_width_y;
+ F32 brush_start_x;
+ F32 brush_start_y;
+ {
+ F32 min_x = brush_in->mPath[0].mV[VX];
+ F32 max_x = min_x;
+ F32 min_y = brush_in->mPath[0].mV[VY];
+ F32 max_y = min_y;
+ for (size_t i = 1; i < brush_in->mPath.size(); ++i)
+ {
+ const F32 x = brush_in->mPath[i].mV[VX];
+ const F32 y = brush_in->mPath[i].mV[VY];
+ min_x = llmin(min_x, x);
+ max_x = llmax(max_x, x);
+ min_y = llmin(min_y, y);
+ max_y = llmax(max_y, y);
+ }
+ brush_width_x = brush_in->mBrushSize + (max_x - min_x);
+ brush_width_y = brush_in->mBrushSize + (max_y - min_y);
+ brush_start_x = min_x - (brush_in->mBrushSize / 2.0f);
+ brush_start_y = min_y - (brush_in->mBrushSize / 2.0f);
+ // Convert brush path information to paintmap pixel space from region
+ // space.
+ brush_width_x *= tex.getWidth() / region.getWidth();
+ brush_width_y *= tex.getHeight() / region.getWidth();
+ brush_start_x *= tex.getWidth() / region.getWidth();
+ brush_start_y *= tex.getHeight() / region.getWidth();
+ }
+ glm::mat4 model;
+ {
+ model = glm::scale(model, glm::vec3(brush_width_x, brush_width_y, 1.0f));
+ model = glm::translate(model, glm::vec3(brush_start_x, brush_start_y, 0.0f));
+ }
+ glm::mat4 modelview = view * model;
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.loadMatrix(glm::value_ptr(modelview));
+
+ // Apply the "brush" to the render target
+ {
+ // TODO: Use different shader for this - currently this is using the stamp shader. The white image is just a placeholder for now
+ shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, LLViewerFetchedTexture::sWhiteImagep);
+ shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f);
+ buf->setBuffer();
+ buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0);
+ }
+
+ // Extract the result back into memory as an RGB paint
+ LLTerrainPaint::ptr_t paint_out = std::make_shared<LLTerrainPaint>();
+ {
+ paint_out->mStartX = U16(floor(brush_start_x));
+ paint_out->mStartY = U16(floor(brush_start_y));
+ const F32 dX = brush_start_x - F32(paint_out->mStartX);
+ const F32 dY = brush_start_y - F32(paint_out->mStartY);
+ paint_out->mWidthX = U16(ceil(brush_width_x + dX));
+ paint_out->mWidthY = U16(ceil(brush_width_y + dY));
+ paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later
+ paint_out->mComponents = LLTerrainPaint::RGB;
+ // The brush strokes are expected to sometimes partially venture
+ // outside of the paintmap bounds.
+ paint_out->confine_to(tex);
+ paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY);
+ constexpr GLint miplevel = 0;
+ const S32 x_offset = paint_out->mStartX;
+ const S32 y_offset = paint_out->mStartY;
+ const S32 width = paint_out->mWidthX;
+ const S32 height = paint_out->mWidthY;
+ constexpr GLenum pixformat = GL_RGB;
+ constexpr GLenum pixtype = GL_UNSIGNED_BYTE;
+ llassert(paint_out->mData.size() <= std::numeric_limits<GLsizei>::max());
+ const GLsizei buf_size = (GLsizei)paint_out->mData.size();
+ U8* pixels = paint_out->mData.data();
+ glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels);
+ }
+
+ // Enqueue the result to the new paint queue, with bit depths per color
+ // channel reduced from 8 to 5, and reduced from RGBA (paintmap
+ // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle
+ // update without alpha mask). This format is suitable for sending
+ // over the network.
+ // *TODO: At some point, queue_out will pass through a network
+ // round-trip which will reduce the bit depth, making the
+ // pre-conversion step not necessary.
+ queue_out.enqueue(paint_out);
+ LLCachedControl<U32> bit_depth(gSavedSettings, "TerrainPaintBitDepth");
+ queue_out.convertBitDepths(queue_out.size()-1, bit_depth);
+ }
+
+ queue_in.clear();
+
+ scratch_target.flush();
+
+ LLGLSLShader::unbind();
+
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.popMatrix();
+
+ return queue_out;
+}
+
+template<typename T>
+LLTerrainQueue<T>::LLTerrainQueue(LLTerrainQueue<T>& other)
+{
+ *this = other;
+}
+
+template<typename T>
+LLTerrainQueue<T>& LLTerrainQueue<T>::operator=(LLTerrainQueue<T>& other)
+{
+ mList = other.mList;
+ return *this;
+}
+
+template<typename T>
+bool LLTerrainQueue<T>::enqueue(std::shared_ptr<T>& t, bool dry_run)
+{
+ if (!dry_run) { mList.push_back(t); }
+ return true;
+}
+
+template<typename T>
+bool LLTerrainQueue<T>::enqueue(std::vector<std::shared_ptr<T>>& list)
+{
+ constexpr bool dry_run = true;
+ for (auto& t : list)
+ {
+ if (!enqueue(t, dry_run)) { return false; }
+ }
+ for (auto& t : list)
+ {
+ enqueue(t);
+ }
+ return true;
+}
+
+template<typename T>
+size_t LLTerrainQueue<T>::size() const
+{
+ return mList.size();
+}
+
+template<typename T>
+bool LLTerrainQueue<T>::empty() const
+{
+ return mList.empty();
+}
+
+template<typename T>
+void LLTerrainQueue<T>::clear()
+{
+ mList.clear();
+}
+
+void LLTerrainPaint::assert_confined_to(const LLTexture& tex) const
+{
+ llassert(mStartX >= 0 && mStartX < tex.getWidth());
+ llassert(mStartY >= 0 && mStartY < tex.getHeight());
+ llassert(mWidthX <= tex.getWidth() - mStartX);
+ llassert(mWidthY <= tex.getHeight() - mStartY);
+}
+
+void LLTerrainPaint::confine_to(const LLTexture& tex)
+{
+ mStartX = llmax(mStartX, 0);
+ mStartY = llmax(mStartY, 0);
+ mWidthX = llmin(mWidthX, tex.getWidth() - mStartX);
+ mWidthY = llmin(mWidthY, tex.getHeight() - mStartY);
+ assert_confined_to(tex);
+}
+
+LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components)
+: mComponents(components)
+{
+ llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA);
+}
+
+LLTerrainPaintQueue::LLTerrainPaintQueue(LLTerrainPaintQueue& other)
+: LLTerrainQueue<LLTerrainPaint>(other)
+, mComponents(other.mComponents)
+{
+ llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA);
+}
+
+LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(LLTerrainPaintQueue& other)
+{
+ LLTerrainQueue<LLTerrainPaint>::operator=(other);
+ mComponents = other.mComponents;
+ return *this;
+}
+
+bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run)
+{
+ llassert(paint);
+ if (!paint) { return false; }
+
+ // The paint struct should be pre-validated before this code is reached.
+ llassert(!paint->mData.empty());
+ // The internal paint map image is currently 8 bits, so that's the maximum
+ // allowed bit depth.
+ llassert(paint->mBitDepth > 0 && paint->mBitDepth <= 8);
+ llassert(paint->mData.size() == (mComponents * paint->mWidthX * paint->mWidthY));
+ llassert(paint->mWidthX > 0);
+ llassert(paint->mWidthY > 0);
+#ifdef SHOW_ASSERT
+ static LLCachedControl<U32> max_texture_width(gSavedSettings, "RenderMaxTextureResolution", 2048);
+#endif
+ llassert(paint->mWidthX <= max_texture_width);
+ llassert(paint->mWidthY <= max_texture_width);
+ llassert(paint->mStartX < max_texture_width);
+ llassert(paint->mStartY < max_texture_width);
+
+ return LLTerrainQueue<LLTerrainPaint>::enqueue(paint, dry_run);
+}
+
+bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& queue)
+{
+ return LLTerrainQueue<LLTerrainPaint>::enqueue(queue.mList);
+}
+
+void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth)
+{
+ llassert(target_bit_depth > 0 && target_bit_depth <= 8);
+ llassert(index < mList.size());
+
+ LLTerrainPaint::ptr_t& paint = mList[index];
+ if (paint->mBitDepth == target_bit_depth) { return; }
+
+ const F32 old_bit_max = F32((1 << paint->mBitDepth) - 1);
+ const F32 new_bit_max = F32((1 << target_bit_depth) - 1);
+ const F32 bit_conversion_factor = new_bit_max / old_bit_max;
+
+ for (U8& color : paint->mData)
+ {
+ color = (U8)llround(F32(color) * bit_conversion_factor);
+ }
+
+ paint->mBitDepth = target_bit_depth;
+}
+
+LLTerrainBrushQueue::LLTerrainBrushQueue()
+: LLTerrainQueue<LLTerrainBrush>()
+{
+}
+
+LLTerrainBrushQueue::LLTerrainBrushQueue(LLTerrainBrushQueue& other)
+: LLTerrainQueue<LLTerrainBrush>(other)
+{
+}
+
+LLTerrainBrushQueue& LLTerrainBrushQueue::operator=(LLTerrainBrushQueue& other)
+{
+ LLTerrainQueue<LLTerrainBrush>::operator=(other);
+ return *this;
+}
+
+bool LLTerrainBrushQueue::enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run)
+{
+ llassert(brush->mBrushSize > 0);
+ llassert(!brush->mPath.empty());
+ llassert(brush->mPathOffset < brush->mPath.size());
+ llassert(brush->mPathOffset < 2); // Harmless, but doesn't do anything useful, so might be a sign of implementation error
+ return LLTerrainQueue<LLTerrainBrush>::enqueue(brush, dry_run);
+}
+
+bool LLTerrainBrushQueue::enqueue(LLTerrainBrushQueue& queue)
+{
+ return LLTerrainQueue<LLTerrainBrush>::enqueue(queue.mList);
+}
+
+template class LLTerrainQueue<LLTerrainPaint>;
+template class LLTerrainQueue<LLTerrainBrush>;
diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h
index 66827862c5..d4859b747b 100644
--- a/indra/newview/llterrainpaintmap.h
+++ b/indra/newview/llterrainpaintmap.h
@@ -26,9 +26,20 @@
#pragma once
+#include <memory>
+#include <vector>
+
+#include "stdtypes.h"
+#include "v2math.h"
+
+class LLTexture;
class LLViewerRegion;
class LLViewerTexture;
+class LLTerrainPaintQueue;
+class LLTerrainBrushQueue;
+// TODO: Terrain painting across regions. Assuming painting is confined to one
+// region for now.
class LLTerrainPaintMap
{
public:
@@ -39,4 +50,121 @@ public:
// to type TERRAIN_PAINT_TYPE_PBR_PAINTMAP.
// Returns true if successful
static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex);
+
+ // This operation clears the queue
+ // TODO: Decide if clearing the queue is needed - seems inconsistent
+ static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue);
+
+ static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in);
+ // TODO: Implement (it's similar to convertPaintQueueRGBAToRGB but different shader + need to calculate the dimensions + need a different vertex buffer for each brush stroke)
+ static LLTerrainPaintQueue convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in);
+};
+
+template<typename T>
+class LLTerrainQueue
+{
+public:
+ LLTerrainQueue() = default;
+ LLTerrainQueue(LLTerrainQueue<T>& other);
+ LLTerrainQueue& operator=(LLTerrainQueue<T>& other);
+
+ bool enqueue(std::shared_ptr<T>& t, bool dry_run = false);
+ size_t size() const;
+ bool empty() const;
+ void clear();
+
+ const std::vector<std::shared_ptr<T>>& get() const { return mList; }
+
+protected:
+ bool enqueue(std::vector<std::shared_ptr<T>>& list);
+
+ std::vector<std::shared_ptr<T>> mList;
+};
+
+// Enqueued paint operations, in texture coordinates.
+// mData is always RGB or RGBA (determined by mComponents), with each U8
+// storing one color with a max value of (1 >> mBitDepth) - 1
+struct LLTerrainPaint
+{
+ using ptr_t = std::shared_ptr<LLTerrainPaint>;
+
+ U16 mStartX;
+ U16 mStartY;
+ U16 mWidthX;
+ U16 mWidthY;
+ U8 mBitDepth;
+ U8 mComponents;
+ static constexpr U8 RGB = 3;
+ static constexpr U8 RGBA = 4;
+ std::vector<U8> mData;
+
+ // Asserts that this paint's start/width fit within the bounds of the
+ // provided texture dimensions.
+ void assert_confined_to(const LLTexture& tex) const;
+ // Confines this paint's start/width so it fits within the bounds of the
+ // provided texture dimensions.
+ // Does not allocate mData.
+ void confine_to(const LLTexture& tex);
+};
+
+class LLTerrainPaintQueue : public LLTerrainQueue<LLTerrainPaint>
+{
+public:
+ LLTerrainPaintQueue() = delete;
+ // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA)
+ LLTerrainPaintQueue(U8 components);
+ LLTerrainPaintQueue(LLTerrainPaintQueue& other);
+ LLTerrainPaintQueue& operator=(LLTerrainPaintQueue& other);
+
+ bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false);
+ bool enqueue(LLTerrainPaintQueue& queue);
+
+ U8 getComponents() const { return mComponents; }
+ // Convert mBitDepth for the LLTerrainPaint in the queue at index
+ // If mBitDepth is already equal to target_bit_depth, no conversion takes
+ // place.
+ // It is currently the responsibility of the paint queue to convert
+ // incoming bits to the right bit depth for the paintmap (this could
+ // change in the future).
+ void convertBitDepths(size_t index, U8 target_bit_depth);
+
+private:
+ U8 mComponents;
+};
+
+struct LLTerrainBrush
+{
+ using ptr_t = std::shared_ptr<LLTerrainBrush>;
+
+ // Width of the brush in region space. The brush is a square texture with
+ // alpha.
+ F32 mBrushSize;
+ // Brush path points in region space, excluding the vertical axis, which
+ // does not contribute to the paint map.
+ std::vector<LLVector2> mPath;
+ // Offset of the brush path to actually start drawing at. An offset of 0
+ // indicates that a brush stroke has just started (i.e. the user just
+ // pressed down the mouse button). An offset greater than 0 indicates the
+ // continuation of a brush stroke. Skipped entries in mPath are not drawn
+ // directly, but are used for stroke orientation and path interpolation.
+ // TODO: For the initial implementation, mPathOffset will be 0 and mPath
+ // will be of length of at most 1, leading to discontinuous paint paths.
+ // Then, mPathOffset may be 1 or 0, 1 indicating the continuation of a
+ // stroke with linear interpolation. It is unlikely that we will implement
+ // anything more sophisticated than that for now.
+ U8 mPathOffset;
+ // Indicates if this is the end of the brush stroke. Can occur if the mouse
+ // button is lifted, or if the mouse temporarily stops while held down.
+ bool mPathEnd;
+};
+
+class LLTerrainBrushQueue : public LLTerrainQueue<LLTerrainBrush>
+{
+public:
+ LLTerrainBrushQueue();
+ LLTerrainBrushQueue(LLTerrainBrushQueue& other);
+ LLTerrainBrushQueue& operator=(LLTerrainBrushQueue& other);
+
+ bool enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run = false);
+ bool enqueue(LLTerrainBrushQueue& queue);
};
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 8821494e91..160674d3d8 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -2090,7 +2090,7 @@ bool LLTextureFetchWorker::deleteOK()
// Allow any pending reads or writes to complete
if (mCacheReadHandle != LLTextureCache::nullHandle())
{
- if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true))
+ if (!mFetcher->mTextureCache || mFetcher->mTextureCache->readComplete(mCacheReadHandle, true))
{
mCacheReadHandle = LLTextureCache::nullHandle();
}
@@ -2101,7 +2101,7 @@ bool LLTextureFetchWorker::deleteOK()
}
if (mCacheWriteHandle != LLTextureCache::nullHandle())
{
- if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
+ if (!mFetcher->mTextureCache || mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
{
mCacheWriteHandle = LLTextureCache::nullHandle();
}
@@ -2835,7 +2835,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
{
LL_PROFILE_ZONE_SCOPED;
- mRequestQueue.tryPost([=]()
+ mRequestQueue.tryPost([=, this]()
{
LLTextureFetchWorker* worker = getWorker(id);
if (worker)
@@ -3526,8 +3526,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
//if (! gViewerAssetStatsThread1)
// return true;
- static volatile bool reporting_started(false);
- static volatile S32 report_sequence(0);
+ static std::atomic<bool> reporting_started(false);
+ static std::atomic<S32> report_sequence(0);
// In mStatsSD, we have a copy we own of the LLSD representation
// of the asset stats. Add some additional fields and ship it off.
diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp
index 166542324d..647210b7e6 100644
--- a/indra/newview/llurldispatcher.cpp
+++ b/indra/newview/llurldispatcher.cpp
@@ -306,7 +306,7 @@ public:
if (tokens.size() < 1) return false;
LLVector3 coords(128, 128, 0);
- if (tokens.size() <= 4)
+ if (tokens.size() >= 3) // Require at least [1] and [2]
{
coords = LLVector3((F32)tokens[1].asReal(),
(F32)tokens[2].asReal(),
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index f8a315d4d8..184c0e7d8b 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -911,6 +911,7 @@ void settings_setup_listeners()
setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged);
setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged);
{
+ setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleSetShaderChanged);
setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleLocalTerrainChanged);
const char* transform_suffixes[] = {
"ScaleU",
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 1f4502323c..cb741e4af9 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -79,6 +79,7 @@
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
#include "llviewershadermgr.h"
+#include "llviewerstats.h"
#include "llviewertexturelist.h"
#include "llviewerwindow.h"
#include "llvoavatarself.h"
@@ -1085,6 +1086,9 @@ void getProfileStatsContext(boost::json::object& stats)
context.emplace("parcelid", parcel->getLocalID());
}
context.emplace("time", LLDate::now().toHTTPDateString("%Y-%m-%dT%H:%M:%S"));
+
+ // supplement with stats packet
+ stats.emplace("stats", LlsdToJson(capture_viewer_stats(true)));
}
std::string getProfileStatsFilename()
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 92db598410..9633e54f29 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -2903,14 +2903,14 @@ void LLViewerMediaImpl::update()
media_tex->ref();
main_queue->postTo(
mTexUpdateQueue, // Worker thread queue
- [=]() // work done on update worker thread
+ [=, this]() // work done on update worker thread
{
#if LL_IMAGEGL_THREAD_CHECK
media_tex->getGLTexture()->mActiveThread = LLThread::currentID();
#endif
doMediaTexUpdate(media_tex, data, data_width, data_height, x_pos, y_pos, width, height, true);
},
- [=]() // callback to main thread
+ [=, this]() // callback to main thread
{
#if LL_IMAGEGL_THREAD_CHECK
media_tex->getGLTexture()->mActiveThread = LLThread::currentID();
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index e1f7393e5e..9ad4926634 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -118,6 +118,7 @@
#include "lltoolmgr.h"
#include "lltoolpie.h"
#include "lltoolselectland.h"
+#include "llterrainpaintmap.h"
#include "lltrans.h"
#include "llviewerdisplay.h" //for gWindowResized
#include "llviewergenericmessage.h"
@@ -1430,16 +1431,21 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t
return false;
}
+ // This calls gLocalTerrainMaterials.setPaintType
+ // It also ensures the terrain bake shader is compiled (handleSetShaderChanged), so call this first
+ // *TODO: Fix compile errors in shader so it can be used for all platforms. Then we can unhide the shader from behind this setting and remove the hook to handleSetShaderChanged. This advanced setting is intended to be used as a local setting for testing terrain, not a feature flag, but it is currently used like a feature flag as a temporary hack.
+ // *TODO: Ideally we would call setPaintType *after* the paint map is well-defined. The terrain draw pool should be able to handle an undefined paint map in the meantime.
+ gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true);
+
U16 dim = (U16)gSavedSettings.getU32("TerrainPaintResolution");
// Ensure a reasonable image size of power two
const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution");
dim = llclamp(dim, 16, max_resolution);
dim = 1 << U32(std::ceil(std::log2(dim)));
+ // TODO: Could probably get away with not using image raw here, now that we aren't writing bits via the CPU (see load_exr for example)
LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
LLPointer<LLViewerTexture> tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true);
const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex);
- // This calls gLocalTerrainMaterials.setPaintType
- gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true);
// If baking the paintmap failed, set the paintmap to nullptr. This
// causes LLDrawPoolTerrain to use a blank paintmap instead.
if (!success) { tex = nullptr; }
@@ -1449,6 +1455,92 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t
}
};
+class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LLViewerTexture* tex = gLocalTerrainMaterials.getPaintMap();
+ if (!tex)
+ {
+ LL_WARNS() << "No local paint map available to edit" << LL_ENDL;
+ return false;
+ }
+
+ LLTerrainBrushQueue& brush_queue = gLocalTerrainMaterials.getBrushQueue();
+ LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue();
+
+ const LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ {
+ LL_WARNS() << "No current region for calculating paint operations" << LL_ENDL;
+ return false;
+ }
+ // TODO: Create the brush
+ // Just a dab for now
+ LLTerrainBrush::ptr_t brush = std::make_shared<LLTerrainBrush>();
+ brush->mBrushSize = 16.0f;
+ brush->mPath.emplace_back(17.0f, 17.0f);
+ brush->mPathOffset = 0;
+ brush->mPathEnd = true;
+ brush_queue.enqueue(brush);
+ LLTerrainPaintQueue brush_as_paint_queue = LLTerrainPaintMap::convertBrushQueueToPaintRGB(*region, *tex, brush_queue);
+ //paint_send_queue.enqueue(brush_as_paint_queue); // TODO: What was this line for? (it might also be a leftover line from an unfinished edit)
+
+ // TODO: Keep this around for later testing (i.e. when reducing framebuffer size and the offsets that requires)
+#if 0
+ // Enqueue a paint
+ // Modifies a subsection of the region paintmap with the material in
+ // the last slot.
+ // It is currently the responsibility of the paint queue to convert
+ // incoming bits to the right bit depth for the paintmap (this could
+ // change in the future).
+ LLTerrainPaint::ptr_t paint = std::make_shared<LLTerrainPaint>();
+ const U16 width = 33;
+ paint->mStartX = 1;
+ paint->mStartY = 1;
+ paint->mWidthX = width;
+ paint->mWidthY = width;
+ constexpr U8 bit_depth = 5;
+ paint->mBitDepth = bit_depth;
+ constexpr U8 max_value = (1 << bit_depth) - 1;
+ const size_t pixel_count = width * width;
+ const U8 components = LLTerrainPaint::RGBA;
+ paint->mComponents = components;
+ paint->mData.resize(components * pixel_count);
+ for (size_t h = 0; h < paint->mWidthY; ++h)
+ {
+ for (size_t w = 0; w < paint->mWidthX; ++w)
+ {
+ const size_t pixel = (h * paint->mWidthX) + w;
+ // Solid blue color
+ paint->mData[(components*pixel) + components - 2] = max_value; // blue
+ //// Alpha grid: 1.0 if odd for either dimension, 0.0 otherwise
+ //const U8 alpha = U8(F32(max_value) * F32(bool(w % 2) || bool(h % 2)));
+ //paint->mData[(components*pixel) + components - 1] = alpha; // alpha
+ // Alpha "frame"
+ const bool edge = w == 0 || h == 0 || w == (paint->mWidthX - 1) || h == (paint->mWidthY - 1);
+ const bool near_edge_frill = ((w == 1 || w == (paint->mWidthX - 2)) && (h % 2 == 0)) ||
+ ((h == 1 || h == (paint->mWidthY - 2)) && (w % 2 == 0));
+ const U8 alpha = U8(F32(max_value) * F32(edge || near_edge_frill));
+ paint->mData[(components*pixel) + components - 1] = alpha; // alpha
+ }
+ }
+ paint_request_queue.enqueue(paint);
+#endif
+
+ // Apply the paint queues ad-hoc right here for now.
+ // *TODO: Eventually the paint queue(s) should be applied at a
+ // predictable time in the viewer frame loop.
+ // TODO: In hindsight... maybe we *should* bind the paintmap to the render buffer. That makes a lot more sense, and we wouldn't have to reduce its resolution by settling for the bake buffer. If we do that, make a comment above convertPaintQueueRGBAToRGB that the texture is modified!
+ LLTerrainPaintQueue paint_send_queue = LLTerrainPaintMap::convertPaintQueueRGBAToRGB(*tex, paint_request_queue);
+ LLTerrainPaintQueue& paint_map_queue = gLocalTerrainMaterials.getPaintMapQueue();
+ paint_map_queue.enqueue(paint_send_queue);
+ LLTerrainPaintMap::applyPaintQueueRGB(*tex, paint_map_queue);
+
+ return true;
+ }
+};
+
class LLAdvancedTerrainDeleteLocalPaintMap : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -7781,15 +7873,29 @@ class LLAvatarSendIM : public view_listener_t
class LLAvatarCall : public view_listener_t
{
+ static LLVOAvatar* findAvatar()
+ {
+ return find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject());
+ }
+
bool handleEvent(const LLSD& userdata)
{
- LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() );
- if(avatar)
+ if (LLVOAvatar* avatar = findAvatar())
{
LLAvatarActions::startCall(avatar->getID());
}
return true;
}
+
+public:
+ static bool isAvailable()
+ {
+ if (LLVOAvatar* avatar = findAvatar())
+ {
+ return LLAvatarActions::canCallTo(avatar->getID());
+ }
+ return LLAvatarActions::canCall();
+ }
};
namespace
@@ -9908,6 +10014,7 @@ void initialize_menus()
// Develop > Terrain
view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
view_listener_t::addMenu(new LLAdvancedTerrainCreateLocalPaintMap(), "Advanced.TerrainCreateLocalPaintMap");
+ view_listener_t::addMenu(new LLAdvancedTerrainEditLocalPaintMap(), "Advanced.TerrainEditLocalPaintMap");
view_listener_t::addMenu(new LLAdvancedTerrainDeleteLocalPaintMap(), "Advanced.TerrainDeleteLocalPaintMap");
// Advanced > UI
@@ -10079,7 +10186,7 @@ void initialize_menus()
registrar.add("Avatar.ShowInspector", boost::bind(&handle_avatar_show_inspector));
view_listener_t::addMenu(new LLAvatarSendIM(), "Avatar.SendIM");
view_listener_t::addMenu(new LLAvatarCall(), "Avatar.Call", cb_info::UNTRUSTED_BLOCK);
- enable.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall));
+ enable.add("Avatar.EnableCall", boost::bind(&LLAvatarCall::isAvailable));
view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse", cb_info::UNTRUSTED_THROTTLE);
view_listener_t::addMenu(new LLAvatarToggleMyProfile(), "Avatar.ToggleMyProfile");
view_listener_t::addMenu(new LLAvatarTogglePicks(), "Avatar.TogglePicks");
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index c4ea8db3cd..d722851851 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2139,6 +2139,21 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
EInstantMessage dialog = (EInstantMessage)d;
LLHost sender = msg->getSender();
+ LLSD metadata;
+ if (msg->getNumberOfBlocksFast(_PREHASH_MetaData) > 0)
+ {
+ S32 metadata_size = msg->getSizeFast(_PREHASH_MetaData, 0, _PREHASH_Data);
+ std::string metadata_buffer;
+ metadata_buffer.resize(metadata_size, 0);
+
+ msg->getBinaryDataFast(_PREHASH_MetaData, _PREHASH_Data, &metadata_buffer[0], metadata_size, 0, metadata_size );
+ std::stringstream metadata_stream(metadata_buffer);
+ if (LLSDSerialize::fromBinary(metadata, metadata_stream, metadata_size) == LLSDParser::PARSE_FAILURE)
+ {
+ metadata.clear();
+ }
+ }
+
LLIMProcessing::processNewMessage(from_id,
from_group,
to_id,
@@ -2153,7 +2168,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
position,
binary_bucket,
binary_bucket_size,
- sender);
+ sender,
+ metadata);
}
void send_do_not_disturb_message (LLMessageSystem* msg, const LLUUID& from_id, const LLUUID& session_id)
@@ -6643,7 +6659,6 @@ void process_initiate_download(LLMessageSystem* msg, void**)
(void**)new std::string(viewer_filename));
}
-
void process_script_teleport_request(LLMessageSystem* msg, void**)
{
if (!gSavedSettings.getBOOL("ScriptsCanShowUI")) return;
@@ -6657,6 +6672,11 @@ void process_script_teleport_request(LLMessageSystem* msg, void**)
msg->getString("Data", "SimName", sim_name);
msg->getVector3("Data", "SimPosition", pos);
msg->getVector3("Data", "LookAt", look_at);
+ U32 flags = (BEACON_SHOW_MAP | BEACON_FOCUS_MAP);
+ if (msg->has("Options"))
+ {
+ msg->getU32("Options", "Flags", flags);
+ }
LLFloaterWorldMap* instance = LLFloaterWorldMap::getInstance();
if(instance)
@@ -6667,7 +6687,13 @@ void process_script_teleport_request(LLMessageSystem* msg, void**)
<< LL_ENDL;
instance->trackURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]);
- LLFloaterReg::showInstance("world_map", "center");
+ if (flags & BEACON_SHOW_MAP)
+ {
+ bool old_auto_focus = instance->getAutoFocus();
+ instance->setAutoFocus(flags & BEACON_FOCUS_MAP);
+ instance->openFloater("center");
+ instance->setAutoFocus(old_auto_focus);
+ }
}
// remove above two lines and replace with below line
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index a8fe221d98..a6d397c039 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -100,6 +100,7 @@ LLGLSLShader gReflectionProbeDisplayProgram;
LLGLSLShader gCopyProgram;
LLGLSLShader gCopyDepthProgram;
LLGLSLShader gPBRTerrainBakeProgram;
+LLGLSLShader gTerrainStampProgram;
//object shaders
LLGLSLShader gObjectPreviewProgram;
@@ -3170,6 +3171,34 @@ bool LLViewerShaderMgr::loadShadersInterface()
}
}
+ if (gSavedSettings.getBOOL("LocalTerrainPaintEnabled"))
+ {
+ if (success)
+ {
+ LLGLSLShader* shader = &gTerrainStampProgram;
+ U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth");
+ // LLTerrainPaintMap currently uses an RGB8 texture internally
+ bit_depth = llclamp(bit_depth, 1, 8);
+ shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth);
+
+ shader->mShaderFiles.clear();
+ shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER));
+ shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER));
+ shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE];
+ const U32 value_range = (1 << bit_depth) - 1;
+ shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range));
+ success = success && shader->createShader();
+ //llassert(success);
+ if (!success)
+ {
+ LL_WARNS() << "Failed to create shader '" << shader->mName << "', disabling!" << LL_ENDL;
+ gSavedSettings.setBOOL("RenderCanUseTerrainBakeShaders", false);
+ // continue as if this shader never happened
+ success = true;
+ }
+ }
+ }
+
if (success)
{
gAlphaMaskProgram.mName = "Alpha Mask Shader";
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index b08796025a..e654967c46 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -175,6 +175,7 @@ extern LLGLSLShader gReflectionProbeDisplayProgram;
extern LLGLSLShader gCopyProgram;
extern LLGLSLShader gCopyDepthProgram;
extern LLGLSLShader gPBRTerrainBakeProgram;
+extern LLGLSLShader gTerrainStampProgram;
//output tex0[tc0] - tex1[tc1]
extern LLGLSLShader gTwoTextureCompareProgram;
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 9d4c072909..a4b06b1e1a 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -67,6 +67,7 @@
#include "lluiusage.h"
#include "lltranslate.h"
#include "llluamanager.h"
+#include "scope_exit.h"
// "Minimal Vulkan" to get max API Version
@@ -510,7 +511,6 @@ void send_viewer_stats(bool include_preferences)
return;
}
- LLSD body;
std::string url = gAgent.getRegion()->getCapability("ViewerStats");
if (url.empty()) {
@@ -518,8 +518,18 @@ void send_viewer_stats(bool include_preferences)
return;
}
- LLViewerStats::instance().getRecording().pause();
+ LLSD body = capture_viewer_stats(include_preferences);
+ LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body,
+ "Statistics posted to sim", "Failed to post statistics to sim");
+}
+
+LLSD capture_viewer_stats(bool include_preferences)
+{
+ LLViewerStats& vstats{ LLViewerStats::instance() };
+ vstats.getRecording().pause();
+ LL::scope_exit cleanup([&vstats]{ vstats.getRecording().resume(); });
+ LLSD body;
LLSD &agent = body["agent"];
time_t ltime;
@@ -791,10 +801,7 @@ void send_viewer_stats(bool include_preferences)
body["session_id"] = gAgentSessionID;
LLViewerStats::getInstance()->addToMessage(body);
-
- LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body,
- "Statistics posted to sim", "Failed to post statistics to sim");
- LLViewerStats::instance().getRecording().resume();
+ return body;
}
LLTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name)
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 8aed1c537e..dd66fee436 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -281,6 +281,7 @@ static const F32 SEND_STATS_PERIOD = 300.0f;
// The following are from (older?) statistics code found in appviewer.
void update_statistics();
+LLSD capture_viewer_stats(bool include_preferences);
void send_viewer_stats(bool include_preferences);
void update_texture_time();
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index cd01f870bf..440eb427cc 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1331,7 +1331,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi
// Check the whitelist, if there's media (otherwise just show it)
if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url))
{
- if ( obj != mDragHoveredObject)
+ if (obj != mDragHoveredObject)
{
// Highlight the dragged object
LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
@@ -1946,6 +1946,11 @@ LLViewerWindow::LLViewerWindow(const Params& p)
}
LLFontManager::initClass();
+
+ // fonts use an GL_UNSIGNED_BYTE image format,
+ // so they need convertion, init buffers if needed
+ LLImageGL::allocateConversionBuffer();
+
// Init font system, load default fonts and generate basic glyphs
// currently it takes aprox. 0.5 sec and we would load these fonts anyway
// before login screen.
diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp
index 52f413792a..45b4da0aac 100644
--- a/indra/newview/llviewerwindowlistener.cpp
+++ b/indra/newview/llviewerwindowlistener.cpp
@@ -35,6 +35,7 @@
// std headers
// external library headers
// other Linden headers
+#include "llcallbacklist.h"
#include "llviewerwindow.h"
LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow):
@@ -46,8 +47,7 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow):
add("saveSnapshot",
"Save screenshot: [\"filename\"] (extension may be specified: bmp, jpeg, png)\n"
"[\"width\"], [\"height\"], [\"showui\"], [\"showhud\"], [\"rebuild\"], [\"type\"]\n"
- "type: \"COLOR\", \"DEPTH\"\n"
- "Post on [\"reply\"] an event containing [\"result\"]",
+ "type: \"COLOR\", \"DEPTH\"\n",
&LLViewerWindowListener::saveSnapshot,
llsd::map("filename", LLSD::String(), "reply", LLSD()));
add("requestReshape",
@@ -57,8 +57,6 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow):
void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
{
- Response response(LLSD(), event);
-
typedef std::map<LLSD::String, LLSnapshotModel::ESnapshotLayerType> TypeMap;
TypeMap types;
#define tp(name) types[#name] = LLSnapshotModel::SNAPSHOT_TYPE_##name
@@ -88,7 +86,8 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
TypeMap::const_iterator found = types.find(event["type"]);
if (found == types.end())
{
- return response.error(stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected."));
+ sendReply(llsd::map("error", stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected.")), event);
+ return;
}
type = found->second;
}
@@ -96,7 +95,8 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
std::string filename(event["filename"]);
if (filename.empty())
{
- return response.error(stringize("File path is empty."));
+ sendReply(llsd::map("error", stringize("File path is empty.")), event);
+ return;
}
LLSnapshotModel::ESnapshotFormat format(LLSnapshotModel::SNAPSHOT_FORMAT_BMP);
@@ -115,9 +115,13 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
}
else if (ext != "bmp")
{
- return response.error(stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected."));
+ sendReply(llsd::map("error", stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected.")), event);
+ return;
}
- response["result"] = mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format);
+ // take snapshot on the main coro
+ doOnIdleOneTime([this, event, filename, width, height, showui, showhud, rebuild, type, format]()
+ { sendReply(llsd::map("result", mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format)), event); });
+
}
void LLViewerWindowListener::requestReshape(LLSD const & event_data) const
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
index 077e6e6cb1..ca76d93cd7 100644
--- a/indra/newview/llvlcomposition.cpp
+++ b/indra/newview/llvlcomposition.cpp
@@ -320,7 +320,11 @@ LLViewerTexture* LLTerrainMaterials::getPaintMap()
void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map)
{
llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP);
+ const bool changed = paint_map != mPaintMap;
mPaintMap = paint_map;
+ // The paint map has changed, so edits are no longer valid
+ mPaintRequestQueue.clear();
+ mPaintMapQueue.clear();
}
// Boost the texture loading priority
diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h
index f15f9bff6a..21fd484375 100644
--- a/indra/newview/llvlcomposition.h
+++ b/indra/newview/llvlcomposition.h
@@ -31,6 +31,7 @@
#include "llviewershadermgr.h"
#include "llviewertexture.h"
#include "llpointer.h"
+#include "llterrainpaintmap.h"
#include "llimage.h"
@@ -87,6 +88,18 @@ public:
void setPaintType(U32 paint_type) { mPaintType = paint_type; }
LLViewerTexture* getPaintMap();
void setPaintMap(LLViewerTexture* paint_map);
+ // Queue of client-triggered brush operations that need to be converted
+ // into a form that can be sent to the server.
+ // TODO: Consider getting rid of mPaintRequestQueue, as it's not really needed (brushes go directly to RGB queue)
+ LLTerrainBrushQueue& getBrushQueue() { return mBrushQueue; }
+ // Queue of client-triggered paint operations that need to be converted
+ // into a form that can be sent to the server.
+ // Paints in this queue are in RGBA format.
+ LLTerrainPaintQueue& getPaintRequestQueue() { return mPaintRequestQueue; }
+ // Paint queue for current paint map - this queue gets applied directly to
+ // the paint map. Paints within are assumed to have already been sent to
+ // the server. Paints in this queue are in RGB format.
+ LLTerrainPaintQueue& getPaintMapQueue() { return mPaintMapQueue; }
protected:
void unboost();
@@ -105,6 +118,9 @@ protected:
U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE;
LLPointer<LLViewerTexture> mPaintMap;
+ LLTerrainBrushQueue mBrushQueue;
+ LLTerrainPaintQueue mPaintRequestQueue{U8(4)};
+ LLTerrainPaintQueue mPaintMapQueue{U8(3)};
};
// Local materials to override all regions
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 1a35a71706..243cba6ffd 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -391,12 +391,21 @@ void LLVoiceClient::setRenderDevice(const std::string& name)
LLWebRTCVoiceClient::getInstance()->setRenderDevice(name);
}
+bool LLVoiceClient::isCaptureNoDevice()
+{
+ return LLWebRTCVoiceClient::getInstance()->isCaptureNoDevice();
+}
+
+bool LLVoiceClient::isRenderNoDevice()
+{
+ return LLWebRTCVoiceClient::getInstance()->isRenderNoDevice();
+}
+
const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices()
{
return LLWebRTCVoiceClient::getInstance()->getCaptureDevices();
}
-
const LLVoiceDeviceList& LLVoiceClient::getRenderDevices()
{
return LLWebRTCVoiceClient::getInstance()->getRenderDevices();
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index d53f512d82..2731b0cc7f 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -192,6 +192,9 @@ public:
virtual LLVoiceDeviceList& getCaptureDevices()=0;
virtual LLVoiceDeviceList& getRenderDevices()=0;
+ virtual bool isCaptureNoDevice() = 0;
+ virtual bool isRenderNoDevice() = 0;
+
virtual void getParticipantList(std::set<LLUUID> &participants)=0;
virtual bool isParticipant(const LLUUID& speaker_id)=0;
//@}
@@ -392,6 +395,8 @@ public:
void setCaptureDevice(const std::string& name);
void setRenderDevice(const std::string& name);
+ bool isCaptureNoDevice();
+ bool isRenderNoDevice();
void setHidden(bool hidden);
const LLVoiceDeviceList& getCaptureDevices();
diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp
index 7691ac54f3..c5704982b8 100644
--- a/indra/newview/llvoicevisualizer.cpp
+++ b/indra/newview/llvoicevisualizer.cpp
@@ -337,7 +337,7 @@ void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah )
//---------------------------------------------------
void LLVoiceVisualizer::render()
{
- static LLCachedControl<bool> show_visualizer(gSavedSettings, "VoiceVisualizerEnabled", false);
+ static LLCachedControl<bool> show_visualizer(gSavedSettings, "VoiceVisualizerEnabled", true);
if (!mVoiceEnabled || !show_visualizer)
{
return;
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 4659fa5f71..b8ddc1f255 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -2775,6 +2775,7 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name)
}
}
}
+
void LLVivoxVoiceClient::setDevicesListUpdated(bool state)
{
mDevicesListUpdated = state;
@@ -6637,13 +6638,13 @@ void LLVivoxVoiceClient::expireVoiceFonts()
}
}
- LLSD args;
- args["URL"] = LLTrans::getString("voice_morphing_url");
- args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url");
-
// Give a notification if any voice fonts have expired.
if (have_expired)
{
+ LLSD args;
+ args["URL"] = LLTrans::getString("voice_morphing_url");
+ args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url");
+
if (expired_in_use)
{
LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args);
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
index 3167705528..cec8b71442 100644
--- a/indra/newview/llvoicevivox.h
+++ b/indra/newview/llvoicevivox.h
@@ -121,6 +121,9 @@ public:
void setCaptureDevice(const std::string& name) override;
void setRenderDevice(const std::string& name) override;
+ bool isCaptureNoDevice() override { return false; };
+ bool isRenderNoDevice() override { return false; };
+
LLVoiceDeviceList& getCaptureDevices() override;
LLVoiceDeviceList& getRenderDevices() override;
//@}
diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp
index 7161f5af5e..6d4f3fe356 100644
--- a/indra/newview/llvoicewebrtc.cpp
+++ b/indra/newview/llvoicewebrtc.cpp
@@ -203,6 +203,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() :
mTuningMode(false),
mTuningMicGain(0.0),
mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume
+ mDeviceSettingsAvailable(false),
mDevicesListUpdated(false),
mSpatialCoordsDirty(false),
@@ -218,7 +219,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() :
mAvatarNameCacheConnection(),
mIsInTuningMode(false),
mIsProcessingChannels(false),
- mIsCoroutineActive(false),
+ mIsTimerActive(false),
mWebRTCPump("WebRTCClientPump"),
mWebRTCDeviceInterface(nullptr)
{
@@ -295,6 +296,20 @@ void LLWebRTCVoiceClient::cleanUp()
mNeighboringRegions.clear();
sessionState::for_each(boost::bind(predShutdownSession, _1));
LL_DEBUGS("Voice") << "Exiting" << LL_ENDL;
+ stopTimer();
+}
+
+void LLWebRTCVoiceClient::stopTimer()
+{
+ if (mIsTimerActive)
+ {
+ LLMuteList::instanceExists();
+ {
+ LLMuteList::getInstance()->removeObserver(this);
+ }
+ mIsTimerActive = false;
+ LL::Timers::instance().cancel(mVoiceTimerHandle);
+ }
}
void LLWebRTCVoiceClient::LogMessage(llwebrtc::LLWebRTCLogCallback::LogLevel level, const std::string& message)
@@ -443,8 +458,7 @@ void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer)
//---------------------------------------------------
// Primary voice loop.
-// This voice loop is called every 100ms plus the time it
-// takes to process the various functions called in the loop
+// This voice loop is called every 100ms
// The loop does the following:
// * gates whether we do channel processing depending on
// whether we're running a WebRTC voice channel or
@@ -456,102 +470,94 @@ void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer)
// connection to various voice channels.
// * Sends updates to the voice server when this agent's
// voice levels, or positions have changed.
-void LLWebRTCVoiceClient::voiceConnectionCoro()
+void LLWebRTCVoiceClient::connectionTimer()
{
- LL_DEBUGS("Voice") << "starting" << LL_ENDL;
- mIsCoroutineActive = true;
- LLCoros::set_consuming(true);
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE;
try
{
- LLMuteList::getInstance()->addObserver(this);
- while (!sShuttingDown)
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE("voiceConnectionCoroLoop")
- // TODO: Doing some measurement and calculation here,
- // we could reduce the timeout to take into account the
- // time spent on the previous loop to have the loop
- // cycle at exactly 100ms, instead of 100ms + loop
- // execution time.
- // Could help with voice updates making for smoother
- // voice when we're busy.
- llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
- if (sShuttingDown) return; // 'this' migh already be invalid
- bool voiceEnabled = mVoiceEnabled;
-
- if (!isAgentAvatarValid())
+ bool voiceEnabled = mVoiceEnabled;
+
+ if (!isAgentAvatarValid())
+ {
+ if (sShuttingDown)
{
- continue;
+ cleanUp();
}
+ return;
+ }
- LLViewerRegion *regionp = gAgent.getRegion();
- if (!regionp)
+ LLViewerRegion* regionp = gAgent.getRegion();
+ if (!regionp)
+ {
+ if (sShuttingDown)
{
- continue;
+ cleanUp();
}
+ return;
+ }
- if (!mProcessChannels)
+ if (!mProcessChannels)
+ {
+ // we've switched away from webrtc voice, so shut all channels down.
+ // leave channel can be called again and again without adverse effects.
+ // it merely tells channels to shut down if they're not already doing so.
+ leaveChannel(false);
+ }
+ else if (inSpatialChannel())
+ {
+ bool useEstateVoice = true;
+ // add session for region or parcel voice.
+ if (!regionp || regionp->getRegionID().isNull())
{
- // we've switched away from webrtc voice, so shut all channels down.
- // leave channel can be called again and again without adverse effects.
- // it merely tells channels to shut down if they're not already doing so.
- leaveChannel(false);
+ // no region, no voice.
+ return;
}
- else if (inSpatialChannel())
- {
- bool useEstateVoice = true;
- // add session for region or parcel voice.
- if (!regionp || regionp->getRegionID().isNull())
- {
- // no region, no voice.
- continue;
- }
- voiceEnabled = voiceEnabled && regionp->isVoiceEnabled();
+ voiceEnabled = voiceEnabled && regionp->isVoiceEnabled();
- if (voiceEnabled)
+ if (voiceEnabled)
+ {
+ LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
+ // check to see if parcel changed.
+ if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID)
{
- LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
- // check to see if parcel changed.
- if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID)
+ // parcel voice
+ if (!parcel->getParcelFlagAllowVoice())
{
- // parcel voice
- if (!parcel->getParcelFlagAllowVoice())
- {
- voiceEnabled = false;
- }
- else if (!parcel->getParcelFlagUseEstateVoiceChannel())
- {
- // use the parcel-specific voice channel.
- S32 parcel_local_id = parcel->getLocalID();
- std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID());
-
- useEstateVoice = false;
- if (!inOrJoiningChannel(channelID))
- {
- startParcelSession(channelID, parcel_local_id);
- }
- }
+ voiceEnabled = false;
}
- if (voiceEnabled && useEstateVoice && !inEstateChannel())
+ else if (!parcel->getParcelFlagUseEstateVoiceChannel())
{
- // estate voice
- startEstateSession();
+ // use the parcel-specific voice channel.
+ S32 parcel_local_id = parcel->getLocalID();
+ std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID());
+
+ useEstateVoice = false;
+ if (!inOrJoiningChannel(channelID))
+ {
+ startParcelSession(channelID, parcel_local_id);
+ }
}
}
- if (!voiceEnabled)
+ if (voiceEnabled && useEstateVoice && !inEstateChannel())
{
- // voice is disabled, so leave and disable PTT
- leaveChannel(true);
- }
- else
- {
- // we're in spatial voice, and voice is enabled, so determine positions in order
- // to send position updates.
- updatePosition();
+ // estate voice
+ startEstateSession();
}
}
+ if (!voiceEnabled)
+ {
+ // voice is disabled, so leave and disable PTT
+ leaveChannel(true);
+ }
+ else
+ {
+ // we're in spatial voice, and voice is enabled, so determine positions in order
+ // to send position updates.
+ updatePosition();
+ }
LL::WorkQueue::postMaybe(mMainQueue,
- [=] {
+ [=, this] {
if (sShuttingDown)
{
return;
@@ -565,10 +571,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()
});
}
}
- catch (const LLCoros::Stop&)
- {
- LL_DEBUGS("LLWebRTCVoiceClient") << "Received a shutdown exception" << LL_ENDL;
- }
catch (const LLContinueError&)
{
LOG_UNHANDLED_EXCEPTION("LLWebRTCVoiceClient");
@@ -582,7 +584,10 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()
throw;
}
- cleanUp();
+ if (sShuttingDown)
+ {
+ cleanUp();
+ }
}
// For spatial, determine which neighboring regions to connect to
@@ -640,12 +645,14 @@ void LLWebRTCVoiceClient::leaveAudioSession()
void LLWebRTCVoiceClient::clearCaptureDevices()
{
LL_DEBUGS("Voice") << "called" << LL_ENDL;
+ mDeviceSettingsAvailable = false;
mCaptureDevices.clear();
}
void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device)
{
LL_INFOS("Voice") << "Voice Capture Device: '" << device.display_name << "' (" << device.full_name << ")" << LL_ENDL;
+ mDeviceSettingsAvailable = false;
mCaptureDevices.push_back(device);
}
@@ -658,6 +665,7 @@ void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name)
{
mWebRTCDeviceInterface->setCaptureDevice(name);
}
+
void LLWebRTCVoiceClient::setDevicesListUpdated(bool state)
{
mDevicesListUpdated = state;
@@ -669,7 +677,7 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi
{
LL::WorkQueue::postMaybe(mMainQueue,
- [=]
+ [=, this]
{
OnDevicesChangedImpl(render_devices, capture_devices);
});
@@ -703,20 +711,22 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi
}
setCaptureDevice(inputDevice);
+ mDeviceSettingsAvailable = true;
setDevicesListUpdated(true);
}
void LLWebRTCVoiceClient::clearRenderDevices()
{
LL_DEBUGS("Voice") << "called" << LL_ENDL;
+ mDeviceSettingsAvailable = false;
mRenderDevices.clear();
}
void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device)
{
LL_INFOS("Voice") << "Voice Render Device: '" << device.display_name << "' (" << device.full_name << ")" << LL_ENDL;
+ mDeviceSettingsAvailable = false;
mRenderDevices.push_back(device);
-
}
LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices()
@@ -729,6 +739,16 @@ void LLWebRTCVoiceClient::setRenderDevice(const std::string& name)
mWebRTCDeviceInterface->setRenderDevice(name);
}
+bool LLWebRTCVoiceClient::isCaptureNoDevice()
+{
+ return mCaptureDevices.empty() || mWebRTCDeviceInterface->isCaptureNoDevice();
+}
+
+bool LLWebRTCVoiceClient::isRenderNoDevice()
+{
+ return mRenderDevices.empty() || mWebRTCDeviceInterface->isRenderNoDevice();
+}
+
void LLWebRTCVoiceClient::tuningStart()
{
if (!mIsInTuningMode)
@@ -754,11 +774,15 @@ bool LLWebRTCVoiceClient::inTuningMode()
void LLWebRTCVoiceClient::tuningSetMicVolume(float volume)
{
- mTuningMicGain = volume;
+ mTuningMicGain = volume;
}
void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
{
+ if (isRenderNoDevice())
+ {
+ volume = 0;
+ }
if (volume != mTuningSpeakerVolume)
{
@@ -768,14 +792,17 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
float LLWebRTCVoiceClient::getAudioLevel()
{
- if (mIsInTuningMode)
+ if (isCaptureNoDevice())
{
- return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f;
+ return 0;
}
- else
+
+ if (mIsInTuningMode)
{
- return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f;
+ return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f;
}
+
+ return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f;
}
float LLWebRTCVoiceClient::tuningGetEnergy(void)
@@ -783,15 +810,6 @@ float LLWebRTCVoiceClient::tuningGetEnergy(void)
return getAudioLevel();
}
-bool LLWebRTCVoiceClient::deviceSettingsAvailable()
-{
- bool result = true;
-
- if(mRenderDevices.empty() || mCaptureDevices.empty())
- result = false;
-
- return result;
-}
bool LLWebRTCVoiceClient::deviceSettingsUpdated()
{
bool updated = mDevicesListUpdated;
@@ -801,7 +819,7 @@ bool LLWebRTCVoiceClient::deviceSettingsUpdated()
void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList)
{
- if(clearCurrentList)
+ if (clearCurrentList)
{
clearCaptureDevices();
clearRenderDevices();
@@ -809,7 +827,6 @@ void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList)
mWebRTCDeviceInterface->refreshDevices();
}
-
void LLWebRTCVoiceClient::setHidden(bool hidden)
{
mHidden = hidden;
@@ -1340,7 +1357,7 @@ bool LLWebRTCVoiceClient::isVoiceWorking() const
// webrtc is working if the coroutine is active in the case of
// webrtc. WebRTC doesn't need to connect to a secondary process
// or a login server to become active.
- return mIsCoroutineActive;
+ return mIsTimerActive;
}
// Returns true if calling back the session URI after the session has closed is possible.
@@ -1523,9 +1540,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume)
{
if (volume != mSpeakerVolume)
{
- {
- mSpeakerVolume = volume;
- }
+ mSpeakerVolume = volume;
sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume));
}
}
@@ -1544,7 +1559,6 @@ void LLWebRTCVoiceClient::setMicGain(F32 gain)
}
}
-
void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE;
@@ -1552,7 +1566,7 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled)
LL_DEBUGS("Voice")
<< "( " << (enabled ? "enabled" : "disabled") << " )"
<< " was "<< (mVoiceEnabled ? "enabled" : "disabled")
- << " coro "<< (mIsCoroutineActive ? "active" : "inactive")
+ << " coro "<< (mIsTimerActive ? "active" : "inactive")
<< LL_ENDL;
if (enabled != mVoiceEnabled)
@@ -1569,10 +1583,12 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled)
status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED;
mSpatialCoordsDirty = true;
updatePosition();
- if (!mIsCoroutineActive)
+ if (!mIsTimerActive)
{
- LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro",
- boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance()));
+ LL_DEBUGS("Voice") << "Starting" << LL_ENDL;
+ mIsTimerActive = true;
+ LLMuteList::getInstance()->addObserver(this);
+ mVoiceTimerHandle = LL::Timers::instance().scheduleEvery([this]() { connectionTimer(); return false; }, UPDATE_THROTTLE_SECONDS);
}
else
{
@@ -2205,7 +2221,7 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection()
void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state)
{
LL::WorkQueue::postMaybe(mMainQueue,
- [=] {
+ [=, this] {
LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL;
switch (state)
@@ -2228,7 +2244,7 @@ void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObs
// callback from llwebrtc
void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate& candidate)
{
- LL::WorkQueue::postMaybe(mMainQueue, [=] { mIceCandidates.push_back(candidate); });
+ LL::WorkQueue::postMaybe(mMainQueue, [=, this] { mIceCandidates.push_back(candidate); });
}
void LLVoiceWebRTCConnection::processIceUpdates()
@@ -2346,7 +2362,7 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection)
void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp)
{
LL::WorkQueue::postMaybe(mMainQueue,
- [=] {
+ [=, this] {
if (mShutDown)
{
return;
@@ -2373,7 +2389,7 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp)
void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface* audio_interface)
{
LL::WorkQueue::postMaybe(mMainQueue,
- [=] {
+ [=, this] {
if (mShutDown)
{
return;
@@ -2395,7 +2411,7 @@ void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterfac
void LLVoiceWebRTCConnection::OnRenegotiationNeeded()
{
LL::WorkQueue::postMaybe(mMainQueue,
- [=] {
+ [=, this] {
LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL;
if (!mShutDown)
{
@@ -2409,7 +2425,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded()
void LLVoiceWebRTCConnection::OnPeerConnectionClosed()
{
LL::WorkQueue::postMaybe(mMainQueue,
- [=] {
+ [=, this] {
LL_DEBUGS("Voice") << "Peer connection has closed." << LL_ENDL;
if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_CLOSE)
{
@@ -2881,7 +2897,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()
// llwebrtc callback
void LLVoiceWebRTCConnection::OnDataReceived(const std::string& data, bool binary)
{
- LL::WorkQueue::postMaybe(mMainQueue, [=] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); });
+ LL::WorkQueue::postMaybe(mMainQueue, [=, this] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); });
}
//
@@ -3037,7 +3053,7 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b
void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface)
{
LL::WorkQueue::postMaybe(mMainQueue,
- [=] {
+ [=, this] {
if (mShutDown)
{
return;
diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h
index ff82d2739d..88ead98950 100644
--- a/indra/newview/llvoicewebrtc.h
+++ b/indra/newview/llvoicewebrtc.h
@@ -30,6 +30,7 @@ class LLWebRTCProtocolParser;
#include "lliopipe.h"
#include "llpumpio.h"
+#include "llcallbacklist.h"
#include "llchainio.h"
#include "lliosocket.h"
#include "v3math.h"
@@ -113,7 +114,7 @@ public:
/// @name Devices
//@{
// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
- bool deviceSettingsAvailable() override;
+ bool deviceSettingsAvailable() override { return mDeviceSettingsAvailable; }
bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel.
// Requery the WebRTC daemon for the current list of input/output devices.
@@ -125,6 +126,9 @@ public:
void setCaptureDevice(const std::string& name) override;
void setRenderDevice(const std::string& name) override;
+ bool isCaptureNoDevice() override;
+ bool isRenderNoDevice() override;
+
LLVoiceDeviceList& getCaptureDevices() override;
LLVoiceDeviceList& getRenderDevices() override;
//@}
@@ -447,19 +451,23 @@ private:
// Coroutine support methods
//---
- void voiceConnectionCoro();
+ void connectionTimer();
//---
/// Clean up objects created during a voice session.
void cleanUp();
+ // stop state machine
+ void stopTimer();
+
LL::WorkQueue::weak_t mMainQueue;
bool mTuningMode;
F32 mTuningMicGain;
int mTuningSpeakerVolume;
+ bool mDeviceSettingsAvailable;
bool mDevicesListUpdated; // set to true when the device list has been updated
- // and false when the panelvoicedevicesettings has queried for an update status.
+ // and false when the panelvoicedevicesettings has queried for an update status.
std::string mSpatialSessionCredentials;
std::string mMainSessionGroupHandle; // handle of the "main" session group.
@@ -534,7 +542,8 @@ private:
bool mIsInTuningMode;
bool mIsProcessingChannels;
- bool mIsCoroutineActive;
+ bool mIsTimerActive;
+ LL::Timers::handle_t mVoiceTimerHandle;
// These variables can last longer than WebRTC in coroutines so we need them as static
static bool sShuttingDown;
diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp
index bf171fe954..d9fcd5811d 100644
--- a/indra/newview/llwatchdog.cpp
+++ b/indra/newview/llwatchdog.cpp
@@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h"
#include "llwatchdog.h"
+#include "llmutex.h"
#include "llthread.h"
constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U;
diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp
index 5951d6a93a..2fd45337fc 100644
--- a/indra/newview/llworldmap.cpp
+++ b/indra/newview/llworldmap.cpp
@@ -118,6 +118,7 @@ LLVector3d LLSimInfo::getGlobalOrigin() const
{
return from_region_handle(mHandle);
}
+
LLVector3 LLSimInfo::getLocalPos(LLVector3d global_pos) const
{
LLVector3d sim_origin = from_region_handle(mHandle);
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index 6b2bd3e6fb..1be6a6cfff 100755
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -517,26 +517,38 @@ void LLWorldMapView::draw()
// Draw the region name in the lower left corner
if (mMapScale >= DRAW_TEXT_THRESHOLD)
{
- std::string mesg;
+ static LLCachedControl<bool> print_coords(gSavedSettings, "MapShowGridCoords");
+ static LLFontGL* font = LLFontGL::getFontSansSerifSmallBold();
+
+ auto print = [&](std::string text, F32 x, F32 y, bool use_ellipses)
+ {
+ font->renderUTF8(text, 0,
+ (F32)llfloor(left + x), (F32)llfloor(bottom + y),
+ LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW,
+ S32_MAX, //max_chars
+ (S32)mMapScale, //max_pixels
+ NULL,
+ use_ellipses);
+ };
+
+ std::string grid_name = info->getName();
if (info->isDown())
{
- mesg = llformat( "%s (%s)", info->getName().c_str(), sStringsMap["offline"].c_str());
+ grid_name += " (" + sStringsMap["offline"] + ")";
}
- else
+
+ if (print_coords)
{
- mesg = info->getName();
+ print(grid_name, 3, 14, true);
+ // Obtain and print the grid map coordinates
+ LLVector3d region_pos = info->getGlobalOrigin();
+ std::string grid_coords = llformat("[%.0f, %.0f]", region_pos[VX] / 256, region_pos[VY] / 256);
+ print(grid_coords, 3, 2, false);
}
- if (!mesg.empty())
+ else
{
- LLFontGL::getFontSansSerifSmallBold()->renderUTF8(
- mesg, 0,
- (F32)llfloor(left + 3), (F32)llfloor(bottom + 2),
- LLColor4::white,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW,
- S32_MAX, //max_chars
- (S32)mMapScale, //max_pixels
- NULL,
- /*use_ellipses*/true);
+ print(grid_name, 3, 2, true);
}
}
}
diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp
index d8ea2b884f..d198f7a33b 100644
--- a/indra/newview/llworldmipmap.cpp
+++ b/indra/newview/llworldmipmap.cpp
@@ -30,7 +30,7 @@
#include "llviewercontrol.h" // LLControlGroup
#include "llviewertexturelist.h"
-#include "math.h" // log()
+#include <cmath> // log()
// Turn this on to output tile stats in the standard output
#define DEBUG_TILES_STAT 0
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 84f026699e..0b4b98d674 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -11248,21 +11248,24 @@ public:
}
};
-
+// Called from LLViewHighlightTransparent when "Highlight Transparent" is toggled
void LLPipeline::rebuildDrawInfo()
{
- for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
- iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
+ const U32 types_to_traverse[] =
{
- LLViewerRegion* region = *iter;
-
- LLOctreeDirty dirty;
-
- LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_VOLUME);
- dirty.traverse(part->mOctree);
+ LLViewerRegion::PARTITION_VOLUME,
+ LLViewerRegion::PARTITION_BRIDGE,
+ LLViewerRegion::PARTITION_AVATAR
+ };
- part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE);
- dirty.traverse(part->mOctree);
+ LLOctreeDirty dirty;
+ for (LLViewerRegion* region : LLWorld::getInstance()->getRegionList())
+ {
+ for (U32 type : types_to_traverse)
+ {
+ LLSpatialPartition* part = region->getSpatialPartition(type);
+ dirty.traverse(part->mOctree);
+ }
}
}
diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua
index 4a1132fe7e..6068a916ed 100644
--- a/indra/newview/scripts/lua/require/LLAgent.lua
+++ b/indra/newview/scripts/lua/require/LLAgent.lua
@@ -1,8 +1,26 @@
local leap = require 'leap'
local mapargs = require 'mapargs'
+local result_view = require 'result_view'
+
+local function result(keys)
+ local result_table = {
+ result=result_view(keys.result),
+ -- call result_table:close() to release result sets before garbage
+ -- collection or script completion
+ close = function(self)
+ result_view.close(keys.result[1])
+ end
+ }
+ -- When the result_table is destroyed, close its result_views.
+ return LL.setdtor('LLAgent result', result_table, result_table.close)
+end
local LLAgent = {}
+function LLAgent.getID()
+ return leap.request('LLAgent', {op = 'getID'}).id
+end
+
function LLAgent.getRegionPosition()
return leap.request('LLAgent', {op = 'getPosition'}).region
end
@@ -95,6 +113,33 @@ function LLAgent.requestStand()
leap.send('LLAgent', {op = 'requestStand'})
end
+-- Get the nearby avatars in a range of provided "dist",
+-- if "dist" is not specified, "RenderFarClip" setting is used
+-- reply will contain "result" table with following fields:
+-- "id", "global_pos", "region_pos", "name", "region_id"
+function LLAgent.getNearbyAvatarsList(...)
+ local args = mapargs('dist', ...)
+ args.op = 'getNearbyAvatarsList'
+ return result(leap.request('LLAgent', args))
+end
+
+-- reply will contain "result" table with following fields:
+-- "id", "global_pos", "region_pos", "region_id"
+function LLAgent.getNearbyObjectsList(...)
+ local args = mapargs('dist', ...)
+ args.op = 'getNearbyObjectsList'
+ return result(leap.request('LLAgent', args))
+end
+
+-- Get screen position of your own avatar or any other (if "avatar_id" is specified)
+-- reply contains "x", "y" coordinates and "onscreen" flag to indicate if it's actually in within the current window
+-- avatar render position is used as the point
+function LLAgent.getAgentScreenPos(...)
+ local args = mapargs('avatar_id', ...)
+ args.op = 'getAgentScreenPos'
+ return leap.request('LLAgent', args)
+end
+
-- ***************************************************************************
-- Autopilot
-- ***************************************************************************
diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua
index cf2695917e..34f3fb75eb 100644
--- a/indra/newview/scripts/lua/require/UI.lua
+++ b/indra/newview/scripts/lua/require/UI.lua
@@ -222,8 +222,10 @@ function UI.hideFloater(floater_name)
leap.send("LLFloaterReg", {op = "hideInstance", name = floater_name})
end
-function UI.toggleFloater(floater_name)
- leap.send("LLFloaterReg", {op = "toggleInstance", name = floater_name})
+function UI.toggleFloater(...)
+ local args = mapargs('name,key', ...)
+ args.op = 'toggleInstance'
+ leap.send("LLFloaterReg", args)
end
function UI.isFloaterVisible(floater_name)
diff --git a/indra/newview/scripts/lua/test_LLChatListener.lua b/indra/newview/scripts/lua/test_LLChatListener.lua
index 0f269b54e6..fa00b048b2 100644
--- a/indra/newview/scripts/lua/test_LLChatListener.lua
+++ b/indra/newview/scripts/lua/test_LLChatListener.lua
@@ -1,5 +1,6 @@
-local LLListener = require 'LLListener'
+local LLAgent = require 'LLAgent'
local LLChat = require 'LLChat'
+local LLListener = require 'LLListener'
local UI = require 'UI'
-- Chat listener script allows to use the following commands in Nearby chat:
@@ -23,9 +24,13 @@ function openOrEcho(message)
end
local listener = LLListener(LLChat.nearbyChatPump)
+local agent_id = LLAgent.getID()
function listener:handleMessages(event_data)
- if string.find(event_data.message, '[LUA]') then
+ -- ignore messages and commands from other avatars
+ if event_data.from_id ~= agent_id then
+ return true
+ elseif string.find(event_data.message, '[LUA]') then
return true
elseif event_data.message == 'stop' then
LLChat.sendNearby('Closing echo script.')
diff --git a/indra/newview/scripts/lua/test_animation.lua b/indra/newview/scripts/lua/test_animation.lua
index 37e7254a6c..ae5eabe148 100644
--- a/indra/newview/scripts/lua/test_animation.lua
+++ b/indra/newview/scripts/lua/test_animation.lua
@@ -7,8 +7,8 @@ animations_id = LLInventory.getBasicFolderID('animatn')
anims = LLInventory.collectDescendentsIf{folder_id=animations_id, type="animatn"}.items
local anim_ids = {}
-for key in pairs(anims) do
- table.insert(anim_ids, key)
+for _, key in pairs(anims) do
+ table.insert(anim_ids, {id = key.id, name = key.name})
end
if #anim_ids == 0 then
@@ -16,17 +16,17 @@ if #anim_ids == 0 then
else
-- Start playing a random animation
math.randomseed(os.time())
- local random_id = anim_ids[math.random(#anim_ids)]
- local anim_info = LLAgent.getAnimationInfo(random_id)
+ local random_anim = anim_ids[math.random(#anim_ids)]
+ local anim_info = LLAgent.getAnimationInfo(random_anim.id)
- print("Starting animation locally: " .. anims[random_id].name)
- print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration)))
- LLAgent.playAnimation{item_id=random_id}
+ print("Starting animation locally: " .. random_anim.name)
+ print("Loop: " .. tostring(anim_info.is_loop) .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration)))
+ LLAgent.playAnimation{item_id=random_anim.id}
-- Stop animation after 3 sec if it's looped or longer than 3 sec
- if anim_info.is_loop == 1 or anim_info.duration > 3 then
+ if anim_info.is_loop or anim_info.duration > 3 then
LL.sleep(3)
print("Stop animation.")
- LLAgent.stopAnimation(random_id)
+ LLAgent.stopAnimation(random_anim.id)
end
end
diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua
index 2158134511..23310c6176 100644
--- a/indra/newview/scripts/lua/test_luafloater_demo.lua
+++ b/indra/newview/scripts/lua/test_luafloater_demo.lua
@@ -14,7 +14,7 @@ function flt:handleEvents(event_data)
end
function flt:commit_disable_ctrl(event_data)
- self:post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)})
+ self:post({action="set_enabled", ctrl_name="open_btn", value = not event_data.value})
end
function flt:commit_title_cmb(event_data)
diff --git a/indra/newview/scripts/lua/test_nearby_avatars.lua b/indra/newview/scripts/lua/test_nearby_avatars.lua
new file mode 100644
index 0000000000..c7fa686076
--- /dev/null
+++ b/indra/newview/scripts/lua/test_nearby_avatars.lua
@@ -0,0 +1,25 @@
+inspect = require 'inspect'
+LLAgent = require 'LLAgent'
+
+-- Get the list of nearby avatars and print the info
+local nearby_avatars = LLAgent.getNearbyAvatarsList()
+if nearby_avatars.result.length == 0 then
+ print("No avatars nearby")
+else
+ print("Nearby avatars:")
+ for _, av in pairs(nearby_avatars.result) do
+ print(av.name ..' ID: ' .. av.id ..' Global pos:' .. inspect(av.global_pos))
+ end
+end
+
+-- Get the list of nearby objects in 3m range and print the info
+local nearby_objects = LLAgent.getNearbyObjectsList(3)
+if nearby_objects.result.length == 0 then
+ print("No objects nearby")
+else
+ print("Nearby objects:")
+ local pos={}
+ for _, obj in pairs(nearby_objects.result) do
+ print('ID: ' .. obj.id ..' Global pos:' .. inspect(obj.global_pos))
+ end
+end
diff --git a/indra/newview/scripts/lua/test_screen_position.lua b/indra/newview/scripts/lua/test_screen_position.lua
new file mode 100644
index 0000000000..94d57339b1
--- /dev/null
+++ b/indra/newview/scripts/lua/test_screen_position.lua
@@ -0,0 +1,8 @@
+LLAgent = require 'LLAgent'
+
+local screen_pos = LLAgent.getAgentScreenPos()
+if screen_pos.onscreen then
+ print("Avatar screen coordinates X: " .. screen_pos.x .. " Y: " .. screen_pos.y)
+else
+ print("Avatar is not on the screen")
+end
diff --git a/indra/newview/scripts/lua/test_toolbars.lua b/indra/newview/scripts/lua/test_toolbars.lua
index 7683fca8a3..9cd1043446 100644
--- a/indra/newview/scripts/lua/test_toolbars.lua
+++ b/indra/newview/scripts/lua/test_toolbars.lua
@@ -20,7 +20,7 @@ if response == 'OK' then
UI.removeToolbarBtn(BUTTONS[i])
end
end
- popup:tip('Toolbars were reshuffled')
+ UI.popup:tip('Toolbars were reshuffled')
else
- popup:tip('Canceled')
+ UI.popup:tip('Canceled')
end
diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml
index e4f99d14e9..c4275d43f7 100644
--- a/indra/newview/skins/default/xui/da/strings.xml
+++ b/indra/newview/skins/default/xui/da/strings.xml
@@ -3723,6 +3723,10 @@ Hvis du bliver ved med at modtage denne besked, kontakt venligst [SUPPORT_SITE].
<string name="conference-title-incoming">
Konference med [AGENT_NAME]
</string>
+ <string name="bot_warning">
+Du chatter med en bot, [NAME]. Del ikke personlige oplysninger.
+Læs mere på https://second.life/scripted-agents.
+ </string>
<string name="no_session_message">
(IM session eksisterer ikke)
</string>
diff --git a/indra/newview/skins/default/xui/de/floater_about_land.xml b/indra/newview/skins/default/xui/de/floater_about_land.xml
index bb9ab26ef5..9c93ff38ad 100644
--- a/indra/newview/skins/default/xui/de/floater_about_land.xml
+++ b/indra/newview/skins/default/xui/de/floater_about_land.xml
@@ -25,7 +25,8 @@
<panel.string name="none_text">(keiner)</panel.string>
<panel.string name="sale_pending_text">(Wird verkauft)</panel.string>
<panel.string name="no_selection_text">Keine Parzelle ausgewählt.</panel.string>
- <panel.string name="time_stamp_template">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</panel.string>
+ <panel.string name="time_stamp_template_ampm">[wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt]</panel.string>
+ <panel.string name="time_stamp_template">[wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]</panel.string>
<text name="Name:">Name:</text>
<text name="Description:">Beschreibung:</text>
<text name="LandType">Typ:</text>
diff --git a/indra/newview/skins/default/xui/de/floater_inspect.xml b/indra/newview/skins/default/xui/de/floater_inspect.xml
index da97ceb2d8..a193e1123e 100644
--- a/indra/newview/skins/default/xui/de/floater_inspect.xml
+++ b/indra/newview/skins/default/xui/de/floater_inspect.xml
@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater min_width="460" name="inspect" title="OBJEKTE UNTERSUCHEN" width="460">
+ <floater.string name="timeStampAMPM">
+ [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt]
+ </floater.string>
<floater.string name="timeStamp">
- [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
+ [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]
</floater.string>
<scroll_list name="object_list" tool_tip="Wählen Sie ein Objekt aus dieser Liste, um es in der Second Life-Welt zu markieren">
<scroll_list.columns label="Objektname" name="object_name"/>
diff --git a/indra/newview/skins/default/xui/de/panel_classified_info.xml b/indra/newview/skins/default/xui/de/panel_classified_info.xml
index 007e9d69f0..1b8caf5f78 100644
--- a/indra/newview/skins/default/xui/de/panel_classified_info.xml
+++ b/indra/newview/skins/default/xui/de/panel_classified_info.xml
@@ -13,7 +13,7 @@
[TELEPORT] teleportieren, [MAP] Karte, [PROFILE] Profil
</panel.string>
<panel.string name="date_fmt">
- [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt]
+ [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt]
</panel.string>
<panel.string name="auto_renew_on">
Aktiviert
diff --git a/indra/newview/skins/default/xui/de/panel_landmark_info.xml b/indra/newview/skins/default/xui/de/panel_landmark_info.xml
index 10cf34c170..8726d5e645 100644
--- a/indra/newview/skins/default/xui/de/panel_landmark_info.xml
+++ b/indra/newview/skins/default/xui/de/panel_landmark_info.xml
@@ -15,8 +15,11 @@
<string name="server_forbidden_text">
Die Informationen über diesen Standort sind zugriffsbeschränkt. Bitte wenden Sie sich bezüglich Ihrer Berechtigungen an den Eigentümer der Parzelle.
</string>
+ <string name="acquired_date_ampm">
+ [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local]
+ </string>
<string name="acquired_date">
- [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
+ [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]
</string>
<button name="back_btn" tool_tip="Hinten"/>
<text name="title" value="Ortsprofil"/>
diff --git a/indra/newview/skins/default/xui/de/panel_place_profile.xml b/indra/newview/skins/default/xui/de/panel_place_profile.xml
index 4077fdab36..a0b5a1e9dc 100644
--- a/indra/newview/skins/default/xui/de/panel_place_profile.xml
+++ b/indra/newview/skins/default/xui/de/panel_place_profile.xml
@@ -38,8 +38,11 @@
<string name="server_forbidden_text">
Die Informationen über diesen Standort sind zugriffsbeschränkt. Bitte wenden Sie sich bezüglich Ihrer Berechtigungen an den Eigentümer der Parzelle.
</string>
+ <string name="acquired_date_ampm">
+ [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local]
+ </string>
<string name="acquired_date">
- [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
+ [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]
</string>
<button name="back_btn" tool_tip="Hinten"/>
<text name="title" value="Ortsprofil"/>
diff --git a/indra/newview/skins/default/xui/de/panel_profile_classified.xml b/indra/newview/skins/default/xui/de/panel_profile_classified.xml
index 5c11a01977..5a4b42870c 100644
--- a/indra/newview/skins/default/xui/de/panel_profile_classified.xml
+++ b/indra/newview/skins/default/xui/de/panel_profile_classified.xml
@@ -13,7 +13,7 @@
[TELEPORT] Teleportieren, [MAP] Karten, [PROFILE] Profil
</panel.string>
<panel.string name="date_fmt">
- [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt]
+ [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt]
</panel.string>
<panel.string name="auto_renew_on">
Aktiviert
diff --git a/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml
index baaa58e1d7..437cde99fe 100644
--- a/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml
+++ b/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml
@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<panel label="Profil" name="panel_profile">
+ <string
+ name="date_format_full"
+ value="SL-Geburtstag: [day,datetime,utc]. [mth,datetime,utc]. [year,datetime,utc]" />
+ <string
+ name="date_format_short"
+ value="SL-Geburtstag: [day,datetime,utc]. [mth,datetime,utc]." />
<string name="status_online">
Zurzeit online
</string>
diff --git a/indra/newview/skins/default/xui/de/panel_status_bar.xml b/indra/newview/skins/default/xui/de/panel_status_bar.xml
index 0829814220..e017c6dd82 100644
--- a/indra/newview/skins/default/xui/de/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/de/panel_status_bar.xml
@@ -10,7 +10,7 @@
[hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt]
</panel.string>
<panel.string name="timeTooltip">
- [weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt]
+ [weekday, datetime, slt], [day, datetime, slt]. [month, datetime, slt] [year, datetime, slt]
</panel.string>
<panel.string name="buycurrencylabel">
[AMT] L$
diff --git a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml
index d5ff203e89..30886d1433 100644
--- a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml
+++ b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml
@@ -19,11 +19,11 @@
Eigentümer kann:
</panel.string>
<panel.string name="acquiredDate">
- [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
+ [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]
+ </panel.string>
+ <panel.string name="acquiredDateAMPM">
+ [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local]
</panel.string>
- <panel.string name="acquiredDateAMPM">
- [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local]
- </panel.string>
<panel.string name="origin_inventory">
(Inventar)
</panel.string>
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index 486d604e9f..dd003e72d9 100644
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -39,7 +39,7 @@ Audiotreiberversion: [AUDIO_DRIVER_VERSION]
LibVLC-Version: [LIBVLC_VERSION]
Voice-Server-Version: [VOICE_VERSION]</string>
<string name="AboutTraffic">Paketverlust: [PACKETS_LOST,number,0]/[PACKETS_IN,number,0] ([PACKETS_PCT,number,1] %)</string>
- <string name="AboutTime">[month, datetime, slt] [day, datetime, slt] [year, datetime, slt] [hour, datetime, slt]:[min, datetime, slt]:[second,datetime,slt]</string>
+ <string name="AboutTime">[day, datetime, slt]. [month, datetime, slt] [year, datetime, slt] [hour, datetime, slt]:[min, datetime, slt]:[second,datetime,slt]</string>
<string name="ErrorFetchingServerReleaseNotesURL">Fehler beim Abrufen der URL für die Server-Versionshinweise.</string>
<string name="BuildConfiguration">Build-Konfiguration</string>
<string name="ProgressRestoring">Wird wiederhergestellt...</string>
@@ -812,7 +812,7 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich unter http://suppo
<string name="Unknown">(unbekannt)</string>
<string name="SummaryForTheWeek" value="Zusammenfassung für diese Woche, beginnend am "/>
<string name="NextStipendDay" value=". Der nächste Stipendium-Tag ist "/>
- <string name="GroupPlanningDate">[mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc]</string>
+ <string name="GroupPlanningDate">[day,datetime,utc].[mthnum,datetime,utc].[year,datetime,utc]</string>
<string name="GroupIndividualShare" value=" Gruppenanteil Einzelanteil"/>
<string name="GroupColumn" value="Gruppe"/>
<string name="Balance">Kontostand</string>
@@ -961,7 +961,7 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich unter http://suppo
<string name="GroupMoneyBalance">Kontostand</string>
<string name="GroupMoneyCredits">Danksagung</string>
<string name="GroupMoneyDebits">Soll</string>
- <string name="GroupMoneyDate">[weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]</string>
+ <string name="GroupMoneyDate">[weekday,datetime,utc] [day,datetime,utc]. [mth,datetime,utc]. [year,datetime,utc]</string>
<string name="AcquiredItems">Erworbene Artikel</string>
<string name="Cancel">Abbrechen</string>
<string name="UploadingCosts">Das Hochladen von [NAME] kostet [AMOUNT] L$</string>
@@ -1613,6 +1613,10 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich bitte an [SUPPORT_
<string name="conference-title-incoming">Konferenz mit [AGENT_NAME]</string>
<string name="inventory_item_offered-im">Inventarobjekt „[ITEM_NAME]“ angeboten</string>
<string name="inventory_folder_offered-im">Inventarordner „[ITEM_NAME]“ angeboten</string>
+ <string name="bot_warning">
+ Sie chatten mit einem Bot, [NAME]. Geben Sie keine persönlichen Informationen weiter.
+Erfahren Sie mehr unter https://second.life/scripted-agents.
+ </string>
<string name="share_alert">Objekte aus dem Inventar hier her ziehen</string>
<string name="facebook_post_success">Sie haben auf Facebook gepostet.</string>
<string name="flickr_post_success">Sie haben auf Flickr gepostet.</string>
@@ -1765,7 +1769,7 @@ Missbrauchsbericht</string>
<string name="dance6">Tanzen6</string>
<string name="dance7">Tanzen7</string>
<string name="dance8">Tanzen8</string>
- <string name="AvatarBirthDateFormat">[mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt]</string>
+ <string name="AvatarBirthDateFormat">[day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt]</string>
<string name="DefaultMimeType">Keine/Keiner</string>
<string name="texture_load_dimensions_error">Bilder, die größer sind als [WIDTH]*[HEIGHT] können nicht geladen werden</string>
<string name="outfit_photo_load_dimensions_error">Max. Fotogröße für Outfit ist [WIDTH]*[HEIGHT]. Bitte verkleinern Sie das Bild oder verwenden Sie ein anderes.</string>
diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml
index 15027f1647..5efe1c958a 100644
--- a/indra/newview/skins/default/xui/en/floater_lua_debug.xml
+++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml
@@ -25,6 +25,17 @@
width="100">
LUA string:
</text>
+ <check_box
+ control_name="LuaDebugShowSource"
+ follows="right|top"
+ height="15"
+ label="Show source info"
+ layout="topleft"
+ top="10"
+ right ="-70"
+ tool_tip="Show source info in Lua Debug Console output"
+ name="show_script_name"
+ width="70"/>
<line_editor
border_style="line"
border_thickness="1"
diff --git a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml
index 6859201650..211cea75d8 100644
--- a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml
+++ b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml
@@ -22,6 +22,7 @@
follows="all"
layout="topleft"
sort_column="script_name"
+ multi_select="true"
name="scripts_list"
top_pad="10"
width="535">
diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml
index 5ab0177de6..6883ec1cd0 100644
--- a/indra/newview/skins/default/xui/en/floater_world_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_world_map.xml
@@ -109,7 +109,7 @@
</panel>
<panel
follows="right|top"
- height="126"
+ height="150"
top_pad="4"
width="238"
left="1"
@@ -285,6 +285,29 @@
by owner
</text>
+ <check_box
+ name="grid_coords_chk"
+ control_name="MapShowGridCoords"
+ layout="topleft"
+ follows="top|right"
+ top_pad="2"
+ left="3"
+ height="16"
+ width="22"
+ />
+ <text
+ name="grid_coords_label"
+ type="string"
+ layout="topleft"
+ follows="top|right"
+ top_delta="2"
+ left_pad="3"
+ height="16"
+ width="220"
+ halign="left"
+ length="1"
+ >Show grid map coordinates</text>
+
<button
follows="top|right"
height="22"
@@ -455,7 +478,7 @@
<panel
follows="right|top|bottom"
- height="330"
+ height="306"
top_pad="0"
width="238"
name="layout_panel_4">
@@ -576,7 +599,7 @@
draw_stripes="false"
bg_writeable_color="MouseGray"
follows="all"
- height="145"
+ height="121"
layout="topleft"
left="28"
name="search_results"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index a68d4517a6..39e4e3220e 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3865,6 +3865,13 @@ function="World.EnvPreset"
</menu_item_call>
<menu_item_call
enabled="true"
+ label="Edit Local Paintmap"
+ name="Edit Local Paintmap">
+ <menu_item_call.on_click
+ function="Advanced.TerrainEditLocalPaintMap" />
+ </menu_item_call>
+ <menu_item_call
+ enabled="true"
label="Delete Local Paintmap"
name="Delete Local Paintmap">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
index ca6e94397d..53551b2f79 100644
--- a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
+++ b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
@@ -156,6 +156,5 @@
mouse_opaque="true"
name="speaking_indicator"
tool_tip="Voice volume"
- visible="true"
width="20" />
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml
index 7902588598..e0b7f71321 100644
--- a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml
+++ b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml
@@ -91,7 +91,6 @@
left_pad="5"
mouse_opaque="true"
name="speaking_indicator"
- visible="false"
width="20" />
</layout_panel>
</layout_stack>
diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml
index 1a89d07cbb..48f6580d61 100644
--- a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml
@@ -34,7 +34,6 @@
mouse_opaque="true"
name="chat_zone_indicator"
top="6"
- visible="true"
width="20" />
<button
follows="right"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
index cb3e0e4b6a..a64b3eee36 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml
@@ -371,7 +371,7 @@
layout="topleft"
left="30"
min_val="0.5"
- max_val="1.5"
+ max_val="4.0"
name="RenderExposure"
show_text="true"
top_pad="14"
diff --git a/indra/newview/skins/default/xui/en/panel_region_general.xml b/indra/newview/skins/default/xui/en/panel_region_general.xml
index 6ef1b6e44a..47e1e669d1 100644
--- a/indra/newview/skins/default/xui/en/panel_region_general.xml
+++ b/indra/newview/skins/default/xui/en/panel_region_general.xml
@@ -33,6 +33,28 @@
unknown
</text>
<text
+ follows="right|top"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ top_delta="0"
+ right="-100"
+ name="estate_id_lbl"
+ width="80">
+ Estate ID:
+ </text>
+ <line_editor
+ follows="right|top"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ top_delta="0"
+ name="estate_id"
+ enabled="false"
+ right="-10"
+ initial_value="unknown"
+ width="85" />
+ <text
follows="left|top"
font="SansSerif"
height="20"
@@ -55,6 +77,39 @@
unknown
</text>
<text
+ follows="right|top"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ top_delta="0"
+ right="-100"
+ name="grid_position_lbl"
+ width="80">
+ Grid Position:
+ </text>
+ <line_editor
+ follows="right|top"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ right="-55"
+ name="grid_position_x"
+ enabled="false"
+ top_delta="0"
+ default_text="n/a"
+ width="40" />
+ <line_editor
+ follows="right|top"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ right="-10"
+ name="grid_position_y"
+ enabled="false"
+ top_delta="0"
+ default_text="n/a"
+ width="40" />
+ <text
follows="left|top"
font="SansSerif"
height="20"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index a0ee52cfd0..1a81e67e3d 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3721,6 +3721,10 @@ Please reinstall viewer from https://secondlife.com/support/downloads/ and cont
<string name="inventory_folder_offered-im">
Inventory folder '[ITEM_NAME]' offered
</string>
+ <string name="bot_warning">
+ You are chatting with a bot, [NAME]. Do not share any personal information.
+Learn more at https://second.life/scripted-agents.
+ </string>
<string name="share_alert">
Drag items from inventory here
</string>
diff --git a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml
index cf995e5833..180071a321 100644
--- a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml
+++ b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml
@@ -37,6 +37,5 @@
right="-3"
mouse_opaque="true"
name="speaking_indicator"
- visible="true"
- width="20" />
+ width="20" />
</conversation_view_participant>
diff --git a/indra/newview/skins/default/xui/en/widgets/output_monitor.xml b/indra/newview/skins/default/xui/en/widgets/output_monitor.xml
index 788c733ca8..10e39fa030 100644
--- a/indra/newview/skins/default/xui/en/widgets/output_monitor.xml
+++ b/indra/newview/skins/default/xui/en/widgets/output_monitor.xml
@@ -10,4 +10,5 @@
image_level_3="VoicePTT_Lvl3"
mouse_opaque="false"
name="output_monitor"
+ visible="false"
/>
diff --git a/indra/newview/skins/default/xui/en/widgets/person_view.xml b/indra/newview/skins/default/xui/en/widgets/person_view.xml
index 82dbdf0dab..e07f340be1 100644
--- a/indra/newview/skins/default/xui/en/widgets/person_view.xml
+++ b/indra/newview/skins/default/xui/en/widgets/person_view.xml
@@ -111,7 +111,6 @@
right="-3"
mouse_opaque="true"
name="speaking_indicator"
- visible="false"
width="20" />
</person_view>
diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml
index 9fcfc2daa5..97e86e994c 100644
--- a/indra/newview/skins/default/xui/es/strings.xml
+++ b/indra/newview/skins/default/xui/es/strings.xml
@@ -1584,6 +1584,10 @@ Si sigues recibiendo este mensaje, contacta con [SUPPORT_SITE].</string>
<string name="conference-title-incoming">Conferencia con [AGENT_NAME]</string>
<string name="inventory_item_offered-im">Ítem del inventario '[ITEM_NAME]' ofrecido</string>
<string name="inventory_folder_offered-im">Carpeta del inventario '[ITEM_NAME]' ofrecida</string>
+ <string name="bot_warning">
+Estás conversando con un bot, [NAME]. No compartas información personal.
+Más información en https://second.life/scripted-agents.
+ </string>
<string name="share_alert">Arrastra los ítems desde el invenbtario hasta aquí</string>
<string name="facebook_post_success">Has publicado en Facebook.</string>
<string name="flickr_post_success">Has publicado en Flickr.</string>
diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml
index 55f6209fe1..60916ef92b 100644
--- a/indra/newview/skins/default/xui/fr/strings.xml
+++ b/indra/newview/skins/default/xui/fr/strings.xml
@@ -1614,6 +1614,10 @@ Si ce message persiste, veuillez aller sur la page [SUPPORT_SITE].</string>
<string name="conference-title-incoming">Conférence avec [AGENT_NAME]</string>
<string name="inventory_item_offered-im">Objet de l’inventaire [ITEM_NAME] offert</string>
<string name="inventory_folder_offered-im">Dossier de l’inventaire [ITEM_NAME] offert</string>
+ <string name="bot_warning">
+Vous discutez avec un bot, [NAME]. Ne partagez pas d’informations personnelles.
+En savoir plus sur https://second.life/scripted-agents.
+ </string>
<string name="share_alert">Faire glisser les objets de l'inventaire ici</string>
<string name="facebook_post_success">Vous avez publié sur Facebook.</string>
<string name="flickr_post_success">Vous avez publié sur Flickr.</string>
diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml
index f77ab1062a..88708a2b4d 100644
--- a/indra/newview/skins/default/xui/it/strings.xml
+++ b/indra/newview/skins/default/xui/it/strings.xml
@@ -1586,6 +1586,10 @@ Se il messaggio persiste, contatta [SUPPORT_SITE].</string>
<string name="conference-title-incoming">Chiamata in conferenza con [AGENT_NAME]</string>
<string name="inventory_item_offered-im">Offerto oggetto di inventario &quot;[ITEM_NAME]&quot;</string>
<string name="inventory_folder_offered-im">Offerta cartella di inventario &quot;[ITEM_NAME]&quot;</string>
+ <string name="bot_warning">
+Stai parlando con un bot, [NAME]. Non condividere informazioni personali.
+Scopri di più su https://second.life/scripted-agents.
+ </string>
<string name="facebook_post_success">Hai pubblicato su Facebook.</string>
<string name="flickr_post_success">Hai pubblicato su Flickr.</string>
<string name="twitter_post_success">Hai pubblicato su Twitter.</string>
diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml
index 71f7c1a034..abc5932943 100644
--- a/indra/newview/skins/default/xui/ja/strings.xml
+++ b/indra/newview/skins/default/xui/ja/strings.xml
@@ -6149,6 +6149,10 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ�
<string name="inventory_folder_offered-im">
フォルダ「[ITEM_NAME]」がインベントリに送られてきました。
</string>
+ <string name="bot_warning">
+[NAME]とチャットしています。個人情報を共有しないでください。
+詳細は https://second.life/scripted-agents をご覧ください。
+ </string>
<string name="share_alert">
インベントリからここにアイテムをドラッグします。
</string>
diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml
index 8032443020..65b487e1b3 100644
--- a/indra/newview/skins/default/xui/pl/strings.xml
+++ b/indra/newview/skins/default/xui/pl/strings.xml
@@ -4412,6 +4412,10 @@ Jeżeli nadal otrzymujesz ten komunikat, skontaktuj się z [SUPPORT_SITE].
<string name="inventory_folder_offered-im">
Zaoferowano folder: '[ITEM_NAME]'
</string>
+ <string name="bot_warning">
+Rozmawiasz z botem [NAME]. Nie udostępniaj żadnych danych osobowych.
+Dowiedz się więcej na https://second.life/scripted-agents.
+ </string>
<string name="share_alert">
Przeciągaj tutaj rzeczy z Szafy
</string>
diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml
index 4ce1e6d2ec..9e66777b5a 100644
--- a/indra/newview/skins/default/xui/pt/strings.xml
+++ b/indra/newview/skins/default/xui/pt/strings.xml
@@ -1549,6 +1549,10 @@ If you continue to receive this message, contact the [SUPPORT_SITE].</string>
<string name="conference-title-incoming">Conversa com [AGENT_NAME]</string>
<string name="inventory_item_offered-im">Item do inventário '[ITEM_NAME]' oferecido</string>
<string name="inventory_folder_offered-im">Pasta do inventário '[ITEM_NAME]' oferecida</string>
+ <string name="bot_warning">
+Você está conversando com um bot, [NAME]. Não compartilhe informações pessoais.
+Saiba mais em https://second.life/scripted-agents.
+ </string>
<string name="facebook_post_success">Você publicou no Facebook.</string>
<string name="flickr_post_success">Você publicou no Flickr.</string>
<string name="twitter_post_success">Você publicou no Twitter.</string>
diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml
index 0079309ba2..174999ea36 100644
--- a/indra/newview/skins/default/xui/ru/strings.xml
+++ b/indra/newview/skins/default/xui/ru/strings.xml
@@ -4576,6 +4576,10 @@ support@secondlife.com.
<string name="inventory_folder_offered-im">
Предложена папка инвентаря «[ITEM_NAME]»
</string>
+ <string name="bot_warning">
+Вы общаетесь с ботом [NAME]. Не передавайте личные данные.
+Подробнее на https://second.life/scripted-agents.
+ </string>
<string name="share_alert">
Перетаскивайте вещи из инвентаря сюда
</string>
diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml
index fa2fd3a802..6c1f6506a2 100644
--- a/indra/newview/skins/default/xui/tr/strings.xml
+++ b/indra/newview/skins/default/xui/tr/strings.xml
@@ -4579,6 +4579,10 @@ Bu iletiyi almaya devam ederseniz, lütfen [SUPPORT_SITE] bölümüne başvurun.
<string name="inventory_folder_offered-im">
&quot;[ITEM_NAME]&quot; envanter klasörü sunuldu
</string>
+ <string name="bot_warning">
+Bir bot ile sohbet ediyorsunuz, [NAME]. Kişisel bilgilerinizi paylaşmayın.
+Daha fazla bilgi için: https://second.life/scripted-agents.
+ </string>
<string name="share_alert">
Envanterinizden buraya öğeler sürükleyin
</string>
diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml
index d053d2b30d..59ba2a7e19 100644
--- a/indra/newview/skins/default/xui/zh/strings.xml
+++ b/indra/newview/skins/default/xui/zh/strings.xml
@@ -4572,6 +4572,10 @@ http://secondlife.com/support 求助解決問題。
<string name="inventory_folder_offered-im">
收納區資料夾&apos;[ITEM_NAME]&apos;已向人提供
</string>
+ <string name="bot_warning">
+您正在与人工智能机器人 [NAME] 聊天。请勿分享任何个人信息。
+了解更多:https://second.life/scripted-agents。
+ </string>
<string name="share_alert">
將收納區物品拖曳到這裡
</string>
diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp
index f0a1b32eed..9687b68451 100644
--- a/indra/newview/tests/llluamanager_test.cpp
+++ b/indra/newview/tests/llluamanager_test.cpp
@@ -55,14 +55,14 @@ namespace tut
// indra/newview/tests/llluamanager_test.cpp =>
// indra/newview
auto newview{ fsyspath(__FILE__).parent_path().parent_path() };
- auto settings{ newview / "app_settings" / "settings.xml" };
+ fsyspath settings{ newview / "app_settings" / "settings.xml" };
// true suppresses implicit declare; implicit declare requires
// that every variable in settings.xml has a Comment, which many don't.
- gSavedSettings.loadFromFile(settings.u8string(), true);
+ gSavedSettings.loadFromFile(settings, true);
// At test time, since we don't have the app bundle available,
// extend LuaRequirePath to include the require directory in the
// source tree.
- auto require{ (newview / "scripts" / "lua" / "require").u8string() };
+ std::string require{ fsyspath(newview / "scripts" / "lua" / "require") };
auto paths{ gSavedSettings.getLLSD("LuaRequirePath") };
bool found = false;
for (const auto& path : llsd::inArray(paths))
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index efc90b8991..18ff6cf7b6 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -1032,6 +1032,12 @@ class Darwin_x86_64_Manifest(ViewerManifest):
):
dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
+ # SDL2
+ for libfile in (
+ 'libSDL2-2.0.dylib',
+ ):
+ dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile)
+
# our apps
executable_path = {}
embedded_apps = [ (os.path.join("llplugin", "slplugin"), "SLPlugin.app") ]