label_3d.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. /**************************************************************************/
  2. /* label_3d.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "label_3d.h"
  31. #include "core/core_string_names.h"
  32. #include "scene/resources/theme.h"
  33. #include "scene/scene_string_names.h"
  34. void Label3D::_bind_methods() {
  35. ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label3D::set_horizontal_alignment);
  36. ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label3D::get_horizontal_alignment);
  37. ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &Label3D::set_vertical_alignment);
  38. ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label3D::get_vertical_alignment);
  39. ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &Label3D::set_modulate);
  40. ClassDB::bind_method(D_METHOD("get_modulate"), &Label3D::get_modulate);
  41. ClassDB::bind_method(D_METHOD("set_outline_modulate", "modulate"), &Label3D::set_outline_modulate);
  42. ClassDB::bind_method(D_METHOD("get_outline_modulate"), &Label3D::get_outline_modulate);
  43. ClassDB::bind_method(D_METHOD("set_text", "text"), &Label3D::set_text);
  44. ClassDB::bind_method(D_METHOD("get_text"), &Label3D::get_text);
  45. ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &Label3D::set_uppercase);
  46. ClassDB::bind_method(D_METHOD("is_uppercase"), &Label3D::is_uppercase);
  47. ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &Label3D::set_render_priority);
  48. ClassDB::bind_method(D_METHOD("get_render_priority"), &Label3D::get_render_priority);
  49. ClassDB::bind_method(D_METHOD("set_outline_render_priority", "priority"), &Label3D::set_outline_render_priority);
  50. ClassDB::bind_method(D_METHOD("get_outline_render_priority"), &Label3D::get_outline_render_priority);
  51. ClassDB::bind_method(D_METHOD("set_font", "font"), &Label3D::set_font);
  52. ClassDB::bind_method(D_METHOD("get_font"), &Label3D::get_font);
  53. ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &Label3D::set_line_spacing);
  54. ClassDB::bind_method(D_METHOD("get_line_spacing"), &Label3D::get_line_spacing);
  55. ClassDB::bind_method(D_METHOD("set_autowrap", "autowrap_mode"), &Label3D::set_autowrap);
  56. ClassDB::bind_method(D_METHOD("get_autowrap"), &Label3D::get_autowrap);
  57. ClassDB::bind_method(D_METHOD("set_width", "width"), &Label3D::set_width);
  58. ClassDB::bind_method(D_METHOD("get_width"), &Label3D::get_width);
  59. ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &Label3D::set_pixel_size);
  60. ClassDB::bind_method(D_METHOD("get_pixel_size"), &Label3D::get_pixel_size);
  61. ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Label3D::set_offset);
  62. ClassDB::bind_method(D_METHOD("get_offset"), &Label3D::get_offset);
  63. ClassDB::bind_method(D_METHOD("set_draw_flag", "flag", "enabled"), &Label3D::set_draw_flag);
  64. ClassDB::bind_method(D_METHOD("get_draw_flag", "flag"), &Label3D::get_draw_flag);
  65. ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &Label3D::set_billboard_mode);
  66. ClassDB::bind_method(D_METHOD("get_billboard_mode"), &Label3D::get_billboard_mode);
  67. ClassDB::bind_method(D_METHOD("set_alpha_cut_mode", "mode"), &Label3D::set_alpha_cut_mode);
  68. ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &Label3D::get_alpha_cut_mode);
  69. ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold", "threshold"), &Label3D::set_alpha_scissor_threshold);
  70. ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold"), &Label3D::get_alpha_scissor_threshold);
  71. ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &Label3D::generate_triangle_mesh);
  72. ClassDB::bind_method(D_METHOD("_queue_update"), &Label3D::_queue_update);
  73. ClassDB::bind_method(D_METHOD("_font_changed"), &Label3D::_font_changed);
  74. ClassDB::bind_method(D_METHOD("_im_update"), &Label3D::_im_update);
  75. ADD_PROPERTY(PropertyInfo(Variant::REAL, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001"), "set_pixel_size", "get_pixel_size");
  76. ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
  77. ADD_GROUP("Flags", "");
  78. ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard"), "set_billboard_mode", "get_billboard_mode");
  79. ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED);
  80. ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED);
  81. ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST);
  82. ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE);
  83. ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode");
  84. ADD_PROPERTY(PropertyInfo(Variant::REAL, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold");
  85. ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(VS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(VS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority");
  86. ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_render_priority", PROPERTY_HINT_RANGE, itos(VS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(VS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_outline_render_priority", "get_outline_render_priority");
  87. ADD_GROUP("Text", "");
  88. ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
  89. ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_modulate"), "set_outline_modulate", "get_outline_modulate");
  90. ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text");
  91. ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font");
  92. ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
  93. ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment");
  94. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
  95. ADD_PROPERTY(PropertyInfo(Variant::REAL, "line_spacing"), "set_line_spacing", "get_line_spacing");
  96. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autowrap"), "set_autowrap", "get_autowrap");
  97. ADD_PROPERTY(PropertyInfo(Variant::REAL, "width"), "set_width", "get_width");
  98. BIND_ENUM_CONSTANT(FLAG_SHADED);
  99. BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED);
  100. BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST);
  101. BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE);
  102. BIND_ENUM_CONSTANT(FLAG_MAX);
  103. BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED);
  104. BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD);
  105. BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS);
  106. BIND_ENUM_CONSTANT(ALIGN_LEFT);
  107. BIND_ENUM_CONSTANT(ALIGN_CENTER);
  108. BIND_ENUM_CONSTANT(ALIGN_RIGHT);
  109. BIND_ENUM_CONSTANT(ALIGN_FILL);
  110. BIND_ENUM_CONSTANT(VALIGN_TOP);
  111. BIND_ENUM_CONSTANT(VALIGN_CENTER);
  112. BIND_ENUM_CONSTANT(VALIGN_BOTTOM);
  113. BIND_ENUM_CONSTANT(VALIGN_FILL);
  114. }
  115. void Label3D::_validate_property(PropertyInfo &property) const {
  116. if (property.name == "material_override" || property.name == "material_overlay") {
  117. property.usage = PROPERTY_USAGE_NOEDITOR;
  118. }
  119. }
  120. int Label3D::get_longest_line_width() const {
  121. Ref<Font> font = _get_font_or_default();
  122. real_t max_line_width = 0;
  123. real_t line_width = 0;
  124. for (int i = 0; i < xl_text.size(); i++) {
  125. CharType current = xl_text[i];
  126. if (uppercase) {
  127. current = String::char_uppercase(current);
  128. }
  129. if (current < 32) {
  130. if (current == '\n') {
  131. if (line_width > max_line_width) {
  132. max_line_width = line_width;
  133. }
  134. line_width = 0;
  135. }
  136. } else {
  137. real_t char_width = font->get_char_size(current, xl_text[i + 1]).width;
  138. line_width += char_width;
  139. }
  140. }
  141. if (line_width > max_line_width) {
  142. max_line_width = line_width;
  143. }
  144. // ceiling to ensure autowrapping does not cut text
  145. return Math::ceil(max_line_width);
  146. }
  147. void Label3D::regenerate_word_cache() {
  148. while (word_cache) {
  149. WordCache *current = word_cache;
  150. word_cache = current->next;
  151. memdelete(current);
  152. }
  153. int max_line_width;
  154. if (autowrap) {
  155. max_line_width = width;
  156. } else {
  157. max_line_width = get_longest_line_width();
  158. }
  159. Ref<Font> font = _get_font_or_default();
  160. real_t current_word_size = 0;
  161. int word_pos = 0;
  162. real_t line_width = 0;
  163. int space_count = 0;
  164. real_t space_width = font->get_char_size(' ').width;
  165. line_count = 1;
  166. bool was_separatable = false;
  167. WordCache *last = nullptr;
  168. for (int i = 0; i <= xl_text.length(); i++) {
  169. CharType current = i < xl_text.length() ? xl_text[i] : L' '; //always a space at the end, so the algo works
  170. if (uppercase) {
  171. current = String::char_uppercase(current);
  172. }
  173. // ranges taken from https://en.wikipedia.org/wiki/Plane_(Unicode)
  174. // if your language is not well supported, consider helping improve
  175. // the unicode support in Godot.
  176. bool separatable = (current >= 0x2E08 && current <= 0x9FFF) || // CJK scripts and symbols.
  177. (current >= 0xAC00 && current <= 0xD7FF) || // Hangul Syllables and Hangul Jamo Extended-B.
  178. (current >= 0xF900 && current <= 0xFAFF) || // CJK Compatibility Ideographs.
  179. (current >= 0xFE30 && current <= 0xFE4F) || // CJK Compatibility Forms.
  180. (current >= 0xFF65 && current <= 0xFF9F) || // Halfwidth forms of katakana
  181. (current >= 0xFFA0 && current <= 0xFFDC) || // Halfwidth forms of compatibility jamo characters for Hangul
  182. (current >= 0x20000 && current <= 0x2FA1F) || // CJK Unified Ideographs Extension B ~ F and CJK Compatibility Ideographs Supplement.
  183. (current >= 0x30000 && current <= 0x3134F); // CJK Unified Ideographs Extension G.
  184. bool insert_newline = false;
  185. real_t char_width = 0;
  186. bool separation_changed = i > 0 && was_separatable != separatable;
  187. was_separatable = separatable;
  188. if (current < 33) { // Control characters and space.
  189. if (current_word_size > 0) { // These characters always create a word-break.
  190. WordCache *wc = memnew(WordCache);
  191. if (word_cache) {
  192. last->next = wc;
  193. } else {
  194. word_cache = wc;
  195. }
  196. last = wc;
  197. wc->pixel_width = current_word_size;
  198. wc->char_pos = word_pos;
  199. wc->word_len = i - word_pos;
  200. wc->space_count = space_count;
  201. current_word_size = 0;
  202. space_count = 0;
  203. } else if ((i == xl_text.length() || current == '\n') && last != nullptr && space_count != 0) {
  204. // In case there are trailing white spaces we add a placeholder word cache with just the spaces.
  205. WordCache *wc = memnew(WordCache);
  206. if (word_cache) {
  207. last->next = wc;
  208. } else {
  209. word_cache = wc;
  210. }
  211. last = wc;
  212. wc->pixel_width = 0;
  213. wc->char_pos = 0;
  214. wc->word_len = 0;
  215. wc->space_count = space_count;
  216. current_word_size = 0;
  217. space_count = 0;
  218. }
  219. if (current == '\n') {
  220. insert_newline = true;
  221. }
  222. if (i < xl_text.length() && xl_text[i] == ' ') {
  223. if (line_width == 0) {
  224. if (current_word_size == 0) {
  225. word_pos = i;
  226. }
  227. current_word_size += space_width;
  228. line_width += space_width;
  229. } else if (line_width > 0 || last == nullptr || last->char_pos != WordCache::CHAR_WRAPLINE) {
  230. space_count++;
  231. line_width += space_width;
  232. } else {
  233. space_count = 0;
  234. }
  235. }
  236. } else { // Characters with graphical representation.
  237. // Word-break on CJK & non-CJK edge.
  238. if (separation_changed && current_word_size > 0) {
  239. WordCache *wc = memnew(WordCache);
  240. if (word_cache) {
  241. last->next = wc;
  242. } else {
  243. word_cache = wc;
  244. }
  245. last = wc;
  246. wc->pixel_width = current_word_size;
  247. wc->char_pos = word_pos;
  248. wc->word_len = i - word_pos;
  249. wc->space_count = space_count;
  250. current_word_size = 0;
  251. space_count = 0;
  252. }
  253. if (current_word_size == 0) {
  254. word_pos = i;
  255. }
  256. char_width = font->get_char_size(current, xl_text[i + 1]).width;
  257. current_word_size += char_width;
  258. line_width += char_width;
  259. // allow autowrap to cut words when they exceed line width
  260. if (autowrap && (current_word_size > max_line_width)) {
  261. separatable = true;
  262. }
  263. }
  264. if ((autowrap && (line_width >= max_line_width) && ((last && last->char_pos >= 0) || separatable)) || insert_newline) {
  265. if (separatable) {
  266. if (current_word_size > 0) {
  267. WordCache *wc = memnew(WordCache);
  268. if (word_cache) {
  269. last->next = wc;
  270. } else {
  271. word_cache = wc;
  272. }
  273. last = wc;
  274. wc->pixel_width = current_word_size - char_width;
  275. wc->char_pos = word_pos;
  276. wc->word_len = i - word_pos;
  277. wc->space_count = space_count;
  278. current_word_size = char_width;
  279. word_pos = i;
  280. }
  281. }
  282. WordCache *wc = memnew(WordCache);
  283. if (word_cache) {
  284. last->next = wc;
  285. } else {
  286. word_cache = wc;
  287. }
  288. last = wc;
  289. wc->pixel_width = 0;
  290. wc->char_pos = insert_newline ? WordCache::CHAR_NEWLINE : WordCache::CHAR_WRAPLINE;
  291. line_width = current_word_size;
  292. line_count++;
  293. space_count = 0;
  294. }
  295. }
  296. word_cache_dirty = false;
  297. }
  298. void Label3D::_notification(int p_what) {
  299. switch (p_what) {
  300. case NOTIFICATION_ENTER_TREE: {
  301. if (!pending_update) {
  302. _im_update();
  303. }
  304. } break;
  305. case NOTIFICATION_TRANSLATION_CHANGED: {
  306. String new_text = tr(text);
  307. if (new_text == xl_text) {
  308. return; // Nothing new.
  309. }
  310. xl_text = new_text;
  311. regenerate_word_cache();
  312. _queue_update();
  313. } break;
  314. }
  315. }
  316. void Label3D::_im_update() {
  317. _shape();
  318. triangle_mesh.unref();
  319. update_gizmo();
  320. pending_update = false;
  321. }
  322. void Label3D::_queue_update() {
  323. if (pending_update) {
  324. return;
  325. }
  326. pending_update = true;
  327. call_deferred(SceneStringNames::get_singleton()->_im_update);
  328. }
  329. AABB Label3D::get_aabb() const {
  330. return aabb;
  331. }
  332. Ref<TriangleMesh> Label3D::generate_triangle_mesh() const {
  333. if (triangle_mesh.is_valid()) {
  334. return triangle_mesh;
  335. }
  336. Ref<Font> font = _get_font_or_default();
  337. if (font.is_null()) {
  338. return Ref<TriangleMesh>();
  339. }
  340. PoolVector<Vector3> faces;
  341. faces.resize(6);
  342. PoolVector<Vector3>::Write facesw = faces.write();
  343. if (word_cache_dirty) {
  344. const_cast<Label3D *>(this)->regenerate_word_cache();
  345. }
  346. float font_h = font->get_height() + line_spacing;
  347. real_t space_w = font->get_char_size(' ').width;
  348. float total_h = line_count * font_h;
  349. float vbegin = 0;
  350. switch (vertical_alignment) {
  351. case VALIGN_FILL:
  352. case VALIGN_TOP: {
  353. // Nothing.
  354. } break;
  355. case VALIGN_CENTER: {
  356. vbegin = (total_h - line_spacing) / 2.0;
  357. } break;
  358. case VALIGN_BOTTOM: {
  359. vbegin = (total_h - line_spacing);
  360. } break;
  361. }
  362. WordCache *wc = word_cache;
  363. if (!wc) {
  364. return Ref<TriangleMesh>();
  365. }
  366. float max_line_w = 0.0;
  367. int line = 0;
  368. while (wc) {
  369. if (line >= line_count) {
  370. break;
  371. }
  372. if (wc->char_pos < 0) {
  373. wc = wc->next;
  374. line++;
  375. continue;
  376. }
  377. WordCache *to = wc;
  378. float taken = 0;
  379. int spaces = 0;
  380. while (to && to->char_pos >= 0) {
  381. taken += to->pixel_width;
  382. if (to->space_count) {
  383. spaces += to->space_count;
  384. }
  385. to = to->next;
  386. }
  387. max_line_w = MAX(max_line_w, (taken + spaces * space_w));
  388. wc = to ? to->next : nullptr;
  389. line++;
  390. }
  391. Vector2 offset = Vector2(0, vbegin);
  392. switch (horizontal_alignment) {
  393. case ALIGN_FILL:
  394. case ALIGN_LEFT: {
  395. // Noting
  396. } break;
  397. case ALIGN_CENTER: {
  398. offset.x = -max_line_w / 2.0;
  399. } break;
  400. case ALIGN_RIGHT: {
  401. offset.x = -max_line_w;
  402. } break;
  403. }
  404. Rect2 final_rect = Rect2(offset + lbl_offset, Size2(max_line_w, total_h));
  405. if (final_rect.size.x == 0 || final_rect.size.y == 0) {
  406. return Ref<TriangleMesh>();
  407. }
  408. real_t pixel_size = get_pixel_size();
  409. Vector2 vertices[4] = {
  410. (final_rect.position + Vector2(0, -final_rect.size.y)) * pixel_size,
  411. (final_rect.position + Vector2(final_rect.size.x, -final_rect.size.y)) * pixel_size,
  412. (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
  413. final_rect.position * pixel_size,
  414. };
  415. static const int indices[6] = {
  416. 0, 1, 2,
  417. 0, 2, 3
  418. };
  419. for (int j = 0; j < 6; j++) {
  420. int i = indices[j];
  421. Vector3 vtx;
  422. vtx[0] = vertices[i][0];
  423. vtx[1] = vertices[i][1];
  424. facesw[j] = vtx;
  425. }
  426. triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
  427. triangle_mesh->create(faces);
  428. return triangle_mesh;
  429. }
  430. PoolVector<Face3> Label3D::get_faces(uint32_t p_usage_flags) const {
  431. return PoolVector<Face3>();
  432. }
  433. float Label3D::_generate_glyph_surfaces(const Ref<Font> &p_font, CharType p_char, CharType p_next, Vector2 p_offset, const Color &p_modulate, int p_priority, bool p_outline) {
  434. Vector2 gl_of;
  435. Vector2 gl_sz;
  436. Rect2 gl_uv;
  437. Size2 texs;
  438. RID tex;
  439. tex = p_font->get_char_texture(p_char, p_next, p_outline);
  440. gl_of = p_font->get_char_tx_offset(p_char, p_next, p_outline);
  441. gl_sz = p_font->get_char_tx_size(p_char, p_next, p_outline);
  442. gl_uv = p_font->get_char_tx_uv_rect(p_char, p_next, p_outline);
  443. texs = p_font->get_char_texture_size(p_char, p_next, p_outline);
  444. SurfaceKey key = SurfaceKey(tex.get_id(), p_priority);
  445. if (!surfaces.has(key)) {
  446. SurfaceData surf;
  447. surf.material = RID_PRIME(VisualServer::get_singleton()->material_create());
  448. // Set defaults for material, names need to match up those in SpatialMaterial
  449. VS::get_singleton()->material_set_param(surf.material, "albedo", Color(1, 1, 1, 1));
  450. VS::get_singleton()->material_set_param(surf.material, "specular", 0.5);
  451. VS::get_singleton()->material_set_param(surf.material, "metallic", 0.0);
  452. VS::get_singleton()->material_set_param(surf.material, "roughness", 1.0);
  453. VS::get_singleton()->material_set_param(surf.material, "uv1_offset", Vector3(0, 0, 0));
  454. VS::get_singleton()->material_set_param(surf.material, "uv1_scale", Vector3(1, 1, 1));
  455. VS::get_singleton()->material_set_param(surf.material, "uv2_offset", Vector3(0, 0, 0));
  456. VS::get_singleton()->material_set_param(surf.material, "uv2_scale", Vector3(1, 1, 1));
  457. VS::get_singleton()->material_set_param(surf.material, "alpha_scissor_threshold", alpha_scissor_threshold);
  458. RID shader_rid = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), true, get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == SpatialMaterial::BILLBOARD_ENABLED, get_billboard_mode() == SpatialMaterial::BILLBOARD_FIXED_Y, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), p_font->is_distance_field_hint());
  459. VS::get_singleton()->material_set_shader(surf.material, VS::get_singleton()->material_get_shader(shader_rid));
  460. VS::get_singleton()->material_set_param(surf.material, "texture_albedo", tex);
  461. if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) {
  462. VS::get_singleton()->material_set_render_priority(surf.material, p_priority);
  463. } else {
  464. surf.z_shift = p_priority;
  465. }
  466. surfaces[key] = surf;
  467. }
  468. SurfaceData &s = surfaces[key];
  469. s.mesh_vertices.resize((s.offset + 1) * 4);
  470. s.mesh_normals.resize((s.offset + 1) * 4);
  471. s.mesh_tangents.resize((s.offset + 1) * 16);
  472. s.mesh_colors.resize((s.offset + 1) * 4);
  473. s.mesh_uvs.resize((s.offset + 1) * 4);
  474. s.mesh_vertices.write()[(s.offset * 4) + 3] = Vector3(p_offset.x + gl_of.x, p_offset.y - gl_of.y - gl_sz.y, s.z_shift) * pixel_size;
  475. s.mesh_vertices.write()[(s.offset * 4) + 2] = Vector3(p_offset.x + gl_of.x + gl_sz.x, p_offset.y - gl_of.y - gl_sz.y, s.z_shift) * pixel_size;
  476. s.mesh_vertices.write()[(s.offset * 4) + 1] = Vector3(p_offset.x + gl_of.x + gl_sz.x, p_offset.y - gl_of.y, s.z_shift) * pixel_size;
  477. s.mesh_vertices.write()[(s.offset * 4) + 0] = Vector3(p_offset.x + gl_of.x, p_offset.y - gl_of.y, s.z_shift) * pixel_size;
  478. for (int i = 0; i < 4; i++) {
  479. s.mesh_normals.write()[(s.offset * 4) + i] = Vector3(0.0, 0.0, 1.0);
  480. s.mesh_tangents.write()[(s.offset * 16) + (i * 4) + 0] = 1.0;
  481. s.mesh_tangents.write()[(s.offset * 16) + (i * 4) + 1] = 0.0;
  482. s.mesh_tangents.write()[(s.offset * 16) + (i * 4) + 2] = 0.0;
  483. s.mesh_tangents.write()[(s.offset * 16) + (i * 4) + 3] = 1.0;
  484. s.mesh_colors.write()[(s.offset * 4) + i] = p_modulate;
  485. s.mesh_uvs.write()[(s.offset * 4) + i] = Vector2();
  486. if (aabb == AABB()) {
  487. aabb.position = s.mesh_vertices[(s.offset * 4) + i];
  488. } else {
  489. aabb.expand_to(s.mesh_vertices[(s.offset * 4) + i]);
  490. }
  491. }
  492. if (tex != RID()) {
  493. s.mesh_uvs.write()[(s.offset * 4) + 3] = Vector2(gl_uv.position.x / texs.x, (gl_uv.position.y + gl_uv.size.y) / texs.y);
  494. s.mesh_uvs.write()[(s.offset * 4) + 2] = Vector2((gl_uv.position.x + gl_uv.size.x) / texs.x, (gl_uv.position.y + gl_uv.size.y) / texs.y);
  495. s.mesh_uvs.write()[(s.offset * 4) + 1] = Vector2((gl_uv.position.x + gl_uv.size.x) / texs.x, gl_uv.position.y / texs.y);
  496. s.mesh_uvs.write()[(s.offset * 4) + 0] = Vector2(gl_uv.position.x / texs.x, gl_uv.position.y / texs.y);
  497. }
  498. s.indices.resize((s.offset + 1) * 6);
  499. s.indices.write()[(s.offset * 6) + 0] = (s.offset * 4) + 0;
  500. s.indices.write()[(s.offset * 6) + 1] = (s.offset * 4) + 1;
  501. s.indices.write()[(s.offset * 6) + 2] = (s.offset * 4) + 2;
  502. s.indices.write()[(s.offset * 6) + 3] = (s.offset * 4) + 0;
  503. s.indices.write()[(s.offset * 6) + 4] = (s.offset * 4) + 2;
  504. s.indices.write()[(s.offset * 6) + 5] = (s.offset * 4) + 3;
  505. s.offset++;
  506. return p_font->get_char_size(p_char, p_next).x;
  507. }
  508. void Label3D::_shape() {
  509. // Clear mesh.
  510. VS::get_singleton()->mesh_clear(mesh);
  511. aabb = AABB();
  512. // Clear materials.
  513. {
  514. const SurfaceKey *k = nullptr;
  515. while ((k = surfaces.next(k))) {
  516. VS::get_singleton()->free(surfaces[*k].material);
  517. }
  518. surfaces.clear();
  519. }
  520. Ref<Font> font = _get_font_or_default();
  521. ERR_FAIL_COND(font.is_null());
  522. if (word_cache_dirty) {
  523. regenerate_word_cache();
  524. }
  525. // Generate surfaces and materials.
  526. float font_h = font->get_height() + line_spacing;
  527. real_t space_w = font->get_char_size(' ').width;
  528. float total_h = line_count * font_h;
  529. float vbegin = 0.0;
  530. switch (vertical_alignment) {
  531. case VALIGN_FILL:
  532. case VALIGN_TOP: {
  533. // Nothing.
  534. } break;
  535. case VALIGN_CENTER: {
  536. vbegin = (total_h - line_spacing) / 2.0;
  537. } break;
  538. case VALIGN_BOTTOM: {
  539. vbegin = (total_h - line_spacing);
  540. } break;
  541. }
  542. WordCache *wc = word_cache;
  543. if (!wc) {
  544. return;
  545. }
  546. int line = 0;
  547. while (wc) {
  548. if (line >= line_count) {
  549. break;
  550. }
  551. if (wc->char_pos < 0) {
  552. wc = wc->next;
  553. line++;
  554. continue;
  555. }
  556. WordCache *from = wc;
  557. WordCache *to = wc;
  558. float taken = 0;
  559. int spaces = 0;
  560. while (to && to->char_pos >= 0) {
  561. taken += to->pixel_width;
  562. if (to->space_count) {
  563. spaces += to->space_count;
  564. }
  565. to = to->next;
  566. }
  567. bool can_fill = to && (to->char_pos == WordCache::CHAR_WRAPLINE || to->char_pos == WordCache::CHAR_NEWLINE);
  568. float x_ofs = 0;
  569. switch (horizontal_alignment) {
  570. case ALIGN_FILL: {
  571. x_ofs = -width / 2.0;
  572. } break;
  573. case ALIGN_LEFT: {
  574. // Noting
  575. } break;
  576. case ALIGN_CENTER: {
  577. x_ofs = -(taken + spaces * space_w) / 2.0;
  578. } break;
  579. case ALIGN_RIGHT: {
  580. x_ofs = -(taken + spaces * space_w);
  581. } break;
  582. }
  583. float y_ofs = 0;
  584. y_ofs -= line * font_h + font->get_ascent();
  585. y_ofs += vbegin;
  586. while (from != to) {
  587. // draw a word
  588. int pos = from->char_pos;
  589. if (from->char_pos < 0) {
  590. ERR_PRINT("BUG");
  591. return;
  592. }
  593. if (from->space_count) {
  594. /* spacing */
  595. x_ofs += space_w * from->space_count;
  596. if (can_fill && horizontal_alignment == ALIGN_FILL && spaces) {
  597. x_ofs += ((width - (taken + space_w * spaces)) / spaces);
  598. }
  599. }
  600. if (font->has_outline()) {
  601. float x_ofs_ol = x_ofs;
  602. for (int i = 0; i < from->word_len; i++) {
  603. CharType c = xl_text[i + pos];
  604. CharType n = xl_text[i + pos + 1];
  605. if (uppercase) {
  606. c = String::char_uppercase(c);
  607. n = String::char_uppercase(n);
  608. }
  609. x_ofs_ol += _generate_glyph_surfaces(font, c, n, lbl_offset + Point2(x_ofs_ol, y_ofs), outline_modulate, outline_render_priority, true);
  610. }
  611. }
  612. for (int i = 0; i < from->word_len; i++) {
  613. CharType c = xl_text[i + pos];
  614. CharType n = xl_text[i + pos + 1];
  615. if (uppercase) {
  616. c = String::char_uppercase(c);
  617. n = String::char_uppercase(n);
  618. }
  619. x_ofs += _generate_glyph_surfaces(font, c, n, lbl_offset + Point2(x_ofs, y_ofs), modulate, render_priority, false);
  620. }
  621. from = from->next;
  622. }
  623. wc = to ? to->next : nullptr;
  624. line++;
  625. }
  626. const SurfaceKey *k = nullptr;
  627. int idx = 0;
  628. while ((k = surfaces.next(k))) {
  629. const SurfaceData &surf = surfaces[*k];
  630. Array mesh_array;
  631. mesh_array.resize(VS::ARRAY_MAX);
  632. mesh_array[VS::ARRAY_VERTEX] = surf.mesh_vertices;
  633. mesh_array[VS::ARRAY_NORMAL] = surf.mesh_normals;
  634. mesh_array[VS::ARRAY_TANGENT] = surf.mesh_tangents;
  635. mesh_array[VS::ARRAY_COLOR] = surf.mesh_colors;
  636. mesh_array[VS::ARRAY_TEX_UV] = surf.mesh_uvs;
  637. mesh_array[VS::ARRAY_INDEX] = surf.indices;
  638. VS::get_singleton()->mesh_add_surface_from_arrays(mesh, VS::PRIMITIVE_TRIANGLES, mesh_array);
  639. VS::get_singleton()->instance_set_surface_material(get_instance(), idx++, surf.material);
  640. }
  641. }
  642. void Label3D::set_text(const String &p_string) {
  643. text = p_string;
  644. xl_text = tr(p_string);
  645. word_cache_dirty = true;
  646. _queue_update();
  647. }
  648. String Label3D::get_text() const {
  649. return text;
  650. }
  651. void Label3D::set_horizontal_alignment(Label3D::Align p_alignment) {
  652. ERR_FAIL_INDEX((int)p_alignment, 4);
  653. if (horizontal_alignment != p_alignment) {
  654. horizontal_alignment = p_alignment;
  655. _queue_update();
  656. }
  657. }
  658. Label3D::Align Label3D::get_horizontal_alignment() const {
  659. return horizontal_alignment;
  660. }
  661. void Label3D::set_vertical_alignment(Label3D::VAlign p_alignment) {
  662. ERR_FAIL_INDEX((int)p_alignment, 4);
  663. if (vertical_alignment != p_alignment) {
  664. vertical_alignment = p_alignment;
  665. _queue_update();
  666. }
  667. }
  668. Label3D::VAlign Label3D::get_vertical_alignment() const {
  669. return vertical_alignment;
  670. }
  671. void Label3D::set_uppercase(bool p_uppercase) {
  672. if (uppercase != p_uppercase) {
  673. uppercase = p_uppercase;
  674. word_cache_dirty = true;
  675. _queue_update();
  676. }
  677. }
  678. bool Label3D::is_uppercase() const {
  679. return uppercase;
  680. }
  681. void Label3D::set_render_priority(int p_priority) {
  682. ERR_FAIL_COND(p_priority < VS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > VS::MATERIAL_RENDER_PRIORITY_MAX);
  683. if (render_priority != p_priority) {
  684. render_priority = p_priority;
  685. _queue_update();
  686. }
  687. }
  688. int Label3D::get_render_priority() const {
  689. return render_priority;
  690. }
  691. void Label3D::set_outline_render_priority(int p_priority) {
  692. ERR_FAIL_COND(p_priority < VS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > VS::MATERIAL_RENDER_PRIORITY_MAX);
  693. if (outline_render_priority != p_priority) {
  694. outline_render_priority = p_priority;
  695. _queue_update();
  696. }
  697. }
  698. int Label3D::get_outline_render_priority() const {
  699. return outline_render_priority;
  700. }
  701. void Label3D::_font_changed() {
  702. word_cache_dirty = true;
  703. _queue_update();
  704. }
  705. void Label3D::set_font(const Ref<Font> &p_font) {
  706. if (font_override != p_font) {
  707. if (font_override.is_valid()) {
  708. font_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_font_changed");
  709. }
  710. font_override = p_font;
  711. if (font_override.is_valid()) {
  712. font_override->connect(CoreStringNames::get_singleton()->changed, this, "_font_changed");
  713. }
  714. _queue_update();
  715. }
  716. }
  717. Ref<Font> Label3D::get_font() const {
  718. return font_override;
  719. }
  720. Ref<Font> Label3D::_get_font_or_default() const {
  721. if (theme_font.is_valid()) {
  722. theme_font->disconnect(CoreStringNames::get_singleton()->changed, const_cast<Label3D *>(this), "_font_changed");
  723. theme_font.unref();
  724. }
  725. if (font_override.is_valid()) {
  726. return font_override;
  727. }
  728. // Check the project-defined Theme resource.
  729. if (Theme::get_project_default().is_valid()) {
  730. List<StringName> theme_types;
  731. Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
  732. for (List<StringName>::Element *E = theme_types.front(); E; E = E->next()) {
  733. if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E->get())) {
  734. Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E->get());
  735. if (f.is_valid()) {
  736. theme_font = f;
  737. theme_font->connect(CoreStringNames::get_singleton()->changed, const_cast<Label3D *>(this), "_font_changed");
  738. }
  739. return f;
  740. }
  741. }
  742. }
  743. // Lastly, fall back on the items defined in the default Theme, if they exist.
  744. {
  745. List<StringName> theme_types;
  746. Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
  747. for (List<StringName>::Element *E = theme_types.front(); E; E = E->next()) {
  748. if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E->get())) {
  749. Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E->get());
  750. if (f.is_valid()) {
  751. theme_font = f;
  752. theme_font->connect(CoreStringNames::get_singleton()->changed, const_cast<Label3D *>(this), "_font_changed");
  753. }
  754. return f;
  755. }
  756. }
  757. }
  758. // If they don't exist, use any type to return the default/empty value.
  759. Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
  760. if (f.is_valid()) {
  761. theme_font = f;
  762. theme_font->connect(CoreStringNames::get_singleton()->changed, const_cast<Label3D *>(this), "_font_changed");
  763. }
  764. return f;
  765. }
  766. void Label3D::set_modulate(const Color &p_color) {
  767. if (modulate != p_color) {
  768. modulate = p_color;
  769. _queue_update();
  770. }
  771. }
  772. Color Label3D::get_modulate() const {
  773. return modulate;
  774. }
  775. void Label3D::set_outline_modulate(const Color &p_color) {
  776. if (outline_modulate != p_color) {
  777. outline_modulate = p_color;
  778. _queue_update();
  779. }
  780. }
  781. Color Label3D::get_outline_modulate() const {
  782. return outline_modulate;
  783. }
  784. void Label3D::set_autowrap(bool p_autowrap) {
  785. if (autowrap != p_autowrap) {
  786. autowrap = p_autowrap;
  787. word_cache_dirty = true;
  788. _queue_update();
  789. }
  790. }
  791. bool Label3D::get_autowrap() const {
  792. return autowrap;
  793. }
  794. void Label3D::set_width(float p_width) {
  795. if (width != p_width) {
  796. width = p_width;
  797. word_cache_dirty = true;
  798. _queue_update();
  799. }
  800. }
  801. float Label3D::get_width() const {
  802. return width;
  803. }
  804. void Label3D::set_pixel_size(real_t p_amount) {
  805. if (pixel_size != p_amount) {
  806. pixel_size = p_amount;
  807. _queue_update();
  808. }
  809. }
  810. real_t Label3D::get_pixel_size() const {
  811. return pixel_size;
  812. }
  813. void Label3D::set_offset(const Point2 &p_offset) {
  814. if (lbl_offset != p_offset) {
  815. lbl_offset = p_offset;
  816. _queue_update();
  817. }
  818. }
  819. Point2 Label3D::get_offset() const {
  820. return lbl_offset;
  821. }
  822. void Label3D::set_line_spacing(float p_line_spacing) {
  823. if (line_spacing != p_line_spacing) {
  824. line_spacing = p_line_spacing;
  825. _queue_update();
  826. }
  827. }
  828. float Label3D::get_line_spacing() const {
  829. return line_spacing;
  830. }
  831. void Label3D::set_draw_flag(DrawFlags p_flag, bool p_enable) {
  832. ERR_FAIL_INDEX(p_flag, FLAG_MAX);
  833. if (flags[p_flag] != p_enable) {
  834. flags[p_flag] = p_enable;
  835. _queue_update();
  836. }
  837. }
  838. bool Label3D::get_draw_flag(DrawFlags p_flag) const {
  839. ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false);
  840. return flags[p_flag];
  841. }
  842. void Label3D::set_billboard_mode(SpatialMaterial::BillboardMode p_mode) {
  843. ERR_FAIL_INDEX(p_mode, 3);
  844. if (billboard_mode != p_mode) {
  845. billboard_mode = p_mode;
  846. _queue_update();
  847. }
  848. }
  849. SpatialMaterial::BillboardMode Label3D::get_billboard_mode() const {
  850. return billboard_mode;
  851. }
  852. void Label3D::set_alpha_cut_mode(AlphaCutMode p_mode) {
  853. ERR_FAIL_INDEX(p_mode, 3);
  854. if (alpha_cut != p_mode) {
  855. alpha_cut = p_mode;
  856. _queue_update();
  857. }
  858. }
  859. Label3D::AlphaCutMode Label3D::get_alpha_cut_mode() const {
  860. return alpha_cut;
  861. }
  862. void Label3D::set_alpha_scissor_threshold(float p_threshold) {
  863. if (alpha_scissor_threshold != p_threshold) {
  864. alpha_scissor_threshold = p_threshold;
  865. _queue_update();
  866. }
  867. }
  868. float Label3D::get_alpha_scissor_threshold() const {
  869. return alpha_scissor_threshold;
  870. }
  871. Label3D::Label3D() {
  872. for (int i = 0; i < FLAG_MAX; i++) {
  873. flags[i] = (i == FLAG_DOUBLE_SIDED);
  874. }
  875. mesh = RID_PRIME(VisualServer::get_singleton()->mesh_create());
  876. set_base(mesh);
  877. }
  878. Label3D::~Label3D() {
  879. while (word_cache) {
  880. WordCache *current = word_cache;
  881. word_cache = current->next;
  882. memdelete(current);
  883. }
  884. VS::get_singleton()->free(mesh);
  885. const SurfaceKey *k = nullptr;
  886. while ((k = surfaces.next(k))) {
  887. VS::get_singleton()->free(surfaces[*k].material);
  888. }
  889. surfaces.clear();
  890. }