Text3D.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. // Copyright (c) 2008-2022 the Urho3D project
  2. // License: MIT
  3. #include "../Precompiled.h"
  4. #include "../Core/Context.h"
  5. #include "../Graphics/Camera.h"
  6. #include "../Graphics/Geometry.h"
  7. #include "../Graphics/Graphics.h"
  8. #include "../Graphics/Material.h"
  9. #include "../Graphics/Technique.h"
  10. #include "../GraphicsAPI/VertexBuffer.h"
  11. #include "../IO/Log.h"
  12. #include "../Resource/ResourceCache.h"
  13. #include "../Scene/Node.h"
  14. #include "../UI/Font.h"
  15. #include "../UI/Text.h"
  16. #include "../UI/Text3D.h"
  17. namespace Urho3D
  18. {
  19. extern const char* horizontalAlignments[];
  20. extern const char* verticalAlignments[];
  21. extern const char* textEffects[];
  22. extern const char* faceCameraModeNames[];
  23. extern const char* GEOMETRY_CATEGORY;
  24. static const float TEXT_SCALING = 1.0f / 128.0f;
  25. static const float DEFAULT_EFFECT_DEPTH_BIAS = 0.1f;
  26. Text3D::Text3D(Context* context) :
  27. Drawable(context, DrawableTypes::Geometry),
  28. text_(context),
  29. vertexBuffer_(new VertexBuffer(context_)),
  30. customWorldTransform_(Matrix3x4::IDENTITY),
  31. faceCameraMode_(FC_NONE),
  32. minAngle_(0.0f),
  33. fixedScreenSize_(false),
  34. textDirty_(true),
  35. geometryDirty_(true),
  36. usingSDFShader_(false),
  37. fontDataLost_(false)
  38. {
  39. text_.SetEffectDepthBias(DEFAULT_EFFECT_DEPTH_BIAS);
  40. }
  41. Text3D::~Text3D() = default;
  42. void Text3D::RegisterObject(Context* context)
  43. {
  44. context->RegisterFactory<Text3D>(GEOMETRY_CATEGORY);
  45. URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, true, AM_DEFAULT);
  46. URHO3D_ACCESSOR_ATTRIBUTE("Font", GetFontAttr, SetFontAttr, ResourceRef(Font::GetTypeStatic()), AM_DEFAULT);
  47. URHO3D_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef(Material::GetTypeStatic()),
  48. AM_DEFAULT);
  49. URHO3D_ATTRIBUTE("Font Size", text_.fontSize_, DEFAULT_FONT_SIZE, AM_DEFAULT);
  50. URHO3D_ACCESSOR_ATTRIBUTE("Text", GetTextAttr, SetTextAttr, String::EMPTY, AM_DEFAULT);
  51. URHO3D_ENUM_ATTRIBUTE("Text Alignment", text_.textAlignment_, horizontalAlignments, HA_LEFT, AM_DEFAULT);
  52. URHO3D_ATTRIBUTE("Row Spacing", text_.rowSpacing_, 1.0f, AM_DEFAULT);
  53. URHO3D_ATTRIBUTE("Word Wrap", text_.wordWrap_, false, AM_DEFAULT);
  54. URHO3D_ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, true, AM_DEFAULT);
  55. URHO3D_ACCESSOR_ATTRIBUTE("Fixed Screen Size", IsFixedScreenSize, SetFixedScreenSize, false, AM_DEFAULT);
  56. URHO3D_ENUM_ATTRIBUTE("Face Camera Mode", faceCameraMode_, faceCameraModeNames, FC_NONE, AM_DEFAULT);
  57. URHO3D_ATTRIBUTE("Min Angle", minAngle_, 0.0f, AM_DEFAULT);
  58. URHO3D_ACCESSOR_ATTRIBUTE("Draw Distance", GetDrawDistance, SetDrawDistance, 0.0f, AM_DEFAULT);
  59. URHO3D_ACCESSOR_ATTRIBUTE("Width", GetWidth, SetWidth, 0, AM_DEFAULT);
  60. URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Horiz Alignment", GetHorizontalAlignment, SetHorizontalAlignment,
  61. horizontalAlignments, HA_LEFT, AM_DEFAULT);
  62. URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Vert Alignment", GetVerticalAlignment, SetVerticalAlignment, verticalAlignments,
  63. VA_TOP, AM_DEFAULT);
  64. URHO3D_ACCESSOR_ATTRIBUTE("Opacity", GetOpacity, SetOpacity, 1.0f, AM_DEFAULT);
  65. URHO3D_ACCESSOR_ATTRIBUTE("Color", GetColorAttr, SetColor, Color::WHITE, AM_DEFAULT);
  66. URHO3D_ATTRIBUTE("Top Left Color", text_.colors_[0], Color::WHITE, AM_DEFAULT);
  67. URHO3D_ATTRIBUTE("Top Right Color", text_.colors_[1], Color::WHITE, AM_DEFAULT);
  68. URHO3D_ATTRIBUTE("Bottom Left Color", text_.colors_[2], Color::WHITE, AM_DEFAULT);
  69. URHO3D_ATTRIBUTE("Bottom Right Color", text_.colors_[3], Color::WHITE, AM_DEFAULT);
  70. URHO3D_ENUM_ATTRIBUTE("Text Effect", text_.textEffect_, textEffects, TE_NONE, AM_DEFAULT);
  71. URHO3D_ATTRIBUTE("Shadow Offset", text_.shadowOffset_, IntVector2(1, 1), AM_DEFAULT);
  72. URHO3D_ATTRIBUTE("Stroke Thickness", text_.strokeThickness_, 1, AM_DEFAULT);
  73. URHO3D_ATTRIBUTE("Round Stroke", text_.roundStroke_, false, AM_DEFAULT);
  74. URHO3D_ACCESSOR_ATTRIBUTE("Effect Color", GetEffectColor, SetEffectColor, Color::BLACK, AM_DEFAULT);
  75. URHO3D_ATTRIBUTE("Effect Depth Bias", text_.effectDepthBias_, DEFAULT_EFFECT_DEPTH_BIAS, AM_DEFAULT);
  76. URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
  77. }
  78. void Text3D::ApplyAttributes()
  79. {
  80. text_.ApplyAttributes();
  81. MarkTextDirty();
  82. UpdateTextBatches();
  83. UpdateTextMaterials();
  84. }
  85. void Text3D::UpdateBatches(const FrameInfo& frame)
  86. {
  87. distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
  88. if (faceCameraMode_ != FC_NONE || fixedScreenSize_)
  89. CalculateFixedScreenSize(frame);
  90. for (i32 i = 0; i < batches_.Size(); ++i)
  91. {
  92. batches_[i].distance_ = distance_;
  93. batches_[i].worldTransform_ = faceCameraMode_ != FC_NONE ? &customWorldTransform_ : &node_->GetWorldTransform();
  94. }
  95. for (i32 i = 0; i < uiBatches_.Size(); ++i)
  96. {
  97. if (uiBatches_[i].texture_ && uiBatches_[i].texture_->IsDataLost())
  98. {
  99. fontDataLost_ = true;
  100. break;
  101. }
  102. }
  103. }
  104. void Text3D::UpdateGeometry(const FrameInfo& frame)
  105. {
  106. if (fontDataLost_)
  107. {
  108. // Re-evaluation of the text triggers the font face to reload itself
  109. UpdateTextBatches();
  110. UpdateTextMaterials();
  111. fontDataLost_ = false;
  112. }
  113. // In case is being rendered from multiple views, recalculate camera facing & fixed size
  114. if (faceCameraMode_ != FC_NONE || fixedScreenSize_)
  115. CalculateFixedScreenSize(frame);
  116. if (geometryDirty_)
  117. {
  118. for (i32 i = 0; i < batches_.Size() && i < uiBatches_.Size(); ++i)
  119. {
  120. Geometry* geometry = geometries_[i];
  121. geometry->SetDrawRange(TRIANGLE_LIST, 0, 0, uiBatches_[i].vertexStart_ / UI_VERTEX_SIZE,
  122. (uiBatches_[i].vertexEnd_ - uiBatches_[i].vertexStart_) / UI_VERTEX_SIZE);
  123. }
  124. }
  125. if ((geometryDirty_ || vertexBuffer_->IsDataLost()) && uiVertexData_.Size())
  126. {
  127. i32 vertexCount = uiVertexData_.Size() / UI_VERTEX_SIZE;
  128. if (vertexBuffer_->GetVertexCount() != vertexCount)
  129. vertexBuffer_->SetSize(vertexCount, VertexElements::Position | VertexElements::Color | VertexElements::TexCoord1);
  130. vertexBuffer_->SetData(&uiVertexData_[0]);
  131. }
  132. geometryDirty_ = false;
  133. }
  134. UpdateGeometryType Text3D::GetUpdateGeometryType()
  135. {
  136. if (geometryDirty_ || fontDataLost_ || vertexBuffer_->IsDataLost() || faceCameraMode_ != FC_NONE || fixedScreenSize_)
  137. return UPDATE_MAIN_THREAD;
  138. else
  139. return UPDATE_NONE;
  140. }
  141. void Text3D::SetMaterial(Material* material)
  142. {
  143. material_ = material;
  144. UpdateTextMaterials(true);
  145. }
  146. bool Text3D::SetFont(const String& fontName, float size)
  147. {
  148. bool success = text_.SetFont(fontName, size);
  149. // Changing font requires materials to be re-evaluated. Material evaluation can not be done in worker threads,
  150. // so UI batches must be brought up-to-date immediately
  151. MarkTextDirty();
  152. UpdateTextBatches();
  153. UpdateTextMaterials();
  154. return success;
  155. }
  156. bool Text3D::SetFont(Font* font, float size)
  157. {
  158. bool success = text_.SetFont(font, size);
  159. MarkTextDirty();
  160. UpdateTextBatches();
  161. UpdateTextMaterials();
  162. return success;
  163. }
  164. bool Text3D::SetFontSize(float size)
  165. {
  166. bool success = text_.SetFontSize(size);
  167. MarkTextDirty();
  168. UpdateTextBatches();
  169. UpdateTextMaterials();
  170. return success;
  171. }
  172. void Text3D::SetText(const String& text)
  173. {
  174. text_.SetText(text);
  175. // Changing text requires materials to be re-evaluated, in case the font is multi-page
  176. MarkTextDirty();
  177. UpdateTextBatches();
  178. UpdateTextMaterials();
  179. }
  180. void Text3D::SetAlignment(HorizontalAlignment hAlign, VerticalAlignment vAlign)
  181. {
  182. text_.SetAlignment(hAlign, vAlign);
  183. MarkTextDirty();
  184. }
  185. void Text3D::SetHorizontalAlignment(HorizontalAlignment align)
  186. {
  187. text_.SetHorizontalAlignment(align);
  188. MarkTextDirty();
  189. }
  190. void Text3D::SetVerticalAlignment(VerticalAlignment align)
  191. {
  192. text_.SetVerticalAlignment(align);
  193. MarkTextDirty();
  194. }
  195. void Text3D::SetTextAlignment(HorizontalAlignment align)
  196. {
  197. text_.SetTextAlignment(align);
  198. MarkTextDirty();
  199. }
  200. void Text3D::SetRowSpacing(float spacing)
  201. {
  202. text_.SetRowSpacing(spacing);
  203. MarkTextDirty();
  204. }
  205. void Text3D::SetWordwrap(bool enable)
  206. {
  207. text_.SetWordwrap(enable);
  208. MarkTextDirty();
  209. }
  210. void Text3D::SetTextEffect(TextEffect textEffect)
  211. {
  212. text_.SetTextEffect(textEffect);
  213. MarkTextDirty();
  214. UpdateTextMaterials(true);
  215. }
  216. void Text3D::SetEffectShadowOffset(const IntVector2& offset)
  217. {
  218. text_.SetEffectShadowOffset(offset);
  219. }
  220. void Text3D::SetEffectStrokeThickness(int thickness)
  221. {
  222. text_.SetEffectStrokeThickness(thickness);
  223. }
  224. void Text3D::SetEffectRoundStroke(bool roundStroke)
  225. {
  226. text_.SetEffectRoundStroke(roundStroke);
  227. }
  228. void Text3D::SetEffectColor(const Color& effectColor)
  229. {
  230. text_.SetEffectColor(effectColor);
  231. MarkTextDirty();
  232. UpdateTextMaterials();
  233. }
  234. void Text3D::SetEffectDepthBias(float bias)
  235. {
  236. text_.SetEffectDepthBias(bias);
  237. MarkTextDirty();
  238. }
  239. void Text3D::SetWidth(int width)
  240. {
  241. text_.SetMinWidth(width);
  242. text_.SetWidth(width);
  243. MarkTextDirty();
  244. }
  245. void Text3D::SetColor(const Color& color)
  246. {
  247. float oldAlpha = text_.GetColor(C_TOPLEFT).a_;
  248. text_.SetColor(color);
  249. MarkTextDirty();
  250. // If alpha changes from zero to nonzero or vice versa, amount of text batches changes (optimization), so do full update
  251. if ((oldAlpha == 0.0f && color.a_ != 0.0f) || (oldAlpha != 0.0f && color.a_ == 0.0f))
  252. {
  253. UpdateTextBatches();
  254. UpdateTextMaterials();
  255. }
  256. }
  257. void Text3D::SetColor(Corner corner, const Color& color)
  258. {
  259. text_.SetColor(corner, color);
  260. MarkTextDirty();
  261. }
  262. void Text3D::SetOpacity(float opacity)
  263. {
  264. float oldOpacity = text_.GetOpacity();
  265. text_.SetOpacity(opacity);
  266. float newOpacity = text_.GetOpacity();
  267. MarkTextDirty();
  268. // If opacity changes from zero to nonzero or vice versa, amount of text batches changes (optimization), so do full update
  269. if ((oldOpacity == 0.0f && newOpacity != 0.0f) || (oldOpacity != 0.0f && newOpacity == 0.0f))
  270. {
  271. UpdateTextBatches();
  272. UpdateTextMaterials();
  273. }
  274. }
  275. void Text3D::SetFixedScreenSize(bool enable)
  276. {
  277. if (enable != fixedScreenSize_)
  278. {
  279. fixedScreenSize_ = enable;
  280. // Bounding box must be recalculated
  281. OnMarkedDirty(node_);
  282. MarkNetworkUpdate();
  283. }
  284. }
  285. void Text3D::SetFaceCameraMode(FaceCameraMode mode)
  286. {
  287. if (mode != faceCameraMode_)
  288. {
  289. faceCameraMode_ = mode;
  290. // Bounding box must be recalculated
  291. OnMarkedDirty(node_);
  292. MarkNetworkUpdate();
  293. }
  294. }
  295. Material* Text3D::GetMaterial() const
  296. {
  297. return material_;
  298. }
  299. Font* Text3D::GetFont() const
  300. {
  301. return text_.GetFont();
  302. }
  303. float Text3D::GetFontSize() const
  304. {
  305. return text_.GetFontSize();
  306. }
  307. const String& Text3D::GetText() const
  308. {
  309. return text_.GetText();
  310. }
  311. HorizontalAlignment Text3D::GetHorizontalAlignment() const
  312. {
  313. return text_.GetHorizontalAlignment();
  314. }
  315. VerticalAlignment Text3D::GetVerticalAlignment() const
  316. {
  317. return text_.GetVerticalAlignment();
  318. }
  319. HorizontalAlignment Text3D::GetTextAlignment() const
  320. {
  321. return text_.GetTextAlignment();
  322. }
  323. float Text3D::GetRowSpacing() const
  324. {
  325. return text_.GetRowSpacing();
  326. }
  327. bool Text3D::GetWordwrap() const
  328. {
  329. return text_.GetWordwrap();
  330. }
  331. TextEffect Text3D::GetTextEffect() const
  332. {
  333. return text_.GetTextEffect();
  334. }
  335. const IntVector2& Text3D::GetEffectShadowOffset() const
  336. {
  337. return text_.GetEffectShadowOffset();
  338. }
  339. int Text3D::GetEffectStrokeThickness() const
  340. {
  341. return text_.GetEffectStrokeThickness();
  342. }
  343. bool Text3D::GetEffectRoundStroke() const
  344. {
  345. return text_.GetEffectRoundStroke();
  346. }
  347. const Color& Text3D::GetEffectColor() const
  348. {
  349. return text_.GetEffectColor();
  350. }
  351. float Text3D::GetEffectDepthBias() const
  352. {
  353. return text_.GetEffectDepthBias();
  354. }
  355. int Text3D::GetWidth() const
  356. {
  357. return text_.GetWidth();
  358. }
  359. int Text3D::GetHeight() const
  360. {
  361. return text_.GetHeight();
  362. }
  363. int Text3D::GetRowHeight() const
  364. {
  365. return text_.GetRowHeight();
  366. }
  367. i32 Text3D::GetNumRows() const
  368. {
  369. return text_.GetNumRows();
  370. }
  371. i32 Text3D::GetNumChars() const
  372. {
  373. return text_.GetNumChars();
  374. }
  375. int Text3D::GetRowWidth(i32 index) const
  376. {
  377. assert(index >= 0);
  378. return text_.GetRowWidth(index);
  379. }
  380. Vector2 Text3D::GetCharPosition(i32 index)
  381. {
  382. assert(index >= 0);
  383. return text_.GetCharPosition(index);
  384. }
  385. Vector2 Text3D::GetCharSize(i32 index)
  386. {
  387. assert(index >= 0);
  388. return text_.GetCharSize(index);
  389. }
  390. const Color& Text3D::GetColor(Corner corner) const
  391. {
  392. return text_.GetColor(corner);
  393. }
  394. float Text3D::GetOpacity() const
  395. {
  396. return text_.GetOpacity();
  397. }
  398. void Text3D::OnNodeSet(Node* node)
  399. {
  400. Drawable::OnNodeSet(node);
  401. if (node)
  402. customWorldTransform_ = node->GetWorldTransform();
  403. }
  404. void Text3D::OnWorldBoundingBoxUpdate()
  405. {
  406. if (textDirty_)
  407. UpdateTextBatches();
  408. // In face camera mode, use the last camera rotation to build the world bounding box
  409. if (faceCameraMode_ != FC_NONE || fixedScreenSize_)
  410. {
  411. worldBoundingBox_ = boundingBox_.Transformed(Matrix3x4(node_->GetWorldPosition(),
  412. customWorldTransform_.Rotation(), customWorldTransform_.Scale()));
  413. }
  414. else
  415. worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
  416. }
  417. void Text3D::MarkTextDirty()
  418. {
  419. textDirty_ = true;
  420. OnMarkedDirty(node_);
  421. MarkNetworkUpdate();
  422. }
  423. void Text3D::SetMaterialAttr(const ResourceRef& value)
  424. {
  425. auto* cache = GetSubsystem<ResourceCache>();
  426. SetMaterial(cache->GetResource<Material>(value.name_));
  427. }
  428. void Text3D::SetFontAttr(const ResourceRef& value)
  429. {
  430. auto* cache = GetSubsystem<ResourceCache>();
  431. text_.font_ = cache->GetResource<Font>(value.name_);
  432. }
  433. void Text3D::SetTextAttr(const String& value)
  434. {
  435. text_.SetTextAttr(value);
  436. }
  437. String Text3D::GetTextAttr() const
  438. {
  439. return text_.GetTextAttr();
  440. }
  441. ResourceRef Text3D::GetMaterialAttr() const
  442. {
  443. return GetResourceRef(material_, Material::GetTypeStatic());
  444. }
  445. ResourceRef Text3D::GetFontAttr() const
  446. {
  447. return GetResourceRef(text_.font_, Font::GetTypeStatic());
  448. }
  449. void Text3D::UpdateTextBatches()
  450. {
  451. uiBatches_.Clear();
  452. uiVertexData_.Clear();
  453. text_.GetBatches(uiBatches_, uiVertexData_, IntRect::ZERO);
  454. Vector3 offset(Vector3::ZERO);
  455. switch (text_.GetHorizontalAlignment())
  456. {
  457. case HA_LEFT:
  458. break;
  459. case HA_CENTER:
  460. offset.x_ -= (float)text_.GetWidth() * 0.5f;
  461. break;
  462. case HA_RIGHT:
  463. offset.x_ -= (float)text_.GetWidth();
  464. break;
  465. case HA_CUSTOM:
  466. break;
  467. }
  468. switch (text_.GetVerticalAlignment())
  469. {
  470. case VA_TOP:
  471. break;
  472. case VA_CENTER:
  473. offset.y_ -= (float)text_.GetHeight() * 0.5f;
  474. break;
  475. case VA_BOTTOM:
  476. offset.y_ -= (float)text_.GetHeight();
  477. break;
  478. case VA_CUSTOM:
  479. break;
  480. }
  481. if (uiVertexData_.Size())
  482. {
  483. boundingBox_.Clear();
  484. for (i32 i = 0; i < uiVertexData_.Size(); i += UI_VERTEX_SIZE)
  485. {
  486. Vector3& position = *(reinterpret_cast<Vector3*>(&uiVertexData_[i]));
  487. position += offset;
  488. position *= TEXT_SCALING;
  489. position.y_ = -position.y_;
  490. boundingBox_.Merge(position);
  491. }
  492. }
  493. else
  494. boundingBox_.Define(Vector3::ZERO, Vector3::ZERO);
  495. textDirty_ = false;
  496. geometryDirty_ = true;
  497. }
  498. void Text3D::UpdateTextMaterials(bool forceUpdate)
  499. {
  500. Font* font = GetFont();
  501. bool isSDFFont = font ? font->IsSDFFont() : false;
  502. batches_.Resize(uiBatches_.Size());
  503. geometries_.Resize(uiBatches_.Size());
  504. for (i32 i = 0; i < batches_.Size(); ++i)
  505. {
  506. if (!geometries_[i])
  507. {
  508. auto* geometry = new Geometry(context_);
  509. geometry->SetVertexBuffer(0, vertexBuffer_);
  510. batches_[i].geometry_ = geometries_[i] = geometry;
  511. }
  512. if (!batches_[i].material_ || forceUpdate || isSDFFont != usingSDFShader_)
  513. {
  514. // If material not defined, create a reasonable default from scratch
  515. if (!material_)
  516. {
  517. auto* material = new Material(context_);
  518. auto* tech = new Technique(context_);
  519. Pass* pass = tech->CreatePass("alpha");
  520. pass->SetVertexShader("Text");
  521. pass->SetPixelShader("Text");
  522. pass->SetBlendMode(BLEND_ALPHA);
  523. pass->SetDepthWrite(false);
  524. material->SetTechnique(0, tech);
  525. material->SetCullMode(CULL_NONE);
  526. batches_[i].material_ = material;
  527. }
  528. else
  529. batches_[i].material_ = material_->Clone();
  530. usingSDFShader_ = isSDFFont;
  531. }
  532. Material* material = batches_[i].material_;
  533. Texture* texture = uiBatches_[i].texture_;
  534. material->SetTexture(TU_DIFFUSE, texture);
  535. if (isSDFFont)
  536. {
  537. // Note: custom defined material is assumed to have right shader defines; they aren't modified here
  538. if (!material_)
  539. {
  540. Technique* tech = material->GetTechnique(0);
  541. Pass* pass = tech ? tech->GetPass("alpha") : nullptr;
  542. if (pass)
  543. {
  544. switch (GetTextEffect())
  545. {
  546. case TE_NONE:
  547. pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD");
  548. break;
  549. case TE_SHADOW:
  550. pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD TEXT_EFFECT_SHADOW");
  551. break;
  552. case TE_STROKE:
  553. pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD TEXT_EFFECT_STROKE");
  554. break;
  555. }
  556. }
  557. }
  558. switch (GetTextEffect())
  559. {
  560. case TE_SHADOW:
  561. if (texture)
  562. {
  563. Vector2 shadowOffset(0.5f / texture->GetWidth(), 0.5f / texture->GetHeight());
  564. material->SetShaderParameter("ShadowOffset", shadowOffset);
  565. }
  566. material->SetShaderParameter("ShadowColor", GetEffectColor());
  567. break;
  568. case TE_STROKE:
  569. material->SetShaderParameter("StrokeColor", GetEffectColor());
  570. break;
  571. default:
  572. break;
  573. }
  574. }
  575. else
  576. {
  577. // If not SDF, set shader defines based on whether font texture is full RGB or just alpha
  578. if (!material_)
  579. {
  580. Technique* tech = material->GetTechnique(0);
  581. Pass* pass = tech ? tech->GetPass("alpha") : nullptr;
  582. if (pass)
  583. {
  584. if (texture && texture->GetFormat() == Graphics::GetAlphaFormat())
  585. pass->SetPixelShaderDefines("ALPHAMAP");
  586. else
  587. pass->SetPixelShaderDefines("");
  588. }
  589. }
  590. }
  591. }
  592. }
  593. void Text3D::CalculateFixedScreenSize(const FrameInfo& frame)
  594. {
  595. Vector3 worldPosition = node_->GetWorldPosition();
  596. Vector3 worldScale = node_->GetWorldScale();
  597. if (fixedScreenSize_)
  598. {
  599. float textScaling = 2.0f / TEXT_SCALING / frame.viewSize_.y_;
  600. float halfViewWorldSize = frame.camera_->GetHalfViewSize();
  601. if (!frame.camera_->IsOrthographic())
  602. {
  603. Matrix4 viewProj(frame.camera_->GetProjection() * frame.camera_->GetView());
  604. Vector4 projPos(viewProj * Vector4(worldPosition, 1.0f));
  605. worldScale *= textScaling * halfViewWorldSize * projPos.w_;
  606. }
  607. else
  608. worldScale *= textScaling * halfViewWorldSize;
  609. }
  610. customWorldTransform_ = Matrix3x4(worldPosition, frame.camera_->GetFaceCameraRotation(
  611. worldPosition, node_->GetWorldRotation(), faceCameraMode_, minAngle_), worldScale);
  612. worldBoundingBoxDirty_ = true;
  613. }
  614. }