1/**************************************************************************/
2/* primitive_meshes.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 "primitive_meshes.h"
32
33#include "core/config/project_settings.h"
34#include "scene/resources/theme.h"
35#include "scene/theme/theme_db.h"
36#include "servers/rendering_server.h"
37#include "thirdparty/misc/polypartition.h"
38
39#define PADDING_REF_SIZE 1024.0
40
41/**
42 PrimitiveMesh
43*/
44void PrimitiveMesh::_update() const {
45 Array arr;
46 if (GDVIRTUAL_CALL(_create_mesh_array, arr)) {
47 ERR_FAIL_COND_MSG(arr.size() != RS::ARRAY_MAX, "_create_mesh_array must return an array of Mesh.ARRAY_MAX elements.");
48 } else {
49 arr.resize(RS::ARRAY_MAX);
50 _create_mesh_array(arr);
51 }
52
53 Vector<Vector3> points = arr[RS::ARRAY_VERTEX];
54
55 ERR_FAIL_COND_MSG(points.size() == 0, "_create_mesh_array must return at least a vertex array.");
56
57 aabb = AABB();
58
59 int pc = points.size();
60 ERR_FAIL_COND(pc == 0);
61 {
62 const Vector3 *r = points.ptr();
63 for (int i = 0; i < pc; i++) {
64 if (i == 0) {
65 aabb.position = r[i];
66 } else {
67 aabb.expand_to(r[i]);
68 }
69 }
70 }
71
72 Vector<int> indices = arr[RS::ARRAY_INDEX];
73
74 if (flip_faces) {
75 Vector<Vector3> normals = arr[RS::ARRAY_NORMAL];
76
77 if (normals.size() && indices.size()) {
78 {
79 int nc = normals.size();
80 Vector3 *w = normals.ptrw();
81 for (int i = 0; i < nc; i++) {
82 w[i] = -w[i];
83 }
84 }
85
86 {
87 int ic = indices.size();
88 int *w = indices.ptrw();
89 for (int i = 0; i < ic; i += 3) {
90 SWAP(w[i + 0], w[i + 1]);
91 }
92 }
93 arr[RS::ARRAY_NORMAL] = normals;
94 arr[RS::ARRAY_INDEX] = indices;
95 }
96 }
97
98 if (add_uv2) {
99 // _create_mesh_array should populate our UV2, this is a fallback in case it doesn't.
100 // As we don't know anything about the geometry we only pad the right and bottom edge
101 // of our texture.
102 Vector<Vector2> uv = arr[RS::ARRAY_TEX_UV];
103 Vector<Vector2> uv2 = arr[RS::ARRAY_TEX_UV2];
104
105 if (uv.size() > 0 && uv2.size() == 0) {
106 Vector2 uv2_scale = get_uv2_scale();
107 uv2.resize(uv.size());
108
109 Vector2 *uv2w = uv2.ptrw();
110 for (int i = 0; i < uv.size(); i++) {
111 uv2w[i] = uv[i] * uv2_scale;
112 }
113 }
114
115 arr[RS::ARRAY_TEX_UV2] = uv2;
116 }
117
118 array_len = pc;
119 index_array_len = indices.size();
120 // in with the new
121 RenderingServer::get_singleton()->mesh_clear(mesh);
122 RenderingServer::get_singleton()->mesh_add_surface_from_arrays(mesh, (RenderingServer::PrimitiveType)primitive_type, arr);
123 RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid());
124
125 pending_request = false;
126
127 clear_cache();
128
129 const_cast<PrimitiveMesh *>(this)->emit_changed();
130}
131
132void PrimitiveMesh::_request_update() {
133 if (pending_request) {
134 return;
135 }
136 _update();
137}
138
139int PrimitiveMesh::get_surface_count() const {
140 if (pending_request) {
141 _update();
142 }
143 return 1;
144}
145
146int PrimitiveMesh::surface_get_array_len(int p_idx) const {
147 ERR_FAIL_INDEX_V(p_idx, 1, -1);
148 if (pending_request) {
149 _update();
150 }
151
152 return array_len;
153}
154
155int PrimitiveMesh::surface_get_array_index_len(int p_idx) const {
156 ERR_FAIL_INDEX_V(p_idx, 1, -1);
157 if (pending_request) {
158 _update();
159 }
160
161 return index_array_len;
162}
163
164Array PrimitiveMesh::surface_get_arrays(int p_surface) const {
165 ERR_FAIL_INDEX_V(p_surface, 1, Array());
166 if (pending_request) {
167 _update();
168 }
169
170 return RenderingServer::get_singleton()->mesh_surface_get_arrays(mesh, 0);
171}
172
173Dictionary PrimitiveMesh::surface_get_lods(int p_surface) const {
174 return Dictionary(); //not really supported
175}
176
177TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) const {
178 return TypedArray<Array>(); //not really supported
179}
180
181BitField<Mesh::ArrayFormat> PrimitiveMesh::surface_get_format(int p_idx) const {
182 ERR_FAIL_INDEX_V(p_idx, 1, 0);
183
184 uint32_t mesh_format = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;
185 if (add_uv2) {
186 mesh_format |= RS::ARRAY_FORMAT_TEX_UV2;
187 }
188
189 return mesh_format;
190}
191
192Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const {
193 return primitive_type;
194}
195
196void PrimitiveMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
197 ERR_FAIL_INDEX(p_idx, 1);
198
199 set_material(p_material);
200}
201
202Ref<Material> PrimitiveMesh::surface_get_material(int p_idx) const {
203 ERR_FAIL_INDEX_V(p_idx, 1, nullptr);
204
205 return material;
206}
207
208int PrimitiveMesh::get_blend_shape_count() const {
209 return 0;
210}
211
212StringName PrimitiveMesh::get_blend_shape_name(int p_index) const {
213 return StringName();
214}
215
216void PrimitiveMesh::set_blend_shape_name(int p_index, const StringName &p_name) {
217}
218
219AABB PrimitiveMesh::get_aabb() const {
220 if (pending_request) {
221 _update();
222 }
223
224 return aabb;
225}
226
227RID PrimitiveMesh::get_rid() const {
228 if (pending_request) {
229 _update();
230 }
231 return mesh;
232}
233
234void PrimitiveMesh::_bind_methods() {
235 ClassDB::bind_method(D_METHOD("_update"), &PrimitiveMesh::_update);
236
237 ClassDB::bind_method(D_METHOD("set_material", "material"), &PrimitiveMesh::set_material);
238 ClassDB::bind_method(D_METHOD("get_material"), &PrimitiveMesh::get_material);
239
240 ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays);
241
242 ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &PrimitiveMesh::set_custom_aabb);
243 ClassDB::bind_method(D_METHOD("get_custom_aabb"), &PrimitiveMesh::get_custom_aabb);
244
245 ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces);
246 ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces);
247
248 ClassDB::bind_method(D_METHOD("set_add_uv2", "add_uv2"), &PrimitiveMesh::set_add_uv2);
249 ClassDB::bind_method(D_METHOD("get_add_uv2"), &PrimitiveMesh::get_add_uv2);
250
251 ClassDB::bind_method(D_METHOD("set_uv2_padding", "uv2_padding"), &PrimitiveMesh::set_uv2_padding);
252 ClassDB::bind_method(D_METHOD("get_uv2_padding"), &PrimitiveMesh::get_uv2_padding);
253
254 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
255 ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
256 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");
257 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_uv2"), "set_add_uv2", "get_add_uv2");
258 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv2_padding", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), "set_uv2_padding", "get_uv2_padding");
259
260 GDVIRTUAL_BIND(_create_mesh_array);
261}
262
263void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
264 material = p_material;
265 if (!pending_request) {
266 // just apply it, else it'll happen when _update is called.
267 RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid());
268 notify_property_list_changed();
269 emit_changed();
270 }
271}
272
273Ref<Material> PrimitiveMesh::get_material() const {
274 return material;
275}
276
277Array PrimitiveMesh::get_mesh_arrays() const {
278 return surface_get_arrays(0);
279}
280
281void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) {
282 custom_aabb = p_custom;
283 RS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);
284 emit_changed();
285}
286
287AABB PrimitiveMesh::get_custom_aabb() const {
288 return custom_aabb;
289}
290
291void PrimitiveMesh::set_flip_faces(bool p_enable) {
292 flip_faces = p_enable;
293 _request_update();
294}
295
296bool PrimitiveMesh::get_flip_faces() const {
297 return flip_faces;
298}
299
300void PrimitiveMesh::set_add_uv2(bool p_enable) {
301 add_uv2 = p_enable;
302 _update_lightmap_size();
303 _request_update();
304}
305
306void PrimitiveMesh::set_uv2_padding(float p_padding) {
307 uv2_padding = p_padding;
308 _update_lightmap_size();
309 _request_update();
310}
311
312Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const {
313 Vector2 uv2_scale;
314 Vector2 lightmap_size = get_lightmap_size_hint();
315
316 // Calculate it as a margin, if no lightmap size hint is given we assume "PADDING_REF_SIZE" as our texture size.
317 uv2_scale.x = p_margin_scale.x * uv2_padding / (lightmap_size.x == 0.0 ? PADDING_REF_SIZE : lightmap_size.x);
318 uv2_scale.y = p_margin_scale.y * uv2_padding / (lightmap_size.y == 0.0 ? PADDING_REF_SIZE : lightmap_size.y);
319
320 // Inverse it to turn our margin into a scale
321 uv2_scale = Vector2(1.0, 1.0) - uv2_scale;
322
323 return uv2_scale;
324}
325
326float PrimitiveMesh::get_lightmap_texel_size() const {
327 float texel_size = GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size");
328
329 if (texel_size <= 0.0) {
330 texel_size = 0.2;
331 }
332
333 return texel_size;
334}
335
336PrimitiveMesh::PrimitiveMesh() {
337 mesh = RenderingServer::get_singleton()->mesh_create();
338}
339
340PrimitiveMesh::~PrimitiveMesh() {
341 ERR_FAIL_NULL(RenderingServer::get_singleton());
342 RenderingServer::get_singleton()->free(mesh);
343}
344
345/**
346 CapsuleMesh
347*/
348
349void CapsuleMesh::_update_lightmap_size() {
350 if (get_add_uv2()) {
351 // size must have changed, update lightmap size hint
352 Size2i _lightmap_size_hint;
353 float texel_size = get_lightmap_texel_size();
354 float padding = get_uv2_padding();
355
356 float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend
357 float vertical_length = radial_length * 2 + (height - 2.0 * radius); // total vertical length
358
359 _lightmap_size_hint.x = MAX(1.0, 4.0 * radial_length / texel_size) + padding;
360 _lightmap_size_hint.y = MAX(1.0, vertical_length / texel_size) + padding;
361
362 set_lightmap_size_hint(_lightmap_size_hint);
363 }
364}
365
366void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
367 bool _add_uv2 = get_add_uv2();
368 float texel_size = get_lightmap_texel_size();
369 float _uv2_padding = get_uv2_padding() * texel_size;
370
371 create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding);
372}
373
374void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings, bool p_add_uv2, const float p_uv2_padding) {
375 int i, j, prevrow, thisrow, point;
376 float x, y, z, u, v, w;
377 float onethird = 1.0 / 3.0;
378 float twothirds = 2.0 / 3.0;
379
380 // Only used if we calculate UV2
381 float radial_width = 2.0 * radius * Math_PI;
382 float radial_h = radial_width / (radial_width + p_uv2_padding);
383 float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend
384 float vertical_length = radial_length * 2 + (height - 2.0 * radius) + p_uv2_padding; // total vertical length
385 float radial_v = radial_length / vertical_length; // v size of top and bottom section
386 float height_v = (height - 2.0 * radius) / vertical_length; // v size of height section
387
388 // note, this has been aligned with our collision shape but I've left the descriptions as top/middle/bottom
389
390 Vector<Vector3> points;
391 Vector<Vector3> normals;
392 Vector<float> tangents;
393 Vector<Vector2> uvs;
394 Vector<Vector2> uv2s;
395 Vector<int> indices;
396 point = 0;
397
398#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
399 tangents.push_back(m_x); \
400 tangents.push_back(m_y); \
401 tangents.push_back(m_z); \
402 tangents.push_back(m_d);
403
404 /* top hemisphere */
405 thisrow = 0;
406 prevrow = 0;
407 for (j = 0; j <= (rings + 1); j++) {
408 v = j;
409
410 v /= (rings + 1);
411 w = sin(0.5 * Math_PI * v);
412 y = radius * cos(0.5 * Math_PI * v);
413
414 for (i = 0; i <= radial_segments; i++) {
415 u = i;
416 u /= radial_segments;
417
418 x = -sin(u * Math_TAU);
419 z = cos(u * Math_TAU);
420
421 Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
422 points.push_back(p + Vector3(0.0, 0.5 * height - radius, 0.0));
423 normals.push_back(p.normalized());
424 ADD_TANGENT(-z, 0.0, -x, 1.0)
425 uvs.push_back(Vector2(u, v * onethird));
426 if (p_add_uv2) {
427 uv2s.push_back(Vector2(u * radial_h, v * radial_v));
428 }
429 point++;
430
431 if (i > 0 && j > 0) {
432 indices.push_back(prevrow + i - 1);
433 indices.push_back(prevrow + i);
434 indices.push_back(thisrow + i - 1);
435
436 indices.push_back(prevrow + i);
437 indices.push_back(thisrow + i);
438 indices.push_back(thisrow + i - 1);
439 }
440 }
441
442 prevrow = thisrow;
443 thisrow = point;
444 }
445
446 /* cylinder */
447 thisrow = point;
448 prevrow = 0;
449 for (j = 0; j <= (rings + 1); j++) {
450 v = j;
451 v /= (rings + 1);
452
453 y = (height - 2.0 * radius) * v;
454 y = (0.5 * height - radius) - y;
455
456 for (i = 0; i <= radial_segments; i++) {
457 u = i;
458 u /= radial_segments;
459
460 x = -sin(u * Math_TAU);
461 z = cos(u * Math_TAU);
462
463 Vector3 p = Vector3(x * radius, y, -z * radius);
464 points.push_back(p);
465 normals.push_back(Vector3(x, 0.0, -z));
466 ADD_TANGENT(-z, 0.0, -x, 1.0)
467 uvs.push_back(Vector2(u, onethird + (v * onethird)));
468 if (p_add_uv2) {
469 uv2s.push_back(Vector2(u * radial_h, radial_v + (v * height_v)));
470 }
471 point++;
472
473 if (i > 0 && j > 0) {
474 indices.push_back(prevrow + i - 1);
475 indices.push_back(prevrow + i);
476 indices.push_back(thisrow + i - 1);
477
478 indices.push_back(prevrow + i);
479 indices.push_back(thisrow + i);
480 indices.push_back(thisrow + i - 1);
481 }
482 }
483
484 prevrow = thisrow;
485 thisrow = point;
486 }
487
488 /* bottom hemisphere */
489 thisrow = point;
490 prevrow = 0;
491 for (j = 0; j <= (rings + 1); j++) {
492 v = j;
493
494 v /= (rings + 1);
495 v += 1.0;
496 w = sin(0.5 * Math_PI * v);
497 y = radius * cos(0.5 * Math_PI * v);
498
499 for (i = 0; i <= radial_segments; i++) {
500 u = i;
501 u /= radial_segments;
502
503 x = -sin(u * Math_TAU);
504 z = cos(u * Math_TAU);
505
506 Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
507 points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0));
508 normals.push_back(p.normalized());
509 ADD_TANGENT(-z, 0.0, -x, 1.0)
510 uvs.push_back(Vector2(u, twothirds + ((v - 1.0) * onethird)));
511 if (p_add_uv2) {
512 uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + ((v - 1.0) * radial_v)));
513 }
514 point++;
515
516 if (i > 0 && j > 0) {
517 indices.push_back(prevrow + i - 1);
518 indices.push_back(prevrow + i);
519 indices.push_back(thisrow + i - 1);
520
521 indices.push_back(prevrow + i);
522 indices.push_back(thisrow + i);
523 indices.push_back(thisrow + i - 1);
524 }
525 }
526
527 prevrow = thisrow;
528 thisrow = point;
529 }
530
531 p_arr[RS::ARRAY_VERTEX] = points;
532 p_arr[RS::ARRAY_NORMAL] = normals;
533 p_arr[RS::ARRAY_TANGENT] = tangents;
534 p_arr[RS::ARRAY_TEX_UV] = uvs;
535 if (p_add_uv2) {
536 p_arr[RS::ARRAY_TEX_UV2] = uv2s;
537 }
538 p_arr[RS::ARRAY_INDEX] = indices;
539}
540
541void CapsuleMesh::_bind_methods() {
542 ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CapsuleMesh::set_radius);
543 ClassDB::bind_method(D_METHOD("get_radius"), &CapsuleMesh::get_radius);
544 ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleMesh::set_height);
545 ClassDB::bind_method(D_METHOD("get_height"), &CapsuleMesh::get_height);
546
547 ClassDB::bind_method(D_METHOD("set_radial_segments", "segments"), &CapsuleMesh::set_radial_segments);
548 ClassDB::bind_method(D_METHOD("get_radial_segments"), &CapsuleMesh::get_radial_segments);
549 ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CapsuleMesh::set_rings);
550 ClassDB::bind_method(D_METHOD("get_rings"), &CapsuleMesh::get_rings);
551
552 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");
553 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height");
554 ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
555 ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
556
557 ADD_LINKED_PROPERTY("radius", "height");
558 ADD_LINKED_PROPERTY("height", "radius");
559}
560
561void CapsuleMesh::set_radius(const float p_radius) {
562 radius = p_radius;
563 if (radius > height * 0.5) {
564 height = radius * 2.0;
565 }
566 _update_lightmap_size();
567 _request_update();
568}
569
570float CapsuleMesh::get_radius() const {
571 return radius;
572}
573
574void CapsuleMesh::set_height(const float p_height) {
575 height = p_height;
576 if (radius > height * 0.5) {
577 radius = height * 0.5;
578 }
579 _update_lightmap_size();
580 _request_update();
581}
582
583float CapsuleMesh::get_height() const {
584 return height;
585}
586
587void CapsuleMesh::set_radial_segments(const int p_segments) {
588 radial_segments = p_segments > 4 ? p_segments : 4;
589 _request_update();
590}
591
592int CapsuleMesh::get_radial_segments() const {
593 return radial_segments;
594}
595
596void CapsuleMesh::set_rings(const int p_rings) {
597 rings = p_rings > 1 ? p_rings : 1;
598 _request_update();
599}
600
601int CapsuleMesh::get_rings() const {
602 return rings;
603}
604
605CapsuleMesh::CapsuleMesh() {}
606
607/**
608 BoxMesh
609*/
610
611void BoxMesh::_update_lightmap_size() {
612 if (get_add_uv2()) {
613 // size must have changed, update lightmap size hint
614 Size2i _lightmap_size_hint;
615 float texel_size = get_lightmap_texel_size();
616 float padding = get_uv2_padding();
617
618 float width = (size.x + size.z) / texel_size;
619 float length = (size.y + size.y + MAX(size.x, size.z)) / texel_size;
620
621 _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding;
622 _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding;
623
624 set_lightmap_size_hint(_lightmap_size_hint);
625 }
626}
627
628void BoxMesh::_create_mesh_array(Array &p_arr) const {
629 // Note about padding, with our box each face of the box faces a different direction so we want a seam
630 // around every face. We thus add our padding to the right and bottom of each face.
631 // With 3 faces along the width and 2 along the height of the texture we need to adjust our scale
632 // accordingly.
633 bool _add_uv2 = get_add_uv2();
634 float texel_size = get_lightmap_texel_size();
635 float _uv2_padding = get_uv2_padding() * texel_size;
636
637 BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding);
638}
639
640void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d, bool p_add_uv2, const float p_uv2_padding) {
641 int i, j, prevrow, thisrow, point;
642 float x, y, z;
643 float onethird = 1.0 / 3.0;
644 float twothirds = 2.0 / 3.0;
645
646 // Only used if we calculate UV2
647 // TODO this could be improved by changing the order depending on which side is the longest (basically the below works best if size.y is the longest)
648 float total_h = (size.x + size.z + (2.0 * p_uv2_padding));
649 float padding_h = p_uv2_padding / total_h;
650 float width_h = size.x / total_h;
651 float depth_h = size.z / total_h;
652 float total_v = (size.y + size.y + MAX(size.x, size.z) + (3.0 * p_uv2_padding));
653 float padding_v = p_uv2_padding / total_v;
654 float width_v = size.x / total_v;
655 float height_v = size.y / total_v;
656 float depth_v = size.z / total_v;
657
658 Vector3 start_pos = size * -0.5;
659
660 // set our bounding box
661
662 Vector<Vector3> points;
663 Vector<Vector3> normals;
664 Vector<float> tangents;
665 Vector<Vector2> uvs;
666 Vector<Vector2> uv2s;
667 Vector<int> indices;
668 point = 0;
669
670#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
671 tangents.push_back(m_x); \
672 tangents.push_back(m_y); \
673 tangents.push_back(m_z); \
674 tangents.push_back(m_d);
675
676 // front + back
677 y = start_pos.y;
678 thisrow = point;
679 prevrow = 0;
680 for (j = 0; j <= subdivide_h + 1; j++) {
681 float v = j;
682 float v2 = v / (subdivide_w + 1.0);
683 v /= (2.0 * (subdivide_h + 1.0));
684
685 x = start_pos.x;
686 for (i = 0; i <= subdivide_w + 1; i++) {
687 float u = i;
688 float u2 = u / (subdivide_w + 1.0);
689 u /= (3.0 * (subdivide_w + 1.0));
690
691 // front
692 points.push_back(Vector3(x, -y, -start_pos.z)); // double negative on the Z!
693 normals.push_back(Vector3(0.0, 0.0, 1.0));
694 ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
695 uvs.push_back(Vector2(u, v));
696 if (p_add_uv2) {
697 uv2s.push_back(Vector2(u2 * width_h, v2 * height_v));
698 }
699 point++;
700
701 // back
702 points.push_back(Vector3(-x, -y, start_pos.z));
703 normals.push_back(Vector3(0.0, 0.0, -1.0));
704 ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);
705 uvs.push_back(Vector2(twothirds + u, v));
706 if (p_add_uv2) {
707 uv2s.push_back(Vector2(u2 * width_h, height_v + padding_v + (v2 * height_v)));
708 }
709 point++;
710
711 if (i > 0 && j > 0) {
712 int i2 = i * 2;
713
714 // front
715 indices.push_back(prevrow + i2 - 2);
716 indices.push_back(prevrow + i2);
717 indices.push_back(thisrow + i2 - 2);
718 indices.push_back(prevrow + i2);
719 indices.push_back(thisrow + i2);
720 indices.push_back(thisrow + i2 - 2);
721
722 // back
723 indices.push_back(prevrow + i2 - 1);
724 indices.push_back(prevrow + i2 + 1);
725 indices.push_back(thisrow + i2 - 1);
726 indices.push_back(prevrow + i2 + 1);
727 indices.push_back(thisrow + i2 + 1);
728 indices.push_back(thisrow + i2 - 1);
729 }
730
731 x += size.x / (subdivide_w + 1.0);
732 }
733
734 y += size.y / (subdivide_h + 1.0);
735 prevrow = thisrow;
736 thisrow = point;
737 }
738
739 // left + right
740 y = start_pos.y;
741 thisrow = point;
742 prevrow = 0;
743 for (j = 0; j <= (subdivide_h + 1); j++) {
744 float v = j;
745 float v2 = v / (subdivide_h + 1.0);
746 v /= (2.0 * (subdivide_h + 1.0));
747
748 z = start_pos.z;
749 for (i = 0; i <= (subdivide_d + 1); i++) {
750 float u = i;
751 float u2 = u / (subdivide_d + 1.0);
752 u /= (3.0 * (subdivide_d + 1.0));
753
754 // right
755 points.push_back(Vector3(-start_pos.x, -y, -z));
756 normals.push_back(Vector3(1.0, 0.0, 0.0));
757 ADD_TANGENT(0.0, 0.0, -1.0, 1.0);
758 uvs.push_back(Vector2(onethird + u, v));
759 if (p_add_uv2) {
760 uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), v2 * height_v));
761 }
762 point++;
763
764 // left
765 points.push_back(Vector3(start_pos.x, -y, z));
766 normals.push_back(Vector3(-1.0, 0.0, 0.0));
767 ADD_TANGENT(0.0, 0.0, 1.0, 1.0);
768 uvs.push_back(Vector2(u, 0.5 + v));
769 if (p_add_uv2) {
770 uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), height_v + padding_v + (v2 * height_v)));
771 }
772 point++;
773
774 if (i > 0 && j > 0) {
775 int i2 = i * 2;
776
777 // right
778 indices.push_back(prevrow + i2 - 2);
779 indices.push_back(prevrow + i2);
780 indices.push_back(thisrow + i2 - 2);
781 indices.push_back(prevrow + i2);
782 indices.push_back(thisrow + i2);
783 indices.push_back(thisrow + i2 - 2);
784
785 // left
786 indices.push_back(prevrow + i2 - 1);
787 indices.push_back(prevrow + i2 + 1);
788 indices.push_back(thisrow + i2 - 1);
789 indices.push_back(prevrow + i2 + 1);
790 indices.push_back(thisrow + i2 + 1);
791 indices.push_back(thisrow + i2 - 1);
792 }
793
794 z += size.z / (subdivide_d + 1.0);
795 }
796
797 y += size.y / (subdivide_h + 1.0);
798 prevrow = thisrow;
799 thisrow = point;
800 }
801
802 // top + bottom
803 z = start_pos.z;
804 thisrow = point;
805 prevrow = 0;
806 for (j = 0; j <= (subdivide_d + 1); j++) {
807 float v = j;
808 float v2 = v / (subdivide_d + 1.0);
809 v /= (2.0 * (subdivide_d + 1.0));
810
811 x = start_pos.x;
812 for (i = 0; i <= (subdivide_w + 1); i++) {
813 float u = i;
814 float u2 = u / (subdivide_w + 1.0);
815 u /= (3.0 * (subdivide_w + 1.0));
816
817 // top
818 points.push_back(Vector3(-x, -start_pos.y, -z));
819 normals.push_back(Vector3(0.0, 1.0, 0.0));
820 ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);
821 uvs.push_back(Vector2(onethird + u, 0.5 + v));
822 if (p_add_uv2) {
823 uv2s.push_back(Vector2(u2 * width_h, ((height_v + padding_v) * 2.0) + (v2 * depth_v)));
824 }
825 point++;
826
827 // bottom
828 points.push_back(Vector3(x, start_pos.y, -z));
829 normals.push_back(Vector3(0.0, -1.0, 0.0));
830 ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
831 uvs.push_back(Vector2(twothirds + u, 0.5 + v));
832 if (p_add_uv2) {
833 uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), ((height_v + padding_v) * 2.0) + (v2 * width_v)));
834 }
835 point++;
836
837 if (i > 0 && j > 0) {
838 int i2 = i * 2;
839
840 // top
841 indices.push_back(prevrow + i2 - 2);
842 indices.push_back(prevrow + i2);
843 indices.push_back(thisrow + i2 - 2);
844 indices.push_back(prevrow + i2);
845 indices.push_back(thisrow + i2);
846 indices.push_back(thisrow + i2 - 2);
847
848 // bottom
849 indices.push_back(prevrow + i2 - 1);
850 indices.push_back(prevrow + i2 + 1);
851 indices.push_back(thisrow + i2 - 1);
852 indices.push_back(prevrow + i2 + 1);
853 indices.push_back(thisrow + i2 + 1);
854 indices.push_back(thisrow + i2 - 1);
855 }
856
857 x += size.x / (subdivide_w + 1.0);
858 }
859
860 z += size.z / (subdivide_d + 1.0);
861 prevrow = thisrow;
862 thisrow = point;
863 }
864
865 p_arr[RS::ARRAY_VERTEX] = points;
866 p_arr[RS::ARRAY_NORMAL] = normals;
867 p_arr[RS::ARRAY_TANGENT] = tangents;
868 p_arr[RS::ARRAY_TEX_UV] = uvs;
869 if (p_add_uv2) {
870 p_arr[RS::ARRAY_TEX_UV2] = uv2s;
871 }
872 p_arr[RS::ARRAY_INDEX] = indices;
873}
874
875void BoxMesh::_bind_methods() {
876 ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxMesh::set_size);
877 ClassDB::bind_method(D_METHOD("get_size"), &BoxMesh::get_size);
878
879 ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &BoxMesh::set_subdivide_width);
880 ClassDB::bind_method(D_METHOD("get_subdivide_width"), &BoxMesh::get_subdivide_width);
881 ClassDB::bind_method(D_METHOD("set_subdivide_height", "divisions"), &BoxMesh::set_subdivide_height);
882 ClassDB::bind_method(D_METHOD("get_subdivide_height"), &BoxMesh::get_subdivide_height);
883 ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &BoxMesh::set_subdivide_depth);
884 ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &BoxMesh::get_subdivide_depth);
885
886 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
887 ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
888 ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");
889 ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
890}
891
892void BoxMesh::set_size(const Vector3 &p_size) {
893 size = p_size;
894 _update_lightmap_size();
895 _request_update();
896}
897
898Vector3 BoxMesh::get_size() const {
899 return size;
900}
901
902void BoxMesh::set_subdivide_width(const int p_divisions) {
903 subdivide_w = p_divisions > 0 ? p_divisions : 0;
904 _request_update();
905}
906
907int BoxMesh::get_subdivide_width() const {
908 return subdivide_w;
909}
910
911void BoxMesh::set_subdivide_height(const int p_divisions) {
912 subdivide_h = p_divisions > 0 ? p_divisions : 0;
913 _request_update();
914}
915
916int BoxMesh::get_subdivide_height() const {
917 return subdivide_h;
918}
919
920void BoxMesh::set_subdivide_depth(const int p_divisions) {
921 subdivide_d = p_divisions > 0 ? p_divisions : 0;
922 _request_update();
923}
924
925int BoxMesh::get_subdivide_depth() const {
926 return subdivide_d;
927}
928
929BoxMesh::BoxMesh() {}
930
931/**
932 CylinderMesh
933*/
934
935void CylinderMesh::_update_lightmap_size() {
936 if (get_add_uv2()) {
937 // size must have changed, update lightmap size hint
938 Size2i _lightmap_size_hint;
939 float texel_size = get_lightmap_texel_size();
940 float padding = get_uv2_padding();
941
942 float top_circumference = top_radius * Math_PI * 2.0;
943 float bottom_circumference = bottom_radius * Math_PI * 2.0;
944
945 float _width = MAX(top_circumference, bottom_circumference) / texel_size + padding;
946 _width = MAX(_width, (((top_radius + bottom_radius) / texel_size) + padding) * 2.0); // this is extremely unlikely to be larger, will only happen if padding is larger then our diameter.
947 _lightmap_size_hint.x = MAX(1.0, _width);
948
949 float _height = ((height + (MAX(top_radius, bottom_radius) * 2.0)) / texel_size) + (2.0 * padding);
950
951 _lightmap_size_hint.y = MAX(1.0, _height);
952
953 set_lightmap_size_hint(_lightmap_size_hint);
954 }
955}
956
957void CylinderMesh::_create_mesh_array(Array &p_arr) const {
958 bool _add_uv2 = get_add_uv2();
959 float texel_size = get_lightmap_texel_size();
960 float _uv2_padding = get_uv2_padding() * texel_size;
961
962 create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding);
963}
964
965void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom, bool p_add_uv2, const float p_uv2_padding) {
966 int i, j, prevrow, thisrow, point;
967 float x, y, z, u, v, radius, radius_h;
968
969 // Only used if we calculate UV2
970 float top_circumference = top_radius * Math_PI * 2.0;
971 float bottom_circumference = bottom_radius * Math_PI * 2.0;
972 float vertical_length = height + MAX(2.0 * top_radius, 2.0 * bottom_radius) + (2.0 * p_uv2_padding);
973 float height_v = height / vertical_length;
974 float padding_v = p_uv2_padding / vertical_length;
975
976 float horizonal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding);
977 float center_h = 0.5 * (horizonal_length - p_uv2_padding) / horizonal_length;
978 float top_h = top_circumference / horizonal_length;
979 float bottom_h = bottom_circumference / horizonal_length;
980 float padding_h = p_uv2_padding / horizonal_length;
981
982 Vector<Vector3> points;
983 Vector<Vector3> normals;
984 Vector<float> tangents;
985 Vector<Vector2> uvs;
986 Vector<Vector2> uv2s;
987 Vector<int> indices;
988 point = 0;
989
990#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
991 tangents.push_back(m_x); \
992 tangents.push_back(m_y); \
993 tangents.push_back(m_z); \
994 tangents.push_back(m_d);
995
996 thisrow = 0;
997 prevrow = 0;
998 const real_t side_normal_y = (bottom_radius - top_radius) / height;
999 for (j = 0; j <= (rings + 1); j++) {
1000 v = j;
1001 v /= (rings + 1);
1002
1003 radius = top_radius + ((bottom_radius - top_radius) * v);
1004 radius_h = top_h + ((bottom_h - top_h) * v);
1005
1006 y = height * v;
1007 y = (height * 0.5) - y;
1008
1009 for (i = 0; i <= radial_segments; i++) {
1010 u = i;
1011 u /= radial_segments;
1012
1013 x = sin(u * Math_TAU);
1014 z = cos(u * Math_TAU);
1015
1016 Vector3 p = Vector3(x * radius, y, z * radius);
1017 points.push_back(p);
1018 normals.push_back(Vector3(x, side_normal_y, z).normalized());
1019 ADD_TANGENT(z, 0.0, -x, 1.0)
1020 uvs.push_back(Vector2(u, v * 0.5));
1021 if (p_add_uv2) {
1022 uv2s.push_back(Vector2(center_h + (u - 0.5) * radius_h, v * height_v));
1023 }
1024 point++;
1025
1026 if (i > 0 && j > 0) {
1027 indices.push_back(prevrow + i - 1);
1028 indices.push_back(prevrow + i);
1029 indices.push_back(thisrow + i - 1);
1030
1031 indices.push_back(prevrow + i);
1032 indices.push_back(thisrow + i);
1033 indices.push_back(thisrow + i - 1);
1034 }
1035 }
1036
1037 prevrow = thisrow;
1038 thisrow = point;
1039 }
1040
1041 // Adjust for bottom section, only used if we calculate UV2s.
1042 top_h = top_radius / horizonal_length;
1043 float top_v = top_radius / vertical_length;
1044 bottom_h = bottom_radius / horizonal_length;
1045 float bottom_v = bottom_radius / vertical_length;
1046
1047 // Add top.
1048 if (cap_top && top_radius > 0.0) {
1049 y = height * 0.5;
1050
1051 thisrow = point;
1052 points.push_back(Vector3(0.0, y, 0.0));
1053 normals.push_back(Vector3(0.0, 1.0, 0.0));
1054 ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
1055 uvs.push_back(Vector2(0.25, 0.75));
1056 if (p_add_uv2) {
1057 uv2s.push_back(Vector2(top_h, height_v + padding_v + MAX(top_v, bottom_v)));
1058 }
1059 point++;
1060
1061 for (i = 0; i <= radial_segments; i++) {
1062 float r = i;
1063 r /= radial_segments;
1064
1065 x = sin(r * Math_TAU);
1066 z = cos(r * Math_TAU);
1067
1068 u = ((x + 1.0) * 0.25);
1069 v = 0.5 + ((z + 1.0) * 0.25);
1070
1071 Vector3 p = Vector3(x * top_radius, y, z * top_radius);
1072 points.push_back(p);
1073 normals.push_back(Vector3(0.0, 1.0, 0.0));
1074 ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
1075 uvs.push_back(Vector2(u, v));
1076 if (p_add_uv2) {
1077 uv2s.push_back(Vector2(top_h + (x * top_h), height_v + padding_v + MAX(top_v, bottom_v) + (z * top_v)));
1078 }
1079 point++;
1080
1081 if (i > 0) {
1082 indices.push_back(thisrow);
1083 indices.push_back(point - 1);
1084 indices.push_back(point - 2);
1085 }
1086 }
1087 }
1088
1089 // Add bottom.
1090 if (cap_bottom && bottom_radius > 0.0) {
1091 y = height * -0.5;
1092
1093 thisrow = point;
1094 points.push_back(Vector3(0.0, y, 0.0));
1095 normals.push_back(Vector3(0.0, -1.0, 0.0));
1096 ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
1097 uvs.push_back(Vector2(0.75, 0.75));
1098 if (p_add_uv2) {
1099 uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h, height_v + padding_v + MAX(top_v, bottom_v)));
1100 }
1101 point++;
1102
1103 for (i = 0; i <= radial_segments; i++) {
1104 float r = i;
1105 r /= radial_segments;
1106
1107 x = sin(r * Math_TAU);
1108 z = cos(r * Math_TAU);
1109
1110 u = 0.5 + ((x + 1.0) * 0.25);
1111 v = 1.0 - ((z + 1.0) * 0.25);
1112
1113 Vector3 p = Vector3(x * bottom_radius, y, z * bottom_radius);
1114 points.push_back(p);
1115 normals.push_back(Vector3(0.0, -1.0, 0.0));
1116 ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
1117 uvs.push_back(Vector2(u, v));
1118 if (p_add_uv2) {
1119 uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h + (x * bottom_h), height_v + padding_v + MAX(top_v, bottom_v) - (z * bottom_v)));
1120 }
1121 point++;
1122
1123 if (i > 0) {
1124 indices.push_back(thisrow);
1125 indices.push_back(point - 2);
1126 indices.push_back(point - 1);
1127 }
1128 }
1129 }
1130
1131 p_arr[RS::ARRAY_VERTEX] = points;
1132 p_arr[RS::ARRAY_NORMAL] = normals;
1133 p_arr[RS::ARRAY_TANGENT] = tangents;
1134 p_arr[RS::ARRAY_TEX_UV] = uvs;
1135 if (p_add_uv2) {
1136 p_arr[RS::ARRAY_TEX_UV2] = uv2s;
1137 }
1138 p_arr[RS::ARRAY_INDEX] = indices;
1139}
1140
1141void CylinderMesh::_bind_methods() {
1142 ClassDB::bind_method(D_METHOD("set_top_radius", "radius"), &CylinderMesh::set_top_radius);
1143 ClassDB::bind_method(D_METHOD("get_top_radius"), &CylinderMesh::get_top_radius);
1144 ClassDB::bind_method(D_METHOD("set_bottom_radius", "radius"), &CylinderMesh::set_bottom_radius);
1145 ClassDB::bind_method(D_METHOD("get_bottom_radius"), &CylinderMesh::get_bottom_radius);
1146 ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderMesh::set_height);
1147 ClassDB::bind_method(D_METHOD("get_height"), &CylinderMesh::get_height);
1148
1149 ClassDB::bind_method(D_METHOD("set_radial_segments", "segments"), &CylinderMesh::set_radial_segments);
1150 ClassDB::bind_method(D_METHOD("get_radial_segments"), &CylinderMesh::get_radial_segments);
1151 ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings);
1152 ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings);
1153
1154 ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &CylinderMesh::set_cap_top);
1155 ClassDB::bind_method(D_METHOD("is_cap_top"), &CylinderMesh::is_cap_top);
1156
1157 ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &CylinderMesh::set_cap_bottom);
1158 ClassDB::bind_method(D_METHOD("is_cap_bottom"), &CylinderMesh::is_cap_bottom);
1159
1160 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_top_radius", "get_top_radius");
1161 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_bottom_radius", "get_bottom_radius");
1162 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_height", "get_height");
1163 ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
1164 ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
1165 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top");
1166 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom");
1167}
1168
1169void CylinderMesh::set_top_radius(const float p_radius) {
1170 top_radius = p_radius;
1171 _update_lightmap_size();
1172 _request_update();
1173}
1174
1175float CylinderMesh::get_top_radius() const {
1176 return top_radius;
1177}
1178
1179void CylinderMesh::set_bottom_radius(const float p_radius) {
1180 bottom_radius = p_radius;
1181 _update_lightmap_size();
1182 _request_update();
1183}
1184
1185float CylinderMesh::get_bottom_radius() const {
1186 return bottom_radius;
1187}
1188
1189void CylinderMesh::set_height(const float p_height) {
1190 height = p_height;
1191 _update_lightmap_size();
1192 _request_update();
1193}
1194
1195float CylinderMesh::get_height() const {
1196 return height;
1197}
1198
1199void CylinderMesh::set_radial_segments(const int p_segments) {
1200 radial_segments = p_segments > 4 ? p_segments : 4;
1201 _request_update();
1202}
1203
1204int CylinderMesh::get_radial_segments() const {
1205 return radial_segments;
1206}
1207
1208void CylinderMesh::set_rings(const int p_rings) {
1209 rings = p_rings > 0 ? p_rings : 0;
1210 _request_update();
1211}
1212
1213int CylinderMesh::get_rings() const {
1214 return rings;
1215}
1216
1217void CylinderMesh::set_cap_top(bool p_cap_top) {
1218 cap_top = p_cap_top;
1219 _request_update();
1220}
1221
1222bool CylinderMesh::is_cap_top() const {
1223 return cap_top;
1224}
1225
1226void CylinderMesh::set_cap_bottom(bool p_cap_bottom) {
1227 cap_bottom = p_cap_bottom;
1228 _request_update();
1229}
1230
1231bool CylinderMesh::is_cap_bottom() const {
1232 return cap_bottom;
1233}
1234
1235CylinderMesh::CylinderMesh() {}
1236
1237/**
1238 PlaneMesh
1239*/
1240
1241void PlaneMesh::_update_lightmap_size() {
1242 if (get_add_uv2()) {
1243 // size must have changed, update lightmap size hint
1244 Size2i _lightmap_size_hint;
1245 float texel_size = get_lightmap_texel_size();
1246 float padding = get_uv2_padding();
1247
1248 _lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding);
1249 _lightmap_size_hint.y = MAX(1.0, (size.y / texel_size) + padding);
1250
1251 set_lightmap_size_hint(_lightmap_size_hint);
1252 }
1253}
1254
1255void PlaneMesh::_create_mesh_array(Array &p_arr) const {
1256 int i, j, prevrow, thisrow, point;
1257 float x, z;
1258
1259 // Plane mesh can use default UV2 calculation as implemented in Primitive Mesh
1260
1261 Size2 start_pos = size * -0.5;
1262
1263 Vector3 normal = Vector3(0.0, 1.0, 0.0);
1264 if (orientation == FACE_X) {
1265 normal = Vector3(1.0, 0.0, 0.0);
1266 } else if (orientation == FACE_Z) {
1267 normal = Vector3(0.0, 0.0, 1.0);
1268 }
1269
1270 Vector<Vector3> points;
1271 Vector<Vector3> normals;
1272 Vector<float> tangents;
1273 Vector<Vector2> uvs;
1274 Vector<int> indices;
1275 point = 0;
1276
1277#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
1278 tangents.push_back(m_x); \
1279 tangents.push_back(m_y); \
1280 tangents.push_back(m_z); \
1281 tangents.push_back(m_d);
1282
1283 /* top + bottom */
1284 z = start_pos.y;
1285 thisrow = point;
1286 prevrow = 0;
1287 for (j = 0; j <= (subdivide_d + 1); j++) {
1288 x = start_pos.x;
1289 for (i = 0; i <= (subdivide_w + 1); i++) {
1290 float u = i;
1291 float v = j;
1292 u /= (subdivide_w + 1.0);
1293 v /= (subdivide_d + 1.0);
1294
1295 if (orientation == FACE_X) {
1296 points.push_back(Vector3(0.0, z, x) + center_offset);
1297 } else if (orientation == FACE_Y) {
1298 points.push_back(Vector3(-x, 0.0, -z) + center_offset);
1299 } else if (orientation == FACE_Z) {
1300 points.push_back(Vector3(-x, z, 0.0) + center_offset);
1301 }
1302 normals.push_back(normal);
1303 ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
1304 uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */
1305 point++;
1306
1307 if (i > 0 && j > 0) {
1308 indices.push_back(prevrow + i - 1);
1309 indices.push_back(prevrow + i);
1310 indices.push_back(thisrow + i - 1);
1311 indices.push_back(prevrow + i);
1312 indices.push_back(thisrow + i);
1313 indices.push_back(thisrow + i - 1);
1314 }
1315
1316 x += size.x / (subdivide_w + 1.0);
1317 }
1318
1319 z += size.y / (subdivide_d + 1.0);
1320 prevrow = thisrow;
1321 thisrow = point;
1322 }
1323
1324 p_arr[RS::ARRAY_VERTEX] = points;
1325 p_arr[RS::ARRAY_NORMAL] = normals;
1326 p_arr[RS::ARRAY_TANGENT] = tangents;
1327 p_arr[RS::ARRAY_TEX_UV] = uvs;
1328 p_arr[RS::ARRAY_INDEX] = indices;
1329}
1330
1331void PlaneMesh::_bind_methods() {
1332 ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaneMesh::set_size);
1333 ClassDB::bind_method(D_METHOD("get_size"), &PlaneMesh::get_size);
1334
1335 ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &PlaneMesh::set_subdivide_width);
1336 ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PlaneMesh::get_subdivide_width);
1337 ClassDB::bind_method(D_METHOD("set_subdivide_depth", "subdivide"), &PlaneMesh::set_subdivide_depth);
1338 ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PlaneMesh::get_subdivide_depth);
1339
1340 ClassDB::bind_method(D_METHOD("set_center_offset", "offset"), &PlaneMesh::set_center_offset);
1341 ClassDB::bind_method(D_METHOD("get_center_offset"), &PlaneMesh::get_center_offset);
1342
1343 ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &PlaneMesh::set_orientation);
1344 ClassDB::bind_method(D_METHOD("get_orientation"), &PlaneMesh::get_orientation);
1345
1346 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
1347 ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
1348 ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
1349 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_center_offset", "get_center_offset");
1350 ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Face X,Face Y,Face Z"), "set_orientation", "get_orientation");
1351
1352 BIND_ENUM_CONSTANT(FACE_X)
1353 BIND_ENUM_CONSTANT(FACE_Y)
1354 BIND_ENUM_CONSTANT(FACE_Z)
1355}
1356
1357void PlaneMesh::set_size(const Size2 &p_size) {
1358 size = p_size;
1359 _update_lightmap_size();
1360 _request_update();
1361}
1362
1363Size2 PlaneMesh::get_size() const {
1364 return size;
1365}
1366
1367void PlaneMesh::set_subdivide_width(const int p_divisions) {
1368 subdivide_w = p_divisions > 0 ? p_divisions : 0;
1369 _request_update();
1370}
1371
1372int PlaneMesh::get_subdivide_width() const {
1373 return subdivide_w;
1374}
1375
1376void PlaneMesh::set_subdivide_depth(const int p_divisions) {
1377 subdivide_d = p_divisions > 0 ? p_divisions : 0;
1378 _request_update();
1379}
1380
1381int PlaneMesh::get_subdivide_depth() const {
1382 return subdivide_d;
1383}
1384
1385void PlaneMesh::set_center_offset(const Vector3 p_offset) {
1386 center_offset = p_offset;
1387 _request_update();
1388}
1389
1390Vector3 PlaneMesh::get_center_offset() const {
1391 return center_offset;
1392}
1393
1394void PlaneMesh::set_orientation(const Orientation p_orientation) {
1395 orientation = p_orientation;
1396 _request_update();
1397}
1398
1399PlaneMesh::Orientation PlaneMesh::get_orientation() const {
1400 return orientation;
1401}
1402
1403PlaneMesh::PlaneMesh() {}
1404
1405/**
1406 PrismMesh
1407*/
1408
1409void PrismMesh::_update_lightmap_size() {
1410 if (get_add_uv2()) {
1411 // size must have changed, update lightmap size hint
1412 Size2i _lightmap_size_hint;
1413 float texel_size = get_lightmap_texel_size();
1414 float padding = get_uv2_padding();
1415
1416 // left_to_right does not effect the surface area of the prism so we ignore that.
1417 // TODO we could combine the two triangles and save some space but we need to re-align the uv1 and adjust the tangent.
1418
1419 float width = (size.x + size.z) / texel_size;
1420 float length = (size.y + size.y + size.z) / texel_size;
1421
1422 _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding;
1423 _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding;
1424
1425 set_lightmap_size_hint(_lightmap_size_hint);
1426 }
1427}
1428
1429void PrismMesh::_create_mesh_array(Array &p_arr) const {
1430 int i, j, prevrow, thisrow, point;
1431 float x, y, z;
1432 float onethird = 1.0 / 3.0;
1433 float twothirds = 2.0 / 3.0;
1434
1435 // Only used if we calculate UV2
1436 bool _add_uv2 = get_add_uv2();
1437 float texel_size = get_lightmap_texel_size();
1438 float _uv2_padding = get_uv2_padding() * texel_size;
1439
1440 float horizontal_total = size.x + size.z + 2.0 * _uv2_padding;
1441 float width_h = size.x / horizontal_total;
1442 float depth_h = size.z / horizontal_total;
1443 float padding_h = _uv2_padding / horizontal_total;
1444
1445 float vertical_total = (size.y + size.y + size.z) + (3.0 * _uv2_padding);
1446 float height_v = size.y / vertical_total;
1447 float depth_v = size.z / vertical_total;
1448 float padding_v = _uv2_padding / vertical_total;
1449
1450 // and start building
1451
1452 Vector3 start_pos = size * -0.5;
1453
1454 // set our bounding box
1455
1456 Vector<Vector3> points;
1457 Vector<Vector3> normals;
1458 Vector<float> tangents;
1459 Vector<Vector2> uvs;
1460 Vector<Vector2> uv2s;
1461 Vector<int> indices;
1462 point = 0;
1463
1464#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
1465 tangents.push_back(m_x); \
1466 tangents.push_back(m_y); \
1467 tangents.push_back(m_z); \
1468 tangents.push_back(m_d);
1469
1470 /* front + back */
1471 y = start_pos.y;
1472 thisrow = point;
1473 prevrow = 0;
1474 for (j = 0; j <= (subdivide_h + 1); j++) {
1475 float scale = (y - start_pos.y) / size.y;
1476 float scaled_size_x = size.x * scale;
1477 float start_x = start_pos.x + (1.0 - scale) * size.x * left_to_right;
1478 float offset_front = (1.0 - scale) * onethird * left_to_right;
1479 float offset_back = (1.0 - scale) * onethird * (1.0 - left_to_right);
1480
1481 float v = j;
1482 float v2 = j / (subdivide_h + 1.0);
1483 v /= (2.0 * (subdivide_h + 1.0));
1484
1485 x = 0.0;
1486 for (i = 0; i <= (subdivide_w + 1); i++) {
1487 float u = i;
1488 float u2 = i / (subdivide_w + 1.0);
1489 u /= (3.0 * (subdivide_w + 1.0));
1490
1491 u *= scale;
1492
1493 /* front */
1494 points.push_back(Vector3(start_x + x, -y, -start_pos.z)); // double negative on the Z!
1495 normals.push_back(Vector3(0.0, 0.0, 1.0));
1496 ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
1497 uvs.push_back(Vector2(offset_front + u, v));
1498 if (_add_uv2) {
1499 uv2s.push_back(Vector2(u2 * scale * width_h, v2 * height_v));
1500 }
1501 point++;
1502
1503 /* back */
1504 points.push_back(Vector3(start_x + scaled_size_x - x, -y, start_pos.z));
1505 normals.push_back(Vector3(0.0, 0.0, -1.0));
1506 ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);
1507 uvs.push_back(Vector2(twothirds + offset_back + u, v));
1508 if (_add_uv2) {
1509 uv2s.push_back(Vector2(u2 * scale * width_h, height_v + padding_v + v2 * height_v));
1510 }
1511 point++;
1512
1513 if (i > 0 && j == 1) {
1514 int i2 = i * 2;
1515
1516 /* front */
1517 indices.push_back(prevrow + i2);
1518 indices.push_back(thisrow + i2);
1519 indices.push_back(thisrow + i2 - 2);
1520
1521 /* back */
1522 indices.push_back(prevrow + i2 + 1);
1523 indices.push_back(thisrow + i2 + 1);
1524 indices.push_back(thisrow + i2 - 1);
1525 } else if (i > 0 && j > 0) {
1526 int i2 = i * 2;
1527
1528 /* front */
1529 indices.push_back(prevrow + i2 - 2);
1530 indices.push_back(prevrow + i2);
1531 indices.push_back(thisrow + i2 - 2);
1532 indices.push_back(prevrow + i2);
1533 indices.push_back(thisrow + i2);
1534 indices.push_back(thisrow + i2 - 2);
1535
1536 /* back */
1537 indices.push_back(prevrow + i2 - 1);
1538 indices.push_back(prevrow + i2 + 1);
1539 indices.push_back(thisrow + i2 - 1);
1540 indices.push_back(prevrow + i2 + 1);
1541 indices.push_back(thisrow + i2 + 1);
1542 indices.push_back(thisrow + i2 - 1);
1543 }
1544
1545 x += scale * size.x / (subdivide_w + 1.0);
1546 }
1547
1548 y += size.y / (subdivide_h + 1.0);
1549 prevrow = thisrow;
1550 thisrow = point;
1551 }
1552
1553 /* left + right */
1554 Vector3 normal_left, normal_right;
1555
1556 normal_left = Vector3(-size.y, size.x * left_to_right, 0.0);
1557 normal_right = Vector3(size.y, size.x * (1.0 - left_to_right), 0.0);
1558 normal_left.normalize();
1559 normal_right.normalize();
1560
1561 y = start_pos.y;
1562 thisrow = point;
1563 prevrow = 0;
1564 for (j = 0; j <= (subdivide_h + 1); j++) {
1565 float v = j;
1566 float v2 = j / (subdivide_h + 1.0);
1567 v /= (2.0 * (subdivide_h + 1.0));
1568
1569 float left, right;
1570 float scale = (y - start_pos.y) / size.y;
1571
1572 left = start_pos.x + (size.x * (1.0 - scale) * left_to_right);
1573 right = left + (size.x * scale);
1574
1575 z = start_pos.z;
1576 for (i = 0; i <= (subdivide_d + 1); i++) {
1577 float u = i;
1578 float u2 = u / (subdivide_d + 1.0);
1579 u /= (3.0 * (subdivide_d + 1.0));
1580
1581 /* right */
1582 points.push_back(Vector3(right, -y, -z));
1583 normals.push_back(normal_right);
1584 ADD_TANGENT(0.0, 0.0, -1.0, 1.0);
1585 uvs.push_back(Vector2(onethird + u, v));
1586 if (_add_uv2) {
1587 uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, v2 * height_v));
1588 }
1589 point++;
1590
1591 /* left */
1592 points.push_back(Vector3(left, -y, z));
1593 normals.push_back(normal_left);
1594 ADD_TANGENT(0.0, 0.0, 1.0, 1.0);
1595 uvs.push_back(Vector2(u, 0.5 + v));
1596 if (_add_uv2) {
1597 uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, height_v + padding_v + v2 * height_v));
1598 }
1599 point++;
1600
1601 if (i > 0 && j > 0) {
1602 int i2 = i * 2;
1603
1604 /* right */
1605 indices.push_back(prevrow + i2 - 2);
1606 indices.push_back(prevrow + i2);
1607 indices.push_back(thisrow + i2 - 2);
1608 indices.push_back(prevrow + i2);
1609 indices.push_back(thisrow + i2);
1610 indices.push_back(thisrow + i2 - 2);
1611
1612 /* left */
1613 indices.push_back(prevrow + i2 - 1);
1614 indices.push_back(prevrow + i2 + 1);
1615 indices.push_back(thisrow + i2 - 1);
1616 indices.push_back(prevrow + i2 + 1);
1617 indices.push_back(thisrow + i2 + 1);
1618 indices.push_back(thisrow + i2 - 1);
1619 }
1620
1621 z += size.z / (subdivide_d + 1.0);
1622 }
1623
1624 y += size.y / (subdivide_h + 1.0);
1625 prevrow = thisrow;
1626 thisrow = point;
1627 }
1628
1629 /* bottom */
1630 z = start_pos.z;
1631 thisrow = point;
1632 prevrow = 0;
1633 for (j = 0; j <= (subdivide_d + 1); j++) {
1634 float v = j;
1635 float v2 = v / (subdivide_d + 1.0);
1636 v /= (2.0 * (subdivide_d + 1.0));
1637
1638 x = start_pos.x;
1639 for (i = 0; i <= (subdivide_w + 1); i++) {
1640 float u = i;
1641 float u2 = u / (subdivide_w + 1.0);
1642 u /= (3.0 * (subdivide_w + 1.0));
1643
1644 /* bottom */
1645 points.push_back(Vector3(x, start_pos.y, -z));
1646 normals.push_back(Vector3(0.0, -1.0, 0.0));
1647 ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
1648 uvs.push_back(Vector2(twothirds + u, 0.5 + v));
1649 if (_add_uv2) {
1650 uv2s.push_back(Vector2(u2 * width_h, 2.0 * (height_v + padding_v) + v2 * depth_v));
1651 }
1652 point++;
1653
1654 if (i > 0 && j > 0) {
1655 /* bottom */
1656 indices.push_back(prevrow + i - 1);
1657 indices.push_back(prevrow + i);
1658 indices.push_back(thisrow + i - 1);
1659 indices.push_back(prevrow + i);
1660 indices.push_back(thisrow + i);
1661 indices.push_back(thisrow + i - 1);
1662 }
1663
1664 x += size.x / (subdivide_w + 1.0);
1665 }
1666
1667 z += size.z / (subdivide_d + 1.0);
1668 prevrow = thisrow;
1669 thisrow = point;
1670 }
1671
1672 p_arr[RS::ARRAY_VERTEX] = points;
1673 p_arr[RS::ARRAY_NORMAL] = normals;
1674 p_arr[RS::ARRAY_TANGENT] = tangents;
1675 p_arr[RS::ARRAY_TEX_UV] = uvs;
1676 if (_add_uv2) {
1677 p_arr[RS::ARRAY_TEX_UV2] = uv2s;
1678 }
1679 p_arr[RS::ARRAY_INDEX] = indices;
1680}
1681
1682void PrismMesh::_bind_methods() {
1683 ClassDB::bind_method(D_METHOD("set_left_to_right", "left_to_right"), &PrismMesh::set_left_to_right);
1684 ClassDB::bind_method(D_METHOD("get_left_to_right"), &PrismMesh::get_left_to_right);
1685
1686 ClassDB::bind_method(D_METHOD("set_size", "size"), &PrismMesh::set_size);
1687 ClassDB::bind_method(D_METHOD("get_size"), &PrismMesh::get_size);
1688
1689 ClassDB::bind_method(D_METHOD("set_subdivide_width", "segments"), &PrismMesh::set_subdivide_width);
1690 ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PrismMesh::get_subdivide_width);
1691 ClassDB::bind_method(D_METHOD("set_subdivide_height", "segments"), &PrismMesh::set_subdivide_height);
1692 ClassDB::bind_method(D_METHOD("get_subdivide_height"), &PrismMesh::get_subdivide_height);
1693 ClassDB::bind_method(D_METHOD("set_subdivide_depth", "segments"), &PrismMesh::set_subdivide_depth);
1694 ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PrismMesh::get_subdivide_depth);
1695
1696 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "left_to_right", PROPERTY_HINT_RANGE, "-2.0,2.0,0.1"), "set_left_to_right", "get_left_to_right");
1697 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
1698 ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");
1699 ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");
1700 ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");
1701}
1702
1703void PrismMesh::set_left_to_right(const float p_left_to_right) {
1704 left_to_right = p_left_to_right;
1705 _request_update();
1706}
1707
1708float PrismMesh::get_left_to_right() const {
1709 return left_to_right;
1710}
1711
1712void PrismMesh::set_size(const Vector3 &p_size) {
1713 size = p_size;
1714 _update_lightmap_size();
1715 _request_update();
1716}
1717
1718Vector3 PrismMesh::get_size() const {
1719 return size;
1720}
1721
1722void PrismMesh::set_subdivide_width(const int p_divisions) {
1723 subdivide_w = p_divisions > 0 ? p_divisions : 0;
1724 _request_update();
1725}
1726
1727int PrismMesh::get_subdivide_width() const {
1728 return subdivide_w;
1729}
1730
1731void PrismMesh::set_subdivide_height(const int p_divisions) {
1732 subdivide_h = p_divisions > 0 ? p_divisions : 0;
1733 _request_update();
1734}
1735
1736int PrismMesh::get_subdivide_height() const {
1737 return subdivide_h;
1738}
1739
1740void PrismMesh::set_subdivide_depth(const int p_divisions) {
1741 subdivide_d = p_divisions > 0 ? p_divisions : 0;
1742 _request_update();
1743}
1744
1745int PrismMesh::get_subdivide_depth() const {
1746 return subdivide_d;
1747}
1748
1749PrismMesh::PrismMesh() {}
1750
1751/**
1752 SphereMesh
1753*/
1754
1755void SphereMesh::_update_lightmap_size() {
1756 if (get_add_uv2()) {
1757 // size must have changed, update lightmap size hint
1758 Size2i _lightmap_size_hint;
1759 float texel_size = get_lightmap_texel_size();
1760 float padding = get_uv2_padding();
1761
1762 float _width = radius * Math_TAU;
1763 _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);
1764 float _height = (is_hemisphere ? 1.0 : 0.5) * height * Math_PI; // note, with hemisphere height is our radius, while with a full sphere it is the diameter..
1765 _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);
1766
1767 set_lightmap_size_hint(_lightmap_size_hint);
1768 }
1769}
1770
1771void SphereMesh::_create_mesh_array(Array &p_arr) const {
1772 bool _add_uv2 = get_add_uv2();
1773 float texel_size = get_lightmap_texel_size();
1774 float _uv2_padding = get_uv2_padding() * texel_size;
1775
1776 create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding);
1777}
1778
1779void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere, bool p_add_uv2, const float p_uv2_padding) {
1780 int i, j, prevrow, thisrow, point;
1781 float x, y, z;
1782
1783 float scale = height * (is_hemisphere ? 1.0 : 0.5);
1784
1785 // Only used if we calculate UV2
1786 float circumference = radius * Math_TAU;
1787 float horizontal_length = circumference + p_uv2_padding;
1788 float center_h = 0.5 * circumference / horizontal_length;
1789
1790 float height_v = scale * Math_PI / ((scale * Math_PI) + p_uv2_padding);
1791
1792 // set our bounding box
1793
1794 Vector<Vector3> points;
1795 Vector<Vector3> normals;
1796 Vector<float> tangents;
1797 Vector<Vector2> uvs;
1798 Vector<Vector2> uv2s;
1799 Vector<int> indices;
1800 point = 0;
1801
1802#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
1803 tangents.push_back(m_x); \
1804 tangents.push_back(m_y); \
1805 tangents.push_back(m_z); \
1806 tangents.push_back(m_d);
1807
1808 thisrow = 0;
1809 prevrow = 0;
1810 for (j = 0; j <= (rings + 1); j++) {
1811 float v = j;
1812 float w;
1813
1814 v /= (rings + 1);
1815 w = sin(Math_PI * v);
1816 y = scale * cos(Math_PI * v);
1817
1818 for (i = 0; i <= radial_segments; i++) {
1819 float u = i;
1820 u /= radial_segments;
1821
1822 x = sin(u * Math_TAU);
1823 z = cos(u * Math_TAU);
1824
1825 if (is_hemisphere && y < 0.0) {
1826 points.push_back(Vector3(x * radius * w, 0.0, z * radius * w));
1827 normals.push_back(Vector3(0.0, -1.0, 0.0));
1828 } else {
1829 Vector3 p = Vector3(x * radius * w, y, z * radius * w);
1830 points.push_back(p);
1831 Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale);
1832 normals.push_back(normal.normalized());
1833 }
1834 ADD_TANGENT(z, 0.0, -x, 1.0)
1835 uvs.push_back(Vector2(u, v));
1836 if (p_add_uv2) {
1837 float w_h = w * 2.0 * center_h;
1838 uv2s.push_back(Vector2(center_h + ((u - 0.5) * w_h), v * height_v));
1839 }
1840 point++;
1841
1842 if (i > 0 && j > 0) {
1843 indices.push_back(prevrow + i - 1);
1844 indices.push_back(prevrow + i);
1845 indices.push_back(thisrow + i - 1);
1846
1847 indices.push_back(prevrow + i);
1848 indices.push_back(thisrow + i);
1849 indices.push_back(thisrow + i - 1);
1850 }
1851 }
1852
1853 prevrow = thisrow;
1854 thisrow = point;
1855 }
1856
1857 p_arr[RS::ARRAY_VERTEX] = points;
1858 p_arr[RS::ARRAY_NORMAL] = normals;
1859 p_arr[RS::ARRAY_TANGENT] = tangents;
1860 p_arr[RS::ARRAY_TEX_UV] = uvs;
1861 if (p_add_uv2) {
1862 p_arr[RS::ARRAY_TEX_UV2] = uv2s;
1863 }
1864 p_arr[RS::ARRAY_INDEX] = indices;
1865}
1866
1867void SphereMesh::_bind_methods() {
1868 ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereMesh::set_radius);
1869 ClassDB::bind_method(D_METHOD("get_radius"), &SphereMesh::get_radius);
1870 ClassDB::bind_method(D_METHOD("set_height", "height"), &SphereMesh::set_height);
1871 ClassDB::bind_method(D_METHOD("get_height"), &SphereMesh::get_height);
1872
1873 ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &SphereMesh::set_radial_segments);
1874 ClassDB::bind_method(D_METHOD("get_radial_segments"), &SphereMesh::get_radial_segments);
1875 ClassDB::bind_method(D_METHOD("set_rings", "rings"), &SphereMesh::set_rings);
1876 ClassDB::bind_method(D_METHOD("get_rings"), &SphereMesh::get_rings);
1877
1878 ClassDB::bind_method(D_METHOD("set_is_hemisphere", "is_hemisphere"), &SphereMesh::set_is_hemisphere);
1879 ClassDB::bind_method(D_METHOD("get_is_hemisphere"), &SphereMesh::get_is_hemisphere);
1880
1881 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");
1882 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height");
1883 ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
1884 ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
1885 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_hemisphere"), "set_is_hemisphere", "get_is_hemisphere");
1886}
1887
1888void SphereMesh::set_radius(const float p_radius) {
1889 radius = p_radius;
1890 _update_lightmap_size();
1891 _request_update();
1892}
1893
1894float SphereMesh::get_radius() const {
1895 return radius;
1896}
1897
1898void SphereMesh::set_height(const float p_height) {
1899 height = p_height;
1900 _update_lightmap_size();
1901 _request_update();
1902}
1903
1904float SphereMesh::get_height() const {
1905 return height;
1906}
1907
1908void SphereMesh::set_radial_segments(const int p_radial_segments) {
1909 radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
1910 _request_update();
1911}
1912
1913int SphereMesh::get_radial_segments() const {
1914 return radial_segments;
1915}
1916
1917void SphereMesh::set_rings(const int p_rings) {
1918 rings = p_rings > 1 ? p_rings : 1;
1919 _request_update();
1920}
1921
1922int SphereMesh::get_rings() const {
1923 return rings;
1924}
1925
1926void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) {
1927 is_hemisphere = p_is_hemisphere;
1928 _update_lightmap_size();
1929 _request_update();
1930}
1931
1932bool SphereMesh::get_is_hemisphere() const {
1933 return is_hemisphere;
1934}
1935
1936SphereMesh::SphereMesh() {}
1937
1938/**
1939 TorusMesh
1940*/
1941
1942void TorusMesh::_update_lightmap_size() {
1943 if (get_add_uv2()) {
1944 // size must have changed, update lightmap size hint
1945 Size2i _lightmap_size_hint;
1946 float texel_size = get_lightmap_texel_size();
1947 float padding = get_uv2_padding();
1948
1949 float min_radius = inner_radius;
1950 float max_radius = outer_radius;
1951
1952 if (min_radius > max_radius) {
1953 SWAP(min_radius, max_radius);
1954 }
1955
1956 float radius = (max_radius - min_radius) * 0.5;
1957
1958 float _width = max_radius * Math_TAU;
1959 _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);
1960 float _height = radius * Math_TAU;
1961 _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);
1962
1963 set_lightmap_size_hint(_lightmap_size_hint);
1964 }
1965}
1966
1967void TorusMesh::_create_mesh_array(Array &p_arr) const {
1968 // set our bounding box
1969
1970 Vector<Vector3> points;
1971 Vector<Vector3> normals;
1972 Vector<float> tangents;
1973 Vector<Vector2> uvs;
1974 Vector<Vector2> uv2s;
1975 Vector<int> indices;
1976
1977#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
1978 tangents.push_back(m_x); \
1979 tangents.push_back(m_y); \
1980 tangents.push_back(m_z); \
1981 tangents.push_back(m_d);
1982
1983 ERR_FAIL_COND_MSG(inner_radius == outer_radius, "Inner radius and outer radius cannot be the same.");
1984
1985 float min_radius = inner_radius;
1986 float max_radius = outer_radius;
1987
1988 if (min_radius > max_radius) {
1989 SWAP(min_radius, max_radius);
1990 }
1991
1992 float radius = (max_radius - min_radius) * 0.5;
1993
1994 // Only used if we calculate UV2
1995 bool _add_uv2 = get_add_uv2();
1996 float texel_size = get_lightmap_texel_size();
1997 float _uv2_padding = get_uv2_padding() * texel_size;
1998
1999 float horizontal_total = max_radius * Math_TAU + _uv2_padding;
2000 float max_h = max_radius * Math_TAU / horizontal_total;
2001 float delta_h = (max_radius - min_radius) * Math_TAU / horizontal_total;
2002
2003 float height_v = radius * Math_TAU / (radius * Math_TAU + _uv2_padding);
2004
2005 for (int i = 0; i <= rings; i++) {
2006 int prevrow = (i - 1) * (ring_segments + 1);
2007 int thisrow = i * (ring_segments + 1);
2008 float inci = float(i) / rings;
2009 float angi = inci * Math_TAU;
2010
2011 Vector2 normali = Vector2(-Math::sin(angi), -Math::cos(angi));
2012
2013 for (int j = 0; j <= ring_segments; j++) {
2014 float incj = float(j) / ring_segments;
2015 float angj = incj * Math_TAU;
2016
2017 Vector2 normalj = Vector2(-Math::cos(angj), Math::sin(angj));
2018 Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0);
2019
2020 float offset_h = 0.5 * (1.0 - normalj.x) * delta_h;
2021 float adj_h = max_h - offset_h;
2022 offset_h *= 0.5;
2023
2024 points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x));
2025 normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x));
2026 ADD_TANGENT(-Math::cos(angi), 0.0, Math::sin(angi), 1.0);
2027 uvs.push_back(Vector2(inci, incj));
2028 if (_add_uv2) {
2029 uv2s.push_back(Vector2(offset_h + inci * adj_h, incj * height_v));
2030 }
2031
2032 if (i > 0 && j > 0) {
2033 indices.push_back(thisrow + j - 1);
2034 indices.push_back(prevrow + j);
2035 indices.push_back(prevrow + j - 1);
2036
2037 indices.push_back(thisrow + j - 1);
2038 indices.push_back(thisrow + j);
2039 indices.push_back(prevrow + j);
2040 }
2041 }
2042 }
2043
2044 p_arr[RS::ARRAY_VERTEX] = points;
2045 p_arr[RS::ARRAY_NORMAL] = normals;
2046 p_arr[RS::ARRAY_TANGENT] = tangents;
2047 p_arr[RS::ARRAY_TEX_UV] = uvs;
2048 if (_add_uv2) {
2049 p_arr[RS::ARRAY_TEX_UV2] = uv2s;
2050 }
2051 p_arr[RS::ARRAY_INDEX] = indices;
2052}
2053
2054void TorusMesh::_bind_methods() {
2055 ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &TorusMesh::set_inner_radius);
2056 ClassDB::bind_method(D_METHOD("get_inner_radius"), &TorusMesh::get_inner_radius);
2057
2058 ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &TorusMesh::set_outer_radius);
2059 ClassDB::bind_method(D_METHOD("get_outer_radius"), &TorusMesh::get_outer_radius);
2060
2061 ClassDB::bind_method(D_METHOD("set_rings", "rings"), &TorusMesh::set_rings);
2062 ClassDB::bind_method(D_METHOD("get_rings"), &TorusMesh::get_rings);
2063
2064 ClassDB::bind_method(D_METHOD("set_ring_segments", "rings"), &TorusMesh::set_ring_segments);
2065 ClassDB::bind_method(D_METHOD("get_ring_segments"), &TorusMesh::get_ring_segments);
2066
2067 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");
2068 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius");
2069 ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "3,128,1,or_greater"), "set_rings", "get_rings");
2070 ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_segments", PROPERTY_HINT_RANGE, "3,64,1,or_greater"), "set_ring_segments", "get_ring_segments");
2071}
2072
2073void TorusMesh::set_inner_radius(const float p_inner_radius) {
2074 inner_radius = p_inner_radius;
2075 _request_update();
2076}
2077
2078float TorusMesh::get_inner_radius() const {
2079 return inner_radius;
2080}
2081
2082void TorusMesh::set_outer_radius(const float p_outer_radius) {
2083 outer_radius = p_outer_radius;
2084 _request_update();
2085}
2086
2087float TorusMesh::get_outer_radius() const {
2088 return outer_radius;
2089}
2090
2091void TorusMesh::set_rings(const int p_rings) {
2092 ERR_FAIL_COND(p_rings < 3);
2093 rings = p_rings;
2094 _request_update();
2095}
2096
2097int TorusMesh::get_rings() const {
2098 return rings;
2099}
2100
2101void TorusMesh::set_ring_segments(const int p_ring_segments) {
2102 ERR_FAIL_COND(p_ring_segments < 3);
2103 ring_segments = p_ring_segments;
2104 _request_update();
2105}
2106
2107int TorusMesh::get_ring_segments() const {
2108 return ring_segments;
2109}
2110
2111TorusMesh::TorusMesh() {}
2112
2113/**
2114 PointMesh
2115*/
2116
2117void PointMesh::_create_mesh_array(Array &p_arr) const {
2118 Vector<Vector3> faces;
2119 faces.resize(1);
2120 faces.set(0, Vector3(0.0, 0.0, 0.0));
2121
2122 p_arr[RS::ARRAY_VERTEX] = faces;
2123}
2124
2125PointMesh::PointMesh() {
2126 primitive_type = PRIMITIVE_POINTS;
2127}
2128// TUBE TRAIL
2129
2130void TubeTrailMesh::set_radius(const float p_radius) {
2131 radius = p_radius;
2132 _request_update();
2133}
2134float TubeTrailMesh::get_radius() const {
2135 return radius;
2136}
2137
2138void TubeTrailMesh::set_radial_steps(const int p_radial_steps) {
2139 ERR_FAIL_COND(p_radial_steps < 3 || p_radial_steps > 128);
2140 radial_steps = p_radial_steps;
2141 _request_update();
2142}
2143int TubeTrailMesh::get_radial_steps() const {
2144 return radial_steps;
2145}
2146
2147void TubeTrailMesh::set_sections(const int p_sections) {
2148 ERR_FAIL_COND(p_sections < 2 || p_sections > 128);
2149 sections = p_sections;
2150 _request_update();
2151}
2152int TubeTrailMesh::get_sections() const {
2153 return sections;
2154}
2155
2156void TubeTrailMesh::set_section_length(float p_section_length) {
2157 section_length = p_section_length;
2158 _request_update();
2159}
2160float TubeTrailMesh::get_section_length() const {
2161 return section_length;
2162}
2163
2164void TubeTrailMesh::set_section_rings(const int p_section_rings) {
2165 ERR_FAIL_COND(p_section_rings < 1 || p_section_rings > 1024);
2166 section_rings = p_section_rings;
2167 _request_update();
2168}
2169int TubeTrailMesh::get_section_rings() const {
2170 return section_rings;
2171}
2172
2173void TubeTrailMesh::set_cap_top(bool p_cap_top) {
2174 cap_top = p_cap_top;
2175 _request_update();
2176}
2177
2178bool TubeTrailMesh::is_cap_top() const {
2179 return cap_top;
2180}
2181
2182void TubeTrailMesh::set_cap_bottom(bool p_cap_bottom) {
2183 cap_bottom = p_cap_bottom;
2184 _request_update();
2185}
2186
2187bool TubeTrailMesh::is_cap_bottom() const {
2188 return cap_bottom;
2189}
2190
2191void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) {
2192 if (curve == p_curve) {
2193 return;
2194 }
2195 if (curve.is_valid()) {
2196 curve->disconnect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed));
2197 }
2198 curve = p_curve;
2199 if (curve.is_valid()) {
2200 curve->connect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed));
2201 }
2202 _request_update();
2203}
2204Ref<Curve> TubeTrailMesh::get_curve() const {
2205 return curve;
2206}
2207
2208void TubeTrailMesh::_curve_changed() {
2209 _request_update();
2210}
2211int TubeTrailMesh::get_builtin_bind_pose_count() const {
2212 return sections + 1;
2213}
2214
2215Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const {
2216 float depth = section_length * sections;
2217
2218 Transform3D xform;
2219 xform.origin.y = depth / 2.0 - section_length * float(p_index);
2220 xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y
2221
2222 return xform;
2223}
2224
2225void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {
2226 // Seeing use case for TubeTrailMesh, no need to do anything more then default UV2 calculation
2227
2228 PackedVector3Array points;
2229 PackedVector3Array normals;
2230 PackedFloat32Array tangents;
2231 PackedVector2Array uvs;
2232 PackedInt32Array bone_indices;
2233 PackedFloat32Array bone_weights;
2234 PackedInt32Array indices;
2235
2236 int point = 0;
2237
2238#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
2239 tangents.push_back(m_x); \
2240 tangents.push_back(m_y); \
2241 tangents.push_back(m_z); \
2242 tangents.push_back(m_d);
2243
2244 int thisrow = 0;
2245 int prevrow = 0;
2246
2247 int total_rings = section_rings * sections;
2248 float depth = section_length * sections;
2249
2250 for (int j = 0; j <= total_rings; j++) {
2251 float v = j;
2252 v /= total_rings;
2253
2254 float y = depth * v;
2255 y = (depth * 0.5) - y;
2256
2257 int bone = j / section_rings;
2258 float blend = 1.0 - float(j % section_rings) / float(section_rings);
2259
2260 for (int i = 0; i <= radial_steps; i++) {
2261 float u = i;
2262 u /= radial_steps;
2263
2264 float r = radius;
2265 if (curve.is_valid() && curve->get_point_count() > 0) {
2266 r *= curve->sample_baked(v);
2267 }
2268 float x = sin(u * Math_TAU);
2269 float z = cos(u * Math_TAU);
2270
2271 Vector3 p = Vector3(x * r, y, z * r);
2272 points.push_back(p);
2273 normals.push_back(Vector3(x, 0, z));
2274 ADD_TANGENT(z, 0.0, -x, 1.0)
2275 uvs.push_back(Vector2(u, v * 0.5));
2276 point++;
2277 {
2278 bone_indices.push_back(bone);
2279 bone_indices.push_back(MIN(sections, bone + 1));
2280 bone_indices.push_back(0);
2281 bone_indices.push_back(0);
2282
2283 bone_weights.push_back(blend);
2284 bone_weights.push_back(1.0 - blend);
2285 bone_weights.push_back(0);
2286 bone_weights.push_back(0);
2287 }
2288
2289 if (i > 0 && j > 0) {
2290 indices.push_back(prevrow + i - 1);
2291 indices.push_back(prevrow + i);
2292 indices.push_back(thisrow + i - 1);
2293
2294 indices.push_back(prevrow + i);
2295 indices.push_back(thisrow + i);
2296 indices.push_back(thisrow + i - 1);
2297 }
2298 }
2299
2300 prevrow = thisrow;
2301 thisrow = point;
2302 }
2303
2304 if (cap_top) {
2305 // add top
2306 float scale_pos = 1.0;
2307 if (curve.is_valid() && curve->get_point_count() > 0) {
2308 scale_pos = curve->sample_baked(0);
2309 }
2310
2311 if (scale_pos > CMP_EPSILON) {
2312 float y = depth * 0.5;
2313
2314 thisrow = point;
2315 points.push_back(Vector3(0.0, y, 0));
2316 normals.push_back(Vector3(0.0, 1.0, 0.0));
2317 ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
2318 uvs.push_back(Vector2(0.25, 0.75));
2319 point++;
2320
2321 bone_indices.push_back(0);
2322 bone_indices.push_back(0);
2323 bone_indices.push_back(0);
2324 bone_indices.push_back(0);
2325
2326 bone_weights.push_back(1.0);
2327 bone_weights.push_back(0);
2328 bone_weights.push_back(0);
2329 bone_weights.push_back(0);
2330
2331 float rm = radius * scale_pos;
2332
2333 for (int i = 0; i <= radial_steps; i++) {
2334 float r = i;
2335 r /= radial_steps;
2336
2337 float x = sin(r * Math_TAU);
2338 float z = cos(r * Math_TAU);
2339
2340 float u = ((x + 1.0) * 0.25);
2341 float v = 0.5 + ((z + 1.0) * 0.25);
2342
2343 Vector3 p = Vector3(x * rm, y, z * rm);
2344 points.push_back(p);
2345 normals.push_back(Vector3(0.0, 1.0, 0.0));
2346 ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
2347 uvs.push_back(Vector2(u, v));
2348 point++;
2349
2350 bone_indices.push_back(0);
2351 bone_indices.push_back(0);
2352 bone_indices.push_back(0);
2353 bone_indices.push_back(0);
2354
2355 bone_weights.push_back(1.0);
2356 bone_weights.push_back(0);
2357 bone_weights.push_back(0);
2358 bone_weights.push_back(0);
2359
2360 if (i > 0) {
2361 indices.push_back(thisrow);
2362 indices.push_back(point - 1);
2363 indices.push_back(point - 2);
2364 }
2365 }
2366 }
2367 }
2368
2369 if (cap_bottom) {
2370 float scale_neg = 1.0;
2371 if (curve.is_valid() && curve->get_point_count() > 0) {
2372 scale_neg = curve->sample_baked(1.0);
2373 }
2374
2375 if (scale_neg > CMP_EPSILON) {
2376 // add bottom
2377 float y = depth * -0.5;
2378
2379 thisrow = point;
2380 points.push_back(Vector3(0.0, y, 0.0));
2381 normals.push_back(Vector3(0.0, -1.0, 0.0));
2382 ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
2383 uvs.push_back(Vector2(0.75, 0.75));
2384 point++;
2385
2386 bone_indices.push_back(sections);
2387 bone_indices.push_back(0);
2388 bone_indices.push_back(0);
2389 bone_indices.push_back(0);
2390
2391 bone_weights.push_back(1.0);
2392 bone_weights.push_back(0);
2393 bone_weights.push_back(0);
2394 bone_weights.push_back(0);
2395
2396 float rm = radius * scale_neg;
2397
2398 for (int i = 0; i <= radial_steps; i++) {
2399 float r = i;
2400 r /= radial_steps;
2401
2402 float x = sin(r * Math_TAU);
2403 float z = cos(r * Math_TAU);
2404
2405 float u = 0.5 + ((x + 1.0) * 0.25);
2406 float v = 1.0 - ((z + 1.0) * 0.25);
2407
2408 Vector3 p = Vector3(x * rm, y, z * rm);
2409 points.push_back(p);
2410 normals.push_back(Vector3(0.0, -1.0, 0.0));
2411 ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
2412 uvs.push_back(Vector2(u, v));
2413 point++;
2414
2415 bone_indices.push_back(sections);
2416 bone_indices.push_back(0);
2417 bone_indices.push_back(0);
2418 bone_indices.push_back(0);
2419
2420 bone_weights.push_back(1.0);
2421 bone_weights.push_back(0);
2422 bone_weights.push_back(0);
2423 bone_weights.push_back(0);
2424
2425 if (i > 0) {
2426 indices.push_back(thisrow);
2427 indices.push_back(point - 2);
2428 indices.push_back(point - 1);
2429 }
2430 }
2431 }
2432 }
2433
2434 p_arr[RS::ARRAY_VERTEX] = points;
2435 p_arr[RS::ARRAY_NORMAL] = normals;
2436 p_arr[RS::ARRAY_TANGENT] = tangents;
2437 p_arr[RS::ARRAY_TEX_UV] = uvs;
2438 p_arr[RS::ARRAY_BONES] = bone_indices;
2439 p_arr[RS::ARRAY_WEIGHTS] = bone_weights;
2440 p_arr[RS::ARRAY_INDEX] = indices;
2441}
2442
2443void TubeTrailMesh::_bind_methods() {
2444 ClassDB::bind_method(D_METHOD("set_radius", "radius"), &TubeTrailMesh::set_radius);
2445 ClassDB::bind_method(D_METHOD("get_radius"), &TubeTrailMesh::get_radius);
2446
2447 ClassDB::bind_method(D_METHOD("set_radial_steps", "radial_steps"), &TubeTrailMesh::set_radial_steps);
2448 ClassDB::bind_method(D_METHOD("get_radial_steps"), &TubeTrailMesh::get_radial_steps);
2449
2450 ClassDB::bind_method(D_METHOD("set_sections", "sections"), &TubeTrailMesh::set_sections);
2451 ClassDB::bind_method(D_METHOD("get_sections"), &TubeTrailMesh::get_sections);
2452
2453 ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &TubeTrailMesh::set_section_length);
2454 ClassDB::bind_method(D_METHOD("get_section_length"), &TubeTrailMesh::get_section_length);
2455
2456 ClassDB::bind_method(D_METHOD("set_section_rings", "section_rings"), &TubeTrailMesh::set_section_rings);
2457 ClassDB::bind_method(D_METHOD("get_section_rings"), &TubeTrailMesh::get_section_rings);
2458
2459 ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &TubeTrailMesh::set_cap_top);
2460 ClassDB::bind_method(D_METHOD("is_cap_top"), &TubeTrailMesh::is_cap_top);
2461
2462 ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &TubeTrailMesh::set_cap_bottom);
2463 ClassDB::bind_method(D_METHOD("is_cap_bottom"), &TubeTrailMesh::is_cap_bottom);
2464
2465 ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve);
2466 ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve);
2467
2468 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");
2469
2470 ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_steps", PROPERTY_HINT_RANGE, "3,128,1"), "set_radial_steps", "get_radial_steps");
2471 ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections");
2472
2473 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater,suffix:m"), "set_section_length", "get_section_length");
2474
2475 ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings");
2476
2477 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top");
2478 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom");
2479
2480 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
2481}
2482
2483TubeTrailMesh::TubeTrailMesh() {
2484}
2485
2486// RIBBON TRAIL
2487
2488void RibbonTrailMesh::set_shape(Shape p_shape) {
2489 shape = p_shape;
2490 _request_update();
2491}
2492RibbonTrailMesh::Shape RibbonTrailMesh::get_shape() const {
2493 return shape;
2494}
2495
2496void RibbonTrailMesh::set_size(const float p_size) {
2497 size = p_size;
2498 _request_update();
2499}
2500float RibbonTrailMesh::get_size() const {
2501 return size;
2502}
2503
2504void RibbonTrailMesh::set_sections(const int p_sections) {
2505 ERR_FAIL_COND(p_sections < 2 || p_sections > 128);
2506 sections = p_sections;
2507 _request_update();
2508}
2509int RibbonTrailMesh::get_sections() const {
2510 return sections;
2511}
2512
2513void RibbonTrailMesh::set_section_length(float p_section_length) {
2514 section_length = p_section_length;
2515 _request_update();
2516}
2517float RibbonTrailMesh::get_section_length() const {
2518 return section_length;
2519}
2520
2521void RibbonTrailMesh::set_section_segments(const int p_section_segments) {
2522 ERR_FAIL_COND(p_section_segments < 1 || p_section_segments > 1024);
2523 section_segments = p_section_segments;
2524 _request_update();
2525}
2526int RibbonTrailMesh::get_section_segments() const {
2527 return section_segments;
2528}
2529
2530void RibbonTrailMesh::set_curve(const Ref<Curve> &p_curve) {
2531 if (curve == p_curve) {
2532 return;
2533 }
2534 if (curve.is_valid()) {
2535 curve->disconnect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed));
2536 }
2537 curve = p_curve;
2538 if (curve.is_valid()) {
2539 curve->connect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed));
2540 }
2541 _request_update();
2542}
2543Ref<Curve> RibbonTrailMesh::get_curve() const {
2544 return curve;
2545}
2546
2547void RibbonTrailMesh::_curve_changed() {
2548 _request_update();
2549}
2550int RibbonTrailMesh::get_builtin_bind_pose_count() const {
2551 return sections + 1;
2552}
2553
2554Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const {
2555 float depth = section_length * sections;
2556
2557 Transform3D xform;
2558 xform.origin.y = depth / 2.0 - section_length * float(p_index);
2559 xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y
2560
2561 return xform;
2562}
2563
2564void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const {
2565 // Seeing use case of ribbon trail mesh, no need to implement special UV2 calculation
2566
2567 PackedVector3Array points;
2568 PackedVector3Array normals;
2569 PackedFloat32Array tangents;
2570 PackedVector2Array uvs;
2571 PackedInt32Array bone_indices;
2572 PackedFloat32Array bone_weights;
2573 PackedInt32Array indices;
2574
2575#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
2576 tangents.push_back(m_x); \
2577 tangents.push_back(m_y); \
2578 tangents.push_back(m_z); \
2579 tangents.push_back(m_d);
2580
2581 int total_segments = section_segments * sections;
2582 float depth = section_length * sections;
2583
2584 for (int j = 0; j <= total_segments; j++) {
2585 float v = j;
2586 v /= total_segments;
2587
2588 float y = depth * v;
2589 y = (depth * 0.5) - y;
2590
2591 int bone = j / section_segments;
2592 float blend = 1.0 - float(j % section_segments) / float(section_segments);
2593
2594 float s = size;
2595
2596 if (curve.is_valid() && curve->get_point_count() > 0) {
2597 s *= curve->sample_baked(v);
2598 }
2599
2600 points.push_back(Vector3(-s * 0.5, y, 0));
2601 points.push_back(Vector3(+s * 0.5, y, 0));
2602 if (shape == SHAPE_CROSS) {
2603 points.push_back(Vector3(0, y, -s * 0.5));
2604 points.push_back(Vector3(0, y, +s * 0.5));
2605 }
2606
2607 normals.push_back(Vector3(0, 0, 1));
2608 normals.push_back(Vector3(0, 0, 1));
2609 if (shape == SHAPE_CROSS) {
2610 normals.push_back(Vector3(1, 0, 0));
2611 normals.push_back(Vector3(1, 0, 0));
2612 }
2613
2614 uvs.push_back(Vector2(0, v));
2615 uvs.push_back(Vector2(1, v));
2616 if (shape == SHAPE_CROSS) {
2617 uvs.push_back(Vector2(0, v));
2618 uvs.push_back(Vector2(1, v));
2619 }
2620
2621 ADD_TANGENT(0.0, 1.0, 0.0, 1.0)
2622 ADD_TANGENT(0.0, 1.0, 0.0, 1.0)
2623 if (shape == SHAPE_CROSS) {
2624 ADD_TANGENT(0.0, 1.0, 0.0, 1.0)
2625 ADD_TANGENT(0.0, 1.0, 0.0, 1.0)
2626 }
2627
2628 for (int i = 0; i < (shape == SHAPE_CROSS ? 4 : 2); i++) {
2629 bone_indices.push_back(bone);
2630 bone_indices.push_back(MIN(sections, bone + 1));
2631 bone_indices.push_back(0);
2632 bone_indices.push_back(0);
2633
2634 bone_weights.push_back(blend);
2635 bone_weights.push_back(1.0 - blend);
2636 bone_weights.push_back(0);
2637 bone_weights.push_back(0);
2638 }
2639
2640 if (j > 0) {
2641 if (shape == SHAPE_CROSS) {
2642 int base = j * 4 - 4;
2643 indices.push_back(base + 0);
2644 indices.push_back(base + 1);
2645 indices.push_back(base + 4);
2646
2647 indices.push_back(base + 1);
2648 indices.push_back(base + 5);
2649 indices.push_back(base + 4);
2650
2651 indices.push_back(base + 2);
2652 indices.push_back(base + 3);
2653 indices.push_back(base + 6);
2654
2655 indices.push_back(base + 3);
2656 indices.push_back(base + 7);
2657 indices.push_back(base + 6);
2658 } else {
2659 int base = j * 2 - 2;
2660 indices.push_back(base + 0);
2661 indices.push_back(base + 1);
2662 indices.push_back(base + 2);
2663
2664 indices.push_back(base + 1);
2665 indices.push_back(base + 3);
2666 indices.push_back(base + 2);
2667 }
2668 }
2669 }
2670
2671 p_arr[RS::ARRAY_VERTEX] = points;
2672 p_arr[RS::ARRAY_NORMAL] = normals;
2673 p_arr[RS::ARRAY_TANGENT] = tangents;
2674 p_arr[RS::ARRAY_TEX_UV] = uvs;
2675 p_arr[RS::ARRAY_BONES] = bone_indices;
2676 p_arr[RS::ARRAY_WEIGHTS] = bone_weights;
2677 p_arr[RS::ARRAY_INDEX] = indices;
2678}
2679
2680void RibbonTrailMesh::_bind_methods() {
2681 ClassDB::bind_method(D_METHOD("set_size", "size"), &RibbonTrailMesh::set_size);
2682 ClassDB::bind_method(D_METHOD("get_size"), &RibbonTrailMesh::get_size);
2683
2684 ClassDB::bind_method(D_METHOD("set_sections", "sections"), &RibbonTrailMesh::set_sections);
2685 ClassDB::bind_method(D_METHOD("get_sections"), &RibbonTrailMesh::get_sections);
2686
2687 ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &RibbonTrailMesh::set_section_length);
2688 ClassDB::bind_method(D_METHOD("get_section_length"), &RibbonTrailMesh::get_section_length);
2689
2690 ClassDB::bind_method(D_METHOD("set_section_segments", "section_segments"), &RibbonTrailMesh::set_section_segments);
2691 ClassDB::bind_method(D_METHOD("get_section_segments"), &RibbonTrailMesh::get_section_segments);
2692
2693 ClassDB::bind_method(D_METHOD("set_curve", "curve"), &RibbonTrailMesh::set_curve);
2694 ClassDB::bind_method(D_METHOD("get_curve"), &RibbonTrailMesh::get_curve);
2695
2696 ClassDB::bind_method(D_METHOD("set_shape", "shape"), &RibbonTrailMesh::set_shape);
2697 ClassDB::bind_method(D_METHOD("get_shape"), &RibbonTrailMesh::get_shape);
2698
2699 ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Flat,Cross"), "set_shape", "get_shape");
2700 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_size", "get_size");
2701 ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections");
2702 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater,suffix:m"), "set_section_length", "get_section_length");
2703 ADD_PROPERTY(PropertyInfo(Variant::INT, "section_segments", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_segments", "get_section_segments");
2704 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
2705
2706 BIND_ENUM_CONSTANT(SHAPE_FLAT)
2707 BIND_ENUM_CONSTANT(SHAPE_CROSS)
2708}
2709
2710RibbonTrailMesh::RibbonTrailMesh() {
2711}
2712
2713/*************************************************************************/
2714/* TextMesh */
2715/*************************************************************************/
2716
2717void TextMesh::_generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph &p_gl) const {
2718 if (cache.has(p_key)) {
2719 return;
2720 }
2721
2722 GlyphMeshData &gl_data = cache[p_key];
2723
2724 Dictionary d = TS->font_get_glyph_contours(p_gl.font_rid, p_gl.font_size, p_gl.index);
2725 Vector2 origin = Vector2(p_gl.x_off, p_gl.y_off) * pixel_size;
2726
2727 PackedVector3Array points = d["points"];
2728 PackedInt32Array contours = d["contours"];
2729 bool orientation = d["orientation"];
2730
2731 if (points.size() < 3 || contours.size() < 1) {
2732 return; // No full contours, only glyph control points (or nothing), ignore.
2733 }
2734
2735 // Approximate Bezier curves as polygons.
2736 // See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html, for more info.
2737 for (int i = 0; i < contours.size(); i++) {
2738 int32_t start = (i == 0) ? 0 : (contours[i - 1] + 1);
2739 int32_t end = contours[i];
2740 Vector<ContourPoint> polygon;
2741
2742 for (int32_t j = start; j <= end; j++) {
2743 if (points[j].z == TextServer::CONTOUR_CURVE_TAG_ON) {
2744 // Point on the curve.
2745 Vector2 p = Vector2(points[j].x, points[j].y) * pixel_size + origin;
2746 polygon.push_back(ContourPoint(p, true));
2747 } else if (points[j].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {
2748 // Conic Bezier arc.
2749 int32_t next = (j == end) ? start : (j + 1);
2750 int32_t prev = (j == start) ? end : (j - 1);
2751 Vector2 p0;
2752 Vector2 p1 = Vector2(points[j].x, points[j].y);
2753 Vector2 p2;
2754
2755 // For successive conic OFF points add a virtual ON point in the middle.
2756 if (points[prev].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {
2757 p0 = (Vector2(points[prev].x, points[prev].y) + Vector2(points[j].x, points[j].y)) / 2.0;
2758 } else if (points[prev].z == TextServer::CONTOUR_CURVE_TAG_ON) {
2759 p0 = Vector2(points[prev].x, points[prev].y);
2760 } else {
2761 ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j));
2762 }
2763 if (points[next].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {
2764 p2 = (Vector2(points[j].x, points[j].y) + Vector2(points[next].x, points[next].y)) / 2.0;
2765 } else if (points[next].z == TextServer::CONTOUR_CURVE_TAG_ON) {
2766 p2 = Vector2(points[next].x, points[next].y);
2767 } else {
2768 ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j));
2769 }
2770
2771 real_t step = CLAMP(curve_step / (p0 - p2).length(), 0.01, 0.5);
2772 real_t t = step;
2773 while (t < 1.0) {
2774 real_t omt = (1.0 - t);
2775 real_t omt2 = omt * omt;
2776 real_t t2 = t * t;
2777
2778 Vector2 point = p1 + omt2 * (p0 - p1) + t2 * (p2 - p1);
2779 Vector2 p = point * pixel_size + origin;
2780 polygon.push_back(ContourPoint(p, false));
2781 t += step;
2782 }
2783 } else if (points[j].z == TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC) {
2784 // Cubic Bezier arc.
2785 int32_t cur = j;
2786 int32_t next1 = (j == end) ? start : (j + 1);
2787 int32_t next2 = (next1 == end) ? start : (next1 + 1);
2788 int32_t prev = (j == start) ? end : (j - 1);
2789
2790 // There must be exactly two OFF points and two ON points for each cubic arc.
2791 if (points[prev].z != TextServer::CONTOUR_CURVE_TAG_ON) {
2792 cur = (cur == 0) ? end : cur - 1;
2793 next1 = (next1 == 0) ? end : next1 - 1;
2794 next2 = (next2 == 0) ? end : next2 - 1;
2795 prev = (prev == 0) ? end : prev - 1;
2796 } else {
2797 j++;
2798 }
2799 ERR_FAIL_COND_MSG(points[prev].z != TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, prev));
2800 ERR_FAIL_COND_MSG(points[cur].z != TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC, vformat("Invalid cubic arc point sequence at %d:%d", i, cur));
2801 ERR_FAIL_COND_MSG(points[next1].z != TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC, vformat("Invalid cubic arc point sequence at %d:%d", i, next1));
2802 ERR_FAIL_COND_MSG(points[next2].z != TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, next2));
2803
2804 Vector2 p0 = Vector2(points[prev].x, points[prev].y);
2805 Vector2 p1 = Vector2(points[cur].x, points[cur].y);
2806 Vector2 p2 = Vector2(points[next1].x, points[next1].y);
2807 Vector2 p3 = Vector2(points[next2].x, points[next2].y);
2808
2809 real_t step = CLAMP(curve_step / (p0 - p3).length(), 0.01, 0.5);
2810 real_t t = step;
2811 while (t < 1.0) {
2812 Vector2 point = p0.bezier_interpolate(p1, p2, p3, t);
2813 Vector2 p = point * pixel_size + origin;
2814 polygon.push_back(ContourPoint(p, false));
2815 t += step;
2816 }
2817 } else {
2818 ERR_FAIL_MSG(vformat("Unknown point tag at %d:%d", i, j));
2819 }
2820 }
2821
2822 if (polygon.size() < 3) {
2823 continue; // Skip glyph control points.
2824 }
2825
2826 if (!orientation) {
2827 polygon.reverse();
2828 }
2829
2830 gl_data.contours.push_back(polygon);
2831 }
2832
2833 // Calculate bounds.
2834 List<TPPLPoly> in_poly;
2835 for (int i = 0; i < gl_data.contours.size(); i++) {
2836 TPPLPoly inp;
2837 inp.Init(gl_data.contours[i].size());
2838 real_t length = 0.0;
2839 for (int j = 0; j < gl_data.contours[i].size(); j++) {
2840 int next = (j + 1 == gl_data.contours[i].size()) ? 0 : (j + 1);
2841
2842 gl_data.min_p.x = MIN(gl_data.min_p.x, gl_data.contours[i][j].point.x);
2843 gl_data.min_p.y = MIN(gl_data.min_p.y, gl_data.contours[i][j].point.y);
2844 gl_data.max_p.x = MAX(gl_data.max_p.x, gl_data.contours[i][j].point.x);
2845 gl_data.max_p.y = MAX(gl_data.max_p.y, gl_data.contours[i][j].point.y);
2846 length += (gl_data.contours[i][next].point - gl_data.contours[i][j].point).length();
2847
2848 inp.GetPoint(j) = gl_data.contours[i][j].point;
2849 }
2850 TPPLOrientation poly_orient = inp.GetOrientation();
2851 if (poly_orient == TPPL_ORIENTATION_CW) {
2852 inp.SetHole(true);
2853 }
2854 in_poly.push_back(inp);
2855 gl_data.contours_info.push_back(ContourInfo(length, poly_orient == TPPL_ORIENTATION_CCW));
2856 }
2857
2858 TPPLPartition tpart;
2859
2860 //Decompose and triangulate.
2861 List<TPPLPoly> out_poly;
2862 if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) {
2863 ERR_FAIL_MSG("Convex decomposing failed. Make sure the font doesn't contain self-intersecting lines, as these are not supported in TextMesh.");
2864 }
2865 List<TPPLPoly> out_tris;
2866 for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) {
2867 if (tpart.Triangulate_OPT(&(I->get()), &out_tris) == 0) {
2868 ERR_FAIL_MSG("Triangulation failed. Make sure the font doesn't contain self-intersecting lines, as these are not supported in TextMesh.");
2869 }
2870 }
2871
2872 for (List<TPPLPoly>::Element *I = out_tris.front(); I; I = I->next()) {
2873 TPPLPoly &tp = I->get();
2874 ERR_FAIL_COND(tp.GetNumPoints() != 3); // Triangles only.
2875
2876 for (int i = 0; i < 3; i++) {
2877 gl_data.triangles.push_back(Vector2(tp.GetPoint(i).x, tp.GetPoint(i).y));
2878 }
2879 }
2880}
2881
2882void TextMesh::_create_mesh_array(Array &p_arr) const {
2883 Ref<Font> font = _get_font_or_default();
2884 ERR_FAIL_COND(font.is_null());
2885
2886 if (dirty_cache) {
2887 cache.clear();
2888 dirty_cache = false;
2889 }
2890
2891 // When a shaped text is invalidated by an external source, we want to reshape it.
2892 if (!TS->shaped_text_is_ready(text_rid)) {
2893 dirty_text = true;
2894 }
2895
2896 for (const RID &line_rid : lines_rid) {
2897 if (!TS->shaped_text_is_ready(line_rid)) {
2898 dirty_lines = true;
2899 break;
2900 }
2901 }
2902
2903 // Update text buffer.
2904 if (dirty_text) {
2905 TS->shaped_text_clear(text_rid);
2906 TS->shaped_text_set_direction(text_rid, text_direction);
2907
2908 String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
2909 TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), language);
2910 for (int i = 0; i < TextServer::SPACING_MAX; i++) {
2911 TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
2912 }
2913
2914 TypedArray<Vector3i> stt;
2915 if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
2916 GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
2917 } else {
2918 stt = TS->parse_structured_text(st_parser, st_args, txt);
2919 }
2920 TS->shaped_text_set_bidi_override(text_rid, stt);
2921
2922 dirty_text = false;
2923 dirty_font = false;
2924 dirty_lines = true;
2925 } else if (dirty_font) {
2926 int spans = TS->shaped_get_span_count(text_rid);
2927 for (int i = 0; i < spans; i++) {
2928 TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features());
2929 }
2930 for (int i = 0; i < TextServer::SPACING_MAX; i++) {
2931 TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
2932 }
2933
2934 dirty_font = false;
2935 dirty_lines = true;
2936 }
2937
2938 if (dirty_lines) {
2939 for (int i = 0; i < lines_rid.size(); i++) {
2940 TS->free_rid(lines_rid[i]);
2941 }
2942 lines_rid.clear();
2943
2944 BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;
2945 switch (autowrap_mode) {
2946 case TextServer::AUTOWRAP_WORD_SMART:
2947 autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY;
2948 break;
2949 case TextServer::AUTOWRAP_WORD:
2950 autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
2951 break;
2952 case TextServer::AUTOWRAP_ARBITRARY:
2953 autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
2954 break;
2955 case TextServer::AUTOWRAP_OFF:
2956 break;
2957 }
2958 PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);
2959
2960 float max_line_w = 0.0;
2961 for (int i = 0; i < line_breaks.size(); i = i + 2) {
2962 RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
2963 max_line_w = MAX(max_line_w, TS->shaped_text_get_width(line));
2964 lines_rid.push_back(line);
2965 }
2966
2967 if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
2968 int jst_to_line = lines_rid.size();
2969 if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) {
2970 jst_to_line = lines_rid.size();
2971 } else {
2972 if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) {
2973 jst_to_line = lines_rid.size() - 1;
2974 }
2975 if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) {
2976 for (int i = lines_rid.size() - 1; i >= 0; i--) {
2977 if (TS->shaped_text_has_visible_chars(lines_rid[i])) {
2978 jst_to_line = i;
2979 break;
2980 }
2981 }
2982 }
2983 }
2984 for (int i = 0; i < jst_to_line; i++) {
2985 TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, jst_flags);
2986 }
2987 }
2988 dirty_lines = false;
2989 }
2990
2991 float total_h = 0.0;
2992 for (int i = 0; i < lines_rid.size(); i++) {
2993 total_h += (TS->shaped_text_get_size(lines_rid[i]).y + line_spacing) * pixel_size;
2994 }
2995
2996 float vbegin = 0.0;
2997 switch (vertical_alignment) {
2998 case VERTICAL_ALIGNMENT_FILL:
2999 case VERTICAL_ALIGNMENT_TOP: {
3000 // Nothing.
3001 } break;
3002 case VERTICAL_ALIGNMENT_CENTER: {
3003 vbegin = (total_h - line_spacing * pixel_size) / 2.0;
3004 } break;
3005 case VERTICAL_ALIGNMENT_BOTTOM: {
3006 vbegin = (total_h - line_spacing * pixel_size);
3007 } break;
3008 }
3009
3010 Vector<Vector3> vertices;
3011 Vector<Vector3> normals;
3012 Vector<float> tangents;
3013 Vector<Vector2> uvs;
3014 Vector<int32_t> indices;
3015
3016 Vector2 min_p = Vector2(INFINITY, INFINITY);
3017 Vector2 max_p = Vector2(-INFINITY, -INFINITY);
3018
3019 int32_t p_size = 0;
3020 int32_t i_size = 0;
3021
3022 Vector2 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size);
3023 for (int i = 0; i < lines_rid.size(); i++) {
3024 const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
3025 int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
3026 float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size;
3027
3028 switch (horizontal_alignment) {
3029 case HORIZONTAL_ALIGNMENT_LEFT:
3030 offset.x = 0.0;
3031 break;
3032 case HORIZONTAL_ALIGNMENT_FILL:
3033 case HORIZONTAL_ALIGNMENT_CENTER: {
3034 offset.x = -line_width / 2.0;
3035 } break;
3036 case HORIZONTAL_ALIGNMENT_RIGHT: {
3037 offset.x = -line_width;
3038 } break;
3039 }
3040 offset.x += lbl_offset.x * pixel_size;
3041 offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size;
3042
3043 bool has_depth = !Math::is_zero_approx(depth);
3044
3045 for (int j = 0; j < gl_size; j++) {
3046 if (glyphs[j].index == 0) {
3047 offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;
3048 continue;
3049 }
3050 if (glyphs[j].font_rid != RID()) {
3051 GlyphMeshKey key = GlyphMeshKey(glyphs[j].font_rid.get_id(), glyphs[j].index);
3052 _generate_glyph_mesh_data(key, glyphs[j]);
3053 GlyphMeshData &gl_data = cache[key];
3054
3055 p_size += glyphs[j].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1);
3056 i_size += glyphs[j].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1);
3057
3058 if (has_depth) {
3059 for (int k = 0; k < gl_data.contours.size(); k++) {
3060 p_size += glyphs[j].repeat * gl_data.contours[k].size() * 4;
3061 i_size += glyphs[j].repeat * gl_data.contours[k].size() * 6;
3062 }
3063 }
3064
3065 for (int r = 0; r < glyphs[j].repeat; r++) {
3066 min_p.x = MIN(gl_data.min_p.x + offset.x, min_p.x);
3067 min_p.y = MIN(gl_data.min_p.y - offset.y, min_p.y);
3068 max_p.x = MAX(gl_data.max_p.x + offset.x, max_p.x);
3069 max_p.y = MAX(gl_data.max_p.y - offset.y, max_p.y);
3070
3071 offset.x += glyphs[j].advance * pixel_size;
3072 }
3073 } else {
3074 p_size += glyphs[j].repeat * 4;
3075 i_size += glyphs[j].repeat * 6;
3076
3077 offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;
3078 }
3079 }
3080 offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size;
3081 }
3082
3083 vertices.resize(p_size);
3084 normals.resize(p_size);
3085 uvs.resize(p_size);
3086 tangents.resize(p_size * 4);
3087 indices.resize(i_size);
3088
3089 Vector3 *vertices_ptr = vertices.ptrw();
3090 Vector3 *normals_ptr = normals.ptrw();
3091 float *tangents_ptr = tangents.ptrw();
3092 Vector2 *uvs_ptr = uvs.ptrw();
3093 int32_t *indices_ptr = indices.ptrw();
3094
3095 // Generate mesh.
3096 int32_t p_idx = 0;
3097 int32_t i_idx = 0;
3098
3099 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size);
3100 for (int i = 0; i < lines_rid.size(); i++) {
3101 const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
3102 int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
3103 float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size;
3104
3105 switch (horizontal_alignment) {
3106 case HORIZONTAL_ALIGNMENT_LEFT:
3107 offset.x = 0.0;
3108 break;
3109 case HORIZONTAL_ALIGNMENT_FILL:
3110 case HORIZONTAL_ALIGNMENT_CENTER: {
3111 offset.x = -line_width / 2.0;
3112 } break;
3113 case HORIZONTAL_ALIGNMENT_RIGHT: {
3114 offset.x = -line_width;
3115 } break;
3116 }
3117 offset.x += lbl_offset.x * pixel_size;
3118 offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size;
3119
3120 bool has_depth = !Math::is_zero_approx(depth);
3121
3122 // Generate glyph data, precalculate size of the arrays and mesh bounds for UV.
3123 for (int j = 0; j < gl_size; j++) {
3124 if (glyphs[j].index == 0) {
3125 offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;
3126 continue;
3127 }
3128 if (glyphs[j].font_rid != RID()) {
3129 GlyphMeshKey key = GlyphMeshKey(glyphs[j].font_rid.get_id(), glyphs[j].index);
3130 _generate_glyph_mesh_data(key, glyphs[j]);
3131 const GlyphMeshData &gl_data = cache[key];
3132
3133 int64_t ts = gl_data.triangles.size();
3134 const Vector2 *ts_ptr = gl_data.triangles.ptr();
3135
3136 for (int r = 0; r < glyphs[j].repeat; r++) {
3137 for (int k = 0; k < ts; k += 3) {
3138 // Add front face.
3139 for (int l = 0; l < 3; l++) {
3140 Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, depth / 2.0);
3141 vertices_ptr[p_idx] = point;
3142 normals_ptr[p_idx] = Vector3(0.0, 0.0, 1.0);
3143 if (has_depth) {
3144 uvs_ptr[p_idx] = Vector2(Math::remap(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(point.y, -max_p.y, -min_p.y, real_t(0.4), real_t(0.0)));
3145 } else {
3146 uvs_ptr[p_idx] = Vector2(Math::remap(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(point.y, -max_p.y, -min_p.y, real_t(1.0), real_t(0.0)));
3147 }
3148 tangents_ptr[p_idx * 4 + 0] = 1.0;
3149 tangents_ptr[p_idx * 4 + 1] = 0.0;
3150 tangents_ptr[p_idx * 4 + 2] = 0.0;
3151 tangents_ptr[p_idx * 4 + 3] = 1.0;
3152 indices_ptr[i_idx++] = p_idx;
3153 p_idx++;
3154 }
3155 if (has_depth) {
3156 // Add back face.
3157 for (int l = 2; l >= 0; l--) {
3158 Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, -depth / 2.0);
3159 vertices_ptr[p_idx] = point;
3160 normals_ptr[p_idx] = Vector3(0.0, 0.0, -1.0);
3161 uvs_ptr[p_idx] = Vector2(Math::remap(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(point.y, -max_p.y, -min_p.y, real_t(0.8), real_t(0.4)));
3162 tangents_ptr[p_idx * 4 + 0] = -1.0;
3163 tangents_ptr[p_idx * 4 + 1] = 0.0;
3164 tangents_ptr[p_idx * 4 + 2] = 0.0;
3165 tangents_ptr[p_idx * 4 + 3] = 1.0;
3166 indices_ptr[i_idx++] = p_idx;
3167 p_idx++;
3168 }
3169 }
3170 }
3171 // Add sides.
3172 if (has_depth) {
3173 for (int k = 0; k < gl_data.contours.size(); k++) {
3174 int64_t ps = gl_data.contours[k].size();
3175 const ContourPoint *ps_ptr = gl_data.contours[k].ptr();
3176 const ContourInfo &ps_info = gl_data.contours_info[k];
3177 real_t length = 0.0;
3178 for (int l = 0; l < ps; l++) {
3179 int prev = (l == 0) ? (ps - 1) : (l - 1);
3180 int next = (l + 1 == ps) ? 0 : (l + 1);
3181 Vector2 d1;
3182 Vector2 d2 = (ps_ptr[next].point - ps_ptr[l].point).normalized();
3183 if (ps_ptr[l].sharp) {
3184 d1 = d2;
3185 } else {
3186 d1 = (ps_ptr[l].point - ps_ptr[prev].point).normalized();
3187 }
3188 real_t seg_len = (ps_ptr[next].point - ps_ptr[l].point).length();
3189
3190 Vector3 quad_faces[4] = {
3191 Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, -depth / 2.0),
3192 Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, -depth / 2.0),
3193 Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, depth / 2.0),
3194 Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, depth / 2.0),
3195 };
3196 for (int m = 0; m < 4; m++) {
3197 const Vector2 &d = ((m % 2) == 0) ? d1 : d2;
3198 real_t u_pos = ((m % 2) == 0) ? length : length + seg_len;
3199 vertices_ptr[p_idx + m] = quad_faces[m];
3200 normals_ptr[p_idx + m] = Vector3(d.y, d.x, 0.0);
3201 if (m < 2) {
3202 uvs_ptr[p_idx + m] = Vector2(Math::remap(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.8 : 0.9);
3203 } else {
3204 uvs_ptr[p_idx + m] = Vector2(Math::remap(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.9 : 1.0);
3205 }
3206 tangents_ptr[(p_idx + m) * 4 + 0] = d.x;
3207 tangents_ptr[(p_idx + m) * 4 + 1] = -d.y;
3208 tangents_ptr[(p_idx + m) * 4 + 2] = 0.0;
3209 tangents_ptr[(p_idx + m) * 4 + 3] = 1.0;
3210 }
3211
3212 indices_ptr[i_idx++] = p_idx;
3213 indices_ptr[i_idx++] = p_idx + 1;
3214 indices_ptr[i_idx++] = p_idx + 2;
3215
3216 indices_ptr[i_idx++] = p_idx + 1;
3217 indices_ptr[i_idx++] = p_idx + 3;
3218 indices_ptr[i_idx++] = p_idx + 2;
3219
3220 length += seg_len;
3221 p_idx += 4;
3222 }
3223 }
3224 }
3225 offset.x += glyphs[j].advance * pixel_size;
3226 }
3227 } else {
3228 // Add fallback quad for missing glyphs.
3229 for (int r = 0; r < glyphs[j].repeat; r++) {
3230 Size2 sz = TS->get_hex_code_box_size(glyphs[j].font_size, glyphs[j].index) * pixel_size;
3231 Vector3 quad_faces[4] = {
3232 Vector3(offset.x, offset.y, 0.0),
3233 Vector3(offset.x, sz.y + offset.y, 0.0),
3234 Vector3(sz.x + offset.x, sz.y + offset.y, 0.0),
3235 Vector3(sz.x + offset.x, offset.y, 0.0),
3236 };
3237 for (int k = 0; k < 4; k++) {
3238 vertices_ptr[p_idx + k] = quad_faces[k];
3239 normals_ptr[p_idx + k] = Vector3(0.0, 0.0, 1.0);
3240 if (has_depth) {
3241 uvs_ptr[p_idx + k] = Vector2(Math::remap(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(quad_faces[k].y, -max_p.y, -min_p.y, real_t(0.4), real_t(0.0)));
3242 } else {
3243 uvs_ptr[p_idx + k] = Vector2(Math::remap(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::remap(quad_faces[k].y, -max_p.y, -min_p.y, real_t(1.0), real_t(0.0)));
3244 }
3245 tangents_ptr[(p_idx + k) * 4 + 0] = 1.0;
3246 tangents_ptr[(p_idx + k) * 4 + 1] = 0.0;
3247 tangents_ptr[(p_idx + k) * 4 + 2] = 0.0;
3248 tangents_ptr[(p_idx + k) * 4 + 3] = 1.0;
3249 }
3250
3251 indices_ptr[i_idx++] = p_idx;
3252 indices_ptr[i_idx++] = p_idx + 1;
3253 indices_ptr[i_idx++] = p_idx + 2;
3254
3255 indices_ptr[i_idx++] = p_idx + 0;
3256 indices_ptr[i_idx++] = p_idx + 2;
3257 indices_ptr[i_idx++] = p_idx + 3;
3258 p_idx += 4;
3259
3260 offset.x += glyphs[j].advance * pixel_size;
3261 }
3262 }
3263 }
3264 offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size;
3265 }
3266
3267 if (indices.is_empty()) {
3268 // If empty, add single triangle to suppress errors.
3269 vertices.push_back(Vector3());
3270 normals.push_back(Vector3());
3271 uvs.push_back(Vector2());
3272 tangents.push_back(1.0);
3273 tangents.push_back(0.0);
3274 tangents.push_back(0.0);
3275 tangents.push_back(1.0);
3276 indices.push_back(0);
3277 indices.push_back(0);
3278 indices.push_back(0);
3279 }
3280
3281 p_arr[RS::ARRAY_VERTEX] = vertices;
3282 p_arr[RS::ARRAY_NORMAL] = normals;
3283 p_arr[RS::ARRAY_TANGENT] = tangents;
3284 p_arr[RS::ARRAY_TEX_UV] = uvs;
3285 p_arr[RS::ARRAY_INDEX] = indices;
3286}
3287
3288void TextMesh::_bind_methods() {
3289 ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &TextMesh::set_horizontal_alignment);
3290 ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &TextMesh::get_horizontal_alignment);
3291
3292 ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &TextMesh::set_vertical_alignment);
3293 ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &TextMesh::get_vertical_alignment);
3294
3295 ClassDB::bind_method(D_METHOD("set_text", "text"), &TextMesh::set_text);
3296 ClassDB::bind_method(D_METHOD("get_text"), &TextMesh::get_text);
3297
3298 ClassDB::bind_method(D_METHOD("set_font", "font"), &TextMesh::set_font);
3299 ClassDB::bind_method(D_METHOD("get_font"), &TextMesh::get_font);
3300
3301 ClassDB::bind_method(D_METHOD("set_font_size", "font_size"), &TextMesh::set_font_size);
3302 ClassDB::bind_method(D_METHOD("get_font_size"), &TextMesh::get_font_size);
3303
3304 ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &TextMesh::set_line_spacing);
3305 ClassDB::bind_method(D_METHOD("get_line_spacing"), &TextMesh::get_line_spacing);
3306
3307 ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &TextMesh::set_autowrap_mode);
3308 ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &TextMesh::get_autowrap_mode);
3309
3310 ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &TextMesh::set_justification_flags);
3311 ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextMesh::get_justification_flags);
3312
3313 ClassDB::bind_method(D_METHOD("set_depth", "depth"), &TextMesh::set_depth);
3314 ClassDB::bind_method(D_METHOD("get_depth"), &TextMesh::get_depth);
3315
3316 ClassDB::bind_method(D_METHOD("set_width", "width"), &TextMesh::set_width);
3317 ClassDB::bind_method(D_METHOD("get_width"), &TextMesh::get_width);
3318
3319 ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &TextMesh::set_pixel_size);
3320 ClassDB::bind_method(D_METHOD("get_pixel_size"), &TextMesh::get_pixel_size);
3321
3322 ClassDB::bind_method(D_METHOD("set_offset", "offset"), &TextMesh::set_offset);
3323 ClassDB::bind_method(D_METHOD("get_offset"), &TextMesh::get_offset);
3324
3325 ClassDB::bind_method(D_METHOD("set_curve_step", "curve_step"), &TextMesh::set_curve_step);
3326 ClassDB::bind_method(D_METHOD("get_curve_step"), &TextMesh::get_curve_step);
3327
3328 ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextMesh::set_text_direction);
3329 ClassDB::bind_method(D_METHOD("get_text_direction"), &TextMesh::get_text_direction);
3330
3331 ClassDB::bind_method(D_METHOD("set_language", "language"), &TextMesh::set_language);
3332 ClassDB::bind_method(D_METHOD("get_language"), &TextMesh::get_language);
3333
3334 ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextMesh::set_structured_text_bidi_override);
3335 ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &TextMesh::get_structured_text_bidi_override);
3336
3337 ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &TextMesh::set_structured_text_bidi_override_options);
3338 ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &TextMesh::get_structured_text_bidi_override_options);
3339
3340 ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &TextMesh::set_uppercase);
3341 ClassDB::bind_method(D_METHOD("is_uppercase"), &TextMesh::is_uppercase);
3342
3343 ClassDB::bind_method(D_METHOD("_font_changed"), &TextMesh::_font_changed);
3344 ClassDB::bind_method(D_METHOD("_request_update"), &TextMesh::_request_update);
3345
3346 ADD_GROUP("Text", "");
3347 ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text");
3348 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font");
3349 ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px"), "set_font_size", "get_font_size");
3350 ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
3351 ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment");
3352 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
3353 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing");
3354 ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
3355 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");
3356
3357 ADD_GROUP("Mesh", "");
3358 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size");
3359 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_step", PROPERTY_HINT_RANGE, "0.1,10,0.1,suffix:px"), "set_curve_step", "get_curve_step");
3360 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater,suffix:m"), "set_depth", "get_depth");
3361 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width");
3362 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
3363
3364 ADD_GROUP("BiDi", "");
3365 ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction");
3366 ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
3367 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");
3368 ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
3369}
3370
3371void TextMesh::_notification(int p_what) {
3372 switch (p_what) {
3373 case MainLoop::NOTIFICATION_TRANSLATION_CHANGED: {
3374 String new_text = tr(text);
3375 if (new_text == xl_text) {
3376 return; // Nothing new.
3377 }
3378 xl_text = new_text;
3379 dirty_text = true;
3380 _request_update();
3381 } break;
3382 }
3383}
3384
3385TextMesh::TextMesh() {
3386 primitive_type = PRIMITIVE_TRIANGLES;
3387 text_rid = TS->create_shaped_text();
3388}
3389
3390TextMesh::~TextMesh() {
3391 for (int i = 0; i < lines_rid.size(); i++) {
3392 TS->free_rid(lines_rid[i]);
3393 }
3394 lines_rid.clear();
3395
3396 TS->free_rid(text_rid);
3397}
3398
3399void TextMesh::set_horizontal_alignment(HorizontalAlignment p_alignment) {
3400 ERR_FAIL_INDEX((int)p_alignment, 4);
3401 if (horizontal_alignment != p_alignment) {
3402 if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
3403 dirty_lines = true;
3404 }
3405 horizontal_alignment = p_alignment;
3406 _request_update();
3407 }
3408}
3409
3410HorizontalAlignment TextMesh::get_horizontal_alignment() const {
3411 return horizontal_alignment;
3412}
3413
3414void TextMesh::set_vertical_alignment(VerticalAlignment p_alignment) {
3415 ERR_FAIL_INDEX((int)p_alignment, 4);
3416 if (vertical_alignment != p_alignment) {
3417 vertical_alignment = p_alignment;
3418 _request_update();
3419 }
3420}
3421
3422VerticalAlignment TextMesh::get_vertical_alignment() const {
3423 return vertical_alignment;
3424}
3425
3426void TextMesh::set_text(const String &p_string) {
3427 if (text != p_string) {
3428 text = p_string;
3429 xl_text = tr(text);
3430 dirty_text = true;
3431 _request_update();
3432 }
3433}
3434
3435String TextMesh::get_text() const {
3436 return text;
3437}
3438
3439void TextMesh::_font_changed() {
3440 dirty_font = true;
3441 dirty_cache = true;
3442 call_deferred(SNAME("_request_update"));
3443}
3444
3445void TextMesh::set_font(const Ref<Font> &p_font) {
3446 if (font_override != p_font) {
3447 if (font_override.is_valid()) {
3448 font_override->disconnect_changed(Callable(this, "_font_changed"));
3449 }
3450 font_override = p_font;
3451 dirty_font = true;
3452 dirty_cache = true;
3453 if (font_override.is_valid()) {
3454 font_override->connect_changed(Callable(this, "_font_changed"));
3455 }
3456 _request_update();
3457 }
3458}
3459
3460Ref<Font> TextMesh::get_font() const {
3461 return font_override;
3462}
3463
3464Ref<Font> TextMesh::_get_font_or_default() const {
3465 if (font_override.is_valid()) {
3466 return font_override;
3467 }
3468
3469 StringName theme_name = "font";
3470 List<StringName> theme_types;
3471 ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), &theme_types);
3472
3473 ThemeContext *global_context = ThemeDB::get_singleton()->get_default_theme_context();
3474 for (const Ref<Theme> &theme : global_context->get_themes()) {
3475 if (theme.is_null()) {
3476 continue;
3477 }
3478
3479 for (const StringName &E : theme_types) {
3480 if (theme->has_font(theme_name, E)) {
3481 return theme->get_font(theme_name, E);
3482 }
3483 }
3484 }
3485
3486 return global_context->get_fallback_theme()->get_font(theme_name, StringName());
3487}
3488
3489void TextMesh::set_font_size(int p_size) {
3490 if (font_size != p_size) {
3491 font_size = CLAMP(p_size, 1, 127);
3492 dirty_font = true;
3493 dirty_cache = true;
3494 _request_update();
3495 }
3496}
3497
3498int TextMesh::get_font_size() const {
3499 return font_size;
3500}
3501
3502void TextMesh::set_line_spacing(float p_line_spacing) {
3503 if (line_spacing != p_line_spacing) {
3504 line_spacing = p_line_spacing;
3505 _request_update();
3506 }
3507}
3508
3509float TextMesh::get_line_spacing() const {
3510 return line_spacing;
3511}
3512
3513void TextMesh::set_autowrap_mode(TextServer::AutowrapMode p_mode) {
3514 if (autowrap_mode != p_mode) {
3515 autowrap_mode = p_mode;
3516 dirty_lines = true;
3517 _request_update();
3518 }
3519}
3520
3521TextServer::AutowrapMode TextMesh::get_autowrap_mode() const {
3522 return autowrap_mode;
3523}
3524
3525void TextMesh::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {
3526 if (jst_flags != p_flags) {
3527 jst_flags = p_flags;
3528 dirty_lines = true;
3529 _request_update();
3530 }
3531}
3532
3533BitField<TextServer::JustificationFlag> TextMesh::get_justification_flags() const {
3534 return jst_flags;
3535}
3536
3537void TextMesh::set_depth(real_t p_depth) {
3538 if (depth != p_depth) {
3539 depth = MAX(p_depth, 0.0);
3540 _request_update();
3541 }
3542}
3543
3544real_t TextMesh::get_depth() const {
3545 return depth;
3546}
3547
3548void TextMesh::set_width(real_t p_width) {
3549 if (width != p_width) {
3550 width = p_width;
3551 dirty_lines = true;
3552 _request_update();
3553 }
3554}
3555
3556real_t TextMesh::get_width() const {
3557 return width;
3558}
3559
3560void TextMesh::set_pixel_size(real_t p_amount) {
3561 if (pixel_size != p_amount) {
3562 pixel_size = CLAMP(p_amount, 0.0001, 128.0);
3563 dirty_cache = true;
3564 _request_update();
3565 }
3566}
3567
3568real_t TextMesh::get_pixel_size() const {
3569 return pixel_size;
3570}
3571
3572void TextMesh::set_offset(const Point2 &p_offset) {
3573 if (lbl_offset != p_offset) {
3574 lbl_offset = p_offset;
3575 _request_update();
3576 }
3577}
3578
3579Point2 TextMesh::get_offset() const {
3580 return lbl_offset;
3581}
3582
3583void TextMesh::set_curve_step(real_t p_step) {
3584 if (curve_step != p_step) {
3585 curve_step = CLAMP(p_step, 0.1, 10.0);
3586 dirty_cache = true;
3587 _request_update();
3588 }
3589}
3590
3591real_t TextMesh::get_curve_step() const {
3592 return curve_step;
3593}
3594
3595void TextMesh::set_text_direction(TextServer::Direction p_text_direction) {
3596 ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
3597 if (text_direction != p_text_direction) {
3598 text_direction = p_text_direction;
3599 dirty_text = true;
3600 _request_update();
3601 }
3602}
3603
3604TextServer::Direction TextMesh::get_text_direction() const {
3605 return text_direction;
3606}
3607
3608void TextMesh::set_language(const String &p_language) {
3609 if (language != p_language) {
3610 language = p_language;
3611 dirty_text = true;
3612 _request_update();
3613 }
3614}
3615
3616String TextMesh::get_language() const {
3617 return language;
3618}
3619
3620void TextMesh::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
3621 if (st_parser != p_parser) {
3622 st_parser = p_parser;
3623 dirty_text = true;
3624 _request_update();
3625 }
3626}
3627
3628TextServer::StructuredTextParser TextMesh::get_structured_text_bidi_override() const {
3629 return st_parser;
3630}
3631
3632void TextMesh::set_structured_text_bidi_override_options(Array p_args) {
3633 if (st_args != p_args) {
3634 st_args = p_args;
3635 dirty_text = true;
3636 _request_update();
3637 }
3638}
3639
3640Array TextMesh::get_structured_text_bidi_override_options() const {
3641 return st_args;
3642}
3643
3644void TextMesh::set_uppercase(bool p_uppercase) {
3645 if (uppercase != p_uppercase) {
3646 uppercase = p_uppercase;
3647 dirty_text = true;
3648 _request_update();
3649 }
3650}
3651
3652bool TextMesh::is_uppercase() const {
3653 return uppercase;
3654}
3655