PageRenderTime 68ms CodeModel.GetById 3ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llviewermediafocus.cpp

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