llpuppetmodule.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. /**
  2. * @file llpuppetmodule.cpp
  3. * @brief Implementation of the LLPuppetModule class.
  4. *
  5. * $LicenseInfo:firstyear=2022&license=viewergpl$
  6. *
  7. * Copyright (c) 2022, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llpuppetmodule.h"
  34. #include "llanimationstates.h"
  35. #include "llheadrotmotion.h"
  36. #include "llleap.h"
  37. #include "llnotifications.h"
  38. #include "llagent.h"
  39. #include "llpuppetmotion.h"
  40. #include "llviewercontrol.h"
  41. #include "llviewerobjectlist.h"
  42. #include "llviewerregion.h"
  43. #include "llvoavatarself.h"
  44. LLPuppetModule::LLPuppetModule()
  45. : LLEventAPI("puppetry", "Integrate external puppetry control module",
  46. "command"), // Dispatches incoming events on "command" key
  47. mRange(25.f),
  48. mPlayServerEcho(false),
  49. mIsSending(false),
  50. mIsReceiving(true)
  51. {
  52. add("get",
  53. "Puppetry plugin module has requested information from the viewer\n"
  54. "Requested data may be a simple string. EX:\n"
  55. " camera_id\n"
  56. " skeleton\n"
  57. "Or a key and dict"
  58. "Response will be a set issued to the plugin module. EX:\n"
  59. " camera_id: <integer>\n"
  60. " skeleton: <llsd>\n"
  61. "multiple items may be requested in a single get",
  62. &LLPuppetModule::processGetRequest);
  63. add("set",
  64. "Puppetry plugin module request to apply settings to the viewer.\n"
  65. "Set data is a structure following the form\n"
  66. " {'<to_be_set>':<value|structure>}\n"
  67. "EX: \n"
  68. " camera_id: <integer>\n"
  69. " joint: {<name>:inverse_kinematics:position[<float>,<float>,<float>]}\n"
  70. "A set may trigger a set to be issued back to the plugin.\n"
  71. "multiple pieces of data may be set in a single set.",
  72. &LLPuppetModule::processSetRequest);
  73. // This function defines viewer-internal API endpoints for this event
  74. // handler.
  75. mSendSkeletonAPI =
  76. gEventPumps.obtain("SkeletonUpdate")
  77. .listen("LLPuppetModule",
  78. [](const LLSD& unused)
  79. {
  80. LLPuppetModule::getInstance()->sendSkeleton();
  81. return false;
  82. });
  83. mSendReportAPI =
  84. gEventPumps.obtain("JointReport")
  85. .listen("LLPuppetModule",
  86. [](const LLSD& sd)
  87. {
  88. LLPuppetModule::getInstance()->sendReport(sd);
  89. return false;
  90. });
  91. LLControlVariable* controlp =
  92. gSavedSettings.getControl("PuppetryUseServerEcho");
  93. if (!controlp)
  94. {
  95. llwarns << "Missing \"PuppetryUseServerEcho\" debug variable."
  96. << llendl;
  97. return;
  98. }
  99. controlp->getSignal()->connect(boost::bind(&LLPuppetModule::settingsObserver));
  100. }
  101. // Puppetry GET requests are processed here. Expected data format:
  102. // data = 'command'
  103. // data = {command:get, data:[thing_one, thing_two, ...]}
  104. // data = {command:get, d:[thing_one, thing_two, ...]}
  105. //static
  106. void LLPuppetModule::processGetRequest(const LLSD& data)
  107. {
  108. if (!isAgentAvatarValid())
  109. {
  110. return;
  111. }
  112. LL_DEBUGS("PuppetrySpam") << "Puppet data: " << data << llendl;
  113. // Always check for short format first...
  114. std::string verb = "d";
  115. if (!data.has(verb))
  116. {
  117. // ... and long format second.
  118. verb = "data";
  119. if (!data.has(verb))
  120. {
  121. llwarns_sparse << "Missing 'data' key in get request" << llendl;
  122. return;
  123. }
  124. }
  125. const LLSD& payload = data[verb];
  126. if (!payload.isArray())
  127. {
  128. llwarns_sparse << "Malformed get request: 'data' value is not an array."
  129. << llendl;
  130. return;
  131. }
  132. LLPuppetModule* self = LLPuppetModule::getInstance();
  133. for (LLSD::array_const_iterator it = payload.beginArray(),
  134. end = payload.endArray();
  135. it != end; ++it)
  136. {
  137. std::string key = it->asString();
  138. if (key == "c" || key == "camera")
  139. {
  140. // getCameraNumber_ returns results immediately as a Response.
  141. self->getCameraNumber_(data);
  142. }
  143. else if (key == "s" || key == "skeleton")
  144. {
  145. self->sendSkeleton(data);
  146. }
  147. }
  148. }
  149. // Puppetry SET requests are processed here.
  150. // Expected data format:
  151. // data = {command:set, data:{inverse_kinematics:{...},joint_state:{...}}
  152. // data = {command:set, d:{i:{...},j:{...}}
  153. //static
  154. void LLPuppetModule::processSetRequest(const LLSD& data)
  155. {
  156. if (!isAgentAvatarValid())
  157. {
  158. return;
  159. }
  160. LL_DEBUGS("PuppetrySpam") << "Puppet data: " << data << llendl;
  161. // Always check for short format first...
  162. std::string verb = "d";
  163. if (!data.has(verb))
  164. {
  165. // ... and long format second.
  166. verb = "data";
  167. if (!data.has(verb))
  168. {
  169. llwarns_sparse << "Missing 'data' key in set request" << llendl;
  170. return;
  171. }
  172. }
  173. const LLSD& payload = data[verb];
  174. if (!payload.isMap())
  175. {
  176. llwarns_sparse << "Malformed set request: 'data' value is not a map."
  177. << llendl;
  178. return;
  179. }
  180. S32 reqid = data.has("reqid") ? data["reqid"].asInteger() : -1;
  181. LLPuppetMotion* motionp = gAgentAvatarp->getPuppetMotion();
  182. if (!motionp)
  183. {
  184. llwarns << "No puppet motion found on self" << llendl;
  185. return;
  186. }
  187. LLPuppetModule* self = LLPuppetModule::getInstance();
  188. for (LLSD::map_const_iterator it = payload.beginMap(),
  189. end = payload.endMap();
  190. it != end; ++it)
  191. {
  192. const std::string& key = it->first;
  193. if (key == "c" || key == "camera")
  194. {
  195. self->setCameraNumber(it->second.asInteger());
  196. continue;
  197. }
  198. const LLSD& joint_data = it->second;
  199. if (joint_data.isMap())
  200. {
  201. self->processJointData(motionp, key, joint_data, reqid);
  202. }
  203. else
  204. {
  205. llwarns_sparse << "Data is not a map for joint " << key
  206. << llendl;
  207. }
  208. }
  209. }
  210. void LLPuppetModule::processJointData(LLPuppetMotion* motionp,
  211. const std::string& key, const LLSD& data,
  212. S32 reqid)
  213. {
  214. // The reference frame depends on the key
  215. S32 ref_frame;
  216. if (key == "i" || key == "inverse_kinematics")
  217. {
  218. ref_frame = LLPuppetJointEvent::ROOT_FRAME;
  219. }
  220. else if (key == "j" || key == "joint_state")
  221. {
  222. ref_frame = LLPuppetJointEvent::PARENT_FRAME;
  223. }
  224. else // Invalid key; ignore...
  225. {
  226. llwarns_once << "Invalid key: " << key
  227. << ". Expected: i/inverse_kinematics or j/joint_state"
  228. <<llendl;
  229. return;
  230. }
  231. for (LLSD::map_const_iterator joint_itr = data.beginMap(),
  232. joint_end = data.endMap();
  233. joint_itr != joint_end; ++joint_itr)
  234. {
  235. std::string joint_name = joint_itr->first;
  236. if (joint_name.empty())
  237. {
  238. continue;
  239. }
  240. const LLSD& params = joint_itr->second;
  241. if (!params.isMap())
  242. {
  243. llwarns_once << "Invalid data for joint data key " << joint_name
  244. << ". Expected a map but got: " << params << llendl;
  245. continue;
  246. }
  247. LLJoint* joint = NULL;
  248. if (LLStringOps::isDigit(joint_name[0]))
  249. {
  250. // Joint name starts with a digit, try it as a joint_id.
  251. joint = gAgentAvatarp->getSkeletonJoint(atoi(joint_name.c_str()));
  252. if (joint)
  253. {
  254. joint_name = joint->getName();
  255. }
  256. }
  257. else
  258. {
  259. U32 joint_key = LLJoint::getKey(joint_name, false);
  260. if (joint_key)
  261. {
  262. joint = gAgentAvatarp->getJoint(joint_key);
  263. }
  264. }
  265. if (!joint)
  266. {
  267. continue; // Joint not found; ignore...
  268. }
  269. if (joint_name == "mHead")
  270. {
  271. // If the head is animated, stop looking at the mouse
  272. disableHeadMotion();
  273. }
  274. // Record that we have seen this joint name
  275. addActiveJoint(joint_name);
  276. constexpr size_t vx = 0;
  277. constexpr size_t vy = 1;
  278. constexpr size_t vz = 2;
  279. LLPuppetJointEvent joint_event;
  280. joint_event.setJointID(joint->getJointNum());
  281. joint_event.setReferenceFrame(ref_frame);
  282. for (LLSD::map_const_iterator param_itr = params.beginMap(),
  283. param_end = params.endMap();
  284. param_itr != param_end; ++param_itr)
  285. {
  286. const LLSD& value = param_itr->second;
  287. std::string param_name = param_itr->first;
  288. constexpr S32 NUM_COMPONENTS = 3;
  289. if (!value.isArray() || value.size() < NUM_COMPONENTS)
  290. {
  291. if (param_name == "d" || param_name == "disable_constraint")
  292. {
  293. joint_event.disableConstraint();
  294. }
  295. else if (param_name == "r" || param_name == "report")
  296. {
  297. // Outputs rot/pos after solution.
  298. joint_event.enableReporting(reqid);
  299. }
  300. continue;
  301. }
  302. LLVector3 v(value.get(vx).asReal(), value.get(vy).asReal(),
  303. value.get(vz).asReal());
  304. // Sanity-check input value
  305. constexpr F32 MAX_PUPPETRY_INPUT = 10.f;
  306. v.clamp(-MAX_PUPPETRY_INPUT, MAX_PUPPETRY_INPUT);
  307. // Note: LLVector3::clamp() does not protect against NaN input, so
  308. // we explicitly check it here.
  309. F32 v_length_squared = v.lengthSquared();
  310. if (llisnan(v_length_squared))
  311. {
  312. continue;
  313. }
  314. if (param_name == "r" || param_name == "rotation")
  315. {
  316. // Packed quaternions have the imaginary part (xyz)
  317. LLQuaternion q;
  318. // Copy the imaginary part
  319. memcpy(q.mQ, v.mV, 3 * sizeof(F32));
  320. // Compute the real part
  321. if (v_length_squared > 1.f)
  322. {
  323. F32 inv_length = 1.f / sqrtf(v_length_squared);
  324. q.mQ[VX] *= inv_length;
  325. q.mQ[VY] *= inv_length;
  326. q.mQ[VZ] *= inv_length;
  327. q.mQ[VW] = 0.f;
  328. }
  329. else
  330. {
  331. q.mQ[VW] = sqrtf(1.f - v_length_squared);
  332. }
  333. joint_event.setRotation(q);
  334. }
  335. else if (param_name == "p" || param_name == "position")
  336. {
  337. joint_event.setPosition(v);
  338. }
  339. else if (param_name == "s" || param_name == "scale")
  340. {
  341. joint_event.setScale(v);
  342. }
  343. }
  344. if (!joint_event.isEmpty())
  345. {
  346. if (!motionp->isActive())
  347. {
  348. gAgentAvatarp->startMotion(ANIM_AGENT_PUPPET_MOTION);
  349. }
  350. motionp->addExpressionEvent(joint_event);
  351. }
  352. }
  353. }
  354. bool LLPuppetModule::launchLeapPlugin(const std::string& filename)
  355. {
  356. if (filename.empty() || !LLPuppetMotion::enabled() || havePuppetModule())
  357. {
  358. return false;
  359. }
  360. // Note: I expanded LLProcess to accept script file names and search for
  361. // a suitable interpreter to launch (see LLProcess() constructor in the
  362. // indra/llcommon/lleap.cpp file). It means we do not need to care about it
  363. // here like LL is doing in their viewer. HB
  364. std::vector<std::string> command;
  365. std::string cmd_str = filename;
  366. command.emplace_back(filename);
  367. // By default this is "--camera", but I made it configurable via a debug
  368. // setting; this option can also be omitted in the command line by using
  369. // an empty string in that setting. HB
  370. std::string camopt = gSavedSettings.getString("PuppetryCameraOption");
  371. if (!camopt.empty())
  372. {
  373. // Get the camera number.
  374. std::string camera = llformat("%d", getCameraNumber());
  375. if (camopt.back() == '=')
  376. {
  377. // An option ending with '=' must normally not use spaces to
  378. // separate it from its parameter and is considered as a single
  379. // command line option. HB
  380. camopt += camera;
  381. command.emplace_back(camopt);
  382. }
  383. else
  384. {
  385. // The camera option and camera number must be separated with a
  386. // space and are two distinct command line options.
  387. command.emplace_back(camopt);
  388. command.emplace_back(camera);
  389. camopt += " " + camera;
  390. }
  391. cmd_str += " " + camopt;
  392. }
  393. llinfos << "Attempting to launch LEAP command: " << cmd_str << llendl;
  394. try
  395. {
  396. LLLeap* leapp = LLLeap::create("Puppetry", command);
  397. if (leapp)
  398. {
  399. leapp->enableBinaryOutput(gSavedSettings.getBool("PuppetryBinaryOutputStream"));
  400. leapp->enableBinaryInput(gSavedSettings.getBool("PuppetryBinaryInputStream"));
  401. setLeapModule(leapp->getWeak(), filename);
  402. llinfos << "Puppetry module successfully created." << llendl;
  403. setSending(true);
  404. sendCameraNumber();
  405. sendSkeleton();
  406. // Save this valid command, for future potential use... HB
  407. gSavedSettings.setString("PuppetryLastCommand", cmd_str);
  408. }
  409. else // This should not happen, unless memory could not be allocated
  410. {
  411. llwarns << "Failed to launch LEAP module." << llendl;
  412. }
  413. }
  414. catch (const LLLeap::Error& e)
  415. {
  416. LLSD args;
  417. args["COMMAND"] = cmd_str;
  418. args["ERROR"] = e.what();
  419. gNotifications.add("LeapModuleFail", args);
  420. return false;
  421. }
  422. return true;
  423. }
  424. bool LLPuppetModule::launchLeapCommand(const std::string& command)
  425. {
  426. if (command.empty() || !LLPuppetMotion::enabled() || havePuppetModule())
  427. {
  428. return false;
  429. }
  430. llinfos << "Attempting to launch LEAP command: " << command << llendl;
  431. try
  432. {
  433. LLLeap* leapp = LLLeap::create("Puppetry", command);
  434. if (leapp)
  435. {
  436. leapp->enableBinaryOutput(gSavedSettings.getBool("PuppetryBinaryOutputStream"));
  437. leapp->enableBinaryInput(gSavedSettings.getBool("PuppetryBinaryInputStream"));
  438. setLeapModule(leapp->getWeak(), leapp->getExecutable());
  439. llinfos << "Puppetry module successfully created." << llendl;
  440. setSending(true);
  441. sendCameraNumber();
  442. sendSkeleton();
  443. }
  444. else // This should not happen, unless memory could not be allocated
  445. {
  446. llwarns << "Failed to launch LEAP module." << llendl;
  447. }
  448. }
  449. catch (const LLLeap::Error& e)
  450. {
  451. LLSD args;
  452. args["COMMAND"] = command;
  453. args["ERROR"] = e.what();
  454. gNotifications.add("LeapModuleFail", args);
  455. return false;
  456. }
  457. return true;
  458. }
  459. void LLPuppetModule::setLeapModule(std::weak_ptr<LLLeap> mod,
  460. const std::string& module_name)
  461. {
  462. mLeapModule = mod;
  463. mModuleName = module_name;
  464. mActiveJoints.clear(); // Make sure data is cleared
  465. if (isAgentAvatarValid())
  466. {
  467. LLPuppetMotion* motionp = gAgentAvatarp->getPuppetMotion();
  468. if (motionp)
  469. {
  470. motionp->clearAll();
  471. }
  472. }
  473. // Sync the echo status with the debug setting. HB
  474. settingsObserver();
  475. }
  476. LLPuppetModule::puppet_module_ptr_t LLPuppetModule::getLeapModule() const
  477. {
  478. // Lock it
  479. return mLeapModule.lock();
  480. }
  481. bool LLPuppetModule::havePuppetModule() const
  482. {
  483. puppet_module_ptr_t mod = getLeapModule();
  484. return (bool)(mod);
  485. }
  486. void LLPuppetModule::disableHeadMotion() const
  487. {
  488. if (!isAgentAvatarValid())
  489. {
  490. return;
  491. }
  492. LLMotion* motionp = gAgentAvatarp->findMotion(ANIM_AGENT_HEAD_ROT);
  493. if (motionp)
  494. {
  495. motionp->disable();
  496. }
  497. }
  498. void LLPuppetModule::enableHeadMotion() const
  499. {
  500. if (!isAgentAvatarValid())
  501. {
  502. return;
  503. }
  504. LLMotion* motionp = gAgentAvatarp->findMotion(ANIM_AGENT_HEAD_ROT);
  505. if (motionp)
  506. {
  507. motionp->enable();
  508. }
  509. }
  510. void LLPuppetModule::clearLeapModule()
  511. {
  512. llinfos << "Sending 'stop' command to Leap module" << llendl;
  513. sendCommand("stop");
  514. enableHeadMotion();
  515. mActiveJoints.clear();
  516. if (isAgentAvatarValid())
  517. {
  518. gAgentAvatarp->stopMotion(ANIM_AGENT_PUPPET_MOTION);
  519. }
  520. mLeapModule.reset();
  521. }
  522. void LLPuppetModule::sendCommand(const std::string& command,
  523. const LLSD& args) const
  524. {
  525. puppet_module_ptr_t mod = getLeapModule();
  526. if (mod)
  527. {
  528. LLSD data;
  529. data["command"] = command;
  530. // args is optional
  531. if (args.isDefined())
  532. {
  533. data["args"] = args;
  534. }
  535. LL_DEBUGS("Puppetry") << "Posting to Leap module: " << command
  536. << LL_ENDL;
  537. gEventPumps.post("puppetry.controller", data);
  538. }
  539. else
  540. {
  541. LL_DEBUGS("Puppetry") << "Puppet module not loaded, dropping command: "
  542. << command << LL_ENDL;
  543. }
  544. }
  545. void LLPuppetModule::setCameraNumber(S32 num)
  546. {
  547. setCameraNumber_(num);
  548. // For a C++ caller, also send the new camera number to the LEAP module.
  549. sendCameraNumber();
  550. }
  551. void LLPuppetModule::setCameraNumber_(S32 num)
  552. {
  553. gSavedSettings.setS32("PuppetryCamera", num);
  554. llinfos << "Camera number set to " << num << llendl;
  555. }
  556. S32 LLPuppetModule::getCameraNumber() const
  557. {
  558. return gSavedSettings.getS32("PuppetryCamera");
  559. }
  560. void LLPuppetModule::getCameraNumber_(const LLSD& request) const
  561. {
  562. // Response sends a reply on destruction.
  563. Response response(llsd::map("camera_id", getCameraNumber()), request);
  564. }
  565. void LLPuppetModule::sendCameraNumber()
  566. {
  567. sendCommand("set_camera", llsd::map("camera_id", getCameraNumber()));
  568. }
  569. void LLPuppetModule::sendReport(const LLSD& sd)
  570. {
  571. sendCommand("joint_report", sd);
  572. }
  573. void LLPuppetModule::sendSkeleton(const LLSD& sd)
  574. {
  575. if (!isAgentAvatarValid())
  576. {
  577. return;
  578. }
  579. LLPuppetMotion* motionp = gAgentAvatarp->getPuppetMotion();
  580. if (motionp)
  581. {
  582. sendCommand("set_skeleton", motionp->getSkeletonData());
  583. }
  584. else
  585. {
  586. llwarns << "No puppet motion found on self" << llendl;
  587. }
  588. }
  589. void LLPuppetModule::sendEnabledParts()
  590. {
  591. sendCommand("enable_parts", llsd::map("parts_mask", getEnabledPart()));
  592. }
  593. // Enables puppetry on body part: head, face, left/right hands...
  594. void LLPuppetModule::setEnabledPart(S32 part_num, bool enable)
  595. {
  596. S32 cur_setting = gSavedSettings.getS32("PuppetryParts") & PPM_ALL;
  597. part_num = part_num & PPM_ALL;
  598. if (enable)
  599. {
  600. cur_setting = cur_setting | part_num;
  601. }
  602. else
  603. {
  604. cur_setting = cur_setting & ~part_num;
  605. }
  606. gSavedSettings.setS32("PuppetryParts", cur_setting);
  607. llinfos << "Puppetry enabled parts mask now " << cur_setting << llendl;
  608. sendEnabledParts(); // Send to module
  609. }
  610. S32 LLPuppetModule::getEnabledPart(S32 mask) const
  611. {
  612. return gSavedSettings.getS32("PuppetryParts") & mask;
  613. }
  614. void LLPuppetModule::addActiveJoint(const std::string& joint_name)
  615. {
  616. mActiveJoints[joint_name] = LLFrameTimer::getTotalSeconds();
  617. }
  618. bool LLPuppetModule::isActiveJoint(const std::string& joint_name)
  619. {
  620. active_joint_map_t::iterator iter = mActiveJoints.find(joint_name);
  621. if (iter != mActiveJoints.end())
  622. {
  623. F64 age = LLFrameTimer::getTotalSeconds() - iter->second;
  624. const F64 PUPPET_SHOW_BONE_AGE = 3.0;
  625. if (age < PUPPET_SHOW_BONE_AGE)
  626. {
  627. // It was recently active
  628. return true;
  629. }
  630. // Delete old data and return not found
  631. mActiveJoints.erase(iter);
  632. }
  633. return false; // Not found
  634. }
  635. void LLPuppetModule::setEcho(bool play_server_echo)
  636. {
  637. setPuppetryOptions(LLSDMap("echo_back", play_server_echo));
  638. }
  639. void LLPuppetModule::setSending(bool sending)
  640. {
  641. setPuppetryOptions(LLSDMap("transmit", sending));
  642. }
  643. void LLPuppetModule::setReceiving(bool receiving)
  644. {
  645. setPuppetryOptions(LLSDMap("receive", receiving));
  646. }
  647. void LLPuppetModule::setRange(F32 range)
  648. {
  649. setPuppetryOptions(LLSDMap("range", range));
  650. }
  651. void LLPuppetModule::setPuppetryOptions(LLSD options)
  652. {
  653. const std::string& url = gAgent.getRegionCapability("Puppetry");
  654. if (url.empty())
  655. {
  656. llwarns << "No Puppetry capability in this region." << llendl;
  657. return;
  658. }
  659. // Start up coroutine to set puppetry options.
  660. if (options.has("echo_back") && options["echo_back"].asBoolean())
  661. {
  662. // Echo implies both transmit and receive.
  663. options["transmit"] = true;
  664. options["receive"] = true;
  665. }
  666. gCoros.launch("setPuppetryOptionsCoro",
  667. [url, options]()
  668. {
  669. LLPuppetModule::setPuppetryOptionsCoro(url, options);
  670. });
  671. }
  672. void LLPuppetModule::parsePuppetryResponse(const LLSD& response)
  673. {
  674. mPlayServerEcho = response["echo_back"].asBoolean();
  675. mIsSending = response["transmit"].asBoolean();
  676. mIsReceiving = response["receive"].asBoolean();
  677. mRange = response["range"].asReal();
  678. // *TODO Mute list and subscribe
  679. llinfos << "Set puppetry parameters from server: echo is "
  680. << (mPlayServerEcho ? "on" : "off") << ", transmit is "
  681. << (mIsSending ? "on" : "off") << ", receiving is "
  682. << (mIsReceiving? "on" : "off") << ", receiving range is "
  683. << mRange << "m" << llendl;
  684. }
  685. //static
  686. void LLPuppetModule::setPuppetryOptionsCoro(const std::string& url,
  687. LLSD options)
  688. {
  689. LLCoreHttpUtil::HttpCoroutineAdapter adapter("setPuppetryOptionsCoro");
  690. LLSD result;
  691. LLCore::HttpStatus status;
  692. S32 retry_count = 0;
  693. while (true)
  694. {
  695. LLSD data = LLSD::emptyMap();
  696. if (options.has("echo_back"))
  697. {
  698. data["echo_back"] = options["echo_back"].asBoolean();
  699. }
  700. if (options.has("transmit"))
  701. {
  702. data["transmit"] = options["transmit"].asBoolean();
  703. }
  704. if (options.has("receive"))
  705. {
  706. data["receive"] = options["receive"].asBoolean();
  707. }
  708. if (options.has("range"))
  709. {
  710. data["range"] = options["range"].asReal();
  711. }
  712. result = adapter.postAndSuspend(url, data);
  713. status =
  714. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  715. if (status.getType() == HTTP_NOT_FOUND)
  716. {
  717. // There seems to be a case at first login where the simulator is
  718. // slow getting all of the caps connected for the agent. It has
  719. // given us back the cap URL but returns a 404 when we try and hit
  720. // it. Pause, take a breath and give it another shot.
  721. if (++retry_count >= 3)
  722. {
  723. llwarns << "Failed to set puppetry echo status after 3 retries."
  724. << llendl;
  725. return;
  726. }
  727. llcoro::suspendUntilTimeout(0.25f);
  728. }
  729. else if (!status)
  730. {
  731. llwarns << "Failed to set puppetry echo status with "
  732. << status.getMessage() << " - Body: " << result << llendl;
  733. return;
  734. }
  735. else
  736. {
  737. break; // Success
  738. }
  739. }
  740. LLPuppetModule::getInstance()->parsePuppetryResponse(result);
  741. }
  742. // I added a way to remember the echo via a debug setting. Let's observe it and
  743. // sync the echo status when needed. HB
  744. //static
  745. void LLPuppetModule::settingsObserver()
  746. {
  747. if (!LLPuppetMotion::enabled())
  748. {
  749. return;
  750. }
  751. LLPuppetModule* self = LLPuppetModule::getInstance();
  752. if (self->havePuppetModule())
  753. {
  754. bool new_echo = gSavedSettings.getBool("PuppetryUseServerEcho");
  755. self->setEcho(new_echo);
  756. if (new_echo)
  757. {
  758. // If we want echo from the server, we need to have receiving on
  759. self->setReceiving(true);
  760. }
  761. }
  762. }