PageRenderTime 110ms CodeModel.GetById 40ms app.highlight 64ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/newview/llhudtext.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 639 lines | 458 code | 97 blank | 84 comment | 58 complexity | cec944d1374a2efbfcb32cce5f43ada8 MD5 | raw file
  1/** 
  2 * @file llhudtext.cpp
  3 * @brief Floating text above objects, set via script with llSetText()
  4 *
  5 * $LicenseInfo:firstyear=2002&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 "llhudtext.h"
 30
 31#include "llrender.h"
 32
 33#include "llagent.h"
 34#include "llviewercontrol.h"
 35#include "llcriticaldamp.h"
 36#include "lldrawable.h"
 37#include "llfontgl.h"
 38#include "llglheaders.h"
 39#include "llhudrender.h"
 40#include "llui.h"
 41#include "llviewercamera.h"
 42#include "llviewertexturelist.h"
 43#include "llviewerobject.h"
 44#include "llvovolume.h"
 45#include "llviewerwindow.h"
 46#include "llstatusbar.h"
 47#include "llmenugl.h"
 48#include "pipeline.h"
 49#include <boost/tokenizer.hpp>
 50
 51
 52const F32 SPRING_STRENGTH = 0.7f;
 53const F32 RESTORATION_SPRING_TIME_CONSTANT = 0.1f;
 54const F32 HORIZONTAL_PADDING = 15.f;
 55const F32 VERTICAL_PADDING = 12.f;
 56const F32 BUFFER_SIZE = 2.f;
 57const F32 MIN_EDGE_OVERLAP = 3.f;
 58const F32 HUD_TEXT_MAX_WIDTH = 190.f;
 59const F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f;
 60const F32 RESIZE_TIME = 0.f;
 61const S32 NUM_OVERLAP_ITERATIONS = 10;
 62const F32 NEIGHBOR_FORCE_FRACTION = 1.f;
 63const F32 POSITION_DAMPING_TC = 0.2f;
 64const F32 MAX_STABLE_CAMERA_VELOCITY = 0.1f;
 65//const F32 LOD_0_SCREEN_COVERAGE = 0.15f;
 66//const F32 LOD_1_SCREEN_COVERAGE = 0.30f;
 67//const F32 LOD_2_SCREEN_COVERAGE = 0.40f;
 68
 69std::set<LLPointer<LLHUDText> > LLHUDText::sTextObjects;
 70std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleTextObjects;
 71std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleHUDTextObjects;
 72BOOL LLHUDText::sDisplayText = TRUE ;
 73
 74bool lltextobject_further_away::operator()(const LLPointer<LLHUDText>& lhs, const LLPointer<LLHUDText>& rhs) const
 75{
 76	return lhs->getDistance() > rhs->getDistance();
 77}
 78
 79
 80LLHUDText::LLHUDText(const U8 type) :
 81			LLHUDObject(type),
 82			mOnHUDAttachment(FALSE),
 83//			mVisibleOffScreen(FALSE),
 84			mWidth(0.f),
 85			mHeight(0.f),
 86			mFontp(LLFontGL::getFontSansSerifSmall()),
 87			mBoldFontp(LLFontGL::getFontSansSerifBold()),
 88			mMass(1.f),
 89			mMaxLines(10),
 90			mOffsetY(0),
 91			mTextAlignment(ALIGN_TEXT_CENTER),
 92			mVertAlignment(ALIGN_VERT_CENTER),
 93//			mLOD(0),
 94			mHidden(FALSE)
 95{
 96	mColor = LLColor4(1.f, 1.f, 1.f, 1.f);
 97	mDoFade = TRUE;
 98	mFadeDistance = 8.f;
 99	mFadeRange = 4.f;
100	mZCompare = TRUE;
101	mOffscreen = FALSE;
102	mRadius = 0.1f;
103	LLPointer<LLHUDText> ptr(this);
104	sTextObjects.insert(ptr);
105}
106
107LLHUDText::~LLHUDText()
108{
109}
110
111void LLHUDText::render()
112{
113	if (!mOnHUDAttachment && sDisplayText)
114	{
115		LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
116		renderText();
117	}
118}
119
120void LLHUDText::renderText()
121{
122	if (!mVisible || mHidden)
123	{
124		return;
125	}
126
127	gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
128
129	LLGLState gls_blend(GL_BLEND, TRUE);
130	LLGLState gls_alpha(GL_ALPHA_TEST, TRUE);
131	
132	LLColor4 shadow_color(0.f, 0.f, 0.f, 1.f);
133	F32 alpha_factor = 1.f;
134	LLColor4 text_color = mColor;
135	if (mDoFade)
136	{
137		if (mLastDistance > mFadeDistance)
138		{
139			alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange);
140			text_color.mV[3] = text_color.mV[3]*alpha_factor;
141		}
142	}
143	if (text_color.mV[3] < 0.01f)
144	{
145		return;
146	}
147	shadow_color.mV[3] = text_color.mV[3];
148
149	mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f));
150
151	// *TODO: cache this image
152	LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Square");
153
154	// *TODO: make this a per-text setting
155	LLColor4 bg_color = LLUIColorTable::instance().getColor("ObjectBubbleColor");
156	bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
157
158	const S32 border_height = 16;
159	const S32 border_width = 16;
160
161	// *TODO move this into helper function
162	F32 border_scale = 1.f;
163
164	if (border_height * 2 > mHeight)
165	{
166		border_scale = (F32)mHeight / ((F32)border_height * 2.f);
167	}
168	if (border_width * 2 > mWidth)
169	{
170		border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f));
171	}
172
173	// scale screen size of borders down
174	//RN: for now, text on hud objects is never occluded
175
176	LLVector3 x_pixel_vec;
177	LLVector3 y_pixel_vec;
178	
179	if (mOnHUDAttachment)
180	{
181		x_pixel_vec = LLVector3::y_axis / (F32)gViewerWindow->getWorldViewWidthRaw();
182		y_pixel_vec = LLVector3::z_axis / (F32)gViewerWindow->getWorldViewHeightRaw();
183	}
184	else
185	{
186		LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
187	}
188
189	LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getTextureWidth(), (F32)border_height / (F32)imagep->getTextureHeight());
190	LLVector3 width_vec = mWidth * x_pixel_vec;
191	LLVector3 height_vec = mHeight * y_pixel_vec;
192	LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec;
193	LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec;
194
195	mRadius = (width_vec + height_vec).magVec() * 0.5f;
196
197	LLVector2 screen_offset;
198	screen_offset = mPositionOffset;
199
200	LLVector3 render_position = mPositionAgent  
201			+ (x_pixel_vec * screen_offset.mV[VX])
202			+ (y_pixel_vec * screen_offset.mV[VY]);
203
204	F32 y_offset = (F32)mOffsetY;
205		
206	// Render label
207	{
208		gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
209	}
210
211	// Render text
212	{
213		// -1 mMaxLines means unlimited lines.
214		S32 start_segment;
215		S32 max_lines = getMaxLines();
216
217		if (max_lines < 0) 
218		{
219			start_segment = 0;
220		}
221		else 
222		{
223			start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
224		}
225
226		for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin() + start_segment;
227			 segment_iter != mTextSegments.end(); ++segment_iter )
228		{
229			const LLFontGL* fontp = segment_iter->mFont;
230			y_offset -= fontp->getLineHeight();
231
232			U8 style = segment_iter->mStyle;
233			LLFontGL::ShadowType shadow = LLFontGL::DROP_SHADOW;
234	
235			F32 x_offset;
236			if (mTextAlignment== ALIGN_TEXT_CENTER)
237			{
238				x_offset = -0.5f*segment_iter->getWidth(fontp);
239			}
240			else // ALIGN_LEFT
241			{
242				x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
243			}
244
245			text_color = segment_iter->mColor;
246			text_color.mV[VALPHA] *= alpha_factor;
247
248			hud_render_text(segment_iter->getText(), render_position, *fontp, style, shadow, x_offset, y_offset, text_color, mOnHUDAttachment);
249		}
250	}
251	/// Reset the default color to white.  The renderer expects this to be the default. 
252	gGL.color4f(1.0f, 1.0f, 1.0f, 1.0f);
253}
254
255void LLHUDText::setString(const std::string &text_utf8)
256{
257	mTextSegments.clear();
258	addLine(text_utf8, mColor);
259}
260
261void LLHUDText::clearString()
262{
263	mTextSegments.clear();
264}
265
266
267void LLHUDText::addLine(const std::string &text_utf8,
268						const LLColor4& color,
269						const LLFontGL::StyleFlags style,
270						const LLFontGL* font)
271{
272	LLWString wline = utf8str_to_wstring(text_utf8);
273	if (!wline.empty())
274	{
275		// use default font for segment if custom font not specified
276		if (!font)
277		{
278			font = mFontp;
279		}
280		typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
281		LLWString seps(utf8str_to_wstring("\r\n"));
282		boost::char_separator<llwchar> sep(seps.c_str());
283
284		tokenizer tokens(wline, sep);
285		tokenizer::iterator iter = tokens.begin();
286
287		while (iter != tokens.end())
288		{
289			U32 line_length = 0;
290			do	
291			{
292				F32 max_pixels = HUD_TEXT_MAX_WIDTH_NO_BUBBLE;
293				S32 segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE);
294				LLHUDTextSegment segment(iter->substr(line_length, segment_length), style, color, font);
295				mTextSegments.push_back(segment);
296				line_length += segment_length;
297			}
298			while (line_length != iter->size());
299			++iter;
300		}
301	}
302}
303
304void LLHUDText::setZCompare(const BOOL zcompare)
305{
306	mZCompare = zcompare;
307}
308
309void LLHUDText::setFont(const LLFontGL* font)
310{
311	mFontp = font;
312}
313
314
315void LLHUDText::setColor(const LLColor4 &color)
316{
317	mColor = color;
318	for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin();
319		 segment_iter != mTextSegments.end(); ++segment_iter )
320	{
321		segment_iter->mColor = color;
322	}
323}
324
325void LLHUDText::setAlpha(F32 alpha)
326{
327	mColor.mV[VALPHA] = alpha;
328	for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin();
329		 segment_iter != mTextSegments.end(); ++segment_iter )
330	{
331		segment_iter->mColor.mV[VALPHA] = alpha;
332	}
333}
334
335
336void LLHUDText::setDoFade(const BOOL do_fade)
337{
338	mDoFade = do_fade;
339}
340
341void LLHUDText::updateVisibility()
342{
343	if (mSourceObject)
344	{
345		mSourceObject->updateText();
346	}
347	
348	mPositionAgent = gAgent.getPosAgentFromGlobal(mPositionGlobal);
349
350	if (!mSourceObject)
351	{
352		//llwarns << "LLHUDText::updateScreenPos -- mSourceObject is NULL!" << llendl;
353		mVisible = TRUE;
354		if (mOnHUDAttachment)
355		{
356			sVisibleHUDTextObjects.push_back(LLPointer<LLHUDText> (this));
357		}
358		else
359		{
360			sVisibleTextObjects.push_back(LLPointer<LLHUDText> (this));
361		}
362		return;
363	}
364
365	// Not visible if parent object is dead
366	if (mSourceObject->isDead())
367	{
368		mVisible = FALSE;
369		return;
370	}
371
372	// for now, all text on hud objects is visible
373	if (mOnHUDAttachment)
374	{
375		mVisible = TRUE;
376		sVisibleHUDTextObjects.push_back(LLPointer<LLHUDText> (this));
377		mLastDistance = mPositionAgent.mV[VX];
378		return;
379	}
380
381	// push text towards camera by radius of object, but not past camera
382	LLVector3 vec_from_camera = mPositionAgent - LLViewerCamera::getInstance()->getOrigin();
383	LLVector3 dir_from_camera = vec_from_camera;
384	dir_from_camera.normVec();
385
386	if (dir_from_camera * LLViewerCamera::getInstance()->getAtAxis() <= 0.f)
387	{ //text is behind camera, don't render
388		mVisible = FALSE;
389		return;
390	}
391		
392	if (vec_from_camera * LLViewerCamera::getInstance()->getAtAxis() <= LLViewerCamera::getInstance()->getNear() + 0.1f + mSourceObject->getVObjRadius())
393	{
394		mPositionAgent = LLViewerCamera::getInstance()->getOrigin() + vec_from_camera * ((LLViewerCamera::getInstance()->getNear() + 0.1f) / (vec_from_camera * LLViewerCamera::getInstance()->getAtAxis()));
395	}
396	else
397	{
398		mPositionAgent -= dir_from_camera * mSourceObject->getVObjRadius();
399	}
400
401	mLastDistance = (mPositionAgent - LLViewerCamera::getInstance()->getOrigin()).magVec();
402
403	if (!mTextSegments.size() || (mDoFade && (mLastDistance > mFadeDistance + mFadeRange)))
404	{
405		mVisible = FALSE;
406		return;
407	}
408
409	LLVector3 x_pixel_vec;
410	LLVector3 y_pixel_vec;
411
412	LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
413
414	LLVector3 render_position = mPositionAgent + 			
415			(x_pixel_vec * mPositionOffset.mV[VX]) +
416			(y_pixel_vec * mPositionOffset.mV[VY]);
417
418	mOffscreen = FALSE;
419	if (!LLViewerCamera::getInstance()->sphereInFrustum(render_position, mRadius))
420	{
421//		if (!mVisibleOffScreen)
422//		{
423			mVisible = FALSE;
424			return;
425//		}
426//		else
427//		{
428//			mOffscreen = TRUE;
429//		}
430	}
431
432	mVisible = TRUE;
433	sVisibleTextObjects.push_back(LLPointer<LLHUDText> (this));
434}
435
436LLVector2 LLHUDText::updateScreenPos(LLVector2 &offset)
437{
438	LLCoordGL screen_pos;
439	LLVector2 screen_pos_vec;
440	LLVector3 x_pixel_vec;
441	LLVector3 y_pixel_vec;
442	LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
443	LLVector3 world_pos = mPositionAgent + (offset.mV[VX] * x_pixel_vec) + (offset.mV[VY] * y_pixel_vec);
444//	if (!LLViewerCamera::getInstance()->projectPosAgentToScreen(world_pos, screen_pos, FALSE) && mVisibleOffScreen)
445//	{
446//		// bubble off-screen, so find a spot for it along screen edge
447//		LLViewerCamera::getInstance()->projectPosAgentToScreenEdge(world_pos, screen_pos);
448//	}
449
450	screen_pos_vec.setVec((F32)screen_pos.mX, (F32)screen_pos.mY);
451
452	LLRect world_rect = gViewerWindow->getWorldViewRectScaled();
453	S32 bottom = world_rect.mBottom + STATUS_BAR_HEIGHT;
454
455	LLVector2 screen_center;
456	screen_center.mV[VX] = llclamp((F32)screen_pos_vec.mV[VX], (F32)world_rect.mLeft + mWidth * 0.5f, (F32)world_rect.mRight - mWidth * 0.5f);
457
458	if(mVertAlignment == ALIGN_VERT_TOP)
459	{
460		screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY], 
461			(F32)bottom, 
462			(F32)world_rect.mTop - mHeight - (F32)MENU_BAR_HEIGHT);
463		mSoftScreenRect.setLeftTopAndSize(screen_center.mV[VX] - (mWidth + BUFFER_SIZE) * 0.5f, 
464			screen_center.mV[VY] + (mHeight + BUFFER_SIZE), mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
465	}
466	else
467	{
468		screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY], 
469			(F32)bottom + mHeight * 0.5f, 
470			(F32)world_rect.mTop - mHeight * 0.5f - (F32)MENU_BAR_HEIGHT);
471		mSoftScreenRect.setCenterAndSize(screen_center.mV[VX], screen_center.mV[VY], mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
472	}
473
474	return offset + (screen_center - LLVector2((F32)screen_pos.mX, (F32)screen_pos.mY));
475}
476
477void LLHUDText::updateSize()
478{
479	F32 height = 0.f;
480	F32 width = 0.f;
481
482	S32 max_lines = getMaxLines();
483	//S32 lines = (max_lines < 0) ? (S32)mTextSegments.size() : llmin((S32)mTextSegments.size(), max_lines);
484	//F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size());
485
486	S32 start_segment;
487	if (max_lines < 0) start_segment = 0;
488	else start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
489
490	std::vector<LLHUDTextSegment>::iterator iter = mTextSegments.begin() + start_segment;
491	while (iter != mTextSegments.end())
492	{
493		const LLFontGL* fontp = iter->mFont;
494		height += fontp->getLineHeight();
495		width = llmax(width, llmin(iter->getWidth(fontp), HUD_TEXT_MAX_WIDTH));
496		++iter;
497	}
498
499	if (width == 0.f)
500	{
501		return;
502	}
503
504	width += HORIZONTAL_PADDING;
505	height += VERTICAL_PADDING;
506
507	// *TODO: Could do some sort of timer-based resize logic here
508	F32 u = 1.f;
509	mWidth = llmax(width, lerp(mWidth, (F32)width, u));
510	mHeight = llmax(height, lerp(mHeight, (F32)height, u));
511}
512
513void LLHUDText::updateAll()
514{
515	// iterate over all text objects, calculate their restoration forces,
516	// and add them to the visible set if they are on screen and close enough
517	sVisibleTextObjects.clear();
518	sVisibleHUDTextObjects.clear();
519	
520	TextObjectIterator text_it;
521	for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
522	{
523		LLHUDText* textp = (*text_it);
524		textp->mTargetPositionOffset.clearVec();
525		textp->updateSize();
526		textp->updateVisibility();
527	}
528	
529	// sort back to front for rendering purposes
530	std::sort(sVisibleTextObjects.begin(), sVisibleTextObjects.end(), lltextobject_further_away());
531	std::sort(sVisibleHUDTextObjects.begin(), sVisibleHUDTextObjects.end(), lltextobject_further_away());
532}
533
534//void LLHUDText::setLOD(S32 lod)
535//{
536//	mLOD = lod;
537//	//RN: uncomment this to visualize LOD levels
538//	//std::string label = llformat("%d", lod);
539//	//setLabel(label);
540//}
541
542S32 LLHUDText::getMaxLines()
543{
544	return mMaxLines;
545	//switch(mLOD)
546	//{
547	//case 0:
548	//	return mMaxLines;
549	//case 1:
550	//	return mMaxLines > 0 ? mMaxLines / 2 : 5;
551	//case 2:
552	//	return mMaxLines > 0 ? mMaxLines / 3 : 2;
553	//default:
554	//	// label only
555	//	return 0;
556	//}
557}
558
559void LLHUDText::markDead()
560{
561	sTextObjects.erase(LLPointer<LLHUDText>(this));
562	LLHUDObject::markDead();
563}
564
565void LLHUDText::renderAllHUD()
566{
567	LLGLState::checkStates();
568	LLGLState::checkTextureChannels();
569	LLGLState::checkClientArrays();
570
571	{
572		LLGLEnable color_mat(GL_COLOR_MATERIAL);
573		LLGLDepthTest depth(GL_FALSE, GL_FALSE);
574		
575		VisibleTextObjectIterator text_it;
576
577		for (text_it = sVisibleHUDTextObjects.begin(); text_it != sVisibleHUDTextObjects.end(); ++text_it)
578		{
579			(*text_it)->renderText();
580		}
581	}
582	
583	LLVertexBuffer::unbind();
584
585    LLVertexBuffer::unbind();
586
587	LLGLState::checkStates();
588	LLGLState::checkTextureChannels();
589	LLGLState::checkClientArrays();
590}
591
592void LLHUDText::shiftAll(const LLVector3& offset)
593{
594	TextObjectIterator text_it;
595	for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
596	{
597		LLHUDText *textp = text_it->get();
598		textp->shift(offset);
599	}
600}
601
602void LLHUDText::shift(const LLVector3& offset)
603{
604	mPositionAgent += offset;
605}
606
607//static
608// called when UI scale changes, to flush font width caches
609void LLHUDText::reshape()
610{
611	TextObjectIterator text_it;
612	for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
613	{
614		LLHUDText* textp = (*text_it);
615		std::vector<LLHUDTextSegment>::iterator segment_iter; 
616		for (segment_iter = textp->mTextSegments.begin();
617			 segment_iter != textp->mTextSegments.end(); ++segment_iter )
618		{
619			segment_iter->clearFontWidthMap();
620		}
621	}
622}
623
624//============================================================================
625
626F32 LLHUDText::LLHUDTextSegment::getWidth(const LLFontGL* font)
627{
628	std::map<const LLFontGL*, F32>::iterator iter = mFontWidthMap.find(font);
629	if (iter != mFontWidthMap.end())
630	{
631		return iter->second;
632	}
633	else
634	{
635		F32 width = font->getWidthF32(mText.c_str());
636		mFontWidthMap[font] = width;
637		return width;
638	}
639}