| 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 | |
| 31 | #include "label_3d.h" |
| 32 | |
| 33 | #include "scene/main/viewport.h" |
| 34 | #include "scene/resources/theme.h" |
| 35 | #include "scene/scene_string_names.h" |
| 36 | #include "scene/theme/theme_db.h" |
| 37 | |
| 38 | void Label3D::_bind_methods() { |
| 39 | ClassDB::bind_method(D_METHOD("set_horizontal_alignment" , "alignment" ), &Label3D::set_horizontal_alignment); |
| 40 | ClassDB::bind_method(D_METHOD("get_horizontal_alignment" ), &Label3D::get_horizontal_alignment); |
| 41 | |
| 42 | ClassDB::bind_method(D_METHOD("set_vertical_alignment" , "alignment" ), &Label3D::set_vertical_alignment); |
| 43 | ClassDB::bind_method(D_METHOD("get_vertical_alignment" ), &Label3D::get_vertical_alignment); |
| 44 | |
| 45 | ClassDB::bind_method(D_METHOD("set_modulate" , "modulate" ), &Label3D::set_modulate); |
| 46 | ClassDB::bind_method(D_METHOD("get_modulate" ), &Label3D::get_modulate); |
| 47 | |
| 48 | ClassDB::bind_method(D_METHOD("set_outline_modulate" , "modulate" ), &Label3D::set_outline_modulate); |
| 49 | ClassDB::bind_method(D_METHOD("get_outline_modulate" ), &Label3D::get_outline_modulate); |
| 50 | |
| 51 | ClassDB::bind_method(D_METHOD("set_text" , "text" ), &Label3D::set_text); |
| 52 | ClassDB::bind_method(D_METHOD("get_text" ), &Label3D::get_text); |
| 53 | |
| 54 | ClassDB::bind_method(D_METHOD("set_text_direction" , "direction" ), &Label3D::set_text_direction); |
| 55 | ClassDB::bind_method(D_METHOD("get_text_direction" ), &Label3D::get_text_direction); |
| 56 | |
| 57 | ClassDB::bind_method(D_METHOD("set_language" , "language" ), &Label3D::set_language); |
| 58 | ClassDB::bind_method(D_METHOD("get_language" ), &Label3D::get_language); |
| 59 | |
| 60 | ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override" , "parser" ), &Label3D::set_structured_text_bidi_override); |
| 61 | ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override" ), &Label3D::get_structured_text_bidi_override); |
| 62 | |
| 63 | ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options" , "args" ), &Label3D::set_structured_text_bidi_override_options); |
| 64 | ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options" ), &Label3D::get_structured_text_bidi_override_options); |
| 65 | |
| 66 | ClassDB::bind_method(D_METHOD("set_uppercase" , "enable" ), &Label3D::set_uppercase); |
| 67 | ClassDB::bind_method(D_METHOD("is_uppercase" ), &Label3D::is_uppercase); |
| 68 | |
| 69 | ClassDB::bind_method(D_METHOD("set_render_priority" , "priority" ), &Label3D::set_render_priority); |
| 70 | ClassDB::bind_method(D_METHOD("get_render_priority" ), &Label3D::get_render_priority); |
| 71 | |
| 72 | ClassDB::bind_method(D_METHOD("set_outline_render_priority" , "priority" ), &Label3D::set_outline_render_priority); |
| 73 | ClassDB::bind_method(D_METHOD("get_outline_render_priority" ), &Label3D::get_outline_render_priority); |
| 74 | |
| 75 | ClassDB::bind_method(D_METHOD("set_font" , "font" ), &Label3D::set_font); |
| 76 | ClassDB::bind_method(D_METHOD("get_font" ), &Label3D::get_font); |
| 77 | |
| 78 | ClassDB::bind_method(D_METHOD("set_font_size" , "size" ), &Label3D::set_font_size); |
| 79 | ClassDB::bind_method(D_METHOD("get_font_size" ), &Label3D::get_font_size); |
| 80 | |
| 81 | ClassDB::bind_method(D_METHOD("set_outline_size" , "outline_size" ), &Label3D::set_outline_size); |
| 82 | ClassDB::bind_method(D_METHOD("get_outline_size" ), &Label3D::get_outline_size); |
| 83 | |
| 84 | ClassDB::bind_method(D_METHOD("set_line_spacing" , "line_spacing" ), &Label3D::set_line_spacing); |
| 85 | ClassDB::bind_method(D_METHOD("get_line_spacing" ), &Label3D::get_line_spacing); |
| 86 | |
| 87 | ClassDB::bind_method(D_METHOD("set_autowrap_mode" , "autowrap_mode" ), &Label3D::set_autowrap_mode); |
| 88 | ClassDB::bind_method(D_METHOD("get_autowrap_mode" ), &Label3D::get_autowrap_mode); |
| 89 | |
| 90 | ClassDB::bind_method(D_METHOD("set_justification_flags" , "justification_flags" ), &Label3D::set_justification_flags); |
| 91 | ClassDB::bind_method(D_METHOD("get_justification_flags" ), &Label3D::get_justification_flags); |
| 92 | |
| 93 | ClassDB::bind_method(D_METHOD("set_width" , "width" ), &Label3D::set_width); |
| 94 | ClassDB::bind_method(D_METHOD("get_width" ), &Label3D::get_width); |
| 95 | |
| 96 | ClassDB::bind_method(D_METHOD("set_pixel_size" , "pixel_size" ), &Label3D::set_pixel_size); |
| 97 | ClassDB::bind_method(D_METHOD("get_pixel_size" ), &Label3D::get_pixel_size); |
| 98 | |
| 99 | ClassDB::bind_method(D_METHOD("set_offset" , "offset" ), &Label3D::set_offset); |
| 100 | ClassDB::bind_method(D_METHOD("get_offset" ), &Label3D::get_offset); |
| 101 | |
| 102 | ClassDB::bind_method(D_METHOD("set_draw_flag" , "flag" , "enabled" ), &Label3D::set_draw_flag); |
| 103 | ClassDB::bind_method(D_METHOD("get_draw_flag" , "flag" ), &Label3D::get_draw_flag); |
| 104 | |
| 105 | ClassDB::bind_method(D_METHOD("set_billboard_mode" , "mode" ), &Label3D::set_billboard_mode); |
| 106 | ClassDB::bind_method(D_METHOD("get_billboard_mode" ), &Label3D::get_billboard_mode); |
| 107 | |
| 108 | ClassDB::bind_method(D_METHOD("set_alpha_cut_mode" , "mode" ), &Label3D::set_alpha_cut_mode); |
| 109 | ClassDB::bind_method(D_METHOD("get_alpha_cut_mode" ), &Label3D::get_alpha_cut_mode); |
| 110 | |
| 111 | ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold" , "threshold" ), &Label3D::set_alpha_scissor_threshold); |
| 112 | ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold" ), &Label3D::get_alpha_scissor_threshold); |
| 113 | |
| 114 | ClassDB::bind_method(D_METHOD("set_alpha_hash_scale" , "threshold" ), &Label3D::set_alpha_hash_scale); |
| 115 | ClassDB::bind_method(D_METHOD("get_alpha_hash_scale" ), &Label3D::get_alpha_hash_scale); |
| 116 | |
| 117 | ClassDB::bind_method(D_METHOD("set_alpha_antialiasing" , "alpha_aa" ), &Label3D::set_alpha_antialiasing); |
| 118 | ClassDB::bind_method(D_METHOD("get_alpha_antialiasing" ), &Label3D::get_alpha_antialiasing); |
| 119 | |
| 120 | ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge" , "edge" ), &Label3D::set_alpha_antialiasing_edge); |
| 121 | ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge" ), &Label3D::get_alpha_antialiasing_edge); |
| 122 | |
| 123 | ClassDB::bind_method(D_METHOD("set_texture_filter" , "mode" ), &Label3D::set_texture_filter); |
| 124 | ClassDB::bind_method(D_METHOD("get_texture_filter" ), &Label3D::get_texture_filter); |
| 125 | |
| 126 | ClassDB::bind_method(D_METHOD("generate_triangle_mesh" ), &Label3D::generate_triangle_mesh); |
| 127 | |
| 128 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size" , PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m" ), "set_pixel_size" , "get_pixel_size" ); |
| 129 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset" , PROPERTY_HINT_NONE, "suffix:px" ), "set_offset" , "get_offset" ); |
| 130 | |
| 131 | ADD_GROUP("Flags" , "" ); |
| 132 | ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard" , PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard" ), "set_billboard_mode" , "get_billboard_mode" ); |
| 133 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded" ), "set_draw_flag" , "get_draw_flag" , FLAG_SHADED); |
| 134 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided" ), "set_draw_flag" , "get_draw_flag" , FLAG_DOUBLE_SIDED); |
| 135 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test" ), "set_draw_flag" , "get_draw_flag" , FLAG_DISABLE_DEPTH_TEST); |
| 136 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size" ), "set_draw_flag" , "get_draw_flag" , FLAG_FIXED_SIZE); |
| 137 | ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut" , PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass,Alpha Hash" ), "set_alpha_cut_mode" , "get_alpha_cut_mode" ); |
| 138 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold" , PROPERTY_HINT_RANGE, "0,1,0.001" ), "set_alpha_scissor_threshold" , "get_alpha_scissor_threshold" ); |
| 139 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale" , PROPERTY_HINT_RANGE, "0,2,0.01" ), "set_alpha_hash_scale" , "get_alpha_hash_scale" ); |
| 140 | ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode" , PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip" ), "set_alpha_antialiasing" , "get_alpha_antialiasing" ); |
| 141 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge" , PROPERTY_HINT_RANGE, "0,1,0.01" ), "set_alpha_antialiasing_edge" , "get_alpha_antialiasing_edge" ); |
| 142 | ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter" , PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic" ), "set_texture_filter" , "get_texture_filter" ); |
| 143 | ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority" , PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1" ), "set_render_priority" , "get_render_priority" ); |
| 144 | ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_render_priority" , PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1" ), "set_outline_render_priority" , "get_outline_render_priority" ); |
| 145 | |
| 146 | ADD_GROUP("Text" , "" ); |
| 147 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate" ), "set_modulate" , "get_modulate" ); |
| 148 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_modulate" ), "set_outline_modulate" , "get_outline_modulate" ); |
| 149 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "text" , PROPERTY_HINT_MULTILINE_TEXT, "" ), "set_text" , "get_text" ); |
| 150 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font" , PROPERTY_HINT_RESOURCE_TYPE, "Font" ), "set_font" , "get_font" ); |
| 151 | ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size" , PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px" ), "set_font_size" , "get_font_size" ); |
| 152 | ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size" , PROPERTY_HINT_RANGE, "0,127,1,suffix:px" ), "set_outline_size" , "get_outline_size" ); |
| 153 | ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment" , PROPERTY_HINT_ENUM, "Left,Center,Right,Fill" ), "set_horizontal_alignment" , "get_horizontal_alignment" ); |
| 154 | ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment" , PROPERTY_HINT_ENUM, "Top,Center,Bottom" ), "set_vertical_alignment" , "get_vertical_alignment" ); |
| 155 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase" ), "set_uppercase" , "is_uppercase" ); |
| 156 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing" , PROPERTY_HINT_NONE, "suffix:px" ), "set_line_spacing" , "get_line_spacing" ); |
| 157 | ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode" , PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)" ), "set_autowrap_mode" , "get_autowrap_mode" ); |
| 158 | ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags" , PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Justify Only After Last Tab:8,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128" ), "set_justification_flags" , "get_justification_flags" ); |
| 159 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width" , PROPERTY_HINT_NONE, "suffix:px" ), "set_width" , "get_width" ); |
| 160 | |
| 161 | ADD_GROUP("BiDi" , "" ); |
| 162 | ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction" , PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left" ), "set_text_direction" , "get_text_direction" ); |
| 163 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "language" , PROPERTY_HINT_LOCALE_ID, "" ), "set_language" , "get_language" ); |
| 164 | ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override" , PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom" ), "set_structured_text_bidi_override" , "get_structured_text_bidi_override" ); |
| 165 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options" ), "set_structured_text_bidi_override_options" , "get_structured_text_bidi_override_options" ); |
| 166 | |
| 167 | BIND_ENUM_CONSTANT(FLAG_SHADED); |
| 168 | BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED); |
| 169 | BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST); |
| 170 | BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE); |
| 171 | BIND_ENUM_CONSTANT(FLAG_MAX); |
| 172 | |
| 173 | BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED); |
| 174 | BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD); |
| 175 | BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS); |
| 176 | BIND_ENUM_CONSTANT(ALPHA_CUT_HASH); |
| 177 | } |
| 178 | |
| 179 | void Label3D::_validate_property(PropertyInfo &p_property) const { |
| 180 | if ( |
| 181 | p_property.name == "material_override" || |
| 182 | p_property.name == "material_overlay" || |
| 183 | p_property.name == "lod_bias" || |
| 184 | p_property.name == "gi_mode" || |
| 185 | p_property.name == "gi_lightmap_scale" ) { |
| 186 | p_property.usage = PROPERTY_USAGE_NO_EDITOR; |
| 187 | } |
| 188 | |
| 189 | if (p_property.name == "cast_shadow" && alpha_cut == ALPHA_CUT_DISABLED) { |
| 190 | // Alpha-blended materials can't cast shadows. |
| 191 | p_property.usage = PROPERTY_USAGE_NO_EDITOR; |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | void Label3D::_notification(int p_what) { |
| 196 | switch (p_what) { |
| 197 | case NOTIFICATION_ENTER_TREE: { |
| 198 | if (!pending_update) { |
| 199 | _im_update(); |
| 200 | } |
| 201 | Viewport *viewport = get_viewport(); |
| 202 | ERR_FAIL_NULL(viewport); |
| 203 | viewport->connect("size_changed" , callable_mp(this, &Label3D::_font_changed)); |
| 204 | } break; |
| 205 | case NOTIFICATION_EXIT_TREE: { |
| 206 | Viewport *viewport = get_viewport(); |
| 207 | ERR_FAIL_NULL(viewport); |
| 208 | viewport->disconnect("size_changed" , callable_mp(this, &Label3D::_font_changed)); |
| 209 | } break; |
| 210 | case NOTIFICATION_TRANSLATION_CHANGED: { |
| 211 | String new_text = tr(text); |
| 212 | if (new_text == xl_text) { |
| 213 | return; // Nothing new. |
| 214 | } |
| 215 | xl_text = new_text; |
| 216 | dirty_text = true; |
| 217 | _queue_update(); |
| 218 | } break; |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | void Label3D::_im_update() { |
| 223 | _shape(); |
| 224 | |
| 225 | triangle_mesh.unref(); |
| 226 | update_gizmos(); |
| 227 | |
| 228 | pending_update = false; |
| 229 | } |
| 230 | |
| 231 | void Label3D::_queue_update() { |
| 232 | if (pending_update) { |
| 233 | return; |
| 234 | } |
| 235 | |
| 236 | pending_update = true; |
| 237 | callable_mp(this, &Label3D::_im_update).call_deferred(); |
| 238 | } |
| 239 | |
| 240 | AABB Label3D::get_aabb() const { |
| 241 | return aabb; |
| 242 | } |
| 243 | |
| 244 | Ref<TriangleMesh> Label3D::generate_triangle_mesh() const { |
| 245 | if (triangle_mesh.is_valid()) { |
| 246 | return triangle_mesh; |
| 247 | } |
| 248 | |
| 249 | Ref<Font> font = _get_font_or_default(); |
| 250 | if (font.is_null()) { |
| 251 | return Ref<TriangleMesh>(); |
| 252 | } |
| 253 | |
| 254 | Vector<Vector3> faces; |
| 255 | faces.resize(6); |
| 256 | Vector3 *facesw = faces.ptrw(); |
| 257 | |
| 258 | float total_h = 0.0; |
| 259 | float max_line_w = 0.0; |
| 260 | for (int i = 0; i < lines_rid.size(); i++) { |
| 261 | total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; |
| 262 | max_line_w = MAX(max_line_w, TS->shaped_text_get_width(lines_rid[i])); |
| 263 | } |
| 264 | |
| 265 | float vbegin = 0; |
| 266 | switch (vertical_alignment) { |
| 267 | case VERTICAL_ALIGNMENT_FILL: |
| 268 | case VERTICAL_ALIGNMENT_TOP: { |
| 269 | // Nothing. |
| 270 | } break; |
| 271 | case VERTICAL_ALIGNMENT_CENTER: { |
| 272 | vbegin = (total_h - line_spacing) / 2.0; |
| 273 | } break; |
| 274 | case VERTICAL_ALIGNMENT_BOTTOM: { |
| 275 | vbegin = (total_h - line_spacing); |
| 276 | } break; |
| 277 | } |
| 278 | |
| 279 | Vector2 offset = Vector2(0, vbegin); |
| 280 | switch (horizontal_alignment) { |
| 281 | case HORIZONTAL_ALIGNMENT_LEFT: |
| 282 | break; |
| 283 | case HORIZONTAL_ALIGNMENT_FILL: |
| 284 | case HORIZONTAL_ALIGNMENT_CENTER: { |
| 285 | offset.x = -max_line_w / 2.0; |
| 286 | } break; |
| 287 | case HORIZONTAL_ALIGNMENT_RIGHT: { |
| 288 | offset.x = -max_line_w; |
| 289 | } break; |
| 290 | } |
| 291 | |
| 292 | Rect2 final_rect = Rect2(offset + lbl_offset, Size2(max_line_w, total_h)); |
| 293 | |
| 294 | if (final_rect.size.x == 0 || final_rect.size.y == 0) { |
| 295 | return Ref<TriangleMesh>(); |
| 296 | } |
| 297 | |
| 298 | real_t px_size = get_pixel_size(); |
| 299 | |
| 300 | Vector2 vertices[4] = { |
| 301 | |
| 302 | (final_rect.position + Vector2(0, -final_rect.size.y)) * px_size, |
| 303 | (final_rect.position + Vector2(final_rect.size.x, -final_rect.size.y)) * px_size, |
| 304 | (final_rect.position + Vector2(final_rect.size.x, 0)) * px_size, |
| 305 | final_rect.position * px_size, |
| 306 | |
| 307 | }; |
| 308 | |
| 309 | static const int indices[6] = { |
| 310 | 0, 1, 2, |
| 311 | 0, 2, 3 |
| 312 | }; |
| 313 | |
| 314 | for (int j = 0; j < 6; j++) { |
| 315 | int i = indices[j]; |
| 316 | Vector3 vtx; |
| 317 | vtx[0] = vertices[i][0]; |
| 318 | vtx[1] = vertices[i][1]; |
| 319 | facesw[j] = vtx; |
| 320 | } |
| 321 | |
| 322 | triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); |
| 323 | triangle_mesh->create(faces); |
| 324 | |
| 325 | return triangle_mesh; |
| 326 | } |
| 327 | |
| 328 | void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority, int p_outline_size) { |
| 329 | for (int j = 0; j < p_glyph.repeat; j++) { |
| 330 | Vector2 gl_of; |
| 331 | Vector2 gl_sz; |
| 332 | Rect2 gl_uv; |
| 333 | Size2 texs; |
| 334 | RID tex; |
| 335 | |
| 336 | if (p_glyph.font_rid != RID()) { |
| 337 | tex = TS->font_get_glyph_texture_rid(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); |
| 338 | if (tex != RID()) { |
| 339 | gl_of = (TS->font_get_glyph_offset(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index) + Vector2(p_glyph.x_off, p_glyph.y_off)) * pixel_size; |
| 340 | gl_sz = TS->font_get_glyph_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index) * pixel_size; |
| 341 | gl_uv = TS->font_get_glyph_uv_rect(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); |
| 342 | texs = TS->font_get_glyph_texture_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); |
| 343 | } |
| 344 | } else { |
| 345 | gl_sz = TS->get_hex_code_box_size(p_glyph.font_size, p_glyph.index) * pixel_size; |
| 346 | gl_of = Vector2(0, -gl_sz.y); |
| 347 | } |
| 348 | |
| 349 | bool msdf = TS->font_is_multichannel_signed_distance_field(p_glyph.font_rid); |
| 350 | |
| 351 | SurfaceKey key = SurfaceKey(tex.get_id(), p_priority, p_outline_size); |
| 352 | if (!surfaces.has(key)) { |
| 353 | SurfaceData surf; |
| 354 | surf.material = RenderingServer::get_singleton()->material_create(); |
| 355 | // Set defaults for material, names need to match up those in StandardMaterial3D |
| 356 | RS::get_singleton()->material_set_param(surf.material, "albedo" , Color(1, 1, 1, 1)); |
| 357 | RS::get_singleton()->material_set_param(surf.material, "specular" , 0.5); |
| 358 | RS::get_singleton()->material_set_param(surf.material, "metallic" , 0.0); |
| 359 | RS::get_singleton()->material_set_param(surf.material, "roughness" , 1.0); |
| 360 | RS::get_singleton()->material_set_param(surf.material, "uv1_offset" , Vector3(0, 0, 0)); |
| 361 | RS::get_singleton()->material_set_param(surf.material, "uv1_scale" , Vector3(1, 1, 1)); |
| 362 | RS::get_singleton()->material_set_param(surf.material, "uv2_offset" , Vector3(0, 0, 0)); |
| 363 | RS::get_singleton()->material_set_param(surf.material, "uv2_scale" , Vector3(1, 1, 1)); |
| 364 | RS::get_singleton()->material_set_param(surf.material, "alpha_scissor_threshold" , alpha_scissor_threshold); |
| 365 | RS::get_singleton()->material_set_param(surf.material, "alpha_hash_scale" , alpha_hash_scale); |
| 366 | RS::get_singleton()->material_set_param(surf.material, "alpha_antialiasing_edge" , alpha_antialiasing_edge); |
| 367 | if (msdf) { |
| 368 | RS::get_singleton()->material_set_param(surf.material, "msdf_pixel_range" , TS->font_get_msdf_pixel_range(p_glyph.font_rid)); |
| 369 | RS::get_singleton()->material_set_param(surf.material, "msdf_outline_size" , p_outline_size); |
| 370 | } |
| 371 | |
| 372 | BaseMaterial3D::Transparency mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA; |
| 373 | if (get_alpha_cut_mode() == ALPHA_CUT_DISCARD) { |
| 374 | mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_SCISSOR; |
| 375 | } else if (get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS) { |
| 376 | mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS; |
| 377 | } else if (get_alpha_cut_mode() == ALPHA_CUT_HASH) { |
| 378 | mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_HASH; |
| 379 | } |
| 380 | |
| 381 | RID shader_rid; |
| 382 | StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, msdf, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), texture_filter, alpha_antialiasing_mode, &shader_rid); |
| 383 | |
| 384 | RS::get_singleton()->material_set_shader(surf.material, shader_rid); |
| 385 | RS::get_singleton()->material_set_param(surf.material, "texture_albedo" , tex); |
| 386 | if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { |
| 387 | RS::get_singleton()->material_set_render_priority(surf.material, p_priority); |
| 388 | } else { |
| 389 | surf.z_shift = p_priority * pixel_size; |
| 390 | } |
| 391 | |
| 392 | surfaces[key] = surf; |
| 393 | } |
| 394 | SurfaceData &s = surfaces[key]; |
| 395 | |
| 396 | s.mesh_vertices.resize((s.offset + 1) * 4); |
| 397 | s.mesh_normals.resize((s.offset + 1) * 4); |
| 398 | s.mesh_tangents.resize((s.offset + 1) * 16); |
| 399 | s.mesh_colors.resize((s.offset + 1) * 4); |
| 400 | s.mesh_uvs.resize((s.offset + 1) * 4); |
| 401 | |
| 402 | s.mesh_vertices.write[(s.offset * 4) + 3] = Vector3(r_offset.x + gl_of.x, r_offset.y - gl_of.y - gl_sz.y, s.z_shift); |
| 403 | s.mesh_vertices.write[(s.offset * 4) + 2] = Vector3(r_offset.x + gl_of.x + gl_sz.x, r_offset.y - gl_of.y - gl_sz.y, s.z_shift); |
| 404 | s.mesh_vertices.write[(s.offset * 4) + 1] = Vector3(r_offset.x + gl_of.x + gl_sz.x, r_offset.y - gl_of.y, s.z_shift); |
| 405 | s.mesh_vertices.write[(s.offset * 4) + 0] = Vector3(r_offset.x + gl_of.x, r_offset.y - gl_of.y, s.z_shift); |
| 406 | |
| 407 | for (int i = 0; i < 4; i++) { |
| 408 | s.mesh_normals.write[(s.offset * 4) + i] = Vector3(0.0, 0.0, 1.0); |
| 409 | s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 0] = 1.0; |
| 410 | s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 1] = 0.0; |
| 411 | s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 2] = 0.0; |
| 412 | s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 3] = 1.0; |
| 413 | s.mesh_colors.write[(s.offset * 4) + i] = p_modulate; |
| 414 | s.mesh_uvs.write[(s.offset * 4) + i] = Vector2(); |
| 415 | |
| 416 | if (aabb == AABB()) { |
| 417 | aabb.position = s.mesh_vertices[(s.offset * 4) + i]; |
| 418 | } else { |
| 419 | aabb.expand_to(s.mesh_vertices[(s.offset * 4) + i]); |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | if (tex != RID()) { |
| 424 | 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); |
| 425 | 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); |
| 426 | 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); |
| 427 | s.mesh_uvs.write[(s.offset * 4) + 0] = Vector2(gl_uv.position.x / texs.x, gl_uv.position.y / texs.y); |
| 428 | } |
| 429 | |
| 430 | s.indices.resize((s.offset + 1) * 6); |
| 431 | s.indices.write[(s.offset * 6) + 0] = (s.offset * 4) + 0; |
| 432 | s.indices.write[(s.offset * 6) + 1] = (s.offset * 4) + 1; |
| 433 | s.indices.write[(s.offset * 6) + 2] = (s.offset * 4) + 2; |
| 434 | s.indices.write[(s.offset * 6) + 3] = (s.offset * 4) + 0; |
| 435 | s.indices.write[(s.offset * 6) + 4] = (s.offset * 4) + 2; |
| 436 | s.indices.write[(s.offset * 6) + 5] = (s.offset * 4) + 3; |
| 437 | |
| 438 | s.offset++; |
| 439 | r_offset.x += p_glyph.advance * pixel_size; |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | void Label3D::_shape() { |
| 444 | // When a shaped text is invalidated by an external source, we want to reshape it. |
| 445 | if (!TS->shaped_text_is_ready(text_rid)) { |
| 446 | dirty_text = true; |
| 447 | } |
| 448 | |
| 449 | for (const RID &line_rid : lines_rid) { |
| 450 | if (!TS->shaped_text_is_ready(line_rid)) { |
| 451 | dirty_lines = true; |
| 452 | break; |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | // Clear mesh. |
| 457 | RS::get_singleton()->mesh_clear(mesh); |
| 458 | aabb = AABB(); |
| 459 | |
| 460 | // Clear materials. |
| 461 | for (const KeyValue<SurfaceKey, SurfaceData> &E : surfaces) { |
| 462 | RenderingServer::get_singleton()->free(E.value.material); |
| 463 | } |
| 464 | surfaces.clear(); |
| 465 | |
| 466 | Ref<Font> font = _get_font_or_default(); |
| 467 | ERR_FAIL_COND(font.is_null()); |
| 468 | |
| 469 | // Update text buffer. |
| 470 | if (dirty_text) { |
| 471 | TS->shaped_text_clear(text_rid); |
| 472 | TS->shaped_text_set_direction(text_rid, text_direction); |
| 473 | |
| 474 | String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; |
| 475 | TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), language); |
| 476 | |
| 477 | TypedArray<Vector3i> stt; |
| 478 | if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { |
| 479 | GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt); |
| 480 | } else { |
| 481 | stt = TS->parse_structured_text(st_parser, st_args, txt); |
| 482 | } |
| 483 | TS->shaped_text_set_bidi_override(text_rid, stt); |
| 484 | |
| 485 | dirty_text = false; |
| 486 | dirty_font = false; |
| 487 | dirty_lines = true; |
| 488 | } else if (dirty_font) { |
| 489 | int spans = TS->shaped_get_span_count(text_rid); |
| 490 | for (int i = 0; i < spans; i++) { |
| 491 | TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); |
| 492 | } |
| 493 | |
| 494 | dirty_font = false; |
| 495 | dirty_lines = true; |
| 496 | } |
| 497 | |
| 498 | if (dirty_lines) { |
| 499 | for (int i = 0; i < lines_rid.size(); i++) { |
| 500 | TS->free_rid(lines_rid[i]); |
| 501 | } |
| 502 | lines_rid.clear(); |
| 503 | |
| 504 | BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; |
| 505 | switch (autowrap_mode) { |
| 506 | case TextServer::AUTOWRAP_WORD_SMART: |
| 507 | autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; |
| 508 | break; |
| 509 | case TextServer::AUTOWRAP_WORD: |
| 510 | autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; |
| 511 | break; |
| 512 | case TextServer::AUTOWRAP_ARBITRARY: |
| 513 | autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; |
| 514 | break; |
| 515 | case TextServer::AUTOWRAP_OFF: |
| 516 | break; |
| 517 | } |
| 518 | autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; |
| 519 | |
| 520 | PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); |
| 521 | float max_line_w = 0.0; |
| 522 | for (int i = 0; i < line_breaks.size(); i = i + 2) { |
| 523 | RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); |
| 524 | max_line_w = MAX(max_line_w, TS->shaped_text_get_width(line)); |
| 525 | lines_rid.push_back(line); |
| 526 | } |
| 527 | |
| 528 | if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { |
| 529 | int jst_to_line = lines_rid.size(); |
| 530 | if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { |
| 531 | jst_to_line = lines_rid.size(); |
| 532 | } else { |
| 533 | if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { |
| 534 | jst_to_line = lines_rid.size() - 1; |
| 535 | } |
| 536 | if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { |
| 537 | for (int i = lines_rid.size() - 1; i >= 0; i--) { |
| 538 | if (TS->shaped_text_has_visible_chars(lines_rid[i])) { |
| 539 | jst_to_line = i; |
| 540 | break; |
| 541 | } |
| 542 | } |
| 543 | } |
| 544 | } |
| 545 | for (int i = 0; i < jst_to_line; i++) { |
| 546 | TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, jst_flags); |
| 547 | } |
| 548 | } |
| 549 | dirty_lines = false; |
| 550 | } |
| 551 | |
| 552 | // Generate surfaces and materials. |
| 553 | float total_h = 0.0; |
| 554 | for (int i = 0; i < lines_rid.size(); i++) { |
| 555 | total_h += (TS->shaped_text_get_size(lines_rid[i]).y + line_spacing) * pixel_size; |
| 556 | } |
| 557 | |
| 558 | float vbegin = 0.0; |
| 559 | switch (vertical_alignment) { |
| 560 | case VERTICAL_ALIGNMENT_FILL: |
| 561 | case VERTICAL_ALIGNMENT_TOP: { |
| 562 | // Nothing. |
| 563 | } break; |
| 564 | case VERTICAL_ALIGNMENT_CENTER: { |
| 565 | vbegin = (total_h - line_spacing * pixel_size) / 2.0; |
| 566 | } break; |
| 567 | case VERTICAL_ALIGNMENT_BOTTOM: { |
| 568 | vbegin = (total_h - line_spacing * pixel_size); |
| 569 | } break; |
| 570 | } |
| 571 | |
| 572 | Vector2 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size); |
| 573 | for (int i = 0; i < lines_rid.size(); i++) { |
| 574 | const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); |
| 575 | int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); |
| 576 | float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size; |
| 577 | |
| 578 | switch (horizontal_alignment) { |
| 579 | case HORIZONTAL_ALIGNMENT_LEFT: |
| 580 | offset.x = 0.0; |
| 581 | break; |
| 582 | case HORIZONTAL_ALIGNMENT_FILL: |
| 583 | case HORIZONTAL_ALIGNMENT_CENTER: { |
| 584 | offset.x = -line_width / 2.0; |
| 585 | } break; |
| 586 | case HORIZONTAL_ALIGNMENT_RIGHT: { |
| 587 | offset.x = -line_width; |
| 588 | } break; |
| 589 | } |
| 590 | offset.x += lbl_offset.x * pixel_size; |
| 591 | offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size; |
| 592 | |
| 593 | if (outline_modulate.a != 0.0 && outline_size > 0) { |
| 594 | // Outline surfaces. |
| 595 | Vector2 ol_offset = offset; |
| 596 | for (int j = 0; j < gl_size; j++) { |
| 597 | _generate_glyph_surfaces(glyphs[j], ol_offset, outline_modulate, outline_render_priority, outline_size); |
| 598 | } |
| 599 | } |
| 600 | |
| 601 | // Main text surfaces. |
| 602 | for (int j = 0; j < gl_size; j++) { |
| 603 | _generate_glyph_surfaces(glyphs[j], offset, modulate, render_priority); |
| 604 | } |
| 605 | offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size; |
| 606 | } |
| 607 | |
| 608 | for (const KeyValue<SurfaceKey, SurfaceData> &E : surfaces) { |
| 609 | Array mesh_array; |
| 610 | mesh_array.resize(RS::ARRAY_MAX); |
| 611 | mesh_array[RS::ARRAY_VERTEX] = E.value.mesh_vertices; |
| 612 | mesh_array[RS::ARRAY_NORMAL] = E.value.mesh_normals; |
| 613 | mesh_array[RS::ARRAY_TANGENT] = E.value.mesh_tangents; |
| 614 | mesh_array[RS::ARRAY_COLOR] = E.value.mesh_colors; |
| 615 | mesh_array[RS::ARRAY_TEX_UV] = E.value.mesh_uvs; |
| 616 | mesh_array[RS::ARRAY_INDEX] = E.value.indices; |
| 617 | |
| 618 | RS::SurfaceData sd; |
| 619 | RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, mesh_array); |
| 620 | |
| 621 | sd.material = E.value.material; |
| 622 | |
| 623 | RS::get_singleton()->mesh_add_surface(mesh, sd); |
| 624 | } |
| 625 | } |
| 626 | |
| 627 | void Label3D::set_text(const String &p_string) { |
| 628 | text = p_string; |
| 629 | xl_text = tr(p_string); |
| 630 | dirty_text = true; |
| 631 | _queue_update(); |
| 632 | } |
| 633 | |
| 634 | String Label3D::get_text() const { |
| 635 | return text; |
| 636 | } |
| 637 | |
| 638 | void Label3D::set_horizontal_alignment(HorizontalAlignment p_alignment) { |
| 639 | ERR_FAIL_INDEX((int)p_alignment, 4); |
| 640 | if (horizontal_alignment != p_alignment) { |
| 641 | if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { |
| 642 | dirty_lines = true; // Reshape lines. |
| 643 | } |
| 644 | horizontal_alignment = p_alignment; |
| 645 | _queue_update(); |
| 646 | } |
| 647 | } |
| 648 | |
| 649 | HorizontalAlignment Label3D::get_horizontal_alignment() const { |
| 650 | return horizontal_alignment; |
| 651 | } |
| 652 | |
| 653 | void Label3D::set_vertical_alignment(VerticalAlignment p_alignment) { |
| 654 | ERR_FAIL_INDEX((int)p_alignment, 4); |
| 655 | if (vertical_alignment != p_alignment) { |
| 656 | vertical_alignment = p_alignment; |
| 657 | _queue_update(); |
| 658 | } |
| 659 | } |
| 660 | |
| 661 | VerticalAlignment Label3D::get_vertical_alignment() const { |
| 662 | return vertical_alignment; |
| 663 | } |
| 664 | |
| 665 | void Label3D::set_text_direction(TextServer::Direction p_text_direction) { |
| 666 | ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); |
| 667 | if (text_direction != p_text_direction) { |
| 668 | text_direction = p_text_direction; |
| 669 | dirty_text = true; |
| 670 | _queue_update(); |
| 671 | } |
| 672 | } |
| 673 | |
| 674 | TextServer::Direction Label3D::get_text_direction() const { |
| 675 | return text_direction; |
| 676 | } |
| 677 | |
| 678 | void Label3D::set_language(const String &p_language) { |
| 679 | if (language != p_language) { |
| 680 | language = p_language; |
| 681 | dirty_text = true; |
| 682 | _queue_update(); |
| 683 | } |
| 684 | } |
| 685 | |
| 686 | String Label3D::get_language() const { |
| 687 | return language; |
| 688 | } |
| 689 | |
| 690 | void Label3D::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { |
| 691 | if (st_parser != p_parser) { |
| 692 | st_parser = p_parser; |
| 693 | dirty_text = true; |
| 694 | _queue_update(); |
| 695 | } |
| 696 | } |
| 697 | |
| 698 | TextServer::StructuredTextParser Label3D::get_structured_text_bidi_override() const { |
| 699 | return st_parser; |
| 700 | } |
| 701 | |
| 702 | void Label3D::set_structured_text_bidi_override_options(Array p_args) { |
| 703 | if (st_args != p_args) { |
| 704 | st_args = p_args; |
| 705 | dirty_text = true; |
| 706 | _queue_update(); |
| 707 | } |
| 708 | } |
| 709 | |
| 710 | Array Label3D::get_structured_text_bidi_override_options() const { |
| 711 | return st_args; |
| 712 | } |
| 713 | |
| 714 | void Label3D::set_uppercase(bool p_uppercase) { |
| 715 | if (uppercase != p_uppercase) { |
| 716 | uppercase = p_uppercase; |
| 717 | dirty_text = true; |
| 718 | _queue_update(); |
| 719 | } |
| 720 | } |
| 721 | |
| 722 | bool Label3D::is_uppercase() const { |
| 723 | return uppercase; |
| 724 | } |
| 725 | |
| 726 | void Label3D::set_render_priority(int p_priority) { |
| 727 | ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); |
| 728 | if (render_priority != p_priority) { |
| 729 | render_priority = p_priority; |
| 730 | _queue_update(); |
| 731 | } |
| 732 | } |
| 733 | |
| 734 | int Label3D::get_render_priority() const { |
| 735 | return render_priority; |
| 736 | } |
| 737 | |
| 738 | void Label3D::set_outline_render_priority(int p_priority) { |
| 739 | ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); |
| 740 | if (outline_render_priority != p_priority) { |
| 741 | outline_render_priority = p_priority; |
| 742 | _queue_update(); |
| 743 | } |
| 744 | } |
| 745 | |
| 746 | int Label3D::get_outline_render_priority() const { |
| 747 | return outline_render_priority; |
| 748 | } |
| 749 | |
| 750 | void Label3D::_font_changed() { |
| 751 | dirty_font = true; |
| 752 | _queue_update(); |
| 753 | } |
| 754 | |
| 755 | void Label3D::set_font(const Ref<Font> &p_font) { |
| 756 | if (font_override != p_font) { |
| 757 | if (font_override.is_valid()) { |
| 758 | font_override->disconnect_changed(callable_mp(this, &Label3D::_font_changed)); |
| 759 | } |
| 760 | font_override = p_font; |
| 761 | dirty_font = true; |
| 762 | if (font_override.is_valid()) { |
| 763 | font_override->connect_changed(callable_mp(this, &Label3D::_font_changed)); |
| 764 | } |
| 765 | _queue_update(); |
| 766 | } |
| 767 | } |
| 768 | |
| 769 | Ref<Font> Label3D::get_font() const { |
| 770 | return font_override; |
| 771 | } |
| 772 | |
| 773 | Ref<Font> Label3D::_get_font_or_default() const { |
| 774 | if (theme_font.is_valid()) { |
| 775 | theme_font->disconnect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); |
| 776 | theme_font.unref(); |
| 777 | } |
| 778 | |
| 779 | if (font_override.is_valid()) { |
| 780 | return font_override; |
| 781 | } |
| 782 | |
| 783 | StringName theme_name = "font" ; |
| 784 | List<StringName> theme_types; |
| 785 | ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), &theme_types); |
| 786 | |
| 787 | ThemeContext *global_context = ThemeDB::get_singleton()->get_default_theme_context(); |
| 788 | for (const Ref<Theme> &theme : global_context->get_themes()) { |
| 789 | if (theme.is_null()) { |
| 790 | continue; |
| 791 | } |
| 792 | |
| 793 | for (const StringName &E : theme_types) { |
| 794 | if (!theme->has_font(theme_name, E)) { |
| 795 | continue; |
| 796 | } |
| 797 | |
| 798 | Ref<Font> f = theme->get_font(theme_name, E); |
| 799 | if (f.is_valid()) { |
| 800 | theme_font = f; |
| 801 | theme_font->connect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); |
| 802 | } |
| 803 | return f; |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | Ref<Font> f = global_context->get_fallback_theme()->get_font(theme_name, StringName()); |
| 808 | if (f.is_valid()) { |
| 809 | theme_font = f; |
| 810 | theme_font->connect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed)); |
| 811 | } |
| 812 | return f; |
| 813 | } |
| 814 | |
| 815 | void Label3D::set_font_size(int p_size) { |
| 816 | if (font_size != p_size) { |
| 817 | font_size = p_size; |
| 818 | dirty_font = true; |
| 819 | _queue_update(); |
| 820 | } |
| 821 | } |
| 822 | |
| 823 | int Label3D::get_font_size() const { |
| 824 | return font_size; |
| 825 | } |
| 826 | |
| 827 | void Label3D::set_outline_size(int p_size) { |
| 828 | if (outline_size != p_size) { |
| 829 | outline_size = p_size; |
| 830 | _queue_update(); |
| 831 | } |
| 832 | } |
| 833 | |
| 834 | int Label3D::get_outline_size() const { |
| 835 | return outline_size; |
| 836 | } |
| 837 | |
| 838 | void Label3D::set_modulate(const Color &p_color) { |
| 839 | if (modulate != p_color) { |
| 840 | modulate = p_color; |
| 841 | _queue_update(); |
| 842 | } |
| 843 | } |
| 844 | |
| 845 | Color Label3D::get_modulate() const { |
| 846 | return modulate; |
| 847 | } |
| 848 | |
| 849 | void Label3D::set_outline_modulate(const Color &p_color) { |
| 850 | if (outline_modulate != p_color) { |
| 851 | outline_modulate = p_color; |
| 852 | _queue_update(); |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | Color Label3D::get_outline_modulate() const { |
| 857 | return outline_modulate; |
| 858 | } |
| 859 | |
| 860 | void Label3D::set_autowrap_mode(TextServer::AutowrapMode p_mode) { |
| 861 | if (autowrap_mode != p_mode) { |
| 862 | autowrap_mode = p_mode; |
| 863 | dirty_lines = true; |
| 864 | _queue_update(); |
| 865 | } |
| 866 | } |
| 867 | |
| 868 | TextServer::AutowrapMode Label3D::get_autowrap_mode() const { |
| 869 | return autowrap_mode; |
| 870 | } |
| 871 | |
| 872 | void Label3D::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) { |
| 873 | if (jst_flags != p_flags) { |
| 874 | jst_flags = p_flags; |
| 875 | dirty_lines = true; |
| 876 | _queue_update(); |
| 877 | } |
| 878 | } |
| 879 | |
| 880 | BitField<TextServer::JustificationFlag> Label3D::get_justification_flags() const { |
| 881 | return jst_flags; |
| 882 | } |
| 883 | |
| 884 | void Label3D::set_width(float p_width) { |
| 885 | if (width != p_width) { |
| 886 | width = p_width; |
| 887 | dirty_lines = true; |
| 888 | _queue_update(); |
| 889 | } |
| 890 | } |
| 891 | |
| 892 | float Label3D::get_width() const { |
| 893 | return width; |
| 894 | } |
| 895 | |
| 896 | void Label3D::set_pixel_size(real_t p_amount) { |
| 897 | if (pixel_size != p_amount) { |
| 898 | pixel_size = p_amount; |
| 899 | _queue_update(); |
| 900 | } |
| 901 | } |
| 902 | |
| 903 | real_t Label3D::get_pixel_size() const { |
| 904 | return pixel_size; |
| 905 | } |
| 906 | |
| 907 | void Label3D::set_offset(const Point2 &p_offset) { |
| 908 | if (lbl_offset != p_offset) { |
| 909 | lbl_offset = p_offset; |
| 910 | _queue_update(); |
| 911 | } |
| 912 | } |
| 913 | |
| 914 | Point2 Label3D::get_offset() const { |
| 915 | return lbl_offset; |
| 916 | } |
| 917 | |
| 918 | void Label3D::set_line_spacing(float p_line_spacing) { |
| 919 | if (line_spacing != p_line_spacing) { |
| 920 | line_spacing = p_line_spacing; |
| 921 | _queue_update(); |
| 922 | } |
| 923 | } |
| 924 | |
| 925 | float Label3D::get_line_spacing() const { |
| 926 | return line_spacing; |
| 927 | } |
| 928 | |
| 929 | void Label3D::set_draw_flag(DrawFlags p_flag, bool p_enable) { |
| 930 | ERR_FAIL_INDEX(p_flag, FLAG_MAX); |
| 931 | if (flags[p_flag] != p_enable) { |
| 932 | flags[p_flag] = p_enable; |
| 933 | _queue_update(); |
| 934 | } |
| 935 | } |
| 936 | |
| 937 | bool Label3D::get_draw_flag(DrawFlags p_flag) const { |
| 938 | ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); |
| 939 | return flags[p_flag]; |
| 940 | } |
| 941 | |
| 942 | void Label3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { |
| 943 | ERR_FAIL_INDEX(p_mode, 3); |
| 944 | if (billboard_mode != p_mode) { |
| 945 | billboard_mode = p_mode; |
| 946 | _queue_update(); |
| 947 | } |
| 948 | } |
| 949 | |
| 950 | StandardMaterial3D::BillboardMode Label3D::get_billboard_mode() const { |
| 951 | return billboard_mode; |
| 952 | } |
| 953 | |
| 954 | void Label3D::set_alpha_cut_mode(AlphaCutMode p_mode) { |
| 955 | ERR_FAIL_INDEX(p_mode, ALPHA_CUT_MAX); |
| 956 | if (alpha_cut != p_mode) { |
| 957 | alpha_cut = p_mode; |
| 958 | _queue_update(); |
| 959 | notify_property_list_changed(); |
| 960 | } |
| 961 | } |
| 962 | |
| 963 | void Label3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { |
| 964 | if (texture_filter != p_filter) { |
| 965 | texture_filter = p_filter; |
| 966 | _queue_update(); |
| 967 | } |
| 968 | } |
| 969 | |
| 970 | StandardMaterial3D::TextureFilter Label3D::get_texture_filter() const { |
| 971 | return texture_filter; |
| 972 | } |
| 973 | |
| 974 | Label3D::AlphaCutMode Label3D::get_alpha_cut_mode() const { |
| 975 | return alpha_cut; |
| 976 | } |
| 977 | |
| 978 | void Label3D::set_alpha_hash_scale(float p_hash_scale) { |
| 979 | if (alpha_hash_scale != p_hash_scale) { |
| 980 | alpha_hash_scale = p_hash_scale; |
| 981 | _queue_update(); |
| 982 | } |
| 983 | } |
| 984 | |
| 985 | float Label3D::get_alpha_hash_scale() const { |
| 986 | return alpha_hash_scale; |
| 987 | } |
| 988 | |
| 989 | void Label3D::set_alpha_scissor_threshold(float p_threshold) { |
| 990 | if (alpha_scissor_threshold != p_threshold) { |
| 991 | alpha_scissor_threshold = p_threshold; |
| 992 | _queue_update(); |
| 993 | } |
| 994 | } |
| 995 | |
| 996 | float Label3D::get_alpha_scissor_threshold() const { |
| 997 | return alpha_scissor_threshold; |
| 998 | } |
| 999 | |
| 1000 | void Label3D::set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa) { |
| 1001 | if (alpha_antialiasing_mode != p_alpha_aa) { |
| 1002 | alpha_antialiasing_mode = p_alpha_aa; |
| 1003 | _queue_update(); |
| 1004 | } |
| 1005 | } |
| 1006 | |
| 1007 | BaseMaterial3D::AlphaAntiAliasing Label3D::get_alpha_antialiasing() const { |
| 1008 | return alpha_antialiasing_mode; |
| 1009 | } |
| 1010 | |
| 1011 | void Label3D::set_alpha_antialiasing_edge(float p_edge) { |
| 1012 | if (alpha_antialiasing_edge != p_edge) { |
| 1013 | alpha_antialiasing_edge = p_edge; |
| 1014 | _queue_update(); |
| 1015 | } |
| 1016 | } |
| 1017 | |
| 1018 | float Label3D::get_alpha_antialiasing_edge() const { |
| 1019 | return alpha_antialiasing_edge; |
| 1020 | } |
| 1021 | |
| 1022 | Label3D::Label3D() { |
| 1023 | for (int i = 0; i < FLAG_MAX; i++) { |
| 1024 | flags[i] = (i == FLAG_DOUBLE_SIDED); |
| 1025 | } |
| 1026 | |
| 1027 | text_rid = TS->create_shaped_text(); |
| 1028 | |
| 1029 | mesh = RenderingServer::get_singleton()->mesh_create(); |
| 1030 | |
| 1031 | // Disable shadow casting by default to improve performance and avoid unintended visual artifacts. |
| 1032 | set_cast_shadows_setting(SHADOW_CASTING_SETTING_OFF); |
| 1033 | |
| 1034 | // Label3D can't contribute to GI in any way, so disable it to improve performance. |
| 1035 | set_gi_mode(GI_MODE_DISABLED); |
| 1036 | |
| 1037 | set_base(mesh); |
| 1038 | } |
| 1039 | |
| 1040 | Label3D::~Label3D() { |
| 1041 | for (int i = 0; i < lines_rid.size(); i++) { |
| 1042 | TS->free_rid(lines_rid[i]); |
| 1043 | } |
| 1044 | lines_rid.clear(); |
| 1045 | |
| 1046 | TS->free_rid(text_rid); |
| 1047 | |
| 1048 | ERR_FAIL_NULL(RenderingServer::get_singleton()); |
| 1049 | RenderingServer::get_singleton()->free(mesh); |
| 1050 | for (KeyValue<SurfaceKey, SurfaceData> E : surfaces) { |
| 1051 | RenderingServer::get_singleton()->free(E.value.material); |
| 1052 | } |
| 1053 | surfaces.clear(); |
| 1054 | } |
| 1055 | |