PageRenderTime 78ms CodeModel.GetById 16ms app.highlight 54ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/newview/lljoystickbutton.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 642 lines | 450 code | 109 blank | 83 comment | 87 complexity | 7b85dba591576acd98fb6a215ee44177 MD5 | raw file
  1/** 
  2 * @file lljoystickbutton.cpp
  3 * @brief LLJoystick class implementation
  4 *
  5 * $LicenseInfo:firstyear=2001&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 "lljoystickbutton.h"
 30
 31// Library includes
 32#include "llcoord.h"
 33#include "indra_constants.h"
 34#include "llrender.h"
 35
 36// Project includes
 37#include "llui.h"
 38#include "llagent.h"
 39#include "llagentcamera.h"
 40#include "llviewertexture.h"
 41#include "llviewertexturelist.h"
 42#include "llviewerwindow.h"
 43#include "llmoveview.h"
 44
 45#include "llglheaders.h"
 46
 47static LLDefaultChildRegistry::Register<LLJoystickAgentSlide> r1("joystick_slide");
 48static LLDefaultChildRegistry::Register<LLJoystickAgentTurn> r2("joystick_turn");
 49static LLDefaultChildRegistry::Register<LLJoystickCameraRotate> r3("joystick_rotate");
 50static LLDefaultChildRegistry::Register<LLJoystickCameraTrack> r5("joystick_track");
 51
 52
 53
 54const F32 NUDGE_TIME = 0.25f;		// in seconds
 55const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
 56
 57//
 58// Public Methods
 59//
 60void QuadrantNames::declareValues()
 61{
 62	declare("origin", JQ_ORIGIN);
 63	declare("up", JQ_UP);
 64	declare("down", JQ_DOWN);
 65	declare("left", JQ_LEFT);
 66	declare("right", JQ_RIGHT);
 67}
 68
 69
 70LLJoystick::LLJoystick(const LLJoystick::Params& p)
 71:	LLButton(p),
 72	mInitialOffset(0, 0),
 73	mLastMouse(0, 0),
 74	mFirstMouse(0, 0),
 75	mVertSlopNear(0),
 76	mVertSlopFar(0),
 77	mHorizSlopNear(0),
 78	mHorizSlopFar(0),
 79	mHeldDown(FALSE),
 80	mHeldDownTimer(),
 81	mInitialQuadrant(p.quadrant)
 82{
 83	setHeldDownCallback(&LLJoystick::onBtnHeldDown, this);
 84}
 85
 86
 87void LLJoystick::updateSlop()
 88{
 89	mVertSlopNear = getRect().getHeight();
 90	mVertSlopFar = getRect().getHeight() * 2;
 91
 92	mHorizSlopNear = getRect().getWidth();
 93	mHorizSlopFar = getRect().getWidth() * 2;
 94
 95	// Compute initial mouse offset based on initial quadrant.
 96	// Place the mouse evenly between the near and far zones.
 97	switch (mInitialQuadrant)
 98	{
 99	case JQ_ORIGIN:
100		mInitialOffset.set(0, 0);
101		break;
102
103	case JQ_UP:
104		mInitialOffset.mX = 0;
105		mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
106		break;
107
108	case JQ_DOWN:
109		mInitialOffset.mX = 0;
110		mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
111		break;
112
113	case JQ_LEFT:
114		mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
115		mInitialOffset.mY = 0;
116		break;
117
118	case JQ_RIGHT:
119		mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
120		mInitialOffset.mY = 0;
121		break;
122
123	default:
124		llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
125		break;
126	}
127
128	return;
129}
130
131bool LLJoystick::pointInCircle(S32 x, S32 y) const 
132{ 
133	if(this->getLocalRect().getHeight() != this->getLocalRect().getWidth())
134	{
135		llwarns << "Joystick shape is not square"<<llendl;
136		return true;
137	}
138	//center is x and y coordinates of center of joystick circle, and also its radius
139	int center = this->getLocalRect().getHeight()/2;
140	bool in_circle = (x - center) * (x - center) + (y - center) * (y - center) <= center * center;
141	return in_circle;
142}
143
144BOOL LLJoystick::handleMouseDown(S32 x, S32 y, MASK mask)
145{
146	//llinfos << "joystick mouse down " << x << ", " << y << llendl;
147	bool handles = false;
148
149	if(pointInCircle(x, y))
150	{
151		mLastMouse.set(x, y);
152		mFirstMouse.set(x, y);
153		mMouseDownTimer.reset();
154		handles = LLButton::handleMouseDown(x, y, mask);
155	}
156
157	return handles;
158}
159
160
161BOOL LLJoystick::handleMouseUp(S32 x, S32 y, MASK mask)
162{
163	// llinfos << "joystick mouse up " << x << ", " << y << llendl;
164
165	if( hasMouseCapture() )
166	{
167		mLastMouse.set(x, y);
168		mHeldDown = FALSE;
169		onMouseUp();
170	}
171
172	return LLButton::handleMouseUp(x, y, mask);
173}
174
175
176BOOL LLJoystick::handleHover(S32 x, S32 y, MASK mask)
177{
178	if( hasMouseCapture() )
179	{
180		mLastMouse.set(x, y);
181	}
182
183	return LLButton::handleHover(x, y, mask);
184}
185
186F32 LLJoystick::getElapsedHeldDownTime()
187{
188	if( mHeldDown )
189	{
190		return getHeldDownTime();
191	}
192	else
193	{
194		return 0.f;
195	}
196}
197
198// static
199void LLJoystick::onBtnHeldDown(void *userdata)
200{
201	LLJoystick *self = (LLJoystick *)userdata;
202	if (self)
203	{
204		self->mHeldDown = TRUE;
205		self->onHeldDown();
206	}
207}
208
209EJoystickQuadrant LLJoystick::selectQuadrant(LLXMLNodePtr node)
210{
211	
212	EJoystickQuadrant quadrant = JQ_RIGHT;
213
214	if (node->hasAttribute("quadrant"))
215	{
216		std::string quadrant_name;
217		node->getAttributeString("quadrant", quadrant_name);
218
219		quadrant = quadrantFromName(quadrant_name);
220	}
221	return quadrant;
222}
223
224
225std::string LLJoystick::nameFromQuadrant(EJoystickQuadrant	quadrant)
226{
227	if (quadrant == JQ_ORIGIN)	    return std::string("origin");
228	else if (quadrant == JQ_UP)	    return std::string("up");
229	else if (quadrant == JQ_DOWN)	return std::string("down");
230	else if (quadrant == JQ_LEFT)	return std::string("left");
231	else if (quadrant == JQ_RIGHT)	return std::string("right");
232	else return std::string();
233}
234
235
236EJoystickQuadrant LLJoystick::quadrantFromName(const std::string& sQuadrant)
237{
238	EJoystickQuadrant quadrant = JQ_RIGHT;
239
240	if (sQuadrant == "origin")
241	{
242		quadrant = JQ_ORIGIN;
243	}
244	else if (sQuadrant == "up")
245	{
246		quadrant = JQ_UP;
247	}
248	else if (sQuadrant == "down")
249	{
250		quadrant = JQ_DOWN;
251	}
252	else if (sQuadrant == "left")
253	{
254		quadrant = JQ_LEFT;
255	}
256	else if (sQuadrant == "right")
257	{
258		quadrant = JQ_RIGHT;
259	}
260
261	return quadrant;
262}
263
264
265//-------------------------------------------------------------------------------
266// LLJoystickAgentTurn
267//-------------------------------------------------------------------------------
268
269void LLJoystickAgentTurn::onHeldDown()
270{
271	F32 time = getElapsedHeldDownTime();
272	updateSlop();
273
274	//llinfos << "move forward/backward (and/or turn)" << llendl;
275
276	S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
277	S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
278
279	float m = (float) (dx)/abs(dy);
280	
281	if (m > 1) {
282		m = 1;
283	}
284	else if (m < -1) {
285		m = -1;
286	}
287	gAgent.moveYaw(-LLFloaterMove::getYawRate(time)*m);
288	
289
290	// handle forward/back movement
291	if (dy > mVertSlopFar)
292	{
293		// ...if mouse is forward of run region run forward
294		gAgent.moveAt(1);
295	}
296	else if (dy > mVertSlopNear)
297	{
298		if( time < NUDGE_TIME )
299		{
300			gAgent.moveAtNudge(1);
301		}
302		else
303		{
304			// ...else if mouse is forward of walk region walk forward
305			// JC 9/5/2002 - Always run / move quickly.
306			gAgent.moveAt(1);
307		}
308	}
309	else if (dy < -mVertSlopFar)
310	{
311		// ...else if mouse is behind run region run backward
312		gAgent.moveAt(-1);
313	}
314	else if (dy < -mVertSlopNear)
315	{
316		if( time < NUDGE_TIME )
317		{
318			gAgent.moveAtNudge(-1);
319		}
320		else
321		{
322			// ...else if mouse is behind walk region walk backward
323			// JC 9/5/2002 - Always run / move quickly.
324			gAgent.moveAt(-1);
325		}
326	}
327}
328
329//-------------------------------------------------------------------------------
330// LLJoystickAgentSlide
331//-------------------------------------------------------------------------------
332
333void LLJoystickAgentSlide::onMouseUp()
334{
335	F32 time = getElapsedHeldDownTime();
336	if( time < NUDGE_TIME )
337	{
338		switch (mInitialQuadrant)
339		{
340		case JQ_LEFT:
341			gAgent.moveLeftNudge(1);
342			break;
343
344		case JQ_RIGHT:
345			gAgent.moveLeftNudge(-1);
346			break;
347
348		default:
349			break;
350		}
351	}
352}
353
354void LLJoystickAgentSlide::onHeldDown()
355{
356	//llinfos << "slide left/right (and/or move forward/backward)" << llendl;
357
358	updateSlop();
359
360	S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
361	S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
362
363	// handle left-right sliding
364	if (dx > mHorizSlopNear)
365	{
366		gAgent.moveLeft(-1);
367	}
368	else if (dx < -mHorizSlopNear)
369	{
370		gAgent.moveLeft(1);
371	}
372
373	// handle forward/back movement
374	if (dy > mVertSlopFar)
375	{
376		// ...if mouse is forward of run region run forward
377		gAgent.moveAt(1);
378	}
379	else if (dy > mVertSlopNear)
380	{
381		// ...else if mouse is forward of walk region walk forward
382		gAgent.moveAtNudge(1);
383	}
384	else if (dy < -mVertSlopFar)
385	{
386		// ...else if mouse is behind run region run backward
387		gAgent.moveAt(-1);
388	}
389	else if (dy < -mVertSlopNear)
390	{
391		// ...else if mouse is behind walk region walk backward
392		gAgent.moveAtNudge(-1);
393	}
394}
395
396
397//-------------------------------------------------------------------------------
398// LLJoystickCameraRotate
399//-------------------------------------------------------------------------------
400
401LLJoystickCameraRotate::LLJoystickCameraRotate(const LLJoystickCameraRotate::Params& p)
402:	LLJoystick(p), 
403	mInLeft( FALSE ),
404	mInTop( FALSE ),
405	mInRight( FALSE ),
406	mInBottom( FALSE )
407{ }
408
409
410void LLJoystickCameraRotate::updateSlop()
411{
412	// do the initial offset calculation based on mousedown location
413
414	// small fixed slop region
415	mVertSlopNear = 16;
416	mVertSlopFar = 32;
417
418	mHorizSlopNear = 16;
419	mHorizSlopFar = 32;
420
421	return;
422}
423
424
425BOOL LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask)
426{
427	updateSlop();
428
429	// Set initial offset based on initial click location
430	S32 horiz_center = getRect().getWidth() / 2;
431	S32 vert_center = getRect().getHeight() / 2;
432
433	S32 dx = x - horiz_center;
434	S32 dy = y - vert_center;
435
436	if (dy > dx && dy > -dx)
437	{
438		// top
439		mInitialOffset.mX = 0;
440		mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
441		mInitialQuadrant = JQ_UP;
442	}
443	else if (dy > dx && dy <= -dx)
444	{
445		// left
446		mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
447		mInitialOffset.mY = 0;
448		mInitialQuadrant = JQ_LEFT;
449	}
450	else if (dy <= dx && dy <= -dx)
451	{
452		// bottom
453		mInitialOffset.mX = 0;
454		mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
455		mInitialQuadrant = JQ_DOWN;
456	}
457	else
458	{
459		// right
460		mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
461		mInitialOffset.mY = 0;
462		mInitialQuadrant = JQ_RIGHT;
463	}
464
465	return LLJoystick::handleMouseDown(x, y, mask);
466}
467
468
469void LLJoystickCameraRotate::onHeldDown()
470{
471	updateSlop();
472
473	S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
474	S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
475
476	// left-right rotation
477	if (dx > mHorizSlopNear)
478	{
479		gAgentCamera.unlockView();
480		gAgentCamera.setOrbitLeftKey(getOrbitRate());
481	}
482	else if (dx < -mHorizSlopNear)
483	{
484		gAgentCamera.unlockView();
485		gAgentCamera.setOrbitRightKey(getOrbitRate());
486	}
487
488	// over/under rotation
489	if (dy > mVertSlopNear)
490	{
491		gAgentCamera.unlockView();
492		gAgentCamera.setOrbitUpKey(getOrbitRate());
493	}
494	else if (dy < -mVertSlopNear)
495	{
496		gAgentCamera.unlockView();
497		gAgentCamera.setOrbitDownKey(getOrbitRate());
498	}
499}
500
501F32 LLJoystickCameraRotate::getOrbitRate()
502{
503	F32 time = getElapsedHeldDownTime();
504	if( time < NUDGE_TIME )
505	{
506		F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
507		//llinfos << rate << llendl;
508		return rate;
509	}
510	else
511	{
512		return 1;
513	}
514}
515
516
517// Only used for drawing
518void LLJoystickCameraRotate::setToggleState( BOOL left, BOOL top, BOOL right, BOOL bottom )
519{
520	mInLeft = left;
521	mInTop = top;
522	mInRight = right;
523	mInBottom = bottom;
524}
525
526void LLJoystickCameraRotate::draw()
527{
528	LLGLSUIDefault gls_ui;
529
530	getImageUnselected()->draw( 0, 0 );
531	LLPointer<LLUIImage> image = getImageSelected();
532
533	if( mInTop )
534	{
535		drawRotatedImage( getImageSelected(), 0 );
536	}
537
538	if( mInRight )
539	{
540		drawRotatedImage( getImageSelected(), 1 );
541	}
542
543	if( mInBottom )
544	{
545		drawRotatedImage( getImageSelected(), 2 );
546	}
547
548	if( mInLeft )
549	{
550		drawRotatedImage( getImageSelected(), 3 );
551	}
552}
553
554// Draws image rotated by multiples of 90 degrees
555void LLJoystickCameraRotate::drawRotatedImage( LLPointer<LLUIImage> image, S32 rotations )
556{
557	S32 width = image->getWidth();
558	S32 height = image->getHeight();
559	LLTexture* texture = image->getImage();
560
561	/*
562	 * Scale  texture coordinate system 
563	 * to handle the different between image size and size of texture.
564	 * If we will use default matrix, 
565	 * it may break texture mapping after rotation.
566	 * see EXT-2023 Camera floater: arrows became shifted when pressed.
567	 */ 
568	F32 uv[][2] = 
569	{
570		{ (F32)width/texture->getWidth(), (F32)height/texture->getHeight() },
571		{ 0.f, (F32)height/texture->getHeight() },
572		{ 0.f, 0.f },
573		{ (F32)width/texture->getWidth(), 0.f }
574	};
575
576	gGL.getTexUnit(0)->bind(texture);
577
578	gGL.color4fv(UI_VERTEX_COLOR.mV);
579	
580	gGL.begin(LLRender::QUADS);
581	{
582		gGL.texCoord2fv( uv[ (rotations + 0) % 4]);
583		gGL.vertex2i(width, height );
584
585		gGL.texCoord2fv( uv[ (rotations + 1) % 4]);
586		gGL.vertex2i(0, height );
587
588		gGL.texCoord2fv( uv[ (rotations + 2) % 4]);
589		gGL.vertex2i(0, 0);
590
591		gGL.texCoord2fv( uv[ (rotations + 3) % 4]);
592		gGL.vertex2i(width, 0);
593	}
594	gGL.end();
595}
596
597
598
599//-------------------------------------------------------------------------------
600// LLJoystickCameraTrack
601//-------------------------------------------------------------------------------
602
603LLJoystickCameraTrack::Params::Params()
604{
605	held_down_delay.seconds(0.0);
606}
607
608LLJoystickCameraTrack::LLJoystickCameraTrack(const LLJoystickCameraTrack::Params& p)
609:	LLJoystickCameraRotate(p)
610{}
611
612
613void LLJoystickCameraTrack::onHeldDown()
614{
615	updateSlop();
616
617	S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
618	S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
619
620	if (dx > mVertSlopNear)
621	{
622		gAgentCamera.unlockView();
623		gAgentCamera.setPanRightKey(getOrbitRate());
624	}
625	else if (dx < -mVertSlopNear)
626	{
627		gAgentCamera.unlockView();
628		gAgentCamera.setPanLeftKey(getOrbitRate());
629	}
630
631	// over/under rotation
632	if (dy > mVertSlopNear)
633	{
634		gAgentCamera.unlockView();
635		gAgentCamera.setPanUpKey(getOrbitRate());
636	}
637	else if (dy < -mVertSlopNear)
638	{
639		gAgentCamera.unlockView();
640		gAgentCamera.setPanDownKey(getOrbitRate());
641	}
642}