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