llviewermediafocus.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:17k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llviewermediafocus.cpp
  3.  * @brief Governs focus on Media prims
  4.  *
  5.  * $LicenseInfo:firstyear=2003&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2003-2010, 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 "llviewermediafocus.h"
  34. //LLViewerMediaFocus
  35. #include "llviewerobjectlist.h"
  36. #include "llpanelprimmediacontrols.h"
  37. #include "llpluginclassmedia.h"
  38. #include "llagent.h"
  39. #include "lltoolpie.h"
  40. #include "llviewercamera.h"
  41. #include "llviewermedia.h"
  42. #include "llhudview.h"
  43. #include "lluictrlfactory.h"
  44. #include "lldrawable.h"
  45. #include "llparcel.h"
  46. #include "llviewerparcelmgr.h"
  47. #include "llweb.h"
  48. #include "llmediaentry.h"
  49. #include "llkeyboard.h"
  50. #include "lltoolmgr.h"
  51. #include "llvovolume.h"
  52. #include "llhelp.h"
  53. //
  54. // LLViewerMediaFocus
  55. //
  56. LLViewerMediaFocus::LLViewerMediaFocus()
  57. : mFocusedObjectFace(0),
  58. mHoverObjectFace(0)
  59. {
  60. }
  61. LLViewerMediaFocus::~LLViewerMediaFocus()
  62. {
  63. // The destructor for LLSingletons happens at atexit() time, which is too late to do much.
  64. // Clean up in cleanupClass() instead.
  65. }
  66. void LLViewerMediaFocus::setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal)
  67. {
  68. LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
  69. LLViewerMediaImpl *old_media_impl = getFocusedMediaImpl();
  70. if(old_media_impl)
  71. {
  72. old_media_impl->focus(false);
  73. }
  74. // Always clear the current selection.  If we're setting focus on a face, we'll reselect the correct object below.
  75. LLSelectMgr::getInstance()->deselectAll();
  76. mSelection = NULL;
  77. if (media_impl.notNull() && objectp.notNull())
  78. {
  79. bool face_auto_zoom = false;
  80. mFocusedImplID = media_impl->getMediaTextureID();
  81. mFocusedObjectID = objectp->getID();
  82. mFocusedObjectFace = face;
  83. mFocusedObjectNormal = pick_normal;
  84. // Set the selection in the selection manager so we can draw the focus ring.
  85. mSelection = LLSelectMgr::getInstance()->selectObjectOnly(objectp, face);
  86. // Focusing on a media face clears its disable flag.
  87. media_impl->setDisabled(false);
  88. LLTextureEntry* tep = objectp->getTE(face);
  89. if(tep->hasMedia())
  90. {
  91. LLMediaEntry* mep = tep->getMediaData();
  92. face_auto_zoom = mep->getAutoZoom();
  93. if(!media_impl->hasMedia())
  94. {
  95. std::string url = mep->getCurrentURL().empty() ? mep->getHomeURL() : mep->getCurrentURL();
  96. media_impl->navigateTo(url, "", true);
  97. }
  98. }
  99. else
  100. {
  101. // This should never happen.
  102. llwarns << "Can't find media entry for focused face" << llendl;
  103. }
  104. media_impl->focus(true);
  105. gFocusMgr.setKeyboardFocus(this);
  106. // We must do this before  processing the media HUD zoom, or it may zoom to the wrong face. 
  107. update();
  108. if(mMediaControls.get())
  109. {
  110. if(face_auto_zoom && ! parcel->getMediaPreventCameraZoom())
  111. {
  112. // Zoom in on this face
  113. mMediaControls.get()->resetZoomLevel();
  114. mMediaControls.get()->nextZoomLevel();
  115. }
  116. else
  117. {
  118. // Reset the controls' zoom level without moving the camera.
  119. // This fixes the case where clicking focus between two non-autozoom faces doesn't change the zoom-out button back to a zoom-in button.
  120. mMediaControls.get()->resetZoomLevel(false);
  121. }
  122. }
  123. }
  124. else
  125. {
  126. if(hasFocus())
  127. {
  128. gFocusMgr.setKeyboardFocus(NULL);
  129. }
  130. mFocusedImplID = LLUUID::null;
  131. if (objectp.notNull())
  132. {
  133. // Still record the focused object...it may mean we need to load media data.
  134. // This will aid us in determining this object is "important enough"
  135. mFocusedObjectID = objectp->getID();
  136. mFocusedObjectFace = face;
  137. }
  138. else {
  139. mFocusedObjectID = LLUUID::null;
  140. mFocusedObjectFace = 0;
  141. }
  142. }
  143. }
  144. void LLViewerMediaFocus::clearFocus()
  145. {
  146. setFocusFace(NULL, 0, NULL);
  147. }
  148. void LLViewerMediaFocus::setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal)
  149. {
  150. if (media_impl.notNull())
  151. {
  152. mHoverImplID = media_impl->getMediaTextureID();
  153. mHoverObjectID = objectp->getID();
  154. mHoverObjectFace = face;
  155. mHoverObjectNormal = pick_normal;
  156. }
  157. else
  158. {
  159. mHoverObjectID = LLUUID::null;
  160. mHoverObjectFace = 0;
  161. mHoverImplID = LLUUID::null;
  162. }
  163. }
  164. void LLViewerMediaFocus::clearHover()
  165. {
  166. setHoverFace(NULL, 0, NULL);
  167. }
  168. bool LLViewerMediaFocus::getFocus()
  169. {
  170. if (gFocusMgr.getKeyboardFocus() == this)
  171. {
  172. return true;
  173. }
  174. return false;
  175. }
  176. // This function selects an ideal viewing distance based on the focused object, pick normal, and padding value
  177. void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only)
  178. {
  179. if (object)
  180. {
  181. gAgent.setFocusOnAvatar(FALSE, ANIMATE);
  182. LLBBox bbox = object->getBoundingBoxAgent();
  183. LLVector3d center = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent());
  184. F32 height;
  185. F32 width;
  186. F32 depth;
  187. F32 angle_of_view;
  188. F32 distance;
  189. // We need the aspect ratio, and the 3 components of the bbox as height, width, and depth.
  190. F32 aspect_ratio = getBBoxAspectRatio(bbox, normal, &height, &width, &depth);
  191. F32 camera_aspect = LLViewerCamera::getInstance()->getAspect();
  192. // We will normally use the side of the volume aligned with the short side of the screen (i.e. the height for 
  193. // a screen in a landscape aspect ratio), however there is an edge case where the aspect ratio of the object is 
  194. // more extreme than the screen.  In this case we invert the logic, using the longer component of both the object
  195. // and the screen.  
  196. bool invert = (camera_aspect > 1.0f && aspect_ratio > camera_aspect) ||
  197. (camera_aspect < 1.0f && aspect_ratio < camera_aspect);
  198. // To calculate the optimum viewing distance we will need the angle of the shorter side of the view rectangle.
  199. // In portrait mode this is the width, and in landscape it is the height.
  200. // We then calculate the distance based on the corresponding side of the object bbox (width for portrait, height for landscape)
  201. // We will add half the depth of the bounding box, as the distance projection uses the center point of the bbox.
  202. if(camera_aspect < 1.0f || invert)
  203. {
  204. angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect());
  205. distance = width * 0.5 * padding_factor / tan(angle_of_view * 0.5f );
  206. }
  207. else
  208. {
  209. angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView());
  210. distance = height * 0.5 * padding_factor / tan(angle_of_view * 0.5f );
  211. }
  212. distance += depth * 0.5;
  213. // Finally animate the camera to this new position and focal point
  214. LLVector3d camera_pos, target_pos;
  215. // The target lookat position is the center of the selection (in global coords)
  216. target_pos = center;
  217. // Target look-from (camera) position is "distance" away from the target along the normal 
  218. LLVector3d pickNormal = LLVector3d(normal);
  219. pickNormal.normalize();
  220.         camera_pos = target_pos + pickNormal * distance;
  221.         if (pickNormal == LLVector3d::z_axis || pickNormal == LLVector3d::z_axis_neg)
  222.         {
  223. // If the normal points directly up, the camera will "flip" around.
  224. // We try to avoid this by adjusting the target camera position a 
  225. // smidge towards current camera position
  226. // *NOTE: this solution is not perfect.  All it attempts to solve is the
  227. // "looking down" problem where the camera flips around when it animates
  228. // to that position.  You still are not guaranteed to be looking at the
  229. // media in the correct orientation.  What this solution does is it will
  230. // put the camera into position keeping as best it can the current 
  231. // orientation with respect to the face.  In other words, if before zoom
  232. // the media appears "upside down" from the camera, after zooming it will
  233. // still be upside down, but at least it will not flip.
  234.             LLVector3d cur_camera_pos = LLVector3d(gAgent.getCameraPositionGlobal());
  235.             LLVector3d delta = (cur_camera_pos - camera_pos);
  236.             F64 len = delta.length();
  237.             delta.normalize();
  238.             // Move 1% of the distance towards original camera location
  239.             camera_pos += 0.01 * len * delta;
  240.         }
  241. // If we are not allowing zooming out and the old camera position is closer to 
  242. // the center then the new intended camera position, don't move camera and return
  243. if (zoom_in_only &&
  244.     (dist_vec_squared(gAgent.getCameraPositionGlobal(), target_pos) < dist_vec_squared(camera_pos, target_pos)))
  245. {
  246. return;
  247. }
  248. gAgent.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() );
  249. }
  250. else
  251. {
  252. // If we have no object, focus back on the avatar.
  253. gAgent.setFocusOnAvatar(TRUE, ANIMATE);
  254. }
  255. }
  256. void LLViewerMediaFocus::onFocusReceived()
  257. {
  258. LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
  259. if(media_impl)
  260. media_impl->focus(true);
  261. LLFocusableElement::onFocusReceived();
  262. }
  263. void LLViewerMediaFocus::onFocusLost()
  264. {
  265. LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
  266. if(media_impl)
  267. media_impl->focus(false);
  268. gViewerWindow->focusClient();
  269. LLFocusableElement::onFocusLost();
  270. }
  271. BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent)
  272. {
  273. LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
  274. if(media_impl)
  275. {
  276. media_impl->handleKeyHere(key, mask);
  277. if (KEY_ESCAPE == key)
  278. {
  279. // Reset camera zoom in this case.
  280. if(mFocusedImplID.notNull())
  281. {
  282. if(mMediaControls.get())
  283. {
  284. mMediaControls.get()->resetZoomLevel(true);
  285. }
  286. }
  287. clearFocus();
  288. }
  289. if ( KEY_F1 == key && LLUI::sHelpImpl && mMediaControls.get())
  290. {
  291. std::string help_topic;
  292. if (mMediaControls.get()->findHelpTopic(help_topic))
  293. {
  294. LLUI::sHelpImpl->showTopic(help_topic);
  295. }
  296. }
  297. }
  298. return true;
  299. }
  300. BOOL LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
  301. {
  302. LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
  303. if(media_impl)
  304. media_impl->handleUnicodeCharHere(uni_char);
  305. return true;
  306. }
  307. BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks)
  308. {
  309. BOOL retval = FALSE;
  310. LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
  311. if(media_impl && media_impl->hasMedia())
  312. {
  313.         // the scrollEvent() API's x and y are not the same as handleScrollWheel's x and y.
  314.         // The latter is the position of the mouse at the time of the event
  315.         // The former is the 'scroll amount' in x and y, respectively.
  316.         // All we have for 'scroll amount' here is 'clicks'.
  317. // We're also not passed the keyboard modifier mask, but we can get that from gKeyboard.
  318. media_impl->getMediaPlugin()->scrollEvent(0, clicks, gKeyboard->currentMask(TRUE));
  319. retval = TRUE;
  320. }
  321. return retval;
  322. }
  323. void LLViewerMediaFocus::update()
  324. {
  325. if(mFocusedImplID.notNull())
  326. {
  327. // We have a focused impl/face.
  328. if(!getFocus())
  329. {
  330. // We've lost keyboard focus -- check to see whether the media controls have it
  331. if(mMediaControls.get() && mMediaControls.get()->hasFocus())
  332. {
  333. // the media controls have focus -- don't clear.
  334. }
  335. else
  336. {
  337. // Someone else has focus -- back off.
  338. clearFocus();
  339. }
  340. }
  341. else if(LLToolMgr::getInstance()->inBuildMode())
  342. {
  343. // Build tools are selected -- clear focus.
  344. clearFocus();
  345. }
  346. }
  347. LLViewerMediaImpl *media_impl = getFocusedMediaImpl();
  348. LLViewerObject *viewer_object = getFocusedObject();
  349. S32 face = mFocusedObjectFace;
  350. LLVector3 normal = mFocusedObjectNormal;
  351. bool focus = true;
  352. if(!media_impl || !viewer_object)
  353. {
  354. focus = false;
  355. media_impl = getHoverMediaImpl();
  356. viewer_object = getHoverObject();
  357. face = mHoverObjectFace;
  358. normal = mHoverObjectNormal;
  359. }
  360. if(media_impl && viewer_object)
  361. {
  362. // We have an object and impl to point at.
  363. // Make sure the media HUD object exists.
  364. if(! mMediaControls.get())
  365. {
  366. LLPanelPrimMediaControls* media_controls = new LLPanelPrimMediaControls();
  367. mMediaControls = media_controls->getHandle();
  368. gHUDView->addChild(media_controls);
  369. }
  370. mMediaControls.get()->setMediaFace(viewer_object, face, media_impl, normal);
  371. }
  372. else
  373. {
  374. // The media HUD is no longer needed.
  375. if(mMediaControls.get())
  376. {
  377. mMediaControls.get()->setMediaFace(NULL, 0, NULL);
  378. }
  379. }
  380. }
  381. // This function calculates the aspect ratio and the world aligned components of a selection bounding box.
  382. F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth)
  383. {
  384. // Convert the selection normal and an up vector to local coordinate space of the bbox
  385. LLVector3 local_normal = bbox.agentToLocalBasis(normal);
  386. LLVector3 z_vec = bbox.agentToLocalBasis(LLVector3(0.0f, 0.0f, 1.0f));
  387. LLVector3 comp1(0.f,0.f,0.f);
  388. LLVector3 comp2(0.f,0.f,0.f);
  389. LLVector3 bbox_max = bbox.getExtentLocal();
  390. F32 dot1 = 0.f;
  391. F32 dot2 = 0.f;
  392. // The largest component of the localized normal vector is the depth component
  393. // meaning that the other two are the legs of the rectangle.
  394. local_normal.abs();
  395. if(local_normal.mV[VX] > local_normal.mV[VY])
  396. {
  397. if(local_normal.mV[VX] > local_normal.mV[VZ])
  398. {
  399. // Use the y and z comps
  400. comp1.mV[VY] = bbox_max.mV[VY];
  401. comp2.mV[VZ] = bbox_max.mV[VZ];
  402. *depth = bbox_max.mV[VX];
  403. }
  404. else
  405. {
  406. // Use the x and y comps
  407. comp1.mV[VY] = bbox_max.mV[VY];
  408. comp2.mV[VZ] = bbox_max.mV[VZ];
  409. *depth = bbox_max.mV[VZ];
  410. }
  411. }
  412. else if(local_normal.mV[VY] > local_normal.mV[VZ])
  413. {
  414. // Use the x and z comps
  415. comp1.mV[VX] = bbox_max.mV[VX];
  416. comp2.mV[VZ] = bbox_max.mV[VZ];
  417. *depth = bbox_max.mV[VY];
  418. }
  419. else
  420. {
  421. // Use the x and y comps
  422. comp1.mV[VY] = bbox_max.mV[VY];
  423. comp2.mV[VZ] = bbox_max.mV[VZ];
  424. *depth = bbox_max.mV[VX];
  425. }
  426. // The height is the vector closest to vertical in the bbox coordinate space (highest dot product value)
  427. dot1 = comp1 * z_vec;
  428. dot2 = comp2 * z_vec;
  429. if(fabs(dot1) > fabs(dot2))
  430. {
  431. *height = comp1.length();
  432. *width = comp2.length();
  433. }
  434. else
  435. {
  436. *height = comp2.length();
  437. *width = comp1.length();
  438. }
  439. // Return the aspect ratio.
  440. return *width / *height;
  441. }
  442. bool LLViewerMediaFocus::isFocusedOnFace(LLPointer<LLViewerObject> objectp, S32 face)
  443. {
  444. return objectp->getID() == mFocusedObjectID && face == mFocusedObjectFace;
  445. }
  446. bool LLViewerMediaFocus::isHoveringOverFace(LLPointer<LLViewerObject> objectp, S32 face)
  447. {
  448. return objectp->getID() == mHoverObjectID && face == mHoverObjectFace;
  449. }
  450. LLViewerMediaImpl* LLViewerMediaFocus::getFocusedMediaImpl()
  451. {
  452. return LLViewerMedia::getMediaImplFromTextureID(mFocusedImplID);
  453. }
  454. LLViewerObject* LLViewerMediaFocus::getFocusedObject()
  455. {
  456. return gObjectList.findObject(mFocusedObjectID);
  457. }
  458. LLViewerMediaImpl* LLViewerMediaFocus::getHoverMediaImpl()
  459. {
  460. return LLViewerMedia::getMediaImplFromTextureID(mHoverImplID);
  461. }
  462. LLViewerObject* LLViewerMediaFocus::getHoverObject()
  463. {
  464. return gObjectList.findObject(mHoverObjectID);
  465. }
  466. void LLViewerMediaFocus::focusZoomOnMedia(LLUUID media_id)
  467. {
  468. LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(media_id);
  469. if(impl)
  470. {
  471. // Get the first object from the media impl's object list.  This is completely arbitrary, but should suffice.
  472. LLVOVolume *obj = impl->getSomeObject();
  473. if(obj)
  474. {
  475. // This media is attached to at least one object.  Figure out which face it's on.
  476. S32 face = obj->getFaceIndexWithMediaImpl(impl, -1);
  477. // We don't have a proper pick normal here, and finding a face's real normal is... complicated.
  478. LLVector3 normal = obj->getApproximateFaceNormal(face);
  479. if(normal.isNull())
  480. {
  481. // If that didn't work, use the inverse of the camera "look at" axis, which should keep the camera pointed in the same direction.
  482. // llinfos << "approximate face normal invalid, using camera direction." << llendl;
  483. normal = LLViewerCamera::getInstance()->getAtAxis();
  484. normal *= (F32)-1.0f;
  485. }
  486. // Attempt to focus/zoom on that face.
  487. setFocusFace(obj, face, impl, normal);
  488. if(mMediaControls.get())
  489. {
  490. mMediaControls.get()->resetZoomLevel();
  491. mMediaControls.get()->nextZoomLevel();
  492. }
  493. }
  494. }
  495. }
  496. void LLViewerMediaFocus::unZoom()
  497. {
  498. if(mMediaControls.get())
  499. {
  500. mMediaControls.get()->resetZoomLevel();
  501. }
  502. }
  503. bool LLViewerMediaFocus::isZoomed() const
  504. {
  505. return (mMediaControls.get() && mMediaControls.get()->getZoomLevel() != LLPanelPrimMediaControls::ZOOM_NONE);
  506. }
  507. LLUUID LLViewerMediaFocus::getControlsMediaID()
  508. {
  509. if(getFocusedMediaImpl())
  510. {
  511. return mFocusedImplID;
  512. }
  513. else if(getHoverMediaImpl())
  514. {
  515. return mHoverImplID;
  516. }
  517. return LLUUID::null;
  518. }