llhudtext.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  1. /**
  2. * @file llhudtext.cpp
  3. * @brief LLHUDText class implementation
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2002-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "boost/tokenizer.hpp"
  34. #include "llhudtext.h"
  35. #include "llglheaders.h"
  36. #include "llmenugl.h"
  37. #include "llimagegl.h"
  38. #include "llrender.h"
  39. #include "llagent.h"
  40. #include "llchatbar.h"
  41. #include "lldrawable.h"
  42. #include "llpipeline.h"
  43. #include "llstatusbar.h"
  44. #include "llviewercamera.h"
  45. #include "llviewercontrol.h"
  46. #include "llviewerdisplay.h" // For hud_render_text()
  47. #include "llviewerobject.h"
  48. #include "llviewertexturelist.h"
  49. #include "llviewerwindow.h"
  50. //MK
  51. #include "mkrlinterface.h"
  52. #include "llviewerregion.h"
  53. #include "llvoavatarself.h"
  54. //mk
  55. #include "llvovolume.h"
  56. constexpr F32 SPRING_STRENGTH = 0.7f;
  57. constexpr F32 HORIZONTAL_PADDING = 15.f;
  58. constexpr F32 VERTICAL_PADDING = 12.f;
  59. constexpr F32 BUFFER_SIZE = 2.f;
  60. constexpr F32 HUD_TEXT_MAX_WIDTH = 190.f;
  61. constexpr F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f;
  62. constexpr S32 NUM_OVERLAP_ITERATIONS = 10;
  63. constexpr F32 POSITION_DAMPING_TC = 0.2f;
  64. constexpr F32 MAX_STABLE_CAMERA_VELOCITY = 0.1f;
  65. constexpr F32 LOD_0_SCREEN_COVERAGE = 0.15f;
  66. constexpr F32 LOD_1_SCREEN_COVERAGE = 0.3f;
  67. constexpr F32 LOD_2_SCREEN_COVERAGE = 0.4f;
  68. LLHUDText::htobj_list_t LLHUDText::sTextObjects;
  69. LLHUDText::visible_list_t LLHUDText::sVisibleTextObjects;
  70. LLHUDText::visible_list_t LLHUDText::sVisibleHUDTextObjects;
  71. bool LLHUDText::sDisplayText = true;
  72. struct hto_further_away
  73. {
  74. LL_INLINE bool operator()(const LLPointer<LLHUDText>& lhs,
  75. const LLPointer<LLHUDText>& rhs) const
  76. {
  77. return lhs->getDistance() > rhs->getDistance();
  78. }
  79. };
  80. LLHUDText::LLHUDText(U8 type)
  81. : LLHUDObject(type),
  82. mFontp(LLFontGL::getFontSansSerifSmall()),
  83. mBoldFontp(LLFontGL::getFontSansSerifBold()),
  84. mColor(LLColor4(1.f, 1.f, 1.f, 1.f)),
  85. mTextAlignment(ALIGN_TEXT_CENTER),
  86. mVertAlignment(ALIGN_VERT_CENTER),
  87. mLOD(0),
  88. mWidth(0.f),
  89. mHeight(0.f),
  90. mMass(1.f),
  91. mMaxLines(10),
  92. mOffsetY(0),
  93. mLastDistance(0.f),
  94. mFadeDistance(8.f),
  95. mFadeRange(4.f),
  96. mRadius(0.1f),
  97. mUseBubble(false),
  98. mUsePixelSize(true),
  99. mVisibleOffScreen(false),
  100. mOffScreen(false),
  101. mHidden(false),
  102. mDoFade(true),
  103. mZCompare(true),
  104. mDropShadow(true)
  105. {
  106. sTextObjects.emplace(this);
  107. }
  108. bool LLHUDText::lineSegmentIntersect(const LLVector4a& start,
  109. const LLVector4a& end,
  110. LLVector4a& intersection,
  111. bool debug_render)
  112. {
  113. if (!mVisible || mHidden)
  114. {
  115. return false;
  116. }
  117. // Do not pick text that is not bound to a viewerobject or is not in a
  118. // bubble
  119. if (!mSourceObject || mSourceObject->mDrawable.isNull() || !mUseBubble)
  120. {
  121. return false;
  122. }
  123. F32 color_alpha = mColor.mV[3];
  124. if (mDoFade && mLastDistance > mFadeDistance)
  125. {
  126. // Could make color_alpha negative, but we do not care since we just
  127. // compare its max value below to decide whether to abort or not. HB
  128. color_alpha *= 1.f - (mLastDistance - mFadeDistance) / mFadeRange;
  129. }
  130. if (color_alpha < 0.01f)
  131. {
  132. return false; // Nothing visible any more to intersect with.
  133. }
  134. mOffsetY = lltrunc(mHeight *
  135. (mVertAlignment == ALIGN_VERT_CENTER ? 0.5f : 1.f));
  136. // Scale screen size of borders down. RN: for now, text on hud objects is
  137. // never occluded.
  138. LLVector3 x_pixel_vec;
  139. LLVector3 y_pixel_vec;
  140. if (mOnHUDAttachment)
  141. {
  142. x_pixel_vec =
  143. LLVector3::y_axis / (F32)gViewerWindowp->getWindowWidth();
  144. y_pixel_vec =
  145. LLVector3::z_axis / (F32)gViewerWindowp->getWindowHeight();
  146. }
  147. else
  148. {
  149. gViewerCamera.getPixelVectors(mPositionAgent, y_pixel_vec,
  150. x_pixel_vec);
  151. }
  152. LLVector3 width_vec = mWidth * x_pixel_vec;
  153. LLVector3 height_vec = mHeight * y_pixel_vec;
  154. LLCoordGL screen_pos;
  155. gViewerCamera.projectPosAgentToScreen(mPositionAgent, screen_pos, false);
  156. LLVector2 screen_offset;
  157. screen_offset = updateScreenPos(mPositionOffset);
  158. LLVector3 render_position = mPositionAgent +
  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 + (F32)mOffsetY * y_pixel_vec -
  164. width_vec * 0.5f - height_vec;
  165. //LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
  166. LLVector3 v[] = {
  167. bg_pos,
  168. bg_pos + width_vec,
  169. bg_pos + width_vec + height_vec,
  170. bg_pos + height_vec,
  171. };
  172. if (debug_render)
  173. {
  174. gGL.begin(LLRender::LINE_STRIP);
  175. gGL.vertex3fv(v[0].mV);
  176. gGL.vertex3fv(v[1].mV);
  177. gGL.vertex3fv(v[2].mV);
  178. gGL.vertex3fv(v[3].mV);
  179. gGL.vertex3fv(v[0].mV);
  180. gGL.vertex3fv(v[2].mV);
  181. gGL.end();
  182. }
  183. LLVector4a dir;
  184. dir.setSub(end,start);
  185. F32 a, b, t;
  186. LLVector4a v0, v1, v2, v3;
  187. v0.load3(v[0].mV);
  188. v1.load3(v[1].mV);
  189. v2.load3(v[2].mV);
  190. v3.load3(v[3].mV);
  191. if (LLTriangleRayIntersect(v0, v1, v2, start, dir, a, b, t) ||
  192. LLTriangleRayIntersect(v2, v3, v0, start, dir, a, b, t))
  193. {
  194. if (t <= 1.f)
  195. {
  196. dir.mul(t);
  197. intersection.setAdd(start, dir);
  198. return true;
  199. }
  200. }
  201. }
  202. return false;
  203. }
  204. void LLHUDText::render()
  205. {
  206. if (!mOnHUDAttachment && sDisplayText)
  207. {
  208. LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
  209. renderText();
  210. }
  211. }
  212. void LLHUDText::renderText()
  213. {
  214. if (!mVisible || mHidden)
  215. {
  216. return;
  217. }
  218. LLTexUnit* unit0 = gGL.getTexUnit(0);
  219. unit0->enable(LLTexUnit::TT_TEXTURE);
  220. LLGLState gls_blend(GL_BLEND, GL_TRUE);
  221. LLColor4 shadow_color(0.f, 0.f, 0.f, 1.f);
  222. F32 alpha_factor = 1.f;
  223. LLColor4 text_color = mColor;
  224. //MK
  225. F32 fade_distance = mFadeDistance;
  226. F32 fade_range = mFadeRange;
  227. if (gRLenabled && gRLInterface.mCamDistDrawMin < fade_distance)
  228. {
  229. fade_distance = gRLInterface.mCamDistDrawMin;
  230. fade_range = 1.f;
  231. }
  232. //mk
  233. if (mDoFade && mLastDistance > fade_distance)
  234. {
  235. alpha_factor = llmax(0.f,
  236. 1.f - (mLastDistance - fade_distance) / fade_range);
  237. text_color.mV[3] = text_color.mV[3] * alpha_factor;
  238. }
  239. if (text_color.mV[3] < 0.01f)
  240. {
  241. return;
  242. }
  243. shadow_color.mV[3] = text_color.mV[3];
  244. if (gUsePBRShaders && mOnHUDAttachment)
  245. {
  246. text_color = linearColor4(text_color);
  247. shadow_color = linearColor4(text_color);
  248. }
  249. mOffsetY = lltrunc(mHeight * (mVertAlignment == ALIGN_VERT_CENTER ? 0.5f
  250. : 1.f));
  251. // *TODO: make this a per-text setting
  252. static LLCachedControl<LLColor4> background_chat_color(gSavedSettings,
  253. "BackgroundChatColor");
  254. LLColor4 bg_color = background_chat_color;
  255. static LLCachedControl<F32> chat_bubble_opacity(gSavedSettings,
  256. "ChatBubbleOpacity");
  257. bg_color.setAlpha(chat_bubble_opacity * alpha_factor);
  258. constexpr S32 border_height = 16;
  259. constexpr S32 border_width = 16;
  260. F32 border_scale = 1.f;
  261. if (border_height * 2 > mHeight)
  262. {
  263. border_scale = (F32)mHeight / ((F32)border_height * 2.f);
  264. }
  265. if (border_width * 2 > mWidth)
  266. {
  267. border_scale = llmin(border_scale,
  268. (F32)mWidth / ((F32)border_width * 2.f));
  269. }
  270. // Scale screen size of borders down
  271. // RN: for now, text on hud objects is never occluded
  272. LLVector3 x_pixel_vec;
  273. LLVector3 y_pixel_vec;
  274. if (mOnHUDAttachment)
  275. {
  276. x_pixel_vec =
  277. LLVector3::y_axis / (F32)gViewerWindowp->getWindowWidth();
  278. y_pixel_vec =
  279. LLVector3::z_axis / (F32)gViewerWindowp->getWindowHeight();
  280. }
  281. else
  282. {
  283. gViewerCamera.getPixelVectors(mPositionAgent, y_pixel_vec,
  284. x_pixel_vec);
  285. }
  286. static const F32 tex_width = LLUIImage::sRoundedSquareWidth;
  287. static const F32 tex_height = LLUIImage::sRoundedSquareHeight;
  288. LLVector2 border_scale_vec((F32)border_width / tex_width,
  289. (F32)border_height / tex_height);
  290. LLVector3 width_vec = mWidth * x_pixel_vec;
  291. LLVector3 height_vec = mHeight * y_pixel_vec;
  292. LLVector3 scaled_border_width = (F32)llfloor(border_scale *
  293. (F32)border_width) *
  294. x_pixel_vec;
  295. LLVector3 scaled_border_height = (F32)llfloor(border_scale *
  296. (F32)border_height) *
  297. y_pixel_vec;
  298. mRadius = (width_vec + height_vec).length() * 0.5f;
  299. LLCoordGL screen_pos;
  300. gViewerCamera.projectPosAgentToScreen(mPositionAgent, screen_pos, false);
  301. LLVector2 screen_offset;
  302. if (!mUseBubble)
  303. {
  304. screen_offset = mPositionOffset;
  305. }
  306. else
  307. {
  308. screen_offset = updateScreenPos(mPositionOffset);
  309. }
  310. LLVector3 render_position = mPositionAgent +
  311. (x_pixel_vec * screen_offset.mV[VX]) +
  312. (y_pixel_vec * screen_offset.mV[VY]);
  313. if (mUseBubble)
  314. {
  315. LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
  316. LLUI::pushMatrix();
  317. {
  318. LLVector3 bg_pos = render_position + (F32)mOffsetY * y_pixel_vec -
  319. width_vec * 0.5f - height_vec;
  320. LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
  321. unit0->bind(LLUIImage::sRoundedSquare->getImage());
  322. gGL.color4fv(bg_color.mV);
  323. gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width,
  324. scaled_border_height, width_vec,
  325. height_vec);
  326. if (mLabelSegments.size())
  327. {
  328. LLUI::pushMatrix();
  329. {
  330. gGL.color4f(text_color.mV[VX], text_color.mV[VY],
  331. text_color.mV[VZ],
  332. chat_bubble_opacity * alpha_factor);
  333. LLVector3 label_height =
  334. (mFontp->getLineHeight() * mLabelSegments.size() +
  335. VERTICAL_PADDING / 3.f) * y_pixel_vec;
  336. LLVector3 label_offset = height_vec - label_height;
  337. LLUI::translate(label_offset.mV[VX], label_offset.mV[VY],
  338. label_offset.mV[VZ]);
  339. gl_segmented_rect_3d_tex_top(border_scale_vec,
  340. scaled_border_width,
  341. scaled_border_height,
  342. width_vec, label_height);
  343. }
  344. LLUI::popMatrix();
  345. }
  346. bool outside_width = fabsf(mPositionOffset.mV[VX]) > mWidth * 0.5f;
  347. bool outside_height = fabsf(mPositionOffset.mV[VY] +
  348. (mVertAlignment == ALIGN_VERT_TOP ?
  349. mHeight * 0.5f : 0.f)) >
  350. mHeight * (mVertAlignment == ALIGN_VERT_TOP ?
  351. mHeight * 0.75f : 0.5f);
  352. // Draw line segments pointing to parent object
  353. if (!mOffScreen && (outside_width || outside_height))
  354. {
  355. LLUI::pushMatrix();
  356. {
  357. gGL.color4fv(bg_color.mV);
  358. LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] *
  359. x_pixel_vec +
  360. mPositionOffset.mV[VY] *
  361. y_pixel_vec);
  362. target_pos += width_vec * 0.5f;
  363. if (mVertAlignment == ALIGN_VERT_CENTER)
  364. {
  365. target_pos += height_vec * 0.5f;
  366. }
  367. target_pos -= 3.f * x_pixel_vec;
  368. target_pos -= 6.f * y_pixel_vec;
  369. LLUI::translate(target_pos.mV[VX], target_pos.mV[VY],
  370. target_pos.mV[VZ]);
  371. gl_segmented_rect_3d_tex(border_scale_vec,
  372. 3.f * x_pixel_vec,
  373. 3.f * y_pixel_vec,
  374. 6.f * x_pixel_vec,
  375. 6.f * y_pixel_vec);
  376. }
  377. LLUI::popMatrix();
  378. unit0->unbind(LLTexUnit::TT_TEXTURE);
  379. LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE,
  380. GL_FALSE);
  381. LLVector3 box_center_offset = width_vec * 0.5f +
  382. height_vec * 0.5f;
  383. LLUI::translate(box_center_offset.mV[VX],
  384. box_center_offset.mV[VY],
  385. box_center_offset.mV[VZ]);
  386. gGL.color4fv(bg_color.mV);
  387. LLUI::setLineWidth(2.f);
  388. gGL.begin(LLRender::LINES);
  389. {
  390. if (outside_width)
  391. {
  392. LLVector3 vert;
  393. // Draw line in x then y
  394. if (mPositionOffset.mV[VX] < 0.f)
  395. {
  396. // Start at right edge
  397. vert = width_vec * 0.5f;
  398. gGL.vertex3fv(vert.mV);
  399. }
  400. else
  401. {
  402. // Start at left edge
  403. vert = width_vec * -0.5f;
  404. gGL.vertex3fv(vert.mV);
  405. }
  406. vert = -mPositionOffset.mV[VX] * x_pixel_vec;
  407. gGL.vertex3fv(vert.mV);
  408. gGL.vertex3fv(vert.mV);
  409. vert -= mPositionOffset.mV[VY] * y_pixel_vec;
  410. if (mVertAlignment == ALIGN_VERT_TOP)
  411. {
  412. vert -= height_vec * 0.5f;
  413. }
  414. gGL.vertex3fv(vert.mV);
  415. }
  416. else
  417. {
  418. LLVector3 vert;
  419. // Draw line in y then x
  420. if (mPositionOffset.mV[VY] < 0.f)
  421. {
  422. // Start at top edge
  423. vert = height_vec * 0.5f -
  424. mPositionOffset.mV[VX] * x_pixel_vec;
  425. gGL.vertex3fv(vert.mV);
  426. }
  427. else
  428. {
  429. // Start at bottom edge
  430. vert = height_vec * -0.5f -
  431. mPositionOffset.mV[VX] * x_pixel_vec;
  432. gGL.vertex3fv(vert.mV);
  433. }
  434. vert = -mPositionOffset.mV[VY] * y_pixel_vec -
  435. mPositionOffset.mV[VX] * x_pixel_vec;
  436. vert -= mVertAlignment == ALIGN_VERT_TOP ? height_vec * 0.5f
  437. : LLVector3::zero;
  438. gGL.vertex3fv(vert.mV);
  439. }
  440. }
  441. gGL.end();
  442. LLUI::setLineWidth(1.0);
  443. }
  444. }
  445. LLUI::popMatrix();
  446. }
  447. F32 x_offset = 0.f;
  448. bool center = mTextAlignment == ALIGN_TEXT_CENTER;
  449. if (!center)
  450. {
  451. // ALIGN_LEFT
  452. x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING * 0.5f);
  453. }
  454. F32 y_offset = (F32)mOffsetY;
  455. // Render label
  456. {
  457. LLColor4 label_color;
  458. for (S32 i = 0, count = mLabelSegments.size(); i < count; ++i)
  459. {
  460. LLHUDTextSegment& segment = mLabelSegments[i];
  461. const LLFontGL* fontp =
  462. segment.mStyle == LLFontGL::BOLD ? mBoldFontp : mFontp;
  463. y_offset -= fontp->getLineHeight();
  464. if (center)
  465. {
  466. x_offset = -0.5f * segment.getWidth(fontp);
  467. }
  468. label_color.mV[VALPHA] = alpha_factor;
  469. if (gUsePBRShaders && mOnHUDAttachment)
  470. {
  471. label_color = linearColor4(label_color);
  472. }
  473. hud_render_text(segment.getText(), render_position, *fontp,
  474. segment.mStyle, x_offset, y_offset,
  475. label_color, mOnHUDAttachment);
  476. }
  477. }
  478. // Render text
  479. {
  480. S32 start_segment;
  481. S32 max_lines = getMaxLines(); // -1 means unlimited lines.
  482. if (max_lines < 0)
  483. {
  484. start_segment = 0;
  485. }
  486. else
  487. {
  488. start_segment = llmax(0, (S32)mTextSegments.size() - max_lines);
  489. }
  490. for (S32 i = start_segment, count = mTextSegments.size(); i < count;
  491. ++i)
  492. {
  493. LLHUDTextSegment& segment = mTextSegments[i];
  494. U8 style = segment.mStyle;
  495. const LLFontGL* fontp = style == LLFontGL::BOLD ? mBoldFontp
  496. : mFontp;
  497. y_offset -= fontp->getLineHeight();
  498. if (mDropShadow)
  499. {
  500. style |= LLFontGL::DROP_SHADOW;
  501. }
  502. if (center)
  503. {
  504. x_offset = -0.5f * segment.getWidth(fontp);
  505. }
  506. text_color = segment.mColor;
  507. text_color.mV[VALPHA] *= alpha_factor;
  508. if (gUsePBRShaders && mOnHUDAttachment)
  509. {
  510. text_color = linearColor4(text_color);
  511. }
  512. hud_render_text(segment.getText(), render_position, *fontp,
  513. style, x_offset, y_offset, text_color,
  514. mOnHUDAttachment);
  515. }
  516. }
  517. // Reset the default color to white. The renderer expects this to be the
  518. // default.
  519. gGL.color4f(1.f, 1.f, 1.f, 1.f);
  520. }
  521. void LLHUDText::setStringUTF8(const std::string& wtext)
  522. {
  523. setString(utf8str_to_wstring(wtext));
  524. }
  525. void LLHUDText::setString(const LLWString& wtext)
  526. {
  527. mTextSegments.clear();
  528. //MK
  529. if (gRLenabled)
  530. {
  531. LLWString local_wtext = wtext;
  532. if (gRLInterface.mContainsShowhovertextall)
  533. {
  534. local_wtext = utf8str_to_wstring("", 0);
  535. }
  536. else if ((mOnHUDAttachment &&
  537. gRLInterface.mContainsShowhovertexthud) ||
  538. (!mOnHUDAttachment &&
  539. gRLInterface.mContainsShowhovertextworld) ||
  540. (mSourceObject &&
  541. gRLInterface.contains("showhovertext:" +
  542. mSourceObject->getID().asString())))
  543. {
  544. local_wtext = utf8str_to_wstring("", 0);
  545. }
  546. else
  547. {
  548. if (gRLInterface.mContainsShowloc)
  549. {
  550. std::string str = wstring_to_utf8str(local_wtext,
  551. local_wtext.length());
  552. str = gRLInterface.getCensoredLocation(str);
  553. local_wtext = utf8str_to_wstring(str, str.length());
  554. }
  555. if (gRLInterface.mContainsShownames ||
  556. gRLInterface.mContainsShownametags)
  557. {
  558. std::string str = wstring_to_utf8str(local_wtext,
  559. local_wtext.length());
  560. str = gRLInterface.getCensoredMessage(str);
  561. local_wtext = utf8str_to_wstring(str, str.length());
  562. }
  563. }
  564. addLine(local_wtext, mColor);
  565. }
  566. else
  567. //mk
  568. addLine(wtext, mColor);
  569. }
  570. void LLHUDText::clearString()
  571. {
  572. mTextSegments.clear();
  573. }
  574. void LLHUDText::addLine(const std::string& str, const LLColor4& color,
  575. const LLFontGL::StyleFlags style)
  576. {
  577. addLine(utf8str_to_wstring(str), color, style);
  578. }
  579. void LLHUDText::addLine(const LLWString& wstr, const LLColor4& color,
  580. const LLFontGL::StyleFlags style)
  581. {
  582. if (!wstr.empty())
  583. {
  584. LLWString wline(wstr);
  585. typedef boost::tokenizer<boost::char_separator<llwchar>,
  586. LLWString::const_iterator,
  587. LLWString> tokenizer;
  588. LLWString seps(utf8str_to_wstring("\r\n"));
  589. boost::char_separator<llwchar> sep(seps.c_str());
  590. tokenizer tokens(wline, sep);
  591. tokenizer::iterator iter = tokens.begin();
  592. tokenizer::iterator tokens_end = tokens.end();
  593. while (iter != tokens_end)
  594. {
  595. U32 line_length = 0;
  596. do
  597. {
  598. S32 segment_length =
  599. mFontp->maxDrawableChars(iter->substr(line_length).c_str(),
  600. mUseBubble ? HUD_TEXT_MAX_WIDTH
  601. : HUD_TEXT_MAX_WIDTH_NO_BUBBLE,
  602. wline.length(), true);
  603. mTextSegments.emplace_back(iter->substr(line_length,
  604. segment_length),
  605. style, color);
  606. line_length += segment_length;
  607. }
  608. while (line_length != iter->size());
  609. ++iter;
  610. }
  611. }
  612. }
  613. void LLHUDText::setLabel(const std::string& label)
  614. {
  615. setLabel(utf8str_to_wstring(label));
  616. }
  617. void LLHUDText::setLabel(const LLWString& wlabel)
  618. {
  619. mLabelSegments.clear();
  620. if (!wlabel.empty())
  621. {
  622. LLWString wstr(wlabel);
  623. LLWString seps(utf8str_to_wstring("\r\n"));
  624. LLWString empty;
  625. typedef boost::tokenizer<boost::char_separator<llwchar>,
  626. LLWString::const_iterator,
  627. LLWString> tokenizer;
  628. boost::char_separator<llwchar> sep(seps.c_str(), empty.c_str(),
  629. boost::keep_empty_tokens);
  630. tokenizer tokens(wstr, sep);
  631. tokenizer::iterator iter = tokens.begin();
  632. tokenizer::iterator tokens_end = tokens.end();
  633. while (iter != tokens_end)
  634. {
  635. U32 line_length = 0;
  636. do
  637. {
  638. S32 segment_length =
  639. mFontp->maxDrawableChars(iter->substr(line_length).c_str(),
  640. mUseBubble ? HUD_TEXT_MAX_WIDTH
  641. : HUD_TEXT_MAX_WIDTH_NO_BUBBLE,
  642. wstr.length(), true);
  643. mLabelSegments.emplace_back(iter->substr(line_length,
  644. segment_length),
  645. LLFontGL::NORMAL, mColor);
  646. line_length += segment_length;
  647. }
  648. while (line_length != iter->size());
  649. ++iter;
  650. }
  651. }
  652. }
  653. void LLHUDText::setColor(const LLColor4& color)
  654. {
  655. mColor = color;
  656. for (S32 i = 0, count = mTextSegments.size(); i < count; ++i)
  657. {
  658. mTextSegments[i].mColor = color;
  659. }
  660. }
  661. void LLHUDText::updateVisibility()
  662. {
  663. if (mSourceObject)
  664. {
  665. mSourceObject->updateText();
  666. }
  667. mPositionAgent = gAgent.getPosAgentFromGlobal(mPositionGlobal);
  668. if (!mSourceObject)
  669. {
  670. mVisible = true;
  671. if (mOnHUDAttachment)
  672. {
  673. sVisibleHUDTextObjects.emplace_back(this);
  674. }
  675. else
  676. {
  677. sVisibleTextObjects.emplace_back(this);
  678. }
  679. return;
  680. }
  681. // Not visible if parent object is dead
  682. if (mSourceObject->isDead())
  683. {
  684. mVisible = false;
  685. return;
  686. }
  687. // For now, all text on HUD objects is visible
  688. if (mOnHUDAttachment)
  689. {
  690. mVisible = true;
  691. mLastDistance = mPositionAgent.mV[VX];
  692. sVisibleHUDTextObjects.emplace_back(this);
  693. return;
  694. }
  695. // Push text towards camera by radius of object, but not past camera
  696. LLVector3 vec_from_camera = mPositionAgent - gViewerCamera.getOrigin();
  697. LLVector3 dir_from_camera = vec_from_camera;
  698. dir_from_camera.normalize();
  699. if (dir_from_camera * gViewerCamera.getAtAxis() <= 0.f)
  700. {
  701. // Text is behind camera, do not render
  702. mVisible = false;
  703. return;
  704. }
  705. if (vec_from_camera * gViewerCamera.getAtAxis() <=
  706. gViewerCamera.getNear() + 0.1f + mSourceObject->getVObjRadius())
  707. {
  708. mPositionAgent = gViewerCamera.getOrigin() + vec_from_camera *
  709. ((gViewerCamera.getNear() + 0.1f) /
  710. (vec_from_camera * gViewerCamera.getAtAxis()));
  711. }
  712. else
  713. {
  714. mPositionAgent -= dir_from_camera * mSourceObject->getVObjRadius();
  715. }
  716. //MK
  717. if (gRLenabled && gRLInterface.mCamDistDrawMin < EXTREMUM)
  718. {
  719. mLastDistance =
  720. (mPositionAgent -
  721. (isAgentAvatarValid() ? gAgentAvatarp->mHeadp->getWorldPosition()
  722. : gAgent.getPositionAgent())).length();
  723. }
  724. else
  725. //mk
  726. {
  727. mLastDistance = (mPositionAgent - gViewerCamera.getOrigin()).length();
  728. }
  729. if (mLOD >= 3 || !mTextSegments.size() ||
  730. (mDoFade && mLastDistance > mFadeDistance + mFadeRange))
  731. {
  732. mVisible = false;
  733. return;
  734. }
  735. LLVector3 x_pixel_vec;
  736. LLVector3 y_pixel_vec;
  737. gViewerCamera.getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
  738. LLVector3 render_position = mPositionAgent +
  739. x_pixel_vec * mPositionOffset.mV[VX] +
  740. y_pixel_vec * mPositionOffset.mV[VY];
  741. mOffScreen = false;
  742. if (!gViewerCamera.sphereInFrustum(render_position, mRadius))
  743. {
  744. if (!mVisibleOffScreen)
  745. {
  746. mVisible = false;
  747. return;
  748. }
  749. mOffScreen = true;
  750. }
  751. mVisible = true;
  752. sVisibleTextObjects.emplace_back(this);
  753. }
  754. LLVector2 LLHUDText::updateScreenPos(LLVector2& offset)
  755. {
  756. LLCoordGL screen_pos;
  757. LLVector2 screen_pos_vec;
  758. LLVector3 x_pixel_vec;
  759. LLVector3 y_pixel_vec;
  760. gViewerCamera.getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
  761. LLVector3 world_pos = mPositionAgent + offset.mV[VX] * x_pixel_vec +
  762. offset.mV[VY] * y_pixel_vec;
  763. if (!gViewerCamera.projectPosAgentToScreen(world_pos, screen_pos, false) &&
  764. mVisibleOffScreen)
  765. {
  766. // Bubble off-screen, so find a spot for it along screen edge
  767. LLVector2 window_center(gViewerWindowp->getWindowDisplayWidth() * 0.5f,
  768. gViewerWindowp->getWindowDisplayHeight() * 0.5f);
  769. LLVector2 delta_from_center(screen_pos.mX - window_center.mV[VX],
  770. screen_pos.mY - window_center.mV[VY]);
  771. delta_from_center.normalize();
  772. F32 camera_aspect = gViewerCamera.getAspect();
  773. F32 delta_aspect = fabsf(delta_from_center.mV[VX] /
  774. delta_from_center.mV[VY]);
  775. if (camera_aspect / llmax(delta_aspect, 0.001f) > 1.f)
  776. {
  777. // Camera has wider aspect ratio than offset vector, so clamp to
  778. // height
  779. delta_from_center *= fabsf(window_center.mV[VY] /
  780. delta_from_center.mV[VY]);
  781. }
  782. else
  783. {
  784. // Camera has narrower aspect ratio than offset vector, so clamp to
  785. // width
  786. delta_from_center *= fabsf(window_center.mV[VX] /
  787. delta_from_center.mV[VX]);
  788. }
  789. screen_pos_vec = window_center + delta_from_center;
  790. }
  791. else
  792. {
  793. screen_pos_vec.set((F32)screen_pos.mX, (F32)screen_pos.mY);
  794. }
  795. S32 bottom = gStatusBarHeight;
  796. if (gChatBarp && gChatBarp->getVisible())
  797. {
  798. bottom += CHAT_BAR_HEIGHT;
  799. }
  800. LLVector2 screen_center;
  801. screen_center.mV[VX] =
  802. llclamp((F32)screen_pos_vec.mV[VX], mWidth * 0.5f,
  803. (F32)gViewerWindowp->getWindowDisplayWidth() - mWidth * 0.5f);
  804. if (mVertAlignment == ALIGN_VERT_TOP)
  805. {
  806. screen_center.mV[VY] =
  807. llclamp((F32)screen_pos_vec.mV[VY], (F32)bottom,
  808. (F32)gViewerWindowp->getWindowDisplayHeight() - mHeight -
  809. (F32)gMenuBarHeight);
  810. mSoftScreenRect.setLeftTopAndSize(screen_center.mV[VX] -
  811. (mWidth + BUFFER_SIZE) * 0.5f,
  812. screen_center.mV[VY] + mHeight +
  813. BUFFER_SIZE, mWidth + BUFFER_SIZE,
  814. mHeight + BUFFER_SIZE);
  815. }
  816. else
  817. {
  818. screen_center.mV[VY] =
  819. llclamp((F32)screen_pos_vec.mV[VY],
  820. (F32)bottom + mHeight * 0.5f,
  821. (F32)gViewerWindowp->getWindowDisplayHeight() -
  822. mHeight * 0.5f - (F32)gMenuBarHeight);
  823. mSoftScreenRect.setCenterAndSize(screen_center.mV[VX],
  824. screen_center.mV[VY],
  825. mWidth + BUFFER_SIZE,
  826. mHeight + BUFFER_SIZE);
  827. }
  828. return offset + screen_center -
  829. LLVector2((F32)screen_pos.mX, (F32)screen_pos.mY);
  830. }
  831. void LLHUDText::updateSize()
  832. {
  833. F32 width = 0.f;
  834. S32 max_lines = getMaxLines();
  835. S32 lines = max_lines < 0 ? (S32)mTextSegments.size()
  836. : llmin((S32)mTextSegments.size(), max_lines);
  837. F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size());
  838. S32 start_segment = 0;
  839. if (max_lines > 0)
  840. {
  841. start_segment = llmax(0, (S32)mTextSegments.size() - max_lines);
  842. }
  843. segments_vec_t::iterator iter = mTextSegments.begin() + start_segment;
  844. segments_vec_t::iterator end = mTextSegments.end();
  845. while (iter != end)
  846. {
  847. width = llmax(width,
  848. llmin(iter++->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
  849. }
  850. iter = mLabelSegments.begin();
  851. end = mLabelSegments.end();
  852. while (iter != end)
  853. {
  854. width = llmax(width,
  855. llmin(iter++->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
  856. }
  857. if (width == 0.f)
  858. {
  859. return;
  860. }
  861. width += HORIZONTAL_PADDING;
  862. height += VERTICAL_PADDING;
  863. mWidth = llmax(width, lerp(mWidth, (F32)width, 1.f));
  864. mHeight = llmax(height, lerp(mHeight, (F32)height, 1.f));
  865. }
  866. void LLHUDText::updateAll()
  867. {
  868. sVisibleTextObjects.clear();
  869. sVisibleHUDTextObjects.clear();
  870. if (sTextObjects.empty())
  871. {
  872. return; // Nothing to do !
  873. }
  874. // Iterate over all text objects, calculate their restoration forces, and
  875. // add them to the visible set if they are on screen and close enough.
  876. for (htobj_list_it_t it = sTextObjects.begin(), end = sTextObjects.end();
  877. it != end; ++it)
  878. {
  879. LLHUDText* textp = it->get();
  880. textp->mTargetPositionOffset.clear();
  881. textp->updateSize();
  882. textp->updateVisibility();
  883. }
  884. const S32 count = sVisibleTextObjects.size();
  885. if (count == 0)
  886. {
  887. if (!sVisibleHUDTextObjects.empty())
  888. {
  889. std::sort(sVisibleHUDTextObjects.begin(),
  890. sVisibleHUDTextObjects.end(), hto_further_away());
  891. }
  892. // Nothing else to do...
  893. return;
  894. }
  895. // Sort back to front for rendering purposes.
  896. // Note: I tried to get rid of these costly std::sort calls by using a
  897. // std::set sorted with hto_further_away() (instead of a std::vector) for
  898. // both sVisibleTextObjects and sVisibleTextObjects, but it looks like it
  899. // does not suffice to get the objects properly and naturally sorted on
  900. // distance in the sets, causing bad HUD hover-text rendering issues... HB
  901. std::sort(sVisibleTextObjects.begin(), sVisibleTextObjects.end(),
  902. hto_further_away());
  903. std::sort(sVisibleHUDTextObjects.begin(), sVisibleHUDTextObjects.end(),
  904. hto_further_away());
  905. // Iterate from front to back, and set LOD based on current screen coverage
  906. F32 screen_area = (F32)(gViewerWindowp->getWindowWidth() *
  907. gViewerWindowp->getWindowHeight());
  908. F32 current_screen_area = 0.f;
  909. for (S32 i = count - 1; i >= 0; --i)
  910. {
  911. LLHUDText* textp = sVisibleTextObjects[i].get();
  912. if (textp->mUseBubble)
  913. {
  914. if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE)
  915. {
  916. textp->setLOD(3);
  917. }
  918. else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE)
  919. {
  920. textp->setLOD(2);
  921. }
  922. else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE)
  923. {
  924. textp->setLOD(1);
  925. }
  926. else
  927. {
  928. textp->setLOD(0);
  929. }
  930. textp->updateSize();
  931. // Find on-screen position and initialize collision rectangle
  932. textp->mTargetPositionOffset =
  933. textp->updateScreenPos(LLVector2::zero);
  934. current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() *
  935. textp->mSoftScreenRect.getHeight());
  936. }
  937. }
  938. if (LLViewerCamera::getVelocityStat().getCurrent() >
  939. MAX_STABLE_CAMERA_VELOCITY)
  940. {
  941. return;
  942. }
  943. for (S32 i = 0; i < NUM_OVERLAP_ITERATIONS; ++i)
  944. {
  945. for (S32 src_idx = 0; src_idx < count; ++src_idx)
  946. {
  947. LLHUDText* srcp = sVisibleTextObjects[src_idx].get();
  948. if (!srcp->mUseBubble)
  949. {
  950. continue;
  951. }
  952. for (S32 dst_idx = src_idx + 1; dst_idx < count; ++dst_idx)
  953. {
  954. LLHUDText* dstp = sVisibleTextObjects[dst_idx].get();
  955. if (!dstp->mUseBubble)
  956. {
  957. continue;
  958. }
  959. if (!srcp->mSoftScreenRect.overlaps(dstp->mSoftScreenRect))
  960. {
  961. continue;
  962. }
  963. LLRectf intersect_rect = srcp->mSoftScreenRect;
  964. intersect_rect.intersectWith(dstp->mSoftScreenRect);
  965. intersect_rect.stretch(-BUFFER_SIZE * 0.5f);
  966. F32 src_center_x = srcp->mSoftScreenRect.getCenterX();
  967. F32 src_center_y = srcp->mSoftScreenRect.getCenterY();
  968. F32 dst_center_x = dstp->mSoftScreenRect.getCenterX();
  969. F32 dst_center_y = dstp->mSoftScreenRect.getCenterY();
  970. F32 intersect_center_x = intersect_rect.getCenterX();
  971. F32 intersect_center_y = intersect_rect.getCenterY();
  972. LLVector2 force =
  973. lerp(LLVector2(dst_center_x - intersect_center_x,
  974. dst_center_y - intersect_center_y),
  975. LLVector2(intersect_center_x - src_center_x,
  976. intersect_center_y - src_center_y), 0.5f);
  977. force.set(dst_center_x - src_center_x,
  978. dst_center_y - src_center_y);
  979. force.normalize();
  980. LLVector2 src_force = -1.f * force;
  981. LLVector2 dst_force = force;
  982. LLVector2 force_strength;
  983. F32 src_mult = dstp->mMass / (dstp->mMass + srcp->mMass);
  984. F32 dst_mult = 1.f - src_mult;
  985. F32 src_aspect_ratio = srcp->mSoftScreenRect.getWidth() /
  986. srcp->mSoftScreenRect.getHeight();
  987. F32 dst_aspect_ratio = dstp->mSoftScreenRect.getWidth() /
  988. dstp->mSoftScreenRect.getHeight();
  989. src_force.mV[VY] *= src_aspect_ratio;
  990. src_force.normalize();
  991. dst_force.mV[VY] *= dst_aspect_ratio;
  992. dst_force.normalize();
  993. src_force.mV[VX] *=
  994. llmin(intersect_rect.getWidth() * src_mult,
  995. intersect_rect.getHeight() * SPRING_STRENGTH);
  996. src_force.mV[VY] *=
  997. llmin(intersect_rect.getHeight() * src_mult,
  998. intersect_rect.getWidth() * SPRING_STRENGTH);
  999. dst_force.mV[VX] *=
  1000. llmin(intersect_rect.getWidth() * dst_mult,
  1001. intersect_rect.getHeight() * SPRING_STRENGTH);
  1002. dst_force.mV[VY] *=
  1003. llmin(intersect_rect.getHeight() * dst_mult,
  1004. intersect_rect.getWidth() * SPRING_STRENGTH);
  1005. srcp->mTargetPositionOffset += src_force;
  1006. dstp->mTargetPositionOffset += dst_force;
  1007. srcp->mTargetPositionOffset =
  1008. srcp->updateScreenPos(srcp->mTargetPositionOffset);
  1009. dstp->mTargetPositionOffset =
  1010. dstp->updateScreenPos(dstp->mTargetPositionOffset);
  1011. }
  1012. }
  1013. }
  1014. for (S32 i = 0; i < count; ++i)
  1015. {
  1016. LLHUDText* textp = sVisibleTextObjects[i].get();
  1017. if (textp->mUseBubble)
  1018. {
  1019. textp->mPositionOffset =
  1020. lerp(textp->mPositionOffset, textp->mTargetPositionOffset,
  1021. LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC));
  1022. }
  1023. }
  1024. }
  1025. S32 LLHUDText::getMaxLines()
  1026. {
  1027. switch (mLOD)
  1028. {
  1029. case 0:
  1030. return mMaxLines;
  1031. case 1:
  1032. return mMaxLines > 0 ? mMaxLines / 2 : 5;
  1033. case 2:
  1034. return mMaxLines > 0 ? mMaxLines / 3 : 2;
  1035. default:
  1036. // Label only
  1037. return 0;
  1038. }
  1039. }
  1040. void LLHUDText::markDead()
  1041. {
  1042. // Hold a pointer until LLHUDObject::markDead() is done with us.
  1043. LLPointer<LLHUDText> self = LLPointer<LLHUDText>(this);
  1044. sTextObjects.erase(self);
  1045. LLHUDObject::markDead();
  1046. }
  1047. void LLHUDText::renderAllHUD()
  1048. {
  1049. {
  1050. LLGLDepthTest depth(GL_FALSE, GL_FALSE);
  1051. for (S32 i = 0, count = sVisibleHUDTextObjects.size(); i < count; ++i)
  1052. {
  1053. LLHUDText* textp = sVisibleHUDTextObjects[i].get();
  1054. textp->renderText();
  1055. }
  1056. }
  1057. LLVertexBuffer::unbind();
  1058. LL_GL_CHECK_STATES;
  1059. }
  1060. void LLHUDText::shiftAll(const LLVector3& offset)
  1061. {
  1062. htobj_list_it_t text_it;
  1063. htobj_list_it_t end = sTextObjects.end();
  1064. for (text_it = sTextObjects.begin(); text_it != end; ++text_it)
  1065. {
  1066. LLHUDText* textp = text_it->get();
  1067. textp->shift(offset);
  1068. }
  1069. }
  1070. //static
  1071. void LLHUDText::addPickable(std::set<LLViewerObject*>& pick_list)
  1072. {
  1073. // This might put an object on the pick list a second time, overriding its
  1074. // mGLName, which is ok.
  1075. // *TODO: we should probably cull against pick frustum.
  1076. for (S32 i = 0, count = sVisibleTextObjects.size(); i < count; ++i)
  1077. {
  1078. LLHUDText* textp = sVisibleTextObjects[i].get();
  1079. if (textp->mUseBubble)
  1080. {
  1081. pick_list.insert(textp->mSourceObject);
  1082. }
  1083. }
  1084. }
  1085. // Called when UI scale changes, to flush font width caches
  1086. //static
  1087. void LLHUDText::reshape()
  1088. {
  1089. for (htobj_list_it_t t_it = sTextObjects.begin(),
  1090. t_end = sTextObjects.end(); t_it != t_end; ++t_it)
  1091. {
  1092. LLHUDText* textp = t_it->get();
  1093. segments_vec_t& text_segs = textp->mTextSegments;
  1094. for (U32 i = 0, count = text_segs.size(); i < count; ++i)
  1095. {
  1096. text_segs[i].clearFontWidthCache();
  1097. }
  1098. segments_vec_t& label_segs = textp->mLabelSegments;
  1099. for (U32 i = 0, count = label_segs.size(); i < count; ++i)
  1100. {
  1101. label_segs[i].clearFontWidthCache();
  1102. }
  1103. }
  1104. }