PageRenderTime 135ms CodeModel.GetById 17ms app.highlight 109ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llhudnametag.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1085 lines | 827 code | 148 blank | 110 comment | 120 complexity | 312893c240456ff6a8c5aeb5442325f2 MD5 | raw file
   1/** 
   2 * @file llhudnametag.cpp
   3 * @brief Name tags for avatars
   4 * @author James Cook
   5 *
   6 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
   7 * Second Life Viewer Source Code
   8 * Copyright (C) 2010, Linden Research, Inc.
   9 * 
  10 * This library is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU Lesser General Public
  12 * License as published by the Free Software Foundation;
  13 * version 2.1 of the License only.
  14 * 
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 * 
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  23 * 
  24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  25 * $/LicenseInfo$
  26 */
  27
  28#include "llviewerprecompiledheaders.h"
  29
  30#include "llhudnametag.h"
  31
  32#include "llrender.h"
  33
  34#include "llagent.h"
  35#include "llviewercontrol.h"
  36#include "llcriticaldamp.h"
  37#include "lldrawable.h"
  38#include "llfontgl.h"
  39#include "llglheaders.h"
  40#include "llhudrender.h"
  41#include "llui.h"
  42#include "llviewercamera.h"
  43#include "llviewertexturelist.h"
  44#include "llviewerobject.h"
  45#include "llvovolume.h"
  46#include "llviewerwindow.h"
  47#include "llstatusbar.h"
  48#include "llmenugl.h"
  49#include "pipeline.h"
  50#include <boost/tokenizer.hpp>
  51
  52
  53const F32 SPRING_STRENGTH = 0.7f;
  54const F32 RESTORATION_SPRING_TIME_CONSTANT = 0.1f;
  55const F32 HORIZONTAL_PADDING = 16.f;
  56const F32 VERTICAL_PADDING = 12.f;
  57const F32 LINE_PADDING = 3.f;			// aka "leading"
  58const F32 BUFFER_SIZE = 2.f;
  59const F32 MIN_EDGE_OVERLAP = 3.f;
  60const F32 HUD_TEXT_MAX_WIDTH = 190.f;
  61const F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f;
  62const F32 RESIZE_TIME = 0.f;
  63const S32 NUM_OVERLAP_ITERATIONS = 10;
  64const F32 NEIGHBOR_FORCE_FRACTION = 1.f;
  65const F32 POSITION_DAMPING_TC = 0.2f;
  66const F32 MAX_STABLE_CAMERA_VELOCITY = 0.1f;
  67const F32 LOD_0_SCREEN_COVERAGE = 0.15f;
  68const F32 LOD_1_SCREEN_COVERAGE = 0.30f;
  69const F32 LOD_2_SCREEN_COVERAGE = 0.40f;
  70
  71std::set<LLPointer<LLHUDNameTag> > LLHUDNameTag::sTextObjects;
  72std::vector<LLPointer<LLHUDNameTag> > LLHUDNameTag::sVisibleTextObjects;
  73BOOL LLHUDNameTag::sDisplayText = TRUE ;
  74
  75bool llhudnametag_further_away::operator()(const LLPointer<LLHUDNameTag>& lhs, const LLPointer<LLHUDNameTag>& rhs) const
  76{
  77	return lhs->getDistance() > rhs->getDistance();
  78}
  79
  80
  81LLHUDNameTag::LLHUDNameTag(const U8 type)
  82:	LLHUDObject(type),
  83	mDoFade(TRUE),
  84	mFadeDistance(8.f),
  85	mFadeRange(4.f),
  86	mLastDistance(0.f),
  87	mZCompare(TRUE),
  88	mVisibleOffScreen(FALSE),
  89	mOffscreen(FALSE),
  90	mColor(1.f, 1.f, 1.f, 1.f),
  91//	mScale(),
  92	mWidth(0.f),
  93	mHeight(0.f),
  94	mFontp(LLFontGL::getFontSansSerifSmall()),
  95	mBoldFontp(LLFontGL::getFontSansSerifBold()),
  96	mSoftScreenRect(),
  97	mPositionAgent(),
  98	mPositionOffset(),
  99	mMass(10.f),
 100	mMaxLines(10),
 101	mOffsetY(0),
 102	mRadius(0.1f),
 103	mTextSegments(),
 104	mLabelSegments(),
 105	mTextAlignment(ALIGN_TEXT_CENTER),
 106	mVertAlignment(ALIGN_VERT_CENTER),
 107	mLOD(0),
 108	mHidden(FALSE)
 109{
 110	LLPointer<LLHUDNameTag> ptr(this);
 111	sTextObjects.insert(ptr);
 112}
 113
 114LLHUDNameTag::~LLHUDNameTag()
 115{
 116}
 117
 118
 119BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3& intersection, BOOL debug_render)
 120{
 121	if (!mVisible || mHidden)
 122	{
 123		return FALSE;
 124	}
 125
 126	// don't pick text that isn't bound to a viewerobject
 127	if (!mSourceObject || mSourceObject->mDrawable.isNull())
 128	{
 129		return FALSE;
 130	}
 131	
 132	F32 alpha_factor = 1.f;
 133	LLColor4 text_color = mColor;
 134	if (mDoFade)
 135	{
 136		if (mLastDistance > mFadeDistance)
 137		{
 138			alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange);
 139			text_color.mV[3] = text_color.mV[3]*alpha_factor;
 140		}
 141	}
 142	if (text_color.mV[3] < 0.01f)
 143	{
 144		return FALSE;
 145	}
 146
 147	mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f));
 148
 149	LLVector3 position = mPositionAgent;
 150
 151	if (mSourceObject)
 152	{ //get intersection of eye through mPositionAgent to plane of source object
 153		//using this position keeps the camera from focusing on some seemingly random 
 154		//point several meters in front of the nametag
 155		const LLVector3& p = mSourceObject->getPositionAgent();
 156		const LLVector3& n = LLViewerCamera::getInstance()->getAtAxis();
 157		const LLVector3& eye = LLViewerCamera::getInstance()->getOrigin();
 158
 159		LLVector3 ray = position-eye;
 160		ray.normalize();
 161
 162		LLVector3 delta = p-position;
 163		F32 dist = delta*n;
 164		F32 dt =  dist/(ray*n);
 165		position += ray*dt;
 166	}
 167
 168	// scale screen size of borders down
 169	//RN: for now, text on hud objects is never occluded
 170
 171	LLVector3 x_pixel_vec;
 172	LLVector3 y_pixel_vec;
 173	
 174	LLViewerCamera::getInstance()->getPixelVectors(position, y_pixel_vec, x_pixel_vec);
 175
 176	LLVector3 width_vec = mWidth * x_pixel_vec;
 177	LLVector3 height_vec = mHeight * y_pixel_vec;
 178	
 179	LLCoordGL screen_pos;
 180	LLViewerCamera::getInstance()->projectPosAgentToScreen(position, screen_pos, FALSE);
 181
 182	LLVector2 screen_offset;
 183	screen_offset = updateScreenPos(mPositionOffset);
 184	
 185	LLVector3 render_position = position  
 186			+ (x_pixel_vec * screen_offset.mV[VX])
 187			+ (y_pixel_vec * screen_offset.mV[VY]);
 188
 189
 190	//if (mUseBubble)
 191	{
 192		LLVector3 bg_pos = render_position
 193			+ (F32)mOffsetY * y_pixel_vec
 194			- (width_vec / 2.f)
 195			- (height_vec);
 196		//LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
 197
 198		LLVector3 v[] = 
 199		{
 200			bg_pos,
 201			bg_pos + width_vec,
 202			bg_pos + width_vec + height_vec,
 203			bg_pos + height_vec,
 204		};
 205
 206		if (debug_render)
 207		{
 208			gGL.begin(LLRender::LINE_STRIP);
 209			gGL.vertex3fv(v[0].mV);
 210			gGL.vertex3fv(v[1].mV);
 211			gGL.vertex3fv(v[2].mV);
 212			gGL.vertex3fv(v[3].mV);
 213			gGL.vertex3fv(v[0].mV);
 214			gGL.vertex3fv(v[2].mV);
 215			gGL.end();
 216		}
 217
 218		LLVector3 dir = end-start;
 219		F32 a, b, t;
 220
 221		if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) ||
 222			LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) )
 223		{
 224			if (t <= 1.f)
 225			{
 226				intersection = start + dir*t;
 227				return TRUE;
 228			}
 229		}
 230	}
 231
 232	return FALSE;
 233}
 234
 235void LLHUDNameTag::render()
 236{
 237	if (sDisplayText)
 238	{
 239		LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
 240		renderText(FALSE);
 241	}
 242}
 243
 244void LLHUDNameTag::renderForSelect()
 245{
 246	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
 247	renderText(TRUE);
 248}
 249
 250void LLHUDNameTag::renderText(BOOL for_select)
 251{
 252	if (!mVisible || mHidden)
 253	{
 254		return;
 255	}
 256
 257	// don't pick text that isn't bound to a viewerobject
 258	if (for_select && 
 259		(!mSourceObject || mSourceObject->mDrawable.isNull()))
 260	{
 261		return;
 262	}
 263	
 264	if (for_select)
 265	{
 266		gGL.getTexUnit(0)->disable();
 267	}
 268	else
 269	{
 270		gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
 271	}
 272
 273	LLGLState gls_blend(GL_BLEND, for_select ? FALSE : TRUE);
 274	LLGLState gls_alpha(GL_ALPHA_TEST, for_select ? FALSE : TRUE);
 275	
 276	LLColor4 shadow_color(0.f, 0.f, 0.f, 1.f);
 277	F32 alpha_factor = 1.f;
 278	LLColor4 text_color = mColor;
 279	if (mDoFade)
 280	{
 281		if (mLastDistance > mFadeDistance)
 282		{
 283			alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange);
 284			text_color.mV[3] = text_color.mV[3]*alpha_factor;
 285		}
 286	}
 287	if (text_color.mV[3] < 0.01f)
 288	{
 289		return;
 290	}
 291	shadow_color.mV[3] = text_color.mV[3];
 292
 293	mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f));
 294
 295	// *TODO: cache this image
 296	LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Rect");
 297
 298	// *TODO: make this a per-text setting
 299	LLColor4 bg_color = LLUIColorTable::instance().getColor("NameTagBackground");
 300	bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
 301
 302	// maybe a no-op?
 303	//const S32 border_height = 16;
 304	//const S32 border_width = 16;
 305	const S32 border_height = 8;
 306	const S32 border_width = 8;
 307
 308	// *TODO move this into helper function
 309	F32 border_scale = 1.f;
 310
 311	if (border_height * 2 > mHeight)
 312	{
 313		border_scale = (F32)mHeight / ((F32)border_height * 2.f);
 314	}
 315	if (border_width * 2 > mWidth)
 316	{
 317		border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f));
 318	}
 319
 320	// scale screen size of borders down
 321	//RN: for now, text on hud objects is never occluded
 322
 323	LLVector3 x_pixel_vec;
 324	LLVector3 y_pixel_vec;
 325	
 326	LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
 327
 328	LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getTextureWidth(), (F32)border_height / (F32)imagep->getTextureHeight());
 329	LLVector3 width_vec = mWidth * x_pixel_vec;
 330	LLVector3 height_vec = mHeight * y_pixel_vec;
 331	LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec;
 332	LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec;
 333
 334	mRadius = (width_vec + height_vec).magVec() * 0.5f;
 335
 336	LLCoordGL screen_pos;
 337	LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE);
 338
 339	LLVector2 screen_offset;
 340//	if (!mUseBubble)
 341//	{
 342//		screen_offset = mPositionOffset;
 343//	}
 344//	else
 345//	{
 346		screen_offset = updateScreenPos(mPositionOffset);
 347//	}
 348
 349	LLVector3 render_position = mPositionAgent  
 350			+ (x_pixel_vec * screen_offset.mV[VX])
 351			+ (y_pixel_vec * screen_offset.mV[VY]);
 352
 353//	if (mUseBubble)
 354	{
 355		LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
 356		LLUI::pushMatrix();
 357		{
 358			LLVector3 bg_pos = render_position
 359				+ (F32)mOffsetY * y_pixel_vec
 360				- (width_vec / 2.f)
 361				- (height_vec);
 362			LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
 363
 364			if (for_select)
 365			{
 366				gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 367				S32 name = mSourceObject->mGLName;
 368				LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name);
 369				gGL.color4ubv(coloru.mV);
 370				gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
 371				LLUI::popMatrix();
 372				return;
 373			}
 374			else
 375			{
 376				gGL.getTexUnit(0)->bind(imagep->getImage());
 377				
 378				gGL.color4fv(bg_color.mV);
 379				gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
 380		
 381				if ( mLabelSegments.size())
 382				{
 383					LLUI::pushMatrix();
 384					{
 385						gGL.color4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
 386						LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec;
 387						LLVector3 label_offset = height_vec - label_height;
 388						LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]);
 389						gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height);
 390					}
 391					LLUI::popMatrix();
 392				}
 393			}
 394
 395			BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f;
 396			BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f);
 397
 398			// draw line segments pointing to parent object
 399			if (!mOffscreen && (outside_width || outside_height))
 400			{
 401				LLUI::pushMatrix();
 402				{
 403					gGL.color4fv(bg_color.mV);
 404					LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec);
 405					target_pos += (width_vec / 2.f);
 406					target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero;
 407					target_pos -= 3.f * x_pixel_vec;
 408					target_pos -= 6.f * y_pixel_vec;
 409					LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]);
 410					gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec);	
 411				}
 412				LLUI::popMatrix();
 413
 414				gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 415				LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE);
 416				
 417				LLVector3 box_center_offset;
 418				box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f);
 419				LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]);
 420				gGL.color4fv(bg_color.mV);
 421				LLUI::setLineWidth(2.0);
 422				gGL.begin(LLRender::LINES);
 423				{
 424					if (outside_width)
 425					{
 426						LLVector3 vert;
 427						// draw line in x then y
 428						if (mPositionOffset.mV[VX] < 0.f)
 429						{
 430							// start at right edge
 431							vert = width_vec * 0.5f;
 432							gGL.vertex3fv(vert.mV);
 433						}
 434						else
 435						{
 436							// start at left edge
 437							vert = width_vec * -0.5f;
 438							gGL.vertex3fv(vert.mV);
 439						}
 440						vert = -mPositionOffset.mV[VX] * x_pixel_vec;
 441						gGL.vertex3fv(vert.mV);
 442						gGL.vertex3fv(vert.mV);
 443						vert -= mPositionOffset.mV[VY] * y_pixel_vec;
 444						vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
 445						gGL.vertex3fv(vert.mV);
 446					}
 447					else
 448					{
 449						LLVector3 vert;
 450						// draw line in y then x
 451						if (mPositionOffset.mV[VY] < 0.f)
 452						{
 453							// start at top edge
 454							vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
 455							gGL.vertex3fv(vert.mV);
 456						}
 457						else
 458						{
 459							// start at bottom edge
 460							vert = (height_vec * -0.5f)  - (mPositionOffset.mV[VX] * x_pixel_vec);
 461							gGL.vertex3fv(vert.mV);
 462						}
 463						vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec;
 464						vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
 465						gGL.vertex3fv(vert.mV);
 466					}
 467				}
 468				gGL.end();
 469				LLUI::setLineWidth(1.0);
 470
 471			}
 472		}
 473		LLUI::popMatrix();
 474	}
 475
 476	F32 y_offset = (F32)mOffsetY;
 477		
 478	// Render label
 479	{
 480		//gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
 481
 482		for(std::vector<LLHUDTextSegment>::iterator segment_iter = mLabelSegments.begin();
 483			segment_iter != mLabelSegments.end(); ++segment_iter )
 484		{
 485			// Label segments use default font
 486			const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp;
 487			y_offset -= fontp->getLineHeight();
 488
 489			F32 x_offset;
 490			if (mTextAlignment == ALIGN_TEXT_CENTER)
 491			{
 492				x_offset = -0.5f*segment_iter->getWidth(fontp);
 493			}
 494			else // ALIGN_LEFT
 495			{
 496				x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
 497			}
 498
 499			LLColor4 label_color(0.f, 0.f, 0.f, 1.f);
 500			label_color.mV[VALPHA] = alpha_factor;
 501			hud_render_text(segment_iter->getText(), render_position, *fontp, segment_iter->mStyle, LLFontGL::NO_SHADOW, x_offset, y_offset, label_color, FALSE);
 502		}
 503	}
 504
 505	// Render text
 506	{
 507		// -1 mMaxLines means unlimited lines.
 508		S32 start_segment;
 509		S32 max_lines = getMaxLines();
 510
 511		if (max_lines < 0) 
 512		{
 513			start_segment = 0;
 514		}
 515		else 
 516		{
 517			start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
 518		}
 519
 520		for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin() + start_segment;
 521			 segment_iter != mTextSegments.end(); ++segment_iter )
 522		{
 523			const LLFontGL* fontp = segment_iter->mFont;
 524			y_offset -= fontp->getLineHeight();
 525			y_offset -= LINE_PADDING;
 526
 527			U8 style = segment_iter->mStyle;
 528			LLFontGL::ShadowType shadow = LLFontGL::DROP_SHADOW;
 529	
 530			F32 x_offset;
 531			if (mTextAlignment== ALIGN_TEXT_CENTER)
 532			{
 533				x_offset = -0.5f*segment_iter->getWidth(fontp);
 534			}
 535			else // ALIGN_LEFT
 536			{
 537				x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
 538
 539				// *HACK
 540				x_offset += 1;
 541			}
 542
 543			text_color = segment_iter->mColor;
 544			text_color.mV[VALPHA] *= alpha_factor;
 545
 546			hud_render_text(segment_iter->getText(), render_position, *fontp, style, shadow, x_offset, y_offset, text_color, FALSE);
 547		}
 548	}
 549	/// Reset the default color to white.  The renderer expects this to be the default. 
 550	gGL.color4f(1.0f, 1.0f, 1.0f, 1.0f);
 551	if (for_select)
 552	{
 553		gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
 554	}
 555}
 556
 557void LLHUDNameTag::setString(const std::string &text_utf8)
 558{
 559	mTextSegments.clear();
 560	addLine(text_utf8, mColor);
 561}
 562
 563void LLHUDNameTag::clearString()
 564{
 565	mTextSegments.clear();
 566}
 567
 568
 569void LLHUDNameTag::addLine(const std::string &text_utf8,
 570						const LLColor4& color,
 571						const LLFontGL::StyleFlags style,
 572						const LLFontGL* font)
 573{
 574	LLWString wline = utf8str_to_wstring(text_utf8);
 575	if (!wline.empty())
 576	{
 577		// use default font for segment if custom font not specified
 578		if (!font)
 579		{
 580			font = mFontp;
 581		}
 582		typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
 583		LLWString seps(utf8str_to_wstring("\r\n"));
 584		boost::char_separator<llwchar> sep(seps.c_str());
 585
 586		tokenizer tokens(wline, sep);
 587		tokenizer::iterator iter = tokens.begin();
 588
 589		while (iter != tokens.end())
 590		{
 591			U32 line_length = 0;
 592			do	
 593			{
 594				F32 max_pixels = HUD_TEXT_MAX_WIDTH;
 595				S32 segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE);
 596				LLHUDTextSegment segment(iter->substr(line_length, segment_length), style, color, font);
 597				mTextSegments.push_back(segment);
 598				line_length += segment_length;
 599			}
 600			while (line_length != iter->size());
 601			++iter;
 602		}
 603	}
 604}
 605
 606void LLHUDNameTag::setLabel(const std::string &label_utf8)
 607{
 608	mLabelSegments.clear();
 609	addLabel(label_utf8);
 610}
 611
 612void LLHUDNameTag::addLabel(const std::string& label_utf8)
 613{
 614	LLWString wstr = utf8string_to_wstring(label_utf8);
 615	if (!wstr.empty())
 616	{
 617		LLWString seps(utf8str_to_wstring("\r\n"));
 618		LLWString empty;
 619
 620		typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
 621		boost::char_separator<llwchar> sep(seps.c_str(), empty.c_str(), boost::keep_empty_tokens);
 622
 623		tokenizer tokens(wstr, sep);
 624		tokenizer::iterator iter = tokens.begin();
 625
 626		while (iter != tokens.end())
 627		{
 628			U32 line_length = 0;
 629			do	
 630			{
 631				S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), 
 632					HUD_TEXT_MAX_WIDTH, wstr.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE);
 633				LLHUDTextSegment segment(iter->substr(line_length, segment_length), LLFontGL::NORMAL, mColor, mFontp);
 634				mLabelSegments.push_back(segment);
 635				line_length += segment_length;
 636			}
 637			while (line_length != iter->size());
 638			++iter;
 639		}
 640	}
 641}
 642
 643void LLHUDNameTag::setZCompare(const BOOL zcompare)
 644{
 645	mZCompare = zcompare;
 646}
 647
 648void LLHUDNameTag::setFont(const LLFontGL* font)
 649{
 650	mFontp = font;
 651}
 652
 653
 654void LLHUDNameTag::setColor(const LLColor4 &color)
 655{
 656	mColor = color;
 657	for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin();
 658		 segment_iter != mTextSegments.end(); ++segment_iter )
 659	{
 660		segment_iter->mColor = color;
 661	}
 662}
 663
 664void LLHUDNameTag::setAlpha(F32 alpha)
 665{
 666	mColor.mV[VALPHA] = alpha;
 667	for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin();
 668		 segment_iter != mTextSegments.end(); ++segment_iter )
 669	{
 670		segment_iter->mColor.mV[VALPHA] = alpha;
 671	}
 672}
 673
 674
 675void LLHUDNameTag::setDoFade(const BOOL do_fade)
 676{
 677	mDoFade = do_fade;
 678}
 679
 680void LLHUDNameTag::updateVisibility()
 681{
 682	if (mSourceObject)
 683	{
 684		mSourceObject->updateText();
 685	}
 686	
 687	mPositionAgent = gAgent.getPosAgentFromGlobal(mPositionGlobal);
 688
 689	if (!mSourceObject)
 690	{
 691		//llwarns << "LLHUDNameTag::updateScreenPos -- mSourceObject is NULL!" << llendl;
 692		mVisible = TRUE;
 693		sVisibleTextObjects.push_back(LLPointer<LLHUDNameTag> (this));
 694		return;
 695	}
 696
 697	// Not visible if parent object is dead
 698	if (mSourceObject->isDead())
 699	{
 700		mVisible = FALSE;
 701		return;
 702	}
 703
 704	// push text towards camera by radius of object, but not past camera
 705	LLVector3 vec_from_camera = mPositionAgent - LLViewerCamera::getInstance()->getOrigin();
 706	LLVector3 dir_from_camera = vec_from_camera;
 707	dir_from_camera.normVec();
 708
 709	if (dir_from_camera * LLViewerCamera::getInstance()->getAtAxis() <= 0.f)
 710	{ //text is behind camera, don't render
 711		mVisible = FALSE;
 712		return;
 713	}
 714		
 715	if (vec_from_camera * LLViewerCamera::getInstance()->getAtAxis() <= LLViewerCamera::getInstance()->getNear() + 0.1f + mSourceObject->getVObjRadius())
 716	{
 717		mPositionAgent = LLViewerCamera::getInstance()->getOrigin() + vec_from_camera * ((LLViewerCamera::getInstance()->getNear() + 0.1f) / (vec_from_camera * LLViewerCamera::getInstance()->getAtAxis()));
 718	}
 719	else
 720	{
 721		mPositionAgent -= dir_from_camera * mSourceObject->getVObjRadius();
 722	}
 723
 724	mLastDistance = (mPositionAgent - LLViewerCamera::getInstance()->getOrigin()).magVec();
 725
 726	if (mLOD >= 3 || !mTextSegments.size() || (mDoFade && (mLastDistance > mFadeDistance + mFadeRange)))
 727	{
 728		mVisible = FALSE;
 729		return;
 730	}
 731
 732	LLVector3 x_pixel_vec;
 733	LLVector3 y_pixel_vec;
 734
 735	LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
 736
 737	LLVector3 render_position = mPositionAgent + 			
 738			(x_pixel_vec * mPositionOffset.mV[VX]) +
 739			(y_pixel_vec * mPositionOffset.mV[VY]);
 740
 741	mOffscreen = FALSE;
 742	if (!LLViewerCamera::getInstance()->sphereInFrustum(render_position, mRadius))
 743	{
 744		if (!mVisibleOffScreen)
 745		{
 746			mVisible = FALSE;
 747			return;
 748		}
 749		else
 750		{
 751			mOffscreen = TRUE;
 752		}
 753	}
 754
 755	mVisible = TRUE;
 756	sVisibleTextObjects.push_back(LLPointer<LLHUDNameTag> (this));
 757}
 758
 759LLVector2 LLHUDNameTag::updateScreenPos(LLVector2 &offset)
 760{
 761	LLCoordGL screen_pos;
 762	LLVector2 screen_pos_vec;
 763	LLVector3 x_pixel_vec;
 764	LLVector3 y_pixel_vec;
 765	LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
 766	LLVector3 world_pos = mPositionAgent + (offset.mV[VX] * x_pixel_vec) + (offset.mV[VY] * y_pixel_vec);
 767	if (!LLViewerCamera::getInstance()->projectPosAgentToScreen(world_pos, screen_pos, FALSE) && mVisibleOffScreen)
 768	{
 769		// bubble off-screen, so find a spot for it along screen edge
 770		LLViewerCamera::getInstance()->projectPosAgentToScreenEdge(world_pos, screen_pos);
 771	}
 772
 773	screen_pos_vec.setVec((F32)screen_pos.mX, (F32)screen_pos.mY);
 774
 775	LLRect world_rect = gViewerWindow->getWorldViewRectScaled();
 776	S32 bottom = world_rect.mBottom + STATUS_BAR_HEIGHT;
 777
 778	LLVector2 screen_center;
 779	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);
 780
 781	if(mVertAlignment == ALIGN_VERT_TOP)
 782	{
 783		screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY], 
 784			(F32)bottom, 
 785			(F32)world_rect.mTop - mHeight - (F32)MENU_BAR_HEIGHT);
 786		mSoftScreenRect.setLeftTopAndSize(screen_center.mV[VX] - (mWidth + BUFFER_SIZE) * 0.5f, 
 787			screen_center.mV[VY] + (mHeight + BUFFER_SIZE), mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
 788	}
 789	else
 790	{
 791		screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY], 
 792			(F32)bottom + mHeight * 0.5f, 
 793			(F32)world_rect.mTop - mHeight * 0.5f - (F32)MENU_BAR_HEIGHT);
 794		mSoftScreenRect.setCenterAndSize(screen_center.mV[VX], screen_center.mV[VY], mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
 795	}
 796
 797	return offset + (screen_center - LLVector2((F32)screen_pos.mX, (F32)screen_pos.mY));
 798}
 799
 800void LLHUDNameTag::updateSize()
 801{
 802	F32 height = 0.f;
 803	F32 width = 0.f;
 804
 805	S32 max_lines = getMaxLines();
 806	//S32 lines = (max_lines < 0) ? (S32)mTextSegments.size() : llmin((S32)mTextSegments.size(), max_lines);
 807	//F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size());
 808
 809	S32 start_segment;
 810	if (max_lines < 0) start_segment = 0;
 811	else start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
 812
 813	std::vector<LLHUDTextSegment>::iterator iter = mTextSegments.begin() + start_segment;
 814	while (iter != mTextSegments.end())
 815	{
 816		const LLFontGL* fontp = iter->mFont;
 817		height += fontp->getLineHeight();
 818		height += LINE_PADDING;
 819		width = llmax(width, llmin(iter->getWidth(fontp), HUD_TEXT_MAX_WIDTH));
 820		++iter;
 821	}
 822
 823	// Don't want line spacing under the last line
 824	if (height > 0.f)
 825	{
 826		height -= LINE_PADDING;
 827	}
 828
 829	iter = mLabelSegments.begin();
 830	while (iter != mLabelSegments.end())
 831	{
 832		height += mFontp->getLineHeight();
 833		width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
 834		++iter;
 835	}
 836	
 837	if (width == 0.f)
 838	{
 839		return;
 840	}
 841
 842	width += HORIZONTAL_PADDING;
 843	height += VERTICAL_PADDING;
 844
 845	// *TODO: Could do a timer-based resize here
 846	//mWidth = llmax(width, lerp(mWidth, (F32)width, u));
 847	//mHeight = llmax(height, lerp(mHeight, (F32)height, u));
 848	mWidth = width;
 849	mHeight = height;
 850}
 851
 852void LLHUDNameTag::updateAll()
 853{
 854	// iterate over all text objects, calculate their restoration forces,
 855	// and add them to the visible set if they are on screen and close enough
 856	sVisibleTextObjects.clear();
 857	
 858	TextObjectIterator text_it;
 859	for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
 860	{
 861		LLHUDNameTag* textp = (*text_it);
 862		textp->mTargetPositionOffset.clearVec();
 863		textp->updateSize();
 864		textp->updateVisibility();
 865	}
 866	
 867	// sort back to front for rendering purposes
 868	std::sort(sVisibleTextObjects.begin(), sVisibleTextObjects.end(), llhudnametag_further_away());
 869
 870	// iterate from front to back, and set LOD based on current screen coverage
 871	F32 screen_area = (F32)(gViewerWindow->getWindowWidthScaled() * gViewerWindow->getWindowHeightScaled());
 872	F32 current_screen_area = 0.f;
 873	std::vector<LLPointer<LLHUDNameTag> >::reverse_iterator r_it;
 874	for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it)
 875	{
 876		LLHUDNameTag* textp = (*r_it);
 877//		if (textp->mUseBubble)
 878//		{
 879			if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE)
 880			{
 881				textp->setLOD(3);
 882			}
 883			else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE)
 884			{
 885				textp->setLOD(2);
 886			}
 887			else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE)
 888			{
 889				textp->setLOD(1);
 890			}
 891			else
 892			{
 893				textp->setLOD(0);
 894			}
 895			textp->updateSize();
 896			// find on-screen position and initialize collision rectangle
 897			textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero);
 898			current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight());
 899//		}
 900	}
 901
 902	LLStat* camera_vel_stat = LLViewerCamera::getInstance()->getVelocityStat();
 903	F32 camera_vel = camera_vel_stat->getCurrent();
 904	if (camera_vel > MAX_STABLE_CAMERA_VELOCITY)
 905	{
 906		return;
 907	}
 908
 909	VisibleTextObjectIterator src_it;
 910
 911	for (S32 i = 0; i < NUM_OVERLAP_ITERATIONS; i++)
 912	{
 913		for (src_it = sVisibleTextObjects.begin(); src_it != sVisibleTextObjects.end(); ++src_it)
 914		{
 915			LLHUDNameTag* src_textp = (*src_it);
 916
 917//			if (!src_textp->mUseBubble)
 918//			{
 919//				continue;
 920//			}
 921			VisibleTextObjectIterator dst_it = src_it;
 922			++dst_it;
 923			for (; dst_it != sVisibleTextObjects.end(); ++dst_it)
 924			{
 925				LLHUDNameTag* dst_textp = (*dst_it);
 926
 927//				if (!dst_textp->mUseBubble)
 928//				{
 929//					continue;
 930//				}
 931				if (src_textp->mSoftScreenRect.overlaps(dst_textp->mSoftScreenRect))
 932				{
 933					LLRectf intersect_rect = src_textp->mSoftScreenRect;
 934					intersect_rect.intersectWith(dst_textp->mSoftScreenRect);
 935					intersect_rect.stretch(-BUFFER_SIZE * 0.5f);
 936					
 937					F32 src_center_x = src_textp->mSoftScreenRect.getCenterX();
 938					F32 src_center_y = src_textp->mSoftScreenRect.getCenterY();
 939					F32 dst_center_x = dst_textp->mSoftScreenRect.getCenterX();
 940					F32 dst_center_y = dst_textp->mSoftScreenRect.getCenterY();
 941					F32 intersect_center_x = intersect_rect.getCenterX();
 942					F32 intersect_center_y = intersect_rect.getCenterY();
 943					LLVector2 force = lerp(LLVector2(dst_center_x - intersect_center_x, dst_center_y - intersect_center_y), 
 944										LLVector2(intersect_center_x - src_center_x, intersect_center_y - src_center_y),
 945										0.5f);
 946					force.setVec(dst_center_x - src_center_x, dst_center_y - src_center_y);
 947					force.normVec();
 948
 949					LLVector2 src_force = -1.f * force;
 950					LLVector2 dst_force = force;
 951
 952					LLVector2 force_strength;
 953					F32 src_mult = dst_textp->mMass / (dst_textp->mMass + src_textp->mMass); 
 954					F32 dst_mult = 1.f - src_mult;
 955					F32 src_aspect_ratio = src_textp->mSoftScreenRect.getWidth() / src_textp->mSoftScreenRect.getHeight();
 956					F32 dst_aspect_ratio = dst_textp->mSoftScreenRect.getWidth() / dst_textp->mSoftScreenRect.getHeight();
 957					src_force.mV[VY] *= src_aspect_ratio;
 958					src_force.normVec();
 959					dst_force.mV[VY] *= dst_aspect_ratio;
 960					dst_force.normVec();
 961
 962					src_force.mV[VX] *= llmin(intersect_rect.getWidth() * src_mult, intersect_rect.getHeight() * SPRING_STRENGTH);
 963					src_force.mV[VY] *= llmin(intersect_rect.getHeight() * src_mult, intersect_rect.getWidth() * SPRING_STRENGTH);
 964					dst_force.mV[VX] *=  llmin(intersect_rect.getWidth() * dst_mult, intersect_rect.getHeight() * SPRING_STRENGTH);
 965					dst_force.mV[VY] *=  llmin(intersect_rect.getHeight() * dst_mult, intersect_rect.getWidth() * SPRING_STRENGTH);
 966					
 967					src_textp->mTargetPositionOffset += src_force;
 968					dst_textp->mTargetPositionOffset += dst_force;
 969					src_textp->mTargetPositionOffset = src_textp->updateScreenPos(src_textp->mTargetPositionOffset);
 970					dst_textp->mTargetPositionOffset = dst_textp->updateScreenPos(dst_textp->mTargetPositionOffset);
 971				}
 972			}
 973		}
 974	}
 975
 976	VisibleTextObjectIterator this_object_it;
 977	for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it)
 978	{
 979//		if (!(*this_object_it)->mUseBubble)
 980//		{
 981//			continue;
 982//		}
 983		(*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC));
 984	}
 985}
 986
 987void LLHUDNameTag::setLOD(S32 lod)
 988{
 989	mLOD = lod;
 990	//RN: uncomment this to visualize LOD levels
 991	//std::string label = llformat("%d", lod);
 992	//setLabel(label);
 993}
 994
 995S32 LLHUDNameTag::getMaxLines()
 996{
 997	switch(mLOD)
 998	{
 999	case 0:
1000		return mMaxLines;
1001	case 1:
1002		return mMaxLines > 0 ? mMaxLines / 2 : 5;
1003	case 2:
1004		return mMaxLines > 0 ? mMaxLines / 3 : 2;
1005	default:
1006		// label only
1007		return 0;
1008	}
1009}
1010
1011void LLHUDNameTag::markDead()
1012{
1013	sTextObjects.erase(LLPointer<LLHUDNameTag>(this));
1014	LLHUDObject::markDead();
1015}
1016
1017void LLHUDNameTag::shiftAll(const LLVector3& offset)
1018{
1019	TextObjectIterator text_it;
1020	for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
1021	{
1022		LLHUDNameTag *textp = text_it->get();
1023		textp->shift(offset);
1024	}
1025}
1026
1027void LLHUDNameTag::shift(const LLVector3& offset)
1028{
1029	mPositionAgent += offset;
1030}
1031
1032//static 
1033void LLHUDNameTag::addPickable(std::set<LLViewerObject*> &pick_list)
1034{
1035	//this might put an object on the pick list a second time, overriding it's mGLName, which is ok
1036	// *FIX: we should probably cull against pick frustum
1037	VisibleTextObjectIterator text_it;
1038	for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it)
1039	{
1040//		if (!(*text_it)->mUseBubble)
1041//		{
1042//			continue;
1043//		}
1044		pick_list.insert((*text_it)->mSourceObject);
1045	}
1046}
1047
1048//static
1049// called when UI scale changes, to flush font width caches
1050void LLHUDNameTag::reshape()
1051{
1052	TextObjectIterator text_it;
1053	for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
1054	{
1055		LLHUDNameTag* textp = (*text_it);
1056		std::vector<LLHUDTextSegment>::iterator segment_iter; 
1057		for (segment_iter = textp->mTextSegments.begin();
1058			 segment_iter != textp->mTextSegments.end(); ++segment_iter )
1059		{
1060			segment_iter->clearFontWidthMap();
1061		}
1062		for(segment_iter = textp->mLabelSegments.begin();
1063			segment_iter != textp->mLabelSegments.end(); ++segment_iter )
1064		{
1065			segment_iter->clearFontWidthMap();
1066		}		
1067	}
1068}
1069
1070//============================================================================
1071
1072F32 LLHUDNameTag::LLHUDTextSegment::getWidth(const LLFontGL* font)
1073{
1074	std::map<const LLFontGL*, F32>::iterator iter = mFontWidthMap.find(font);
1075	if (iter != mFontWidthMap.end())
1076	{
1077		return iter->second;
1078	}
1079	else
1080	{
1081		F32 width = font->getWidthF32(mText.c_str());
1082		mFontWidthMap[font] = width;
1083		return width;
1084	}
1085}