Merge branch 'release/2025.04' of https://github.com/secondlife/viewer

# Conflicts:
#	indra/newview/llagentlistener.cpp
#	indra/newview/llappearancemgr.cpp
#	indra/newview/llviewerparcelmgr.cpp
#	indra/newview/llvoavatar.cpp
#	indra/newview/llvoavatar.h
#	indra/newview/llvoavatarself.h
master
Ansariel 2025-04-04 12:46:39 +02:00
commit beb3ff3d36
33 changed files with 916 additions and 217 deletions

View File

@ -67,7 +67,6 @@ elseif (WINDOWS)
legacy_stdio_definitions
)
else()
include(CMakeFindFrameworks)
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
find_library(CARBON_LIBRARY Carbon)
find_library(COCOA_LIBRARY Cocoa)

View File

@ -553,6 +553,61 @@ LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter
} // namespace llsd
/*****************************************************************************
* toArray(), toMap()
*****************************************************************************/
namespace llsd
{
// For some T convertible to LLSD, given std::vector<T> myVec,
// toArray(myVec) returns an LLSD array whose entries correspond to the
// items in myVec.
// For some U convertible to LLSD, given function U xform(const T&),
// toArray(myVec, xform) returns an LLSD array whose every entry is
// xform(item) of the corresponding item in myVec.
// toArray() actually works with any container<C> usable with range
// 'for', not just std::vector.
// (Once we get C++20 we can use std::identity instead of this default lambda.)
template<typename C, typename FUNC>
LLSD toArray(const C& container, FUNC&& func = [](const auto& arg) { return arg; })
{
LLSD array;
for (const auto& item : container)
{
array.append(std::forward<FUNC>(func)(item));
}
return array;
}
// For some T convertible to LLSD, given std::map<std::string, T> myMap,
// toMap(myMap) returns an LLSD map whose entries correspond to the
// (key, value) pairs in myMap.
// For some U convertible to LLSD, given function
// std::pair<std::string, U> xform(const std::pair<std::string, T>&),
// toMap(myMap, xform) returns an LLSD map whose every entry is
// xform(pair) of the corresponding (key, value) pair in myMap.
// toMap() actually works with any container usable with range 'for', not
// just std::map. It need not even be an associative container, as long as
// you pass an xform function that returns std::pair<std::string, U>.
// (Once we get C++20 we can use std::identity instead of this default lambda.)
template<typename C, typename FUNC>
LLSD toMap(const C& container, FUNC&& func = [](const auto& arg) { return arg; })
{
LLSD map;
for (const auto& pair : container)
{
const auto& [key, value] = std::forward<FUNC>(func)(pair);
map[key] = value;
}
return map;
}
} // namespace llsd
/*****************************************************************************
* boost::hash<LLSD>
*****************************************************************************/
// Specialization for generating a hash value from an LLSD block.
namespace boost
{

View File

@ -1247,23 +1247,40 @@ S32 LLGLSLShader::disableTexture(S32 uniform, LLTexUnit::eTextureType mode)
llassert(false);
return -1;
}
S32 index = mTexture[uniform];
if (index != -1 && gGL.getTexUnit(index)->getCurrType() != LLTexUnit::TT_NONE)
if (index < 0)
{
if (gDebugGL && gGL.getTexUnit(index)->getCurrType() != mode)
// Invalid texture index - nothing to disable
return index;
}
LLTexUnit* tex_unit = gGL.getTexUnit(index);
if (!tex_unit)
{
// Invalid texture unit
LL_WARNS_ONCE("Shader") << "Invalid texture unit at index: " << index << LL_ENDL;
return index;
}
LLTexUnit::eTextureType curr_type = tex_unit->getCurrType();
if (curr_type != LLTexUnit::TT_NONE)
{
if (gDebugGL && curr_type != mode)
{
if (gDebugSession)
{
gFailLog << "Texture channel " << index << " texture type corrupted." << std::endl;
gFailLog << "Texture channel " << index << " texture type corrupted. Expected: " << mode << ", Found: " << curr_type << std::endl;
ll_fail("LLGLSLShader::disableTexture failed");
}
else
{
LL_ERRS() << "Texture channel " << index << " texture type corrupted." << LL_ENDL;
LL_ERRS() << "Texture channel " << index << " texture type corrupted. Expected: " << mode << ", Found: " << curr_type << LL_ENDL;
}
}
gGL.getTexUnit(index)->disable();
tex_unit->disable();
}
return index;
}

View File

@ -191,7 +191,6 @@ endif (llwindow_HEADER_FILES)
target_include_directories(llwindow INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
if (DARWIN)
include(CMakeFindFrameworks)
find_library(CARBON_LIBRARY Carbon)
target_link_libraries(llwindow ${CARBON_LIBRARY})
endif (DARWIN)

View File

@ -210,6 +210,7 @@ set(viewer_SOURCE_FILES
llagentwearables.cpp
llanimstatelabels.cpp
llappcorehttp.cpp
llappearancelistener.cpp
llappearancemgr.cpp
llappviewer.cpp
llappviewerlistener.cpp
@ -1028,6 +1029,7 @@ set(viewer_HEADER_FILES
llanimstatelabels.h
llappcorehttp.h
llappearance.h
llappearancelistener.h
llappearancemgr.h
llappviewer.h
llappviewerlistener.h

View File

@ -31,25 +31,17 @@
#include "llviewerprecompiledheaders.h"
#include "fsnearbychatbarlistener.h"
// <FS:Zi> Remove floating chat bar
// #include "llnearbychatbar.h"
#include "fsnearbychathub.h"
// </FS:Zi>
#include "llagent.h"
#include "llchat.h"
#include "llviewercontrol.h"
#include "stringize.h"
static const F32 CHAT_THROTTLE_PERIOD = 1.f;
// <FS:Zi> Remove floating chat bar
// LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChatBar & chatbar)
// : LLEventAPI("LLChatBar",
// "LLChatBar listener to (e.g.) sendChat, etc."),
// mChatbar(chatbar)
FSNearbyChatBarListener::FSNearbyChatBarListener()
: LLEventAPI("LLChatBar",
"LLChatBar listener to (e.g.) sendChat, etc.")
// </FS:Zi>
FSNearbyChatBarListener::FSNearbyChatBarListener() :
LLEventAPI("LLChatBar", "LLChatBar listener to (e.g.) sendChat, etc.")
{
add("sendChat",
"Send chat to the simulator:\n"
@ -61,8 +53,17 @@ FSNearbyChatBarListener::FSNearbyChatBarListener()
// "sendChat" command
void FSNearbyChatBarListener::sendChat(LLSD const & chat_data) const
void FSNearbyChatBarListener::sendChat(LLSD const& chat_data)
{
F64 cur_time = LLTimer::getElapsedSeconds();
if (cur_time < mLastThrottleTime + CHAT_THROTTLE_PERIOD)
{
LL_DEBUGS("FSNearbyChatBarListener") << "'sendChat' was throttled" << LL_ENDL;
return;
}
mLastThrottleTime = cur_time;
// Extract the data
std::string chat_text = chat_data["message"].asString();
@ -91,20 +92,12 @@ void FSNearbyChatBarListener::sendChat(LLSD const & chat_data) const
}
// Have to prepend /42 style channel numbers
std::string chat_to_send;
if (channel == 0)
if (channel)
{
chat_to_send = chat_text;
}
else
{
chat_to_send += "/";
chat_to_send += chat_data["channel"].asString();
chat_to_send += " ";
chat_to_send += chat_text;
chat_text = stringize("/", chat_data["channel"].asString(), " ", chat_text);
}
// Send it as if it was typed in
FSNearbyChat::instance().sendChatFromViewer(chat_to_send, type_o_chat, (channel == 0) && gSavedSettings.getBOOL("PlayChatAnim"));
FSNearbyChat::instance().sendChatFromViewer(chat_text, type_o_chat, channel == 0 && gSavedSettings.getBOOL("PlayChatAnim"));
}

View File

@ -35,19 +35,16 @@
#include "lleventapi.h"
class LLSD;
// class LLNearbyChatBar; // <FS:Zi> Remove floating chat bar
class FSNearbyChatBarListener : public LLEventAPI
{
public:
// <FS:Zi> Remove floating chat bar
// LLNearbyChatBarListener(LLNearbyChatBar & chatbar);
FSNearbyChatBarListener();
// </FS:Zi>
private:
void sendChat(LLSD const & chat_data) const;
void sendChat(LLSD const& chat_data);
F64 mLastThrottleTime{ 0.0 };
// LLNearbyChatBar & mChatbar;
};

View File

@ -2,11 +2,11 @@
* @file groupchatlistener.cpp
* @author Nat Goodspeed
* @date 2011-04-11
* @brief Implementation for groupchatlistener.
* @brief Implementation for LLGroupChatListener.
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
* 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
@ -34,43 +34,69 @@
// std headers
// external library headers
// other Linden headers
#include "llchat.h"
#include "llgroupactions.h"
#include "llimview.h"
namespace {
void startIm_wrapper(LLSD const & event)
{
LLUUID session_id = LLGroupActions::startIM(event["id"].asUUID());
sendReply(LLSDMap("session_id", LLSD(session_id)), event);
}
void send_message_wrapper(const std::string& text, const LLUUID& session_id, const LLUUID& group_id)
{
LLIMModel::sendMessage(text, session_id, group_id, IM_SESSION_GROUP_START);
}
}
GroupChatListener::GroupChatListener():
LLGroupChatListener::LLGroupChatListener():
LLEventAPI("GroupChat",
"API to enter, leave, send and intercept group chat messages")
{
add("startIM",
"Enter a group chat in group with UUID [\"id\"]\n"
add("startGroupChat",
"Enter a group chat in group with UUID [\"group_id\"]\n"
"Assumes the logged-in agent is already a member of this group.",
&startIm_wrapper);
add("endIM",
"Leave a group chat in group with UUID [\"id\"]\n"
&LLGroupChatListener::startGroupChat,
llsd::map("group_id", LLSD()));
add("leaveGroupChat",
"Leave a group chat in group with UUID [\"group_id\"]\n"
"Assumes a prior successful startIM request.",
&LLGroupActions::endIM,
llsd::array("id"));
add("sendIM",
"send a groupchat IM",
&send_message_wrapper,
llsd::array("text", "session_id", "group_id"));
&LLGroupChatListener::leaveGroupChat,
llsd::map("group_id", LLSD()));
add("sendGroupIM",
"send a [\"message\"] to group with UUID [\"group_id\"]",
&LLGroupChatListener::sendGroupIM,
llsd::map("message", LLSD(), "group_id", LLSD()));
}
bool is_in_group(LLEventAPI::Response &response, const LLSD &data)
{
if (!LLGroupActions::isInGroup(data["group_id"]))
{
response.error(stringize("You are not the member of the group:", std::quoted(data["group_id"].asString())));
return false;
}
return true;
}
void LLGroupChatListener::startGroupChat(LLSD const &data)
{
Response response(LLSD(), data);
if (!is_in_group(response, data))
{
return;
}
if (LLGroupActions::startIM(data["group_id"]).isNull())
{
return response.error(stringize("Failed to start group chat session ", std::quoted(data["group_id"].asString())));
}
}
void LLGroupChatListener::leaveGroupChat(LLSD const &data)
{
Response response(LLSD(), data);
if (is_in_group(response, data))
{
LLGroupActions::endIM(data["group_id"].asUUID());
}
}
void LLGroupChatListener::sendGroupIM(LLSD const &data)
{
Response response(LLSD(), data);
if (!is_in_group(response, data))
{
return;
}
LLUUID group_id(data["group_id"]);
LLIMModel::sendMessage(data["message"], gIMMgr->computeSessionID(IM_SESSION_GROUP_START, group_id), group_id, IM_SESSION_SEND);
}
/*
static void sendMessage(const std::string& utf8_text, const LLUUID& im_session_id,
const LLUUID& other_participant_id, EInstantMessage dialog);
*/

View File

@ -26,15 +26,20 @@
* $/LicenseInfo$
*/
#if ! defined(LL_GROUPCHATLISTENER_H)
#define LL_GROUPCHATLISTENER_H
#if ! defined(LL_LLGROUPCHATLISTENER_H)
#define LL_LLGROUPCHATLISTENER_H
#include "lleventapi.h"
class GroupChatListener: public LLEventAPI
class LLGroupChatListener: public LLEventAPI
{
public:
GroupChatListener();
LLGroupChatListener();
private:
void startGroupChat(LLSD const &data);
void leaveGroupChat(LLSD const &data);
void sendGroupIM(LLSD const &data);
};
#endif /* ! defined(LL_GROUPCHATLISTENER_H) */
#endif /* ! defined(LL_LLGROUPCHATLISTENER_H) */

View File

@ -31,19 +31,25 @@
#include "llagentlistener.h"
#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"
#include "llviewerregion.h"
#include "llvoavatarself.h"
#include "llsdutil.h"
#include "llsdutil_math.h"
#include "lltoolgrab.h"
#include "llhudeffectlookat.h"
#include "llagentcamera.h"
#include "llviewercamera.h"
// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a)
#include "rlvactions.h"
#include "rlvhandler.h"
@ -74,13 +80,6 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
add("resetAxes",
"Set the agent to a fixed orientation (optionally specify [\"lookat\"] = array of [x, y, z])",
&LLAgentListener::resetAxes);
add("getAxes",
"Obsolete - use getPosition instead\n"
"Send information about the agent's orientation on [\"reply\"]:\n"
"[\"euler\"]: map of {roll, pitch, yaw}\n"
"[\"quat\"]: array of [x, y, z, w] quaternion values",
&LLAgentListener::getAxes,
LLSDMap("reply", LLSD()));
add("getPosition",
"Send information about the agent's position and orientation on [\"reply\"]:\n"
"[\"region\"]: array of region {x, y, z} position\n"
@ -92,33 +91,34 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
add("startAutoPilot",
"Start the autopilot system using the following parameters:\n"
"[\"target_global\"]: array of target global {x, y, z} position\n"
"[\"stop_distance\"]: target maxiumum distance from target [default: autopilot guess]\n"
"[\"stop_distance\"]: maximum stop distance from target [default: autopilot guess]\n"
"[\"target_rotation\"]: array of [x, y, z, w] quaternion values [default: no target]\n"
"[\"rotation_threshold\"]: target maximum angle from target facing rotation [default: 0.03 radians]\n"
"[\"behavior_name\"]: name of the autopilot behavior [default: \"\"]"
"[\"allow_flying\"]: allow flying during autopilot [default: True]",
//"[\"callback_pump\"]: pump to send success/failure and callback data to [default: none]\n"
//"[\"callback_data\"]: data to send back during a callback [default: none]",
&LLAgentListener::startAutoPilot);
"[\"behavior_name\"]: name of the autopilot behavior [default: \"\"]\n"
"[\"allow_flying\"]: allow flying during autopilot [default: True]\n"
"event with [\"success\"] flag is sent to 'LLAutopilot' event pump, when auto pilot is terminated",
&LLAgentListener::startAutoPilot,
llsd::map("target_global", LLSD()));
add("getAutoPilot",
"Send information about current state of the autopilot system to [\"reply\"]:\n"
"[\"enabled\"]: boolean indicating whether or not autopilot is enabled\n"
"[\"target_global\"]: array of target global {x, y, z} position\n"
"[\"leader_id\"]: uuid of target autopilot is following\n"
"[\"stop_distance\"]: target maximum distance from target\n"
"[\"stop_distance\"]: maximum stop distance from target\n"
"[\"target_distance\"]: last known distance from target\n"
"[\"use_rotation\"]: boolean indicating if autopilot has a target facing rotation\n"
"[\"target_facing\"]: array of {x, y} target direction to face\n"
"[\"rotation_threshold\"]: target maximum angle from target facing rotation\n"
"[\"behavior_name\"]: name of the autopilot behavior",
&LLAgentListener::getAutoPilot,
LLSDMap("reply", LLSD()));
llsd::map("reply", LLSD()));
add("startFollowPilot",
"[\"leader_id\"]: uuid of target to follow using the autopilot system (optional with avatar_name)\n"
"[\"avatar_name\"]: avatar name to follow using the autopilot system (optional with leader_id)\n"
"[\"allow_flying\"]: allow flying during autopilot [default: True]\n"
"[\"stop_distance\"]: target maxiumum distance from target [default: autopilot guess]",
&LLAgentListener::startFollowPilot);
"[\"stop_distance\"]: maximum stop distance from target [default: autopilot guess]",
&LLAgentListener::startFollowPilot,
llsd::map("reply", LLSD()));
add("setAutoPilotTarget",
"Update target for currently running autopilot:\n"
"[\"target_global\"]: array of target global {x, y, z} position",
@ -143,6 +143,69 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
"[\"contrib\"]: user's land contribution to this group\n",
&LLAgentListener::getGroups,
LLSDMap("reply", LLSD()));
//camera params are similar to LSL, see https://wiki.secondlife.com/wiki/LlSetCameraParams
add("setCameraParams",
"Set Follow camera params, and then activate it:\n"
"[\"camera_pos\"]: vector3, camera position in region coordinates\n"
"[\"focus_pos\"]: vector3, what the camera is aimed at (in region coordinates)\n"
"[\"focus_offset\"]: vector3, adjusts the camera focus position relative to the target, default is (1, 0, 0)\n"
"[\"distance\"]: float (meters), distance the camera wants to be from its target, default is 3\n"
"[\"focus_threshold\"]: float (meters), sets the radius of a sphere around the camera's target position within which its focus is not affected by target motion, default is 1\n"
"[\"camera_threshold\"]: float (meters), sets the radius of a sphere around the camera's ideal position within which it is not affected by target motion, default is 1\n"
"[\"focus_lag\"]: float (seconds), how much the camera lags as it tries to aim towards the target, default is 0.1\n"
"[\"camera_lag\"]: float (seconds), how much the camera lags as it tries to move towards its 'ideal' position, default is 0.1\n"
"[\"camera_pitch\"]: float (degrees), adjusts the angular amount that the camera aims straight ahead vs. straight down, maintaining the same distance, default is 0\n"
"[\"behindness_angle\"]: float (degrees), sets the angle in degrees within which the camera is not constrained by changes in target rotation, default is 10\n"
"[\"behindness_lag\"]: float (seconds), sets how strongly the camera is forced to stay behind the target if outside of behindness angle, default is 0\n"
"[\"camera_locked\"]: bool, locks the camera position so it will not move\n"
"[\"focus_locked\"]: bool, locks the camera focus so it will not move",
&LLAgentListener::setFollowCamParams);
add("setFollowCamActive",
"Turns on or off scripted control of the camera using boolean [\"active\"]",
&LLAgentListener::setFollowCamActive,
llsd::map("active", LLSD()));
add("removeCameraParams",
"Reset Follow camera params",
&LLAgentListener::removeFollowCamParams);
add("playAnimation",
"Play [\"item_id\"] animation locally (by default) or [\"inworld\"] (when set to true)",
&LLAgentListener::playAnimation,
llsd::map("item_id", LLSD(), "reply", LLSD()));
add("stopAnimation",
"Stop playing [\"item_id\"] animation",
&LLAgentListener::stopAnimation,
llsd::map("item_id", LLSD(), "reply", LLSD()));
add("getAnimationInfo",
"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
@ -173,7 +236,7 @@ void LLAgentListener::requestSit(LLSD const & event_data) const
//mAgent.getAvatarObject()->sitOnObject();
// shamelessly ripped from llviewermenu.cpp:handle_sit_or_stand()
// *TODO - find a permanent place to share this code properly.
Response response(LLSD(), event_data);
LLViewerObject *object = NULL;
if (event_data.has("obj_uuid"))
{
@ -182,7 +245,13 @@ void LLAgentListener::requestSit(LLSD const & event_data) const
else if (event_data.has("position"))
{
LLVector3 target_position = ll_vector3_from_sd(event_data["position"]);
object = findObjectClosestTo(target_position);
object = findObjectClosestTo(target_position, true);
}
else
{
//just sit on the ground
mAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
return;
}
// [RLVa:KB] - Checked: 2010-03-06 (RLVa-1.2.0c) | Modified: RLVa-1.1.0j
@ -219,8 +288,7 @@ void LLAgentListener::requestSit(LLSD const & event_data) const
}
else
{
LL_WARNS() << "LLAgent requestSit could not find the sit target: "
<< event_data << LL_ENDL;
response.error("requestSit could not find the sit target");
}
}
@ -238,7 +306,7 @@ void LLAgentListener::requestStand(LLSD const & event_data) const
}
LLViewerObject * LLAgentListener::findObjectClosestTo( const LLVector3 & position ) const
LLViewerObject * LLAgentListener::findObjectClosestTo(const LLVector3 & position, bool sit_target) const
{
LLViewerObject *object = NULL;
@ -249,8 +317,13 @@ LLViewerObject * LLAgentListener::findObjectClosestTo( const LLVector3 & positio
while (cur_index < num_objects)
{
LLViewerObject * cur_object = gObjectList.getObject(cur_index++);
if (cur_object)
{ // Calculate distance from the target position
if (cur_object && !cur_object->isAttachment())
{
if(sit_target && (cur_object->getPCode() != LL_PCODE_VOLUME))
{
continue;
}
// Calculate distance from the target position
LLVector3 target_diff = cur_object->getPositionRegion() - position;
F32 distance_to_target = target_diff.length();
if (distance_to_target < min_distance)
@ -329,22 +402,6 @@ void LLAgentListener::resetAxes(const LLSD& event_data) const
}
}
void LLAgentListener::getAxes(const LLSD& event_data) const
{
LLQuaternion quat(mAgent.getQuat());
F32 roll, pitch, yaw;
quat.getEulerAngles(&roll, &pitch, &yaw);
// The official query API for LLQuaternion's [x, y, z, w] values is its
// public member mQ...
LLSD reply = LLSD::emptyMap();
reply["quat"] = llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ));
reply["euler"] = LLSD::emptyMap();
reply["euler"]["roll"] = roll;
reply["euler"]["pitch"] = pitch;
reply["euler"]["yaw"] = yaw;
sendReply(reply, event_data);
}
void LLAgentListener::getPosition(const LLSD& event_data) const
{
F32 roll, pitch, yaw;
@ -366,14 +423,13 @@ void LLAgentListener::getPosition(const LLSD& event_data) const
void LLAgentListener::startAutoPilot(LLSD const & event_data)
{
LLQuaternion target_rotation_value;
LLQuaternion* target_rotation = NULL;
if (event_data.has("target_rotation"))
{
target_rotation_value = ll_quaternion_from_sd(event_data["target_rotation"]);
LLQuaternion target_rotation_value = ll_quaternion_from_sd(event_data["target_rotation"]);
target_rotation = &target_rotation_value;
}
// *TODO: Use callback_pump and callback_data
F32 rotation_threshold = 0.03f;
if (event_data.has("rotation_threshold"))
{
@ -393,13 +449,24 @@ void LLAgentListener::startAutoPilot(LLSD const & event_data)
stop_distance = (F32)event_data["stop_distance"].asReal();
}
std::string behavior_name = LLCoros::getName();
if (event_data.has("behavior_name"))
{
behavior_name = event_data["behavior_name"].asString();
}
// Clear follow target, this is doing a path
mFollowTarget.setNull();
auto finish_cb = [](bool success, void*)
{
LLEventPumps::instance().obtain("LLAutopilot").post(llsd::map("success", success));
};
mAgent.startAutoPilotGlobal(ll_vector3d_from_sd(event_data["target_global"]),
event_data["behavior_name"],
behavior_name,
target_rotation,
NULL, NULL,
finish_cb, NULL,
stop_distance,
rotation_threshold,
allow_flying);
@ -407,7 +474,7 @@ void LLAgentListener::startAutoPilot(LLSD const & event_data)
void LLAgentListener::getAutoPilot(const LLSD& event_data) const
{
LLSD reply = LLSD::emptyMap();
Response reply(LLSD(), event_data);
LLSD::Boolean enabled = mAgent.getAutoPilot();
reply["enabled"] = enabled;
@ -436,12 +503,11 @@ void LLAgentListener::getAutoPilot(const LLSD& event_data) const
reply["rotation_threshold"] = mAgent.getAutoPilotRotationThreshold();
reply["behavior_name"] = mAgent.getAutoPilotBehaviorName();
reply["fly"] = (LLSD::Boolean) mAgent.getFlying();
sendReply(reply, event_data);
}
void LLAgentListener::startFollowPilot(LLSD const & event_data)
{
Response response(LLSD(), event_data);
LLUUID target_id;
bool allow_flying = true;
@ -475,6 +541,10 @@ void LLAgentListener::startFollowPilot(LLSD const & event_data)
}
}
}
else
{
return response.error("'leader_id' or 'avatar_name' should be specified");
}
F32 stop_distance = 0.f;
if (event_data.has("stop_distance"))
@ -482,13 +552,16 @@ void LLAgentListener::startFollowPilot(LLSD const & event_data)
stop_distance = (F32)event_data["stop_distance"].asReal();
}
if (target_id.notNull())
if (!gObjectList.findObject(target_id))
{
mAgent.setFlying(allow_flying);
mFollowTarget = target_id; // Save follow target so we can report distance later
mAgent.startFollowPilot(target_id, allow_flying, stop_distance);
std::string target_info = event_data.has("leader_id") ? event_data["leader_id"] : event_data["avatar_name"];
return response.error(stringize("Target ", std::quoted(target_info), " was not found"));
}
mAgent.setFlying(allow_flying);
mFollowTarget = target_id; // Save follow target so we can report distance later
mAgent.startFollowPilot(target_id, allow_flying, stop_distance);
}
void LLAgentListener::setAutoPilotTarget(LLSD const & event_data) const
@ -552,3 +625,209 @@ void LLAgentListener::getGroups(const LLSD& event) const
}
sendReply(LLSDMap("groups", reply), event);
}
/*----------------------------- camera control -----------------------------*/
// specialize LLSDParam to support (const LLVector3&) arguments -- this
// wouldn't even be necessary except that the relevant LLVector3 constructor
// is explicitly explicit
template <>
class LLSDParam<const LLVector3&>: public LLSDParamBase
{
public:
LLSDParam(const LLSD& value): value(LLVector3(value)) {}
operator const LLVector3&() const { return value; }
private:
LLVector3 value;
};
// accept any of a number of similar LLFollowCamMgr methods with different
// argument types, and return a wrapper lambda that accepts LLSD and converts
// to the target argument type
template <typename T>
auto wrap(void (LLFollowCamMgr::*method)(const LLUUID& source, T arg))
{
return [method](LLFollowCamMgr& followcam, const LLUUID& source, const LLSD& arg)
{ (followcam.*method)(source, LLSDParam<T>(arg)); };
}
// table of supported LLFollowCamMgr methods,
// with the corresponding setFollowCamParams() argument keys
static std::pair<std::string, std::function<void(LLFollowCamMgr&, const LLUUID&, const LLSD&)>>
cam_params[] =
{
{ "camera_pos", wrap(&LLFollowCamMgr::setPosition) },
{ "focus_pos", wrap(&LLFollowCamMgr::setFocus) },
{ "focus_offset", wrap(&LLFollowCamMgr::setFocusOffset) },
{ "camera_locked", wrap(&LLFollowCamMgr::setPositionLocked) },
{ "focus_locked", wrap(&LLFollowCamMgr::setFocusLocked) },
{ "distance", wrap(&LLFollowCamMgr::setDistance) },
{ "focus_threshold", wrap(&LLFollowCamMgr::setFocusThreshold) },
{ "camera_threshold", wrap(&LLFollowCamMgr::setPositionThreshold) },
{ "focus_lag", wrap(&LLFollowCamMgr::setFocusLag) },
{ "camera_lag", wrap(&LLFollowCamMgr::setPositionLag) },
{ "camera_pitch", wrap(&LLFollowCamMgr::setPitch) },
{ "behindness_lag", wrap(&LLFollowCamMgr::setBehindnessLag) },
{ "behindness_angle", wrap(&LLFollowCamMgr::setBehindnessAngle) },
};
void LLAgentListener::setFollowCamParams(const LLSD& event) const
{
auto& followcam{ LLFollowCamMgr::instance() };
for (const auto& pair : cam_params)
{
if (event.has(pair.first))
{
pair.second(followcam, gAgentID, event[pair.first]);
}
}
followcam.setCameraActive(gAgentID, true);
}
void LLAgentListener::setFollowCamActive(LLSD const & event) const
{
LLFollowCamMgr::getInstance()->setCameraActive(gAgentID, event["active"]);
}
void LLAgentListener::removeFollowCamParams(LLSD const & event) const
{
LLFollowCamMgr::getInstance()->removeFollowCamParams(gAgentID);
}
LLViewerInventoryItem* get_anim_item(LLEventAPI::Response &response, const LLSD &event_data)
{
LLViewerInventoryItem* item = gInventory.getItem(event_data["item_id"].asUUID());
if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION))
{
response.error(stringize("Animation item ", std::quoted(event_data["item_id"].asString()), " was not found"));
return NULL;
}
return item;
}
void LLAgentListener::playAnimation(LLSD const &event_data)
{
Response response(LLSD(), event_data);
if (LLViewerInventoryItem* item = get_anim_item(response, event_data))
{
if (event_data["inworld"].asBoolean())
{
mAgent.sendAnimationRequest(item->getAssetUUID(), ANIM_REQUEST_START);
}
else
{
gAgentAvatarp->startMotion(item->getAssetUUID());
}
}
}
void LLAgentListener::stopAnimation(LLSD const &event_data)
{
Response response(LLSD(), event_data);
if (LLViewerInventoryItem* item = get_anim_item(response, event_data))
{
gAgentAvatarp->stopMotion(item->getAssetUUID());
mAgent.sendAnimationRequest(item->getAssetUUID(), ANIM_REQUEST_STOP);
}
}
void LLAgentListener::getAnimationInfo(LLSD const &event_data)
{
Response response(LLSD(), event_data);
if (LLViewerInventoryItem* item = get_anim_item(response, event_data))
{
// if motion exists, will return existing one
LLMotion* motion = gAgentAvatarp->createMotion(item->getAssetUUID());
response["anim_info"] = llsd::map("duration", motion->getDuration(),
"is_loop", motion->getLoop(),
"num_joints", motion->getNumJointMotions(),
"asset_id", item->getAssetUUID(),
"priority", motion->getPriority());
}
}
void LLAgentListener::getID(LLSD const& event_data)
{
Response response(llsd::map("id", gAgentID), event_data);
}
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);
F32 radius = get_search_radius(event_data);
LLVector3d agent_pos = gAgent.getPositionGlobal();
for (LLCharacter* character : LLCharacter::sInstances)
{
LLVOAvatar* avatar = (LLVOAvatar*)character;
if (avatar && !avatar->isDead() && !avatar->isControlAvatar() && !avatar->isSelf())
{
if ((dist_vec_squared(avatar->getPositionGlobal(), agent_pos) <= radius))
{
LLAvatarName av_name;
LLAvatarNameCache::get(avatar->getID(), &av_name);
LLVector3 region_pos = avatar->getCharacterPosition();
response["result"].append(llsd::map("id", avatar->getID(), "global_pos", ll_sd_from_vector3d(avatar->getPosGlobalFromAgent(region_pos)),
"region_pos", ll_sd_from_vector3(region_pos), "name", av_name.getUserName(), "region_id", avatar->getRegion()->getRegionID()));
}
}
}
}
void LLAgentListener::getNearbyObjectsList(LLSD const& event_data)
{
Response response(LLSD(), event_data);
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))
{
response["result"].append(llsd::map("id", object->getID(), "global_pos", ll_sd_from_vector3d(object->getPositionGlobal()), "region_pos",
ll_sd_from_vector3(object->getPositionRegion()), "region_id", object->getRegion()->getRegionID()));
}
}
}
}
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;
}

View File

@ -48,7 +48,6 @@ private:
void requestStand(LLSD const & event_data) const;
void requestTouch(LLSD const & event_data) const;
void resetAxes(const LLSD& event_data) const;
void getAxes(const LLSD& event_data) const;
void getGroups(const LLSD& event) const;
void getPosition(const LLSD& event_data) const;
void startAutoPilot(const LLSD& event_data);
@ -58,7 +57,20 @@ private:
void stopAutoPilot(const LLSD& event_data) const;
void lookAt(LLSD const & event_data) const;
LLViewerObject * findObjectClosestTo( const LLVector3 & position ) const;
void setFollowCamParams(LLSD const & event_data) const;
void setFollowCamActive(LLSD const & event_data) const;
void removeFollowCamParams(LLSD const & event_data) const;
void playAnimation(LLSD const &event_data);
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:
LLAgent & mAgent;

View File

@ -0,0 +1,158 @@
/**
* @file llappearancelistener.cpp
*
* $LicenseInfo:firstyear=2024&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$
*/
#include "llviewerprecompiledheaders.h"
#include "llappearancelistener.h"
#include "llappearancemgr.h"
#include "llinventoryfunctions.h"
#include "lltransutil.h"
#include "llwearableitemslist.h"
#include "stringize.h"
LLAppearanceListener::LLAppearanceListener()
: LLEventAPI("LLAppearance",
"API to wear a specified outfit and wear/remove individual items")
{
add("wearOutfit",
"Wear outfit by folder id: [\"folder_id\"] OR by folder name: [\"folder_name\"]\n"
"When [\"append\"] is true, outfit will be added to COF\n"
"otherwise it will replace current oufit",
&LLAppearanceListener::wearOutfit);
add("wearItems",
"Wear items by id: [items_id]",
&LLAppearanceListener::wearItems,
llsd::map("items_id", LLSD(), "replace", LLSD()));
add("detachItems",
"Detach items by id: [items_id]",
&LLAppearanceListener::detachItems,
llsd::map("items_id", LLSD()));
add("getOutfitsList",
"Return the table with Outfits info(id and name)",
&LLAppearanceListener::getOutfitsList);
add("getOutfitItems",
"Return the table of items with info(id : name, wearable_type, is_worn) inside specified outfit folder",
&LLAppearanceListener::getOutfitItems);
}
void LLAppearanceListener::wearOutfit(LLSD const &data)
{
Response response(LLSD(), data);
if (!data.has("folder_id") && !data.has("folder_name"))
{
return response.error("Either [folder_id] or [folder_name] is required");
}
bool append = data.has("append") ? data["append"].asBoolean() : false;
if (!LLAppearanceMgr::instance().wearOutfit(data, append))
{
response.error("Failed to wear outfit");
}
}
void LLAppearanceListener::wearItems(LLSD const &data)
{
const LLSD& items_id{ data["items_id"] };
uuid_vec_t ids;
if (!items_id.isArray())
{
ids.push_back(items_id.asUUID());
}
else // array
{
for (const auto& id : llsd::inArray(items_id))
{
ids.push_back(id);
}
}
LLAppearanceMgr::instance().wearItemsOnAvatar(ids, true, data["replace"].asBoolean());
}
void LLAppearanceListener::detachItems(LLSD const &data)
{
const LLSD& items_id{ data["items_id"] };
uuid_vec_t ids;
if (!items_id.isArray())
{
ids.push_back(items_id.asUUID());
}
else // array
{
for (const auto& id : llsd::inArray(items_id))
{
ids.push_back(id);
}
}
LLAppearanceMgr::instance().removeItemsFromAvatar(ids);
}
void LLAppearanceListener::getOutfitsList(LLSD const &data)
{
Response response(LLSD(), data);
const LLUUID outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
LLIsType is_category(LLAssetType::AT_CATEGORY);
gInventory.collectDescendentsIf(outfits_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, is_category);
response["outfits"] = llsd::toMap(cat_array,
[](const LLPointer<LLViewerInventoryCategory> &cat)
{ return std::make_pair(cat->getUUID().asString(), cat->getName()); });
}
void LLAppearanceListener::getOutfitItems(LLSD const &data)
{
Response response(LLSD(), data);
LLUUID outfit_id(data["outfit_id"].asUUID());
LLViewerInventoryCategory *cat = gInventory.getCategory(outfit_id);
if (!cat || cat->getPreferredType() != LLFolderType::FT_OUTFIT)
{
return response.error(stringize("Couldn't find outfit ", outfit_id.asString()));
}
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
LLFindOutfitItems collector = LLFindOutfitItems();
gInventory.collectDescendentsIf(outfit_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector);
response["items"] = llsd::toMap(item_array,
[](const LLPointer<LLViewerInventoryItem> &it)
{
return std::make_pair(
it->getUUID().asString(),
llsd::map(
"name", it->getName(),
"wearable_type", LLWearableType::getInstance()->getTypeName(it->isWearableType() ? it->getWearableType() : LLWearableType::WT_NONE),
"is_worn", get_is_item_worn(it)));
});
}

View File

@ -0,0 +1,46 @@
/**
* @file llappearancelistener.h
*
* $LicenseInfo:firstyear=2024&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$
*/
#ifndef LL_LLAPPEARANCELISTENER_H
#define LL_LLAPPEARANCELISTENER_H
#include "lleventapi.h"
class LLAppearanceListener : public LLEventAPI
{
public:
LLAppearanceListener();
private:
void wearOutfit(LLSD const &data);
void wearItems(LLSD const &data);
void detachItems(LLSD const &data);
void getOutfitsList(LLSD const &data);
void getOutfitItems(LLSD const &data);
};
#endif // LL_LLAPPEARANCELISTENER_H

View File

@ -31,6 +31,7 @@
#include "llagent.h"
#include "llagentcamera.h"
#include "llagentwearables.h"
#include "llappearancelistener.h"
#include "llappearancemgr.h"
#include "llattachmentsmgr.h"
#include "llcommandhandler.h"
@ -75,6 +76,8 @@
#include "fslslbridge.h"
LLAppearanceListener sAppearanceListener;
namespace
{
const S32 BAKE_RETRY_MAX_COUNT = 5;
@ -5227,6 +5230,11 @@ bool wear_category(const LLSD& query_map, bool append)
return false;
}
bool LLAppearanceMgr::wearOutfit(const LLSD& query_map, bool append)
{
return wear_category(query_map, append);
}
class LLWearFolderHandler : public LLCommandHandler
{
public:

View File

@ -65,6 +65,7 @@ public:
void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append);
void wearCategoryFinal(const LLUUID& cat_id, bool copy_items, bool append);
void wearOutfitByName(const std::string& name);
bool wearOutfit(const LLSD& query_map, bool append = false);
void changeOutfit(bool proceed, const LLUUID& category, bool append);
void replaceCurrentOutfit(const LLUUID& new_outfit);
void renameOutfit(const LLUUID& outfit_id);

View File

@ -54,6 +54,7 @@
#include "llfirstuse.h"
#include "llfloaterimnearbychat.h"
#include "llfloaterimnearbychatlistener.h"
#include "llagent.h" // gAgent
#include "llgesturemgr.h"
#include "llmultigesture.h"
@ -78,6 +79,8 @@
S32 LLFloaterIMNearbyChat::sLastSpecialChatChannel = 0;
static LLFloaterIMNearbyChatListener sChatListener;
constexpr S32 EXPANDED_HEIGHT = 266;
constexpr S32 COLLAPSED_HEIGHT = 60;
constexpr S32 EXPANDED_MIN_HEIGHT = 150;

View File

@ -36,12 +36,12 @@
#include "llagent.h"
#include "llchat.h"
#include "llviewercontrol.h"
#include "stringize.h"
static const F32 CHAT_THROTTLE_PERIOD = 1.f;
LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar)
: LLEventAPI("LLChatBar",
"LLChatBar listener to (e.g.) sendChat, etc."),
mChatbar(chatbar)
LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener() :
LLEventAPI("LLChatBar", "LLChatBar listener to (e.g.) sendChat, etc.")
{
add("sendChat",
"Send chat to the simulator:\n"
@ -51,10 +51,18 @@ LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyCh
&LLFloaterIMNearbyChatListener::sendChat);
}
// "sendChat" command
void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
void LLFloaterIMNearbyChatListener::sendChat(LLSD const& chat_data)
{
F64 cur_time = LLTimer::getElapsedSeconds();
if (cur_time < mLastThrottleTime + CHAT_THROTTLE_PERIOD)
{
LL_WARNS("LLFloaterIMNearbyChatListener") << "'sendChat' was throttled" << LL_ENDL;
return;
}
mLastThrottleTime = cur_time;
// Extract the data
std::string chat_text = chat_data["message"].asString();
@ -83,21 +91,13 @@ void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
}
// Have to prepend /42 style channel numbers
std::string chat_to_send;
if (channel == 0)
if (channel)
{
chat_to_send = chat_text;
}
else
{
chat_to_send += "/";
chat_to_send += chat_data["channel"].asString();
chat_to_send += " ";
chat_to_send += chat_text;
chat_text = stringize("/", chat_data["channel"].asString(), " ", chat_text);
}
// Send it as if it was typed in
mChatbar.sendChatFromViewer(chat_to_send, type_o_chat, ((bool)(channel == 0)) && gSavedSettings.getBOOL("PlayChatAnim"));
LLFloaterIMNearbyChat::sendChatFromViewer(chat_text, type_o_chat, (channel == 0) && gSavedSettings.getBOOL("PlayChatAnim"));
}
#endif

View File

@ -39,12 +39,12 @@ class LLFloaterIMNearbyChat;
class LLFloaterIMNearbyChatListener : public LLEventAPI
{
public:
LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar);
LLFloaterIMNearbyChatListener();
private:
void sendChat(LLSD const & chat_data) const;
void sendChat(LLSD const & chat_data);
LLFloaterIMNearbyChat & mChatbar;
F64 mLastThrottleTime{0};
};
#endif // LL_LLFLOATERIMNEARBYCHATLISTENER_H

View File

@ -62,7 +62,7 @@
//
// Globals
//
static GroupChatListener sGroupChatListener;
static LLGroupChatListener sGroupChatListener;
class LLGroupHandler : public LLCommandHandler
{

View File

@ -709,11 +709,15 @@ void LLLocalBitmap::updateGLTFMaterials(LLUUID old_id, LLUUID new_id)
// do not create a new material, reuse existing pointer
// so that mTextureEntires remains untouched
LLGLTFMaterial* render_mat = entry->getGLTFRenderMaterial();
if (render_mat)
if (render_mat && render_mat != mat)
{
*render_mat = *mat;
render_mat->applyOverride(*override_mat); // can update mGLTFMaterialWithLocalTextures
}
else
{
LL_WARNS() << "A TE had an override, but no render material" << LL_ENDL;
}
}
}
}

View File

@ -187,20 +187,21 @@ std::string getLodSuffix(S32 lod)
return suffix;
}
void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut)
static bool FindModel(const LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut)
{
for (auto scene_iter = scene.begin(); scene_iter != scene.end(); scene_iter++)
for (const auto& scene_pair : scene)
{
for (auto model_iter = scene_iter->second.begin(); model_iter != scene_iter->second.end(); model_iter++)
for (const auto& model_iter : scene_pair.second)
{
if (model_iter->mModel && (model_iter->mModel->mLabel == name_to_match))
if (model_iter.mModel && (model_iter.mModel->mLabel == name_to_match))
{
baseModelOut = model_iter->mModel;
matOut = scene_iter->first;
return;
baseModelOut = model_iter.mModel;
matOut = scene_pair.first;
return true;
}
}
}
return false;
}
//-----------------------------------------------------------------------------
@ -504,10 +505,8 @@ void LLModelPreview::rebuildUploadData()
mat *= scale_mat;
for (auto model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
{ // for each instance with said transform applied
LLModelInstance instance = *model_iter;
for (LLModelInstance& instance : iter->second)
{ //for each instance with said transform applied
LLModel* base_model = instance.mModel;
if (base_model && !requested_name.empty())
@ -541,7 +540,7 @@ void LLModelPreview::rebuildUploadData()
}
else
{
//Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for
// Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for
extensionLOD = mPhysicsSearchLOD;
}
@ -555,9 +554,9 @@ void LLModelPreview::rebuildUploadData()
name_to_match += toAdd;
}
FindModel(mScene[i], name_to_match, lod_model, transform);
bool found = FindModel(mScene[i], name_to_match, lod_model, transform);
if (!lod_model && i != LLModel::LOD_PHYSICS)
if (!found && i != LLModel::LOD_PHYSICS)
{
if (mImporterDebug)
{
@ -570,7 +569,7 @@ void LLModelPreview::rebuildUploadData()
}
int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i;
while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model)
for (; searchLOD <= LLModel::LOD_HIGH; ++searchLOD)
{
// <FS:Beq> user defined LOD names
// std::string name_to_match = instance.mLabel;
@ -590,8 +589,8 @@ void LLModelPreview::rebuildUploadData()
// See if we can find an appropriately named model in LOD 'searchLOD'
//
FindModel(mScene[searchLOD], name_to_match, lod_model, transform);
searchLOD++;
if (FindModel(mScene[searchLOD], name_to_match, lod_model, transform))
break;
}
}
}
@ -1478,8 +1477,7 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
LLModel* found_model = NULL;
LLMatrix4 transform;
FindModel(mBaseScene, loaded_name, found_model, transform);
if (found_model)
if (FindModel(mBaseScene, loaded_name, found_model, transform))
{ // don't rename correctly named models (even if they are placed in a wrong order)
name_based = true;
}

View File

@ -580,32 +580,48 @@ void LLPanelVolume::getState( )
return object->getMaterial();
}
} func;
bool material_same = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, material_code );
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
bool material_same = selection->getSelectedTEValue( &func, material_code );
std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright");
if (editable && single_volume && material_same)
{
mComboMaterial->setEnabled( true );
if (material_code == LL_MCODE_LIGHT)
{
if (mComboMaterial->getItemCount() == mComboMaterialItemCount)
{
mComboMaterial->add(LEGACY_FULLBRIGHT_DESC);
}
mComboMaterial->setSimple(LEGACY_FULLBRIGHT_DESC);
}
else
{
if (mComboMaterial->getItemCount() != mComboMaterialItemCount)
{
mComboMaterial->remove(LEGACY_FULLBRIGHT_DESC);
}
mComboMaterial->setSimple(std::string(LLMaterialTable::basic.getName(material_code)));
bool enable_material = editable && single_volume && material_same;
LLCachedControl<bool> edit_linked(gSavedSettings, "EditLinkedParts", false);
if (!enable_material && !edit_linked())
{
LLViewerObject* root = selection->getPrimaryObject();
while (root && !root->isAvatar() && root->getParent())
{
LLViewerObject* parent = (LLViewerObject*)root->getParent();
if (parent->isAvatar())
{
break;
}
root = parent;
}
if (root)
{
material_code = root->getMaterial();
}
}
mComboMaterial->setEnabled(enable_material);
if (material_code == LL_MCODE_LIGHT)
{
if (mComboMaterial->getItemCount() == mComboMaterialItemCount)
{
mComboMaterial->add(LEGACY_FULLBRIGHT_DESC);
}
mComboMaterial->setSimple(LEGACY_FULLBRIGHT_DESC);
}
else
{
mComboMaterial->setEnabled( false );
if (mComboMaterial->getItemCount() != mComboMaterialItemCount)
{
mComboMaterial->remove(LEGACY_FULLBRIGHT_DESC);
}
mComboMaterial->setSimple(std::string(LLMaterialTable::basic.getName(material_code)));
}
// Physics properties

View File

@ -7786,7 +7786,7 @@ void LLSelectMgr::updatePointAt()
LLVector3 select_offset;
const LLPickInfo& pick = gViewerWindow->getLastPick();
LLViewerObject *click_object = pick.getObject();
bool was_hud = pick.mPickHUD && !click_object->isHUDAttachment();
bool was_hud = pick.mPickHUD && click_object && !click_object->isHUDAttachment();
if (click_object && click_object->isSelected() && !was_hud)
{
// clicked on another object in our selection group, use that as target

View File

@ -458,6 +458,14 @@ void LLToast::setVisible(bool show)
void LLToast::updateHoveredState()
{
if (!mWrapperPanel)
{
// Shouldn't be happening.
// mWrapperPanel should have been inited in the constructor
// This needs to be figured out and fixed
llassert(false);
return;
}
S32 x, y;
LLUI::getInstance()->getMousePositionScreen(&x, &y);

View File

@ -42,6 +42,7 @@
// Viewer includes
#include "llagent.h"
#include "llagentaccess.h"
#include "llcallbacklist.h"
#include "llviewerparcelaskplay.h"
#include "llviewerwindow.h"
#include "llviewercontrol.h"
@ -1822,6 +1823,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
{
instance->mTeleportFinishedSignal(instance->mTeleportInProgressPosition, false);
}
instance->postTeleportFinished(instance->mTeleportWithinRegion);
instance->mTeleportWithinRegion = false;
}
parcel->setParcelEnvironmentVersion(parcel_environment_version);
LL_DEBUGS("ENVIRONMENT") << "Parcel environment version is " << parcel->getParcelEnvironmentVersion() << LL_ENDL;
@ -2817,6 +2820,8 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos
// Local teleport. We already have the agent parcel data.
// Emit the signal immediately.
getInstance()->mTeleportFinishedSignal(new_pos, local);
postTeleportFinished(true);
}
else
{
@ -2825,12 +2830,14 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos
// Let's wait for the update and then emit the signal.
mTeleportInProgressPosition = new_pos;
mTeleportInProgress = true;
mTeleportWithinRegion = local;
}
}
void LLViewerParcelMgr::onTeleportFailed()
{
mTeleportFailedSignal();
LLEventPumps::instance().obtain("LLTeleport").post(llsd::map("success", false));
}
bool LLViewerParcelMgr::getTeleportInProgress()
@ -2839,6 +2846,23 @@ bool LLViewerParcelMgr::getTeleportInProgress()
|| gAgent.getTeleportState() > LLAgent::TELEPORT_NONE; // For LOCAL, no mTeleportInProgress
}
void LLViewerParcelMgr::postTeleportFinished(bool local)
{
auto post = []()
{
LLEventPumps::instance().obtain("LLTeleport").post(llsd::map("success", true));
};
if (local)
{
static LLCachedControl<F32> teleport_local_delay(gSavedSettings, "TeleportLocalDelay");
doAfterInterval(post, teleport_local_delay + 0.5f);
}
else
{
post();
}
}
// [SL:KB] - Patch: World-MinimapOverlay | Checked: 2012-06-20 (Catznip-3.3)
boost::signals2::connection LLViewerParcelMgr::setCollisionUpdateCallback(const collision_update_signal_t::slot_type& cb)
{

View File

@ -309,6 +309,8 @@ public:
void onTeleportFailed();
bool getTeleportInProgress();
void postTeleportFinished(bool local);
static bool isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power);
static bool isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power);
@ -358,7 +360,9 @@ private:
std::vector<LLParcelObserver*> mObservers;
// Used to communicate between onTeleportFinished() and processParcelProperties()
bool mTeleportInProgress;
bool mTeleportWithinRegion{ false };
LLVector3d mTeleportInProgressPosition;
teleport_finished_signal_t mTeleportFinishedSignal;
teleport_failed_signal_t mTeleportFailedSignal;

View File

@ -142,8 +142,9 @@ extern F32 SPEED_ADJUST_MAX_SEC;
extern F32 ANIM_SPEED_MAX;
extern F32 ANIM_SPEED_MIN;
extern U32 JOINT_COUNT_REQUIRED_FOR_FULLRIG;
const F32 MAX_HOVER_Z = 2.0f;
const F32 MIN_HOVER_Z = -2.0f;
const F32 MAX_HOVER_Z = 3.0;
const F32 MIN_HOVER_Z = -3.0;
const F32 MIN_ATTACHMENT_COMPLEXITY = 0.f;
const F32 DEFAULT_MAX_ATTACHMENT_COMPLEXITY = 1.0e6f;

View File

@ -569,10 +569,10 @@ public:
U32 renderTransparent(bool first_pass);
void renderCollisionVolumes();
void renderBones(const std::string &selected_joint = std::string());
virtual void renderJoints();
void renderOnlySelectedBones(const std::vector<std::string> &selected_joints);
void renderBoxAroundJointAttachments(LLJoint * joint);
void renderJoints();
static void deleteCachedImages(bool clearAll=true);
static void destroyGL();
static void restoreGL();

View File

@ -1032,6 +1032,7 @@ void LLVOAvatarSelf::idleUpdate(LLAgent &agent, const F64 &time)
LLJoint *LLVOAvatarSelf::getJoint( const JointKey &name )
// </FS:ND>
{
std::lock_guard lock(mJointMapMutex);
LLJoint *jointp = NULL;
jointp = LLVOAvatar::getJoint(name);
if (!jointp && mScreenp)
@ -1055,6 +1056,14 @@ LLJoint *LLVOAvatarSelf::getJoint( const JointKey &name )
return jointp;
}
//virtual
void LLVOAvatarSelf::renderJoints()
{
std::lock_guard lock(mJointMapMutex);
LLVOAvatar::renderJoints();
}
// virtual
// <FS:Ansariel> [Legacy Bake]
//bool LLVOAvatarSelf::setVisualParamWeight(const LLVisualParam *which_param, F32 weight)

View File

@ -96,6 +96,8 @@ public:
/*virtual*/ LLJoint* getJoint( const JointKey &name );
// </FS:ND>
/*virtual*/ void renderJoints();
// <FS:Ansariel> [Legacy Bake]
///*virtual*/ bool setVisualParamWeight(const LLVisualParam *which_param, F32 weight);
///*virtual*/ bool setVisualParamWeight(const char* param_name, F32 weight);
@ -122,6 +124,8 @@ private:
//bool setParamWeight(const LLViewerVisualParam *param, F32 weight);
bool setParamWeight(const LLViewerVisualParam *param, F32 weight, bool upload_bake = false);
std::mutex mJointMapMutex; // getJoint gets used from mesh thread
/********************************************************************************
** **
** STATE

View File

@ -33,7 +33,6 @@
#include "llagentwearables.h"
#include "llappearancemgr.h"
#include "llinventoryfunctions.h"
#include "llinventoryicon.h"
#include "llgesturemgr.h"
#include "lltransutil.h"
@ -47,15 +46,6 @@
#include "lltextbox.h"
#include "llresmgr.h"
class LLFindOutfitItems : public LLInventoryCollectFunctor
{
public:
LLFindOutfitItems() {}
virtual ~LLFindOutfitItems() {}
virtual bool operator()(LLInventoryCategory* cat,
LLInventoryItem* item);
};
bool LLFindOutfitItems::operator()(LLInventoryCategory* cat,
LLInventoryItem* item)
{

View File

@ -32,6 +32,7 @@
#include "llsingleton.h"
// newview
#include "llinventoryfunctions.h"
#include "llinventoryitemslist.h"
#include "llinventorylistitem.h"
#include "lllistcontextmenu.h"
@ -570,4 +571,12 @@ protected:
boost::signals2::connection mAttachmentsChangedCallbackConnection;
};
class LLFindOutfitItems : public LLInventoryCollectFunctor
{
public:
LLFindOutfitItems() {}
virtual ~LLFindOutfitItems() {}
virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
};
#endif //LL_LLWEARABLEITEMSLIST_H

View File

@ -86,21 +86,37 @@ def save_xml(tree, file_path, xml_decl, indent_text=False, indent_tab=False, rm_
except IOError as e:
print(f"Error saving file {file_path}: {e}")
def process_directory(directory_path, indent_text=False, indent_tab=False, rm_space=False, rewrite_decl=False):
def process_xml_files(file_paths, indent_text=False, indent_tab=False, rm_space=False, rewrite_decl=False):
found_files = False
if file_paths:
found_files = True
for file_path in file_paths:
xml_decl = get_xml_declaration(file_path)
tree = parse_xml_file(file_path)
if tree is not None:
save_xml(tree, file_path, xml_decl, indent_text, indent_tab, rm_space, rewrite_decl)
return found_files
def process_directory(directory_path, indent_text=False, indent_tab=False, rm_space=False, rewrite_decl=False, file_pattern=None, recursive=False):
if not os.path.isdir(directory_path):
print(f"Directory not found: {directory_path}")
return
xml_files = glob.glob(os.path.join(directory_path, "*.xml"))
if not xml_files:
print(f"No XML files found in directory: {directory_path}")
return
pattern = file_pattern if file_pattern else "*.xml"
found_files = False
for file_path in xml_files:
xml_decl = get_xml_declaration(file_path)
tree = parse_xml_file(file_path)
if tree is not None:
save_xml(tree, file_path, xml_decl, indent_text, indent_tab, rm_space, rewrite_decl)
if not recursive:
# Non-recursive mode
xml_files = glob.glob(os.path.join(directory_path, pattern))
found_files = process_xml_files(xml_files, indent_text, indent_tab, rm_space, rewrite_decl)
else:
# Recursive mode
for root, dirs, files in os.walk(directory_path):
xml_files = glob.glob(os.path.join(root, pattern))
found_files = process_xml_files(xml_files, indent_text, indent_tab, rm_space, rewrite_decl)
if not found_files:
print(f"No XML files found in {'directory tree' if recursive else 'directory'}: {directory_path}")
if __name__ == "__main__":
if len(sys.argv) < 2 or '--help' in sys.argv:
@ -112,9 +128,13 @@ if __name__ == "__main__":
print(" --indent-tab Uses tabs instead of spaces for indentation.")
print(" --rm-space Removes spaces in self-closing tags.")
print(" --rewrite_decl Replaces the XML declaration line.")
print(" --file <pattern> Only process files matching the pattern")
print(" --recursive Process files in all subdirectories")
print("\nCommon Usage:")
print(" To format XML files with text indentation, tab indentation, and removal of spaces in self-closing tags:")
print(" python fix_xml_indentations.py /path/to/xmls --indent-text --indent-tab --rm-space")
print("\n To format specific XML files recursively through all subdirectories:")
print(" python fix_xml_indentations.py /path/to/xmls --file floater_*.xml --recursive")
sys.exit(1)
directory_path = sys.argv[1]
@ -122,4 +142,16 @@ if __name__ == "__main__":
indent_tab = '--indent-tab' in sys.argv
rm_space = '--rm-space' in sys.argv
rewrite_decl = '--rewrite_decl' in sys.argv
process_directory(directory_path, indent_text, indent_tab, rm_space, rewrite_decl)
recursive = '--recursive' in sys.argv
# Get file pattern if specified
file_pattern = None
if '--file' in sys.argv:
try:
file_index = sys.argv.index('--file') + 1
if file_index < len(sys.argv):
file_pattern = sys.argv[file_index]
except ValueError:
pass
process_directory(directory_path, indent_text, indent_tab, rm_space, rewrite_decl, file_pattern, recursive)