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