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 | |