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