diff options
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llcommon/lua_function.cpp | 17 | ||||
-rw-r--r-- | indra/llcommon/lua_function.h | 162 | ||||
-rw-r--r-- | indra/newview/llagentlistener.cpp | 118 | ||||
-rw-r--r-- | indra/newview/llagentlistener.h | 5 | ||||
-rw-r--r-- | indra/newview/scripts/lua/luafloater_camera_control.xml | 244 | ||||
-rw-r--r-- | indra/newview/scripts/lua/require/LLAgent.lua | 45 | ||||
-rw-r--r-- | indra/newview/scripts/lua/test_camera_control.lua | 49 |
7 files changed, 605 insertions, 35 deletions
diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 42bba80ed5..b173d17ede 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -32,6 +32,8 @@ #include "lualistener.h" #include "stringize.h" +using namespace std::literals; // e.g. std::string_view literals: "this"sv + const S32 INTERRUPTS_MAX_LIMIT = 20000; const S32 INTERRUPTS_SUSPEND_LIMIT = 100; @@ -41,7 +43,7 @@ const S32 INTERRUPTS_SUSPEND_LIMIT = 100; int DistinctInt::mValues{0}; /***************************************************************************** -* luau namespace +* lluau namespace *****************************************************************************/ namespace { @@ -93,21 +95,14 @@ fsyspath lluau::source_path(lua_State* L) void lluau::set_interrupts_counter(lua_State *L, S32 counter) { - luaL_checkstack(L, 2, nullptr); - lua_pushstring(L, "_INTERRUPTS"); - lua_pushinteger(L, counter); - lua_rawset(L, LUA_REGISTRYINDEX); + lua_rawsetfield(L, LUA_REGISTRYINDEX, "_INTERRUPTS"sv, lua_Integer(counter)); } void lluau::check_interrupts_counter(lua_State* L) { - luaL_checkstack(L, 1, nullptr); - lua_pushstring(L, "_INTERRUPTS"); - lua_rawget(L, LUA_REGISTRYINDEX); - S32 counter = lua_tointeger(L, -1); - lua_pop(L, 1); + auto counter = lua_rawgetfield<lua_Integer>(L, LUA_REGISTRYINDEX, "_INTERRUPTS"sv); - lluau::set_interrupts_counter(L, ++counter); + set_interrupts_counter(L, ++counter); if (counter > INTERRUPTS_MAX_LIMIT) { lluau::error(L, "Possible infinite loop, terminated."); diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 7f7a7566f3..7b59af30f5 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -18,6 +18,7 @@ #include "luau/lualib.h" #include "fsyspath.h" #include "llerror.h" +#include "llsd.h" #include "stringize.h" #include <exception> // std::uncaught_exceptions() #include <memory> // std::shared_ptr @@ -169,6 +170,167 @@ private: }; /***************************************************************************** +* lua_push() wrappers for generic code +*****************************************************************************/ +inline +void lua_push(lua_State* L, bool b) +{ + lua_pushboolean(L, int(b)); +} + +inline +void lua_push(lua_State* L, lua_CFunction fn) +{ + lua_pushcfunction(L, fn, ""); +} + +inline +void lua_push(lua_State* L, lua_Integer n) +{ + lua_pushinteger(L, n); +} + +inline +void lua_push(lua_State* L, void* p) +{ + lua_pushlightuserdata(L, p); +} + +inline +void lua_push(lua_State* L, const LLSD& data) +{ + lua_pushllsd(L, data); +} + +inline +void lua_push(lua_State* L, const char* s, size_t len) +{ + lua_pushlstring(L, s, len); +} + +inline +void lua_push(lua_State* L) +{ + lua_pushnil(L); +} + +inline +void lua_push(lua_State* L, lua_Number n) +{ + lua_pushnumber(L, n); +} + +inline +void lua_push(lua_State* L, const std::string& s) +{ + lua_pushstdstring(L, s); +} + +inline +void lua_push(lua_State* L, const char* s) +{ + lua_pushstring(L, s); +} + +/***************************************************************************** +* lua_to() wrappers for generic code +*****************************************************************************/ +template <typename T> +auto lua_to(lua_State* L, int index); + +template <> +inline +auto lua_to<bool>(lua_State* L, int index) +{ + return lua_toboolean(L, index); +} + +template <> +inline +auto lua_to<lua_CFunction>(lua_State* L, int index) +{ + return lua_tocfunction(L, index); +} + +template <> +inline +auto lua_to<lua_Integer>(lua_State* L, int index) +{ + return lua_tointeger(L, index); +} + +template <> +inline +auto lua_to<LLSD>(lua_State* L, int index) +{ + return lua_tollsd(L, index); +} + +template <> +inline +auto lua_to<lua_Number>(lua_State* L, int index) +{ + return lua_tonumber(L, index); +} + +template <> +inline +auto lua_to<std::string>(lua_State* L, int index) +{ + return lua_tostdstring(L, index); +} + +template <> +inline +auto lua_to<void*>(lua_State* L, int index) +{ + return lua_touserdata(L, index); +} + +/***************************************************************************** +* field operations +*****************************************************************************/ +// return to C++, from table at index, the value of field k +template <typename T> +auto lua_getfieldv(lua_State* L, int index, const char* k) +{ + luaL_checkstack(L, 1, nullptr); + lua_getfield(L, index, k); + LuaPopper pop(L, 1); + return lua_to<T>(L, -1); +} + +// set in table at index, as field k, the specified C++ value +template <typename T> +auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) +{ + luaL_checkstack(L, 1, nullptr); + lua_push(L, value); + lua_setfield(L, index, k); +} + +// return to C++, from table at index, the value of field k (without metamethods) +template <typename T> +auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) +{ + luaL_checkstack(L, 1, nullptr); + lua_pushlstring(L, k.data(), k.length()); + lua_rawget(L, index); + LuaPopper pop(L, 1); + return lua_to<T>(L, -1); +} + +// set in table at index, as field k, the specified C++ value (without metamethods) +template <typename T> +void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value) +{ + luaL_checkstack(L, 2, nullptr); + lua_pushlstring(L, k.data(), k.length()); + lua_push(L, value); + lua_rawset(L, index); +} + +/***************************************************************************** * lua_function (and helper class LuaFunction) *****************************************************************************/ /** diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 54998f3945..80460666a5 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -31,6 +31,7 @@ #include "llagentlistener.h" #include "llagent.h" +#include "llagentcamera.h" #include "llvoavatar.h" #include "llcommandhandler.h" #include "llslurl.h" @@ -44,6 +45,7 @@ #include "lltoolgrab.h" #include "llhudeffectlookat.h" #include "llagentcamera.h" +#include <functional> LLAgentListener::LLAgentListener(LLAgent &agent) : LLEventAPI("LLAgent", @@ -69,13 +71,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" @@ -138,6 +133,30 @@ 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); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -296,22 +315,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; @@ -519,3 +522,72 @@ 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); +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index c544d089ce..2a24de3f52 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -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,6 +57,10 @@ private: void stopAutoPilot(const LLSD& event_data) const; void lookAt(LLSD const & event_data) const; + void setFollowCamParams(LLSD const & event_data) const; + void setFollowCamActive(LLSD const & event_data) const; + void removeFollowCamParams(LLSD const & event_data) const; + LLViewerObject * findObjectClosestTo( const LLVector3 & position ) const; private: diff --git a/indra/newview/scripts/lua/luafloater_camera_control.xml b/indra/newview/scripts/lua/luafloater_camera_control.xml new file mode 100644 index 0000000000..0601a363e5 --- /dev/null +++ b/indra/newview/scripts/lua/luafloater_camera_control.xml @@ -0,0 +1,244 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + legacy_header_height="18" + height="350" + layout="topleft" + name="camera_demo" + title="Follow camera control" + width="360"> + <text + type="string" + follows="left|top" + height="10" + width="100" + layout="topleft" + top="30" + left="10" + name="camera_lbl"> + Camera position: + </text> + <text + type="string" + follows="left|top" + height="10" + width="10" + layout="topleft" + top_pad="12" + left="10" + value="X" + name="cam_x_lbl"/> + <line_editor + border_style="line" + border_thickness="1" + follows="left|bottom" + font="SansSerif" + height="20" + layout="topleft" + max_length_bytes="50" + name="cam_x" + top_delta="-2" + left_pad="5" + width="65" /> + <button + follows="left|bottom" + height="20" + image_overlay="Icon_Copy" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + tool_tip="Use Agent position" + name="agent_cam_btn" + left_pad="10" + width="20" > + </button> + <text + type="string" + follows="left|top" + height="10" + width="10" + layout="topleft" + top_pad="7" + left="10" + value="Y" + name="cam_y_lbl"/> + <line_editor + border_style="line" + border_thickness="1" + follows="left|bottom" + font="SansSerif" + height="20" + layout="topleft" + max_length_bytes="50" + name="cam_y" + top_delta="-2" + left_pad="5" + width="65" /> + <text + type="string" + follows="left|top" + height="10" + width="10" + layout="topleft" + top_pad="7" + left="10" + value="Z" + name="cam_z_lbl"/> + <line_editor + border_style="line" + border_thickness="1" + follows="left|bottom" + font="SansSerif" + height="20" + layout="topleft" + max_length_bytes="50" + name="cam_z" + top_delta="-2" + left_pad="5" + width="65" /> + <text + type="string" + follows="left|top" + height="10" + width="100" + layout="topleft" + top="30" + left="145" + name="focus_lbl"> + Focus position: + </text> + <text + type="string" + follows="left|top" + height="10" + width="10" + layout="topleft" + top_pad="12" + left="145" + value="X" + name="cam_x_lbl"/> + <line_editor + border_style="line" + border_thickness="1" + follows="left|bottom" + font="SansSerif" + height="20" + layout="topleft" + max_length_bytes="50" + name="focus_x" + top_delta="-2" + left_pad="5" + width="60" /> + <button + follows="left|bottom" + height="20" + image_overlay="Icon_Copy" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + tool_tip="Use Agent position" + name="agent_focus_btn" + left_pad="10" + width="20" > + </button> + <text + type="string" + follows="left|top" + height="10" + width="10" + layout="topleft" + top_pad="7" + left="145" + value="Y" + name="cam_y_lbl"/> + <line_editor + border_style="line" + border_thickness="1" + follows="left|bottom" + font="SansSerif" + height="20" + layout="topleft" + max_length_bytes="50" + name="focus_y" + top_delta="-2" + left_pad="5" + width="60" /> + <text + type="string" + follows="left|top" + height="10" + width="10" + layout="topleft" + top_pad="7" + left="145" + value="Z" + name="cam_z_lbl"/> + <line_editor + border_style="line" + border_thickness="1" + follows="left|bottom" + font="SansSerif" + height="20" + layout="topleft" + max_length_bytes="50" + name="focus_z" + top_delta="-2" + left_pad="5" + width="60" /> + <text + type="string" + follows="left|top" + height="10" + width="100" + layout="topleft" + top="30" + left="270" + name="focus_lbl"> + Lock: + </text> + <check_box + width="80" + height="21" + layout="topleft" + follows="top|left" + top_pad="12" + label="Camera" + name="lock_camera_ctrl"/> + <check_box + width="80" + height="21" + layout="topleft" + follows="top|left" + top_pad="2" + label="Focus" + name="lock_focus_ctrl"/> + <button + follows="left|bottom" + height="25" + label="Update params" + layout="topleft" + name="update_btn" + left="10" + top="140" + width="120" > + </button> + <button + follows="left|bottom" + height="25" + label="Reset" + layout="topleft" + name="reset_btn" + left_pad="15" + width="90" > + </button> + <text_editor + follows="top|left" + font="SansSerif" + height="160" + left="5" + enabled="false" + name="events_editor" + top_pad="15" + word_wrap="true" + max_length="65536" + width="350"/> +</floater> diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua new file mode 100644 index 0000000000..bc9a6b23a0 --- /dev/null +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -0,0 +1,45 @@ +local leap = require 'leap' +local mapargs = require 'mapargs' + +local LLAgent = {} + +function LLAgent.getRegionPosition() + return leap.request('LLAgent', {op = 'getPosition'}).region +end + +function LLAgent.getGlobalPosition() + return leap.request('LLAgent', {op = 'getPosition'}).global +end + +-- Use LL.leaphelp('LLAgent') and see 'setCameraParams' to get more info about params +-- -- TYPE -- DEFAULT -- RANGE +-- LLAgent.setCamera{ [, camera_pos] -- vector3 +-- [, focus_pos] -- vector3 +-- [, focus_offset] -- vector3 -- {1,0,0} -- {-10,-10,-10} to {10,10,10} +-- [, distance] -- float (meters) -- 3 -- 0.5 to 50 +-- [, focus_threshold] -- float (meters) -- 1 -- 0 to 4 +-- [, camera_threshold] -- float (meters) -- 1 -- 0 to 4 +-- [, focus_lag] -- float (seconds) -- 0.1 -- 0 to 3 +-- [, camera_lag] -- float (seconds) -- 0.1 -- 0 to 3 +-- [, camera_pitch] -- float (degrees) -- 0 -- -45 to 80 +-- [, behindness_angle] -- float (degrees) -- 10 -- 0 to 180 +-- [, behindness_lag] -- float (seconds) -- 0 -- 0 to 3 +-- [, camera_locked] -- bool -- false +-- [, focus_locked]} -- bool -- false +function LLAgent.setCamera(...) + local args = mapargs('camera_pos,focus_pos,focus_offset,focus_lag,camera_lag,' .. + 'distance,focus_threshold,camera_threshold,camera_pitch,' .. + 'camera_locked,focus_locked,behindness_angle,behindness_lag', ...) + args.op = 'setCameraParams' + leap.send('LLAgent', args) +end + +function LLAgent.setFollowCamActive(active) + leap.send('LLAgent', {op = 'setFollowCamActive', active = active}) +end + +function LLAgent.removeCamParams() + leap.send('LLAgent', {op = 'removeCameraParams'}) +end + +return LLAgent diff --git a/indra/newview/scripts/lua/test_camera_control.lua b/indra/newview/scripts/lua/test_camera_control.lua new file mode 100644 index 0000000000..9b35cdf8cd --- /dev/null +++ b/indra/newview/scripts/lua/test_camera_control.lua @@ -0,0 +1,49 @@ +local Floater = require 'Floater' +local LLAgent = require 'LLAgent' +local startup = require 'startup' + +local flt = Floater('luafloater_camera_control.xml') + +function getValue(ctrl_name) + return flt:request({action="get_value", ctrl_name=ctrl_name}).value +end + +function setValue(ctrl_name, value) + flt:post({action="set_value", ctrl_name=ctrl_name, value=value}) +end + +function flt:commit_update_btn(event_data) + lock_focus = getValue('lock_focus_ctrl') + lock_camera = getValue('lock_camera_ctrl') + local camera_pos = {getValue('cam_x'), getValue('cam_y'), getValue('cam_z')} + local focus_pos = {getValue('focus_x'), getValue('focus_y'), getValue('focus_z')} + + LLAgent.setCamera{camera_pos=camera_pos, focus_pos=focus_pos, + focus_locked=lock_focus, camera_locked=lock_camera} + + self:post({action="add_text", ctrl_name="events_editor", + value = {'Updating FollowCam params', 'camera_pos:', camera_pos, 'focus_pos:', focus_pos, + 'lock_focus:', lock_focus, 'lock_camera:', lock_camera}}) +end + +function flt:commit_agent_cam_btn(event_data) + agent_pos = LLAgent.getRegionPosition() + setValue('cam_x', math.floor(agent_pos[1])) + setValue('cam_y', math.floor(agent_pos[2])) + setValue('cam_z', math.floor(agent_pos[3])) +end + +function flt:commit_agent_focus_btn(event_data) + agent_pos = LLAgent.getRegionPosition() + setValue('focus_x', math.floor(agent_pos[1])) + setValue('focus_y', math.floor(agent_pos[2])) + setValue('focus_z', math.floor(agent_pos[3])) +end + +function flt:commit_reset_btn(event_data) + LLAgent.removeCamParams() + LLAgent.setFollowCamActive(false) +end + +startup.wait('STATE_STARTED') +flt:show() |