Path: blob/master/scene/resources/3d/primitive_meshes.cpp
9903 views
/**************************************************************************/1/* primitive_meshes.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "primitive_meshes.h"3132#include "core/config/project_settings.h"33#include "core/math/math_funcs.h"34#include "scene/resources/theme.h"35#include "scene/theme/theme_db.h"36#include "servers/rendering_server.h"37#include "thirdparty/misc/polypartition.h"3839#define PADDING_REF_SIZE 1024.04041/**42PrimitiveMesh43*/44void PrimitiveMesh::_update() const {45Array arr;46if (GDVIRTUAL_CALL(_create_mesh_array, arr)) {47ERR_FAIL_COND_MSG(arr.size() != RS::ARRAY_MAX, "_create_mesh_array must return an array of Mesh.ARRAY_MAX elements.");48} else {49arr.resize(RS::ARRAY_MAX);50_create_mesh_array(arr);51}5253Vector<Vector3> points = arr[RS::ARRAY_VERTEX];5455ERR_FAIL_COND_MSG(points.is_empty(), "_create_mesh_array must return at least a vertex array.");5657aabb = AABB();5859int pc = points.size();60ERR_FAIL_COND(pc == 0);61{62const Vector3 *r = points.ptr();63for (int i = 0; i < pc; i++) {64if (i == 0) {65aabb.position = r[i];66} else {67aabb.expand_to(r[i]);68}69}70}7172Vector<int> indices = arr[RS::ARRAY_INDEX];7374if (flip_faces) {75Vector<Vector3> normals = arr[RS::ARRAY_NORMAL];7677if (normals.size() && indices.size()) {78{79int nc = normals.size();80Vector3 *w = normals.ptrw();81for (int i = 0; i < nc; i++) {82w[i] = -w[i];83}84}8586{87int ic = indices.size();88int *w = indices.ptrw();89for (int i = 0; i < ic; i += 3) {90SWAP(w[i + 0], w[i + 1]);91}92}93arr[RS::ARRAY_NORMAL] = normals;94arr[RS::ARRAY_INDEX] = indices;95}96}9798if (add_uv2) {99// _create_mesh_array should populate our UV2, this is a fallback in case it doesn't.100// As we don't know anything about the geometry we only pad the right and bottom edge101// of our texture.102Vector<Vector2> uv = arr[RS::ARRAY_TEX_UV];103Vector<Vector2> uv2 = arr[RS::ARRAY_TEX_UV2];104105if (uv.size() > 0 && uv2.is_empty()) {106Vector2 uv2_scale = get_uv2_scale();107uv2.resize(uv.size());108109Vector2 *uv2w = uv2.ptrw();110for (int i = 0; i < uv.size(); i++) {111uv2w[i] = uv[i] * uv2_scale;112}113}114115arr[RS::ARRAY_TEX_UV2] = uv2;116}117118array_len = pc;119index_array_len = indices.size();120// in with the new121RenderingServer::get_singleton()->mesh_clear(mesh);122RenderingServer::get_singleton()->mesh_add_surface_from_arrays(mesh, (RenderingServer::PrimitiveType)primitive_type, arr);123RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid());124125pending_request = false;126127clear_cache();128129const_cast<PrimitiveMesh *>(this)->emit_changed();130}131132void PrimitiveMesh::request_update() {133if (pending_request) {134return;135}136_update();137}138139int PrimitiveMesh::get_surface_count() const {140if (pending_request) {141_update();142}143return 1;144}145146int PrimitiveMesh::surface_get_array_len(int p_idx) const {147ERR_FAIL_INDEX_V(p_idx, 1, -1);148if (pending_request) {149_update();150}151152return array_len;153}154155int PrimitiveMesh::surface_get_array_index_len(int p_idx) const {156ERR_FAIL_INDEX_V(p_idx, 1, -1);157if (pending_request) {158_update();159}160161return index_array_len;162}163164Array PrimitiveMesh::surface_get_arrays(int p_surface) const {165ERR_FAIL_INDEX_V(p_surface, 1, Array());166if (pending_request) {167_update();168}169170return RenderingServer::get_singleton()->mesh_surface_get_arrays(mesh, 0);171}172173Dictionary PrimitiveMesh::surface_get_lods(int p_surface) const {174return Dictionary(); //not really supported175}176177TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) const {178return TypedArray<Array>(); //not really supported179}180181BitField<Mesh::ArrayFormat> PrimitiveMesh::surface_get_format(int p_idx) const {182ERR_FAIL_INDEX_V(p_idx, 1, 0);183184uint64_t mesh_format = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;185if (add_uv2) {186mesh_format |= RS::ARRAY_FORMAT_TEX_UV2;187}188189return mesh_format;190}191192Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const {193return primitive_type;194}195196void PrimitiveMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {197ERR_FAIL_INDEX(p_idx, 1);198199set_material(p_material);200}201202Ref<Material> PrimitiveMesh::surface_get_material(int p_idx) const {203ERR_FAIL_INDEX_V(p_idx, 1, nullptr);204205return material;206}207208int PrimitiveMesh::get_blend_shape_count() const {209return 0;210}211212StringName PrimitiveMesh::get_blend_shape_name(int p_index) const {213return StringName();214}215216void PrimitiveMesh::set_blend_shape_name(int p_index, const StringName &p_name) {217}218219AABB PrimitiveMesh::get_aabb() const {220if (pending_request) {221_update();222}223224return aabb;225}226227RID PrimitiveMesh::get_rid() const {228if (pending_request) {229_update();230}231return mesh;232}233234void PrimitiveMesh::_bind_methods() {235ClassDB::bind_method(D_METHOD("set_material", "material"), &PrimitiveMesh::set_material);236ClassDB::bind_method(D_METHOD("get_material"), &PrimitiveMesh::get_material);237238ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays);239240ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &PrimitiveMesh::set_custom_aabb);241ClassDB::bind_method(D_METHOD("get_custom_aabb"), &PrimitiveMesh::get_custom_aabb);242243ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces);244ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces);245246ClassDB::bind_method(D_METHOD("set_add_uv2", "add_uv2"), &PrimitiveMesh::set_add_uv2);247ClassDB::bind_method(D_METHOD("get_add_uv2"), &PrimitiveMesh::get_add_uv2);248249ClassDB::bind_method(D_METHOD("set_uv2_padding", "uv2_padding"), &PrimitiveMesh::set_uv2_padding);250ClassDB::bind_method(D_METHOD("get_uv2_padding"), &PrimitiveMesh::get_uv2_padding);251252ClassDB::bind_method(D_METHOD("request_update"), &PrimitiveMesh::request_update);253254ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");255ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");256ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");257ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_uv2"), "set_add_uv2", "get_add_uv2");258ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv2_padding", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), "set_uv2_padding", "get_uv2_padding");259260GDVIRTUAL_BIND(_create_mesh_array);261}262263void PrimitiveMesh::set_material(const Ref<Material> &p_material) {264if (p_material == material) {265return;266}267material = p_material;268if (!pending_request) {269// just apply it, else it'll happen when _update is called.270RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid());271notify_property_list_changed();272emit_changed();273}274}275276Ref<Material> PrimitiveMesh::get_material() const {277return material;278}279280Array PrimitiveMesh::get_mesh_arrays() const {281return surface_get_arrays(0);282}283284void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) {285if (p_custom.is_equal_approx(custom_aabb)) {286return;287}288custom_aabb = p_custom;289RS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);290emit_changed();291}292293AABB PrimitiveMesh::get_custom_aabb() const {294return custom_aabb;295}296297void PrimitiveMesh::set_flip_faces(bool p_enable) {298if (p_enable == flip_faces) {299return;300}301flip_faces = p_enable;302request_update();303}304305bool PrimitiveMesh::get_flip_faces() const {306return flip_faces;307}308309void PrimitiveMesh::set_add_uv2(bool p_enable) {310if (p_enable == add_uv2) {311return;312}313add_uv2 = p_enable;314_update_lightmap_size();315request_update();316}317318void PrimitiveMesh::set_uv2_padding(float p_padding) {319if (Math::is_equal_approx(p_padding, uv2_padding)) {320return;321}322uv2_padding = p_padding;323_update_lightmap_size();324request_update();325}326327Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const {328Vector2 uv2_scale;329Vector2 lightmap_size = get_lightmap_size_hint();330331// Calculate it as a margin, if no lightmap size hint is given we assume "PADDING_REF_SIZE" as our texture size.332uv2_scale.x = p_margin_scale.x * uv2_padding / (lightmap_size.x == 0.0 ? PADDING_REF_SIZE : lightmap_size.x);333uv2_scale.y = p_margin_scale.y * uv2_padding / (lightmap_size.y == 0.0 ? PADDING_REF_SIZE : lightmap_size.y);334335// Inverse it to turn our margin into a scale336uv2_scale = Vector2(1.0, 1.0) - uv2_scale;337338return uv2_scale;339}340341float PrimitiveMesh::get_lightmap_texel_size() const {342return texel_size;343}344345void PrimitiveMesh::_on_settings_changed() {346float new_texel_size = float(GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"));347if (new_texel_size <= 0.0) {348new_texel_size = 0.2;349}350if (texel_size == new_texel_size) {351return;352}353354texel_size = new_texel_size;355_update_lightmap_size();356request_update();357}358359PrimitiveMesh::PrimitiveMesh() {360ERR_FAIL_NULL(RenderingServer::get_singleton());361mesh = RenderingServer::get_singleton()->mesh_create();362363ERR_FAIL_NULL(ProjectSettings::get_singleton());364texel_size = float(GLOBAL_GET_CACHED(float, "rendering/lightmapping/primitive_meshes/texel_size"));365if (texel_size <= 0.0) {366texel_size = 0.2;367}368ProjectSettings *project_settings = ProjectSettings::get_singleton();369project_settings->connect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed));370}371372PrimitiveMesh::~PrimitiveMesh() {373ERR_FAIL_NULL(RenderingServer::get_singleton());374RenderingServer::get_singleton()->free(mesh);375376ERR_FAIL_NULL(ProjectSettings::get_singleton());377ProjectSettings *project_settings = ProjectSettings::get_singleton();378project_settings->disconnect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed));379}380381/**382CapsuleMesh383*/384385void CapsuleMesh::_update_lightmap_size() {386if (get_add_uv2()) {387// size must have changed, update lightmap size hint388Size2i _lightmap_size_hint;389float padding = get_uv2_padding();390391float radial_length = radius * Math::PI * 0.5; // circumference of 90 degree bend392float vertical_length = radial_length * 2 + (height - 2.0 * radius); // total vertical length393394_lightmap_size_hint.x = MAX(1.0, 4.0 * radial_length / texel_size) + padding;395_lightmap_size_hint.y = MAX(1.0, vertical_length / texel_size) + padding;396397set_lightmap_size_hint(_lightmap_size_hint);398}399}400401void CapsuleMesh::_create_mesh_array(Array &p_arr) const {402bool _add_uv2 = get_add_uv2();403float _uv2_padding = get_uv2_padding() * texel_size;404405create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding);406}407408void 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) {409int i, j, prevrow, thisrow, point;410float x, y, z, u, v, w;411float onethird = 1.0 / 3.0;412float twothirds = 2.0 / 3.0;413414// Only used if we calculate UV2415float radial_width = 2.0 * radius * Math::PI;416float radial_h = radial_width / (radial_width + p_uv2_padding);417float radial_length = radius * Math::PI * 0.5; // circumference of 90 degree bend418float vertical_length = radial_length * 2 + (height - 2.0 * radius) + p_uv2_padding; // total vertical length419float radial_v = radial_length / vertical_length; // v size of top and bottom section420float height_v = (height - 2.0 * radius) / vertical_length; // v size of height section421422// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't423// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.424int num_points = (rings + 2) * (radial_segments + 1) * 2;425LocalVector<Vector3> points;426points.reserve(num_points);427LocalVector<Vector3> normals;428normals.reserve(num_points);429LocalVector<float> tangents;430tangents.reserve(num_points * 4);431LocalVector<Vector2> uvs;432uvs.reserve(num_points);433LocalVector<Vector2> uv2s;434if (p_add_uv2) {435uv2s.reserve(num_points);436}437LocalVector<int> indices;438indices.reserve((rings + 1) * (radial_segments) * 6 * 2);439point = 0;440441#define ADD_TANGENT(m_x, m_y, m_z, m_d) \442tangents.push_back(m_x); \443tangents.push_back(m_y); \444tangents.push_back(m_z); \445tangents.push_back(m_d);446447// Note, this has been aligned with our collision shape but I've left the descriptions as top/middle/bottom.448449/* top hemisphere */450thisrow = 0;451prevrow = 0;452for (j = 0; j <= (rings + 1); j++) {453v = j;454455v /= (rings + 1);456if (j == (rings + 1)) {457w = 1.0;458y = 0.0;459} else {460w = Math::sin(0.5 * Math::PI * v);461y = Math::cos(0.5 * Math::PI * v);462}463464for (i = 0; i <= radial_segments; i++) {465u = i;466u /= radial_segments;467468if (i == radial_segments) {469x = 0.0;470z = 1.0;471} else {472x = -Math::sin(u * Math::TAU);473z = Math::cos(u * Math::TAU);474}475476Vector3 p = Vector3(x * w, y, -z * w);477points.push_back(p * radius + Vector3(0.0, 0.5 * height - radius, 0.0));478normals.push_back(p);479ADD_TANGENT(-z, 0.0, -x, 1.0)480uvs.push_back(Vector2(u, v * onethird));481if (p_add_uv2) {482uv2s.push_back(Vector2(u * radial_h, v * radial_v));483}484point++;485486if (i > 0 && j > 0) {487indices.push_back(prevrow + i - 1);488indices.push_back(prevrow + i);489indices.push_back(thisrow + i - 1);490491indices.push_back(prevrow + i);492indices.push_back(thisrow + i);493indices.push_back(thisrow + i - 1);494}495}496497prevrow = thisrow;498thisrow = point;499}500501/* cylinder */502thisrow = point;503prevrow = 0;504for (j = 0; j <= (rings + 1); j++) {505v = j;506v /= (rings + 1);507508y = (height - 2.0 * radius) * v;509y = (0.5 * height - radius) - y;510511for (i = 0; i <= radial_segments; i++) {512u = i;513u /= radial_segments;514515if (i == radial_segments) {516x = 0.0;517z = 1.0;518} else {519x = -Math::sin(u * Math::TAU);520z = Math::cos(u * Math::TAU);521}522523Vector3 p = Vector3(x * radius, y, -z * radius);524points.push_back(p);525normals.push_back(Vector3(x, 0.0, -z));526ADD_TANGENT(-z, 0.0, -x, 1.0)527uvs.push_back(Vector2(u, onethird + (v * onethird)));528if (p_add_uv2) {529uv2s.push_back(Vector2(u * radial_h, radial_v + (v * height_v)));530}531point++;532533if (i > 0 && j > 0) {534indices.push_back(prevrow + i - 1);535indices.push_back(prevrow + i);536indices.push_back(thisrow + i - 1);537538indices.push_back(prevrow + i);539indices.push_back(thisrow + i);540indices.push_back(thisrow + i - 1);541}542}543544prevrow = thisrow;545thisrow = point;546}547548/* bottom hemisphere */549thisrow = point;550prevrow = 0;551for (j = 0; j <= (rings + 1); j++) {552v = j;553554v /= (rings + 1);555if (j == (rings + 1)) {556w = 0.0;557y = -1.0;558} else {559w = Math::cos(0.5 * Math::PI * v);560y = -Math::sin(0.5 * Math::PI * v);561}562563for (i = 0; i <= radial_segments; i++) {564u = i;565u /= radial_segments;566567if (i == radial_segments) {568x = 0.0;569z = 1.0;570} else {571x = -Math::sin(u * Math::TAU);572z = Math::cos(u * Math::TAU);573}574575Vector3 p = Vector3(x * w, y, -z * w);576points.push_back(p * radius + Vector3(0.0, -0.5 * height + radius, 0.0));577normals.push_back(p);578ADD_TANGENT(-z, 0.0, -x, 1.0)579uvs.push_back(Vector2(u, twothirds + v * onethird));580if (p_add_uv2) {581uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + v * radial_v));582}583point++;584585if (i > 0 && j > 0) {586indices.push_back(prevrow + i - 1);587indices.push_back(prevrow + i);588indices.push_back(thisrow + i - 1);589590indices.push_back(prevrow + i);591indices.push_back(thisrow + i);592indices.push_back(thisrow + i - 1);593}594}595596prevrow = thisrow;597thisrow = point;598}599600p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);601p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);602p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);603p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);604if (p_add_uv2) {605p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);606}607p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);608}609610void CapsuleMesh::_bind_methods() {611ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CapsuleMesh::set_radius);612ClassDB::bind_method(D_METHOD("get_radius"), &CapsuleMesh::get_radius);613ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleMesh::set_height);614ClassDB::bind_method(D_METHOD("get_height"), &CapsuleMesh::get_height);615616ClassDB::bind_method(D_METHOD("set_radial_segments", "segments"), &CapsuleMesh::set_radial_segments);617ClassDB::bind_method(D_METHOD("get_radial_segments"), &CapsuleMesh::get_radial_segments);618ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CapsuleMesh::set_rings);619ClassDB::bind_method(D_METHOD("get_rings"), &CapsuleMesh::get_rings);620621ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");622ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height");623ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");624ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings");625626ADD_LINKED_PROPERTY("radius", "height");627ADD_LINKED_PROPERTY("height", "radius");628}629630void CapsuleMesh::set_radius(const float p_radius) {631if (Math::is_equal_approx(radius, p_radius)) {632return;633}634635radius = p_radius;636if (radius > height * 0.5) {637height = radius * 2.0;638}639_update_lightmap_size();640request_update();641}642643float CapsuleMesh::get_radius() const {644return radius;645}646647void CapsuleMesh::set_height(const float p_height) {648if (Math::is_equal_approx(height, p_height)) {649return;650}651652height = p_height;653if (radius > height * 0.5) {654radius = height * 0.5;655}656_update_lightmap_size();657request_update();658}659660float CapsuleMesh::get_height() const {661return height;662}663664void CapsuleMesh::set_radial_segments(const int p_segments) {665if (radial_segments == p_segments) {666return;667}668669radial_segments = p_segments > 4 ? p_segments : 4;670request_update();671}672673int CapsuleMesh::get_radial_segments() const {674return radial_segments;675}676677void CapsuleMesh::set_rings(const int p_rings) {678if (rings == p_rings) {679return;680}681682ERR_FAIL_COND(p_rings < 0);683rings = p_rings;684request_update();685}686687int CapsuleMesh::get_rings() const {688return rings;689}690691/**692BoxMesh693*/694695void BoxMesh::_update_lightmap_size() {696if (get_add_uv2()) {697// size must have changed, update lightmap size hint698Size2i _lightmap_size_hint;699float padding = get_uv2_padding();700701float width = (size.x + size.z) / texel_size;702float length = (size.y + size.y + MAX(size.x, size.z)) / texel_size;703704_lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding;705_lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding;706707set_lightmap_size_hint(_lightmap_size_hint);708}709}710711void BoxMesh::_create_mesh_array(Array &p_arr) const {712// Note about padding, with our box each face of the box faces a different direction so we want a seam713// around every face. We thus add our padding to the right and bottom of each face.714// With 3 faces along the width and 2 along the height of the texture we need to adjust our scale715// accordingly.716bool _add_uv2 = get_add_uv2();717float _uv2_padding = get_uv2_padding() * texel_size;718719BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding);720}721722void 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) {723int i, j, prevrow, thisrow, point;724float x, y, z;725float onethird = 1.0 / 3.0;726float twothirds = 2.0 / 3.0;727728// Only used if we calculate UV2729// 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)730float total_h = (size.x + size.z + (2.0 * p_uv2_padding));731float padding_h = p_uv2_padding / total_h;732float width_h = size.x / total_h;733float depth_h = size.z / total_h;734float total_v = (size.y + size.y + MAX(size.x, size.z) + (3.0 * p_uv2_padding));735float padding_v = p_uv2_padding / total_v;736float width_v = size.x / total_v;737float height_v = size.y / total_v;738float depth_v = size.z / total_v;739740Vector3 start_pos = size * -0.5;741742// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't743// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.744int num_points = (subdivide_h + 2) * (subdivide_w + 2) * 6;745LocalVector<Vector3> points;746points.reserve(num_points);747LocalVector<Vector3> normals;748normals.reserve(num_points);749LocalVector<float> tangents;750tangents.reserve(num_points * 4);751LocalVector<Vector2> uvs;752uvs.reserve(num_points);753LocalVector<Vector2> uv2s;754if (p_add_uv2) {755uv2s.reserve(num_points);756}757LocalVector<int> indices;758indices.reserve((subdivide_h + 1) * (subdivide_w + 1) * 6 * 6);759point = 0;760761#define ADD_TANGENT(m_x, m_y, m_z, m_d) \762tangents.push_back(m_x); \763tangents.push_back(m_y); \764tangents.push_back(m_z); \765tangents.push_back(m_d);766767// front + back768y = start_pos.y;769thisrow = point;770prevrow = 0;771for (j = 0; j <= subdivide_h + 1; j++) {772float v = j;773float v2 = v / (subdivide_w + 1.0);774v /= (2.0 * (subdivide_h + 1.0));775776x = start_pos.x;777for (i = 0; i <= subdivide_w + 1; i++) {778float u = i;779float u2 = u / (subdivide_w + 1.0);780u /= (3.0 * (subdivide_w + 1.0));781782// front783points.push_back(Vector3(x, -y, -start_pos.z)); // double negative on the Z!784normals.push_back(Vector3(0.0, 0.0, 1.0));785ADD_TANGENT(1.0, 0.0, 0.0, 1.0);786uvs.push_back(Vector2(u, v));787if (p_add_uv2) {788uv2s.push_back(Vector2(u2 * width_h, v2 * height_v));789}790point++;791792// back793points.push_back(Vector3(-x, -y, start_pos.z));794normals.push_back(Vector3(0.0, 0.0, -1.0));795ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);796uvs.push_back(Vector2(twothirds + u, v));797if (p_add_uv2) {798uv2s.push_back(Vector2(u2 * width_h, height_v + padding_v + (v2 * height_v)));799}800point++;801802if (i > 0 && j > 0) {803int i2 = i * 2;804805// front806indices.push_back(prevrow + i2 - 2);807indices.push_back(prevrow + i2);808indices.push_back(thisrow + i2 - 2);809indices.push_back(prevrow + i2);810indices.push_back(thisrow + i2);811indices.push_back(thisrow + i2 - 2);812813// back814indices.push_back(prevrow + i2 - 1);815indices.push_back(prevrow + i2 + 1);816indices.push_back(thisrow + i2 - 1);817indices.push_back(prevrow + i2 + 1);818indices.push_back(thisrow + i2 + 1);819indices.push_back(thisrow + i2 - 1);820}821822x += size.x / (subdivide_w + 1.0);823}824825y += size.y / (subdivide_h + 1.0);826prevrow = thisrow;827thisrow = point;828}829830// left + right831y = start_pos.y;832thisrow = point;833prevrow = 0;834for (j = 0; j <= (subdivide_h + 1); j++) {835float v = j;836float v2 = v / (subdivide_h + 1.0);837v /= (2.0 * (subdivide_h + 1.0));838839z = start_pos.z;840for (i = 0; i <= (subdivide_d + 1); i++) {841float u = i;842float u2 = u / (subdivide_d + 1.0);843u /= (3.0 * (subdivide_d + 1.0));844845// right846points.push_back(Vector3(-start_pos.x, -y, -z));847normals.push_back(Vector3(1.0, 0.0, 0.0));848ADD_TANGENT(0.0, 0.0, -1.0, 1.0);849uvs.push_back(Vector2(onethird + u, v));850if (p_add_uv2) {851uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), v2 * height_v));852}853point++;854855// left856points.push_back(Vector3(start_pos.x, -y, z));857normals.push_back(Vector3(-1.0, 0.0, 0.0));858ADD_TANGENT(0.0, 0.0, 1.0, 1.0);859uvs.push_back(Vector2(u, 0.5 + v));860if (p_add_uv2) {861uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), height_v + padding_v + (v2 * height_v)));862}863point++;864865if (i > 0 && j > 0) {866int i2 = i * 2;867868// right869indices.push_back(prevrow + i2 - 2);870indices.push_back(prevrow + i2);871indices.push_back(thisrow + i2 - 2);872indices.push_back(prevrow + i2);873indices.push_back(thisrow + i2);874indices.push_back(thisrow + i2 - 2);875876// left877indices.push_back(prevrow + i2 - 1);878indices.push_back(prevrow + i2 + 1);879indices.push_back(thisrow + i2 - 1);880indices.push_back(prevrow + i2 + 1);881indices.push_back(thisrow + i2 + 1);882indices.push_back(thisrow + i2 - 1);883}884885z += size.z / (subdivide_d + 1.0);886}887888y += size.y / (subdivide_h + 1.0);889prevrow = thisrow;890thisrow = point;891}892893// top + bottom894z = start_pos.z;895thisrow = point;896prevrow = 0;897for (j = 0; j <= (subdivide_d + 1); j++) {898float v = j;899float v2 = v / (subdivide_d + 1.0);900v /= (2.0 * (subdivide_d + 1.0));901902x = start_pos.x;903for (i = 0; i <= (subdivide_w + 1); i++) {904float u = i;905float u2 = u / (subdivide_w + 1.0);906u /= (3.0 * (subdivide_w + 1.0));907908// top909points.push_back(Vector3(-x, -start_pos.y, -z));910normals.push_back(Vector3(0.0, 1.0, 0.0));911ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);912uvs.push_back(Vector2(onethird + u, 0.5 + v));913if (p_add_uv2) {914uv2s.push_back(Vector2(u2 * width_h, ((height_v + padding_v) * 2.0) + (v2 * depth_v)));915}916point++;917918// bottom919points.push_back(Vector3(x, start_pos.y, -z));920normals.push_back(Vector3(0.0, -1.0, 0.0));921ADD_TANGENT(1.0, 0.0, 0.0, 1.0);922uvs.push_back(Vector2(twothirds + u, 0.5 + v));923if (p_add_uv2) {924uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), ((height_v + padding_v) * 2.0) + (v2 * width_v)));925}926point++;927928if (i > 0 && j > 0) {929int i2 = i * 2;930931// top932indices.push_back(prevrow + i2 - 2);933indices.push_back(prevrow + i2);934indices.push_back(thisrow + i2 - 2);935indices.push_back(prevrow + i2);936indices.push_back(thisrow + i2);937indices.push_back(thisrow + i2 - 2);938939// bottom940indices.push_back(prevrow + i2 - 1);941indices.push_back(prevrow + i2 + 1);942indices.push_back(thisrow + i2 - 1);943indices.push_back(prevrow + i2 + 1);944indices.push_back(thisrow + i2 + 1);945indices.push_back(thisrow + i2 - 1);946}947948x += size.x / (subdivide_w + 1.0);949}950951z += size.z / (subdivide_d + 1.0);952prevrow = thisrow;953thisrow = point;954}955956p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);957p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);958p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);959p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);960if (p_add_uv2) {961p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);962}963p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);964}965966void BoxMesh::_bind_methods() {967ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxMesh::set_size);968ClassDB::bind_method(D_METHOD("get_size"), &BoxMesh::get_size);969970ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &BoxMesh::set_subdivide_width);971ClassDB::bind_method(D_METHOD("get_subdivide_width"), &BoxMesh::get_subdivide_width);972ClassDB::bind_method(D_METHOD("set_subdivide_height", "divisions"), &BoxMesh::set_subdivide_height);973ClassDB::bind_method(D_METHOD("get_subdivide_height"), &BoxMesh::get_subdivide_height);974ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &BoxMesh::set_subdivide_depth);975ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &BoxMesh::get_subdivide_depth);976977ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");978ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");979ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");980ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");981}982983void BoxMesh::set_size(const Vector3 &p_size) {984if (p_size.is_equal_approx(size)) {985return;986}987988size = p_size;989_update_lightmap_size();990request_update();991}992993Vector3 BoxMesh::get_size() const {994return size;995}996997void BoxMesh::set_subdivide_width(const int p_divisions) {998if (p_divisions == subdivide_w) {999return;1000}10011002subdivide_w = p_divisions > 0 ? p_divisions : 0;1003request_update();1004}10051006int BoxMesh::get_subdivide_width() const {1007return subdivide_w;1008}10091010void BoxMesh::set_subdivide_height(const int p_divisions) {1011if (p_divisions == subdivide_h) {1012return;1013}10141015subdivide_h = p_divisions > 0 ? p_divisions : 0;1016request_update();1017}10181019int BoxMesh::get_subdivide_height() const {1020return subdivide_h;1021}10221023void BoxMesh::set_subdivide_depth(const int p_divisions) {1024if (p_divisions == subdivide_d) {1025return;1026}10271028subdivide_d = p_divisions > 0 ? p_divisions : 0;1029request_update();1030}10311032int BoxMesh::get_subdivide_depth() const {1033return subdivide_d;1034}10351036/**1037CylinderMesh1038*/10391040void CylinderMesh::_update_lightmap_size() {1041if (get_add_uv2()) {1042// size must have changed, update lightmap size hint1043Size2i _lightmap_size_hint;1044float padding = get_uv2_padding();10451046float top_circumference = top_radius * Math::PI * 2.0;1047float bottom_circumference = bottom_radius * Math::PI * 2.0;10481049float _width = MAX(top_circumference, bottom_circumference) / texel_size + padding;1050_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.1051_lightmap_size_hint.x = MAX(1.0, _width);10521053float _height = ((height + (MAX(top_radius, bottom_radius) * 2.0)) / texel_size) + (2.0 * padding);10541055_lightmap_size_hint.y = MAX(1.0, _height);10561057set_lightmap_size_hint(_lightmap_size_hint);1058}1059}10601061void CylinderMesh::_create_mesh_array(Array &p_arr) const {1062bool _add_uv2 = get_add_uv2();1063float _uv2_padding = get_uv2_padding() * texel_size;10641065create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding);1066}10671068void 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) {1069int i, j, prevrow, thisrow, point;1070float x, y, z, u, v, radius, radius_h;10711072// Only used if we calculate UV21073float top_circumference = top_radius * Math::PI * 2.0;1074float bottom_circumference = bottom_radius * Math::PI * 2.0;1075float vertical_length = height + MAX(2.0 * top_radius, 2.0 * bottom_radius) + (2.0 * p_uv2_padding);1076float height_v = height / vertical_length;1077float padding_v = p_uv2_padding / vertical_length;10781079float horizontal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding);1080float center_h = 0.5 * (horizontal_length - p_uv2_padding) / horizontal_length;1081float top_h = top_circumference / horizontal_length;1082float bottom_h = bottom_circumference / horizontal_length;1083float padding_h = p_uv2_padding / horizontal_length;10841085// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't1086// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.1087int num_points = (rings + 2) * (radial_segments + 1) + 4 + 2 * radial_segments;1088LocalVector<Vector3> points;1089points.reserve(num_points);1090LocalVector<Vector3> normals;1091normals.reserve(num_points);1092LocalVector<float> tangents;1093tangents.reserve(num_points * 4);1094LocalVector<Vector2> uvs;1095uvs.reserve(num_points);1096LocalVector<Vector2> uv2s;1097if (p_add_uv2) {1098uv2s.reserve(num_points);1099}1100LocalVector<int> indices;1101indices.reserve((rings + 1) * (radial_segments) * 6 + 6 * radial_segments);1102point = 0;11031104#define ADD_TANGENT(m_x, m_y, m_z, m_d) \1105tangents.push_back(m_x); \1106tangents.push_back(m_y); \1107tangents.push_back(m_z); \1108tangents.push_back(m_d);11091110thisrow = 0;1111prevrow = 0;1112const real_t side_normal_y = (bottom_radius - top_radius) / height;1113for (j = 0; j <= (rings + 1); j++) {1114v = j;1115v /= (rings + 1);11161117radius = top_radius + ((bottom_radius - top_radius) * v);1118radius_h = top_h + ((bottom_h - top_h) * v);11191120y = height * v;1121y = (height * 0.5) - y;11221123for (i = 0; i <= radial_segments; i++) {1124u = i;1125u /= radial_segments;11261127if (i == radial_segments) {1128x = 0.0;1129z = 1.0;1130} else {1131x = Math::sin(u * Math::TAU);1132z = Math::cos(u * Math::TAU);1133}11341135Vector3 p = Vector3(x * radius, y, z * radius);1136points.push_back(p);1137normals.push_back(Vector3(x, side_normal_y, z).normalized());1138ADD_TANGENT(z, 0.0, -x, 1.0)1139uvs.push_back(Vector2(u, v * 0.5));1140if (p_add_uv2) {1141uv2s.push_back(Vector2(center_h + (u - 0.5) * radius_h, v * height_v));1142}1143point++;11441145if (i > 0 && j > 0) {1146indices.push_back(prevrow + i - 1);1147indices.push_back(prevrow + i);1148indices.push_back(thisrow + i - 1);11491150indices.push_back(prevrow + i);1151indices.push_back(thisrow + i);1152indices.push_back(thisrow + i - 1);1153}1154}11551156prevrow = thisrow;1157thisrow = point;1158}11591160// Adjust for bottom section, only used if we calculate UV2s.1161top_h = top_radius / horizontal_length;1162float top_v = top_radius / vertical_length;1163bottom_h = bottom_radius / horizontal_length;1164float bottom_v = bottom_radius / vertical_length;11651166// Add top.1167if (cap_top && top_radius > 0.0) {1168y = height * 0.5;11691170thisrow = point;1171points.push_back(Vector3(0.0, y, 0.0));1172normals.push_back(Vector3(0.0, 1.0, 0.0));1173ADD_TANGENT(1.0, 0.0, 0.0, 1.0)1174uvs.push_back(Vector2(0.25, 0.75));1175if (p_add_uv2) {1176uv2s.push_back(Vector2(top_h, height_v + padding_v + MAX(top_v, bottom_v)));1177}1178point++;11791180for (i = 0; i <= radial_segments; i++) {1181float r = i;1182r /= radial_segments;11831184if (i == radial_segments) {1185x = 0.0;1186z = 1.0;1187} else {1188x = Math::sin(r * Math::TAU);1189z = Math::cos(r * Math::TAU);1190}11911192u = ((x + 1.0) * 0.25);1193v = 0.5 + ((z + 1.0) * 0.25);11941195Vector3 p = Vector3(x * top_radius, y, z * top_radius);1196points.push_back(p);1197normals.push_back(Vector3(0.0, 1.0, 0.0));1198ADD_TANGENT(1.0, 0.0, 0.0, 1.0)1199uvs.push_back(Vector2(u, v));1200if (p_add_uv2) {1201uv2s.push_back(Vector2(top_h + (x * top_h), height_v + padding_v + MAX(top_v, bottom_v) + (z * top_v)));1202}1203point++;12041205if (i > 0) {1206indices.push_back(thisrow);1207indices.push_back(point - 1);1208indices.push_back(point - 2);1209}1210}1211}12121213// Add bottom.1214if (cap_bottom && bottom_radius > 0.0) {1215y = height * -0.5;12161217thisrow = point;1218points.push_back(Vector3(0.0, y, 0.0));1219normals.push_back(Vector3(0.0, -1.0, 0.0));1220ADD_TANGENT(1.0, 0.0, 0.0, 1.0)1221uvs.push_back(Vector2(0.75, 0.75));1222if (p_add_uv2) {1223uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h, height_v + padding_v + MAX(top_v, bottom_v)));1224}1225point++;12261227for (i = 0; i <= radial_segments; i++) {1228float r = i;1229r /= radial_segments;12301231if (i == radial_segments) {1232x = 0.0;1233z = 1.0;1234} else {1235x = Math::sin(r * Math::TAU);1236z = Math::cos(r * Math::TAU);1237}12381239u = 0.5 + ((x + 1.0) * 0.25);1240v = 1.0 - ((z + 1.0) * 0.25);12411242Vector3 p = Vector3(x * bottom_radius, y, z * bottom_radius);1243points.push_back(p);1244normals.push_back(Vector3(0.0, -1.0, 0.0));1245ADD_TANGENT(1.0, 0.0, 0.0, 1.0)1246uvs.push_back(Vector2(u, v));1247if (p_add_uv2) {1248uv2s.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)));1249}1250point++;12511252if (i > 0) {1253indices.push_back(thisrow);1254indices.push_back(point - 2);1255indices.push_back(point - 1);1256}1257}1258}12591260p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);1261p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);1262p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);1263p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);1264if (p_add_uv2) {1265p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);1266}1267p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);1268}12691270void CylinderMesh::_bind_methods() {1271ClassDB::bind_method(D_METHOD("set_top_radius", "radius"), &CylinderMesh::set_top_radius);1272ClassDB::bind_method(D_METHOD("get_top_radius"), &CylinderMesh::get_top_radius);1273ClassDB::bind_method(D_METHOD("set_bottom_radius", "radius"), &CylinderMesh::set_bottom_radius);1274ClassDB::bind_method(D_METHOD("get_bottom_radius"), &CylinderMesh::get_bottom_radius);1275ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderMesh::set_height);1276ClassDB::bind_method(D_METHOD("get_height"), &CylinderMesh::get_height);12771278ClassDB::bind_method(D_METHOD("set_radial_segments", "segments"), &CylinderMesh::set_radial_segments);1279ClassDB::bind_method(D_METHOD("get_radial_segments"), &CylinderMesh::get_radial_segments);1280ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings);1281ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings);12821283ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &CylinderMesh::set_cap_top);1284ClassDB::bind_method(D_METHOD("is_cap_top"), &CylinderMesh::is_cap_top);12851286ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &CylinderMesh::set_cap_bottom);1287ClassDB::bind_method(D_METHOD("is_cap_bottom"), &CylinderMesh::is_cap_bottom);12881289ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_top_radius", "get_top_radius");1290ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_bottom_radius", "get_bottom_radius");1291ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_height", "get_height");1292ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");1293ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings");1294ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top");1295ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom");1296}12971298void CylinderMesh::set_top_radius(const float p_radius) {1299if (Math::is_equal_approx(p_radius, top_radius)) {1300return;1301}13021303top_radius = p_radius;1304_update_lightmap_size();1305request_update();1306}13071308float CylinderMesh::get_top_radius() const {1309return top_radius;1310}13111312void CylinderMesh::set_bottom_radius(const float p_radius) {1313if (Math::is_equal_approx(p_radius, bottom_radius)) {1314return;1315}13161317bottom_radius = p_radius;1318_update_lightmap_size();1319request_update();1320}13211322float CylinderMesh::get_bottom_radius() const {1323return bottom_radius;1324}13251326void CylinderMesh::set_height(const float p_height) {1327if (Math::is_equal_approx(p_height, height)) {1328return;1329}13301331height = p_height;1332_update_lightmap_size();1333request_update();1334}13351336float CylinderMesh::get_height() const {1337return height;1338}13391340void CylinderMesh::set_radial_segments(const int p_segments) {1341if (p_segments == radial_segments) {1342return;1343}13441345radial_segments = p_segments > 4 ? p_segments : 4;1346request_update();1347}13481349int CylinderMesh::get_radial_segments() const {1350return radial_segments;1351}13521353void CylinderMesh::set_rings(const int p_rings) {1354if (p_rings == rings) {1355return;1356}13571358ERR_FAIL_COND(p_rings < 0);1359rings = p_rings;1360request_update();1361}13621363int CylinderMesh::get_rings() const {1364return rings;1365}13661367void CylinderMesh::set_cap_top(bool p_cap_top) {1368if (p_cap_top == cap_top) {1369return;1370}13711372cap_top = p_cap_top;1373request_update();1374}13751376bool CylinderMesh::is_cap_top() const {1377return cap_top;1378}13791380void CylinderMesh::set_cap_bottom(bool p_cap_bottom) {1381if (p_cap_bottom == cap_bottom) {1382return;1383}13841385cap_bottom = p_cap_bottom;1386request_update();1387}13881389bool CylinderMesh::is_cap_bottom() const {1390return cap_bottom;1391}13921393/**1394PlaneMesh1395*/13961397void PlaneMesh::_update_lightmap_size() {1398if (get_add_uv2()) {1399// size must have changed, update lightmap size hint1400Size2i _lightmap_size_hint;1401float padding = get_uv2_padding();14021403_lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding);1404_lightmap_size_hint.y = MAX(1.0, (size.y / texel_size) + padding);14051406set_lightmap_size_hint(_lightmap_size_hint);1407}1408}14091410void PlaneMesh::_create_mesh_array(Array &p_arr) const {1411int i, j, prevrow, thisrow, point;1412float x, z;14131414// Plane mesh can use default UV2 calculation as implemented in Primitive Mesh14151416Size2 start_pos = size * -0.5;14171418Vector3 normal = Vector3(0.0, 1.0, 0.0);1419if (orientation == FACE_X) {1420normal = Vector3(1.0, 0.0, 0.0);1421} else if (orientation == FACE_Z) {1422normal = Vector3(0.0, 0.0, 1.0);1423}14241425// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't1426// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.1427int num_points = (subdivide_d + 2) * (subdivide_w + 2);1428LocalVector<Vector3> points;1429points.reserve(num_points);1430LocalVector<Vector3> normals;1431normals.reserve(num_points);1432LocalVector<float> tangents;1433tangents.reserve(num_points * 4);1434LocalVector<Vector2> uvs;1435uvs.reserve(num_points);1436LocalVector<int> indices;1437indices.reserve((subdivide_d + 1) * (subdivide_w + 1) * 6);1438point = 0;14391440#define ADD_TANGENT(m_x, m_y, m_z, m_d) \1441tangents.push_back(m_x); \1442tangents.push_back(m_y); \1443tangents.push_back(m_z); \1444tangents.push_back(m_d);14451446/* top + bottom */1447z = start_pos.y;1448thisrow = point;1449prevrow = 0;1450for (j = 0; j <= (subdivide_d + 1); j++) {1451x = start_pos.x;1452for (i = 0; i <= (subdivide_w + 1); i++) {1453float u = i;1454float v = j;1455u /= (subdivide_w + 1.0);1456v /= (subdivide_d + 1.0);14571458if (orientation == FACE_X) {1459points.push_back(Vector3(0.0, z, x) + center_offset);1460} else if (orientation == FACE_Y) {1461points.push_back(Vector3(-x, 0.0, -z) + center_offset);1462} else if (orientation == FACE_Z) {1463points.push_back(Vector3(-x, z, 0.0) + center_offset);1464}1465normals.push_back(normal);1466if (orientation == FACE_X) {1467ADD_TANGENT(0.0, 0.0, -1.0, 1.0);1468} else {1469ADD_TANGENT(1.0, 0.0, 0.0, 1.0);1470}1471uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */1472point++;14731474if (i > 0 && j > 0) {1475indices.push_back(prevrow + i - 1);1476indices.push_back(prevrow + i);1477indices.push_back(thisrow + i - 1);1478indices.push_back(prevrow + i);1479indices.push_back(thisrow + i);1480indices.push_back(thisrow + i - 1);1481}14821483x += size.x / (subdivide_w + 1.0);1484}14851486z += size.y / (subdivide_d + 1.0);1487prevrow = thisrow;1488thisrow = point;1489}14901491p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);1492p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);1493p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);1494p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);1495p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);1496}14971498void PlaneMesh::_bind_methods() {1499ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaneMesh::set_size);1500ClassDB::bind_method(D_METHOD("get_size"), &PlaneMesh::get_size);15011502ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &PlaneMesh::set_subdivide_width);1503ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PlaneMesh::get_subdivide_width);1504ClassDB::bind_method(D_METHOD("set_subdivide_depth", "subdivide"), &PlaneMesh::set_subdivide_depth);1505ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PlaneMesh::get_subdivide_depth);15061507ClassDB::bind_method(D_METHOD("set_center_offset", "offset"), &PlaneMesh::set_center_offset);1508ClassDB::bind_method(D_METHOD("get_center_offset"), &PlaneMesh::get_center_offset);15091510ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &PlaneMesh::set_orientation);1511ClassDB::bind_method(D_METHOD("get_orientation"), &PlaneMesh::get_orientation);15121513ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");1514ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");1515ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");1516ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_center_offset", "get_center_offset");1517ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Face X,Face Y,Face Z"), "set_orientation", "get_orientation");15181519BIND_ENUM_CONSTANT(FACE_X)1520BIND_ENUM_CONSTANT(FACE_Y)1521BIND_ENUM_CONSTANT(FACE_Z)1522}15231524void PlaneMesh::set_size(const Size2 &p_size) {1525if (p_size == size) {1526return;1527}1528size = p_size;1529_update_lightmap_size();1530request_update();1531}15321533Size2 PlaneMesh::get_size() const {1534return size;1535}15361537void PlaneMesh::set_subdivide_width(const int p_divisions) {1538if (p_divisions == subdivide_w || (subdivide_w == 0 && p_divisions < 0)) {1539return;1540}1541subdivide_w = p_divisions > 0 ? p_divisions : 0;1542request_update();1543}15441545int PlaneMesh::get_subdivide_width() const {1546return subdivide_w;1547}15481549void PlaneMesh::set_subdivide_depth(const int p_divisions) {1550if (p_divisions == subdivide_d || (subdivide_d == 0 && p_divisions < 0)) {1551return;1552}1553subdivide_d = p_divisions > 0 ? p_divisions : 0;1554request_update();1555}15561557int PlaneMesh::get_subdivide_depth() const {1558return subdivide_d;1559}15601561void PlaneMesh::set_center_offset(const Vector3 p_offset) {1562if (p_offset.is_equal_approx(center_offset)) {1563return;1564}1565center_offset = p_offset;1566request_update();1567}15681569Vector3 PlaneMesh::get_center_offset() const {1570return center_offset;1571}15721573void PlaneMesh::set_orientation(const Orientation p_orientation) {1574if (p_orientation == orientation) {1575return;1576}1577orientation = p_orientation;1578request_update();1579}15801581PlaneMesh::Orientation PlaneMesh::get_orientation() const {1582return orientation;1583}15841585/**1586PrismMesh1587*/15881589void PrismMesh::_update_lightmap_size() {1590if (get_add_uv2()) {1591// size must have changed, update lightmap size hint1592Size2i _lightmap_size_hint;1593float padding = get_uv2_padding();15941595// left_to_right does not effect the surface area of the prism so we ignore that.1596// TODO we could combine the two triangles and save some space but we need to re-align the uv1 and adjust the tangent.15971598float width = (size.x + size.z) / texel_size;1599float length = (size.y + size.y + size.z) / texel_size;16001601_lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding;1602_lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding;16031604set_lightmap_size_hint(_lightmap_size_hint);1605}1606}16071608void PrismMesh::_create_mesh_array(Array &p_arr) const {1609int i, j, prevrow, thisrow, point;1610float x, y, z;1611float onethird = 1.0 / 3.0;1612float twothirds = 2.0 / 3.0;16131614// Only used if we calculate UV21615bool _add_uv2 = get_add_uv2();1616float _uv2_padding = get_uv2_padding() * texel_size;16171618float horizontal_total = size.x + size.z + 2.0 * _uv2_padding;1619float width_h = size.x / horizontal_total;1620float depth_h = size.z / horizontal_total;1621float padding_h = _uv2_padding / horizontal_total;16221623float vertical_total = (size.y + size.y + size.z) + (3.0 * _uv2_padding);1624float height_v = size.y / vertical_total;1625float depth_v = size.z / vertical_total;1626float padding_v = _uv2_padding / vertical_total;16271628// and start building16291630Vector3 start_pos = size * -0.5;16311632// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't1633// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.1634int num_points = (subdivide_h + 2) * (subdivide_w + 2) * 2 + (subdivide_h + 2) * (subdivide_d + 2) * 2 + (subdivide_d + 2) * (subdivide_w + 2);1635LocalVector<Vector3> points;1636points.reserve(num_points);1637LocalVector<Vector3> normals;1638normals.reserve(num_points);1639LocalVector<float> tangents;1640tangents.reserve(num_points * 4);1641LocalVector<Vector2> uvs;1642uvs.reserve(num_points);1643LocalVector<Vector2> uv2s;1644if (_add_uv2) {1645uv2s.reserve(num_points);1646}16471648int num_indices = (subdivide_h + 1) * (subdivide_w + 1) * 12 + (subdivide_w + 1) * 6;1649num_indices += (subdivide_h + 1) * (subdivide_d + 1) * 12;1650num_indices += (subdivide_d + 1) * (subdivide_w + 1) * 6;1651LocalVector<int> indices;1652indices.reserve(num_indices);1653point = 0;16541655#define ADD_TANGENT(m_x, m_y, m_z, m_d) \1656tangents.push_back(m_x); \1657tangents.push_back(m_y); \1658tangents.push_back(m_z); \1659tangents.push_back(m_d);16601661/* front + back */1662y = start_pos.y;1663thisrow = point;1664prevrow = 0;1665for (j = 0; j <= (subdivide_h + 1); j++) {1666float scale = j / (subdivide_h + 1.0);1667float scaled_size_x = size.x * scale;1668float start_x = start_pos.x + (1.0 - scale) * size.x * left_to_right;1669float offset_front = (1.0 - scale) * onethird * left_to_right;1670float offset_back = (1.0 - scale) * onethird * (1.0 - left_to_right);16711672float v = j;1673float v2 = scale;1674v /= 2.0 * (subdivide_h + 1.0);16751676x = 0.0;1677for (i = 0; i <= (subdivide_w + 1); i++) {1678float u = i;1679float u2 = i / (subdivide_w + 1.0);1680u /= (3.0 * (subdivide_w + 1.0));16811682u *= scale;16831684/* front */1685points.push_back(Vector3(start_x + x, -y, -start_pos.z)); // double negative on the Z!1686normals.push_back(Vector3(0.0, 0.0, 1.0));1687ADD_TANGENT(1.0, 0.0, 0.0, 1.0);1688uvs.push_back(Vector2(offset_front + u, v));1689if (_add_uv2) {1690uv2s.push_back(Vector2(u2 * scale * width_h, v2 * height_v));1691}1692point++;16931694/* back */1695points.push_back(Vector3(start_x + scaled_size_x - x, -y, start_pos.z));1696normals.push_back(Vector3(0.0, 0.0, -1.0));1697ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);1698uvs.push_back(Vector2(twothirds + offset_back + u, v));1699if (_add_uv2) {1700uv2s.push_back(Vector2(u2 * scale * width_h, height_v + padding_v + v2 * height_v));1701}1702point++;17031704if (i > 0 && j == 1) {1705int i2 = i * 2;17061707/* front */1708indices.push_back(prevrow + i2);1709indices.push_back(thisrow + i2);1710indices.push_back(thisrow + i2 - 2);17111712/* back */1713indices.push_back(prevrow + i2 + 1);1714indices.push_back(thisrow + i2 + 1);1715indices.push_back(thisrow + i2 - 1);1716} else if (i > 0 && j > 0) {1717int i2 = i * 2;17181719/* front */1720indices.push_back(prevrow + i2 - 2);1721indices.push_back(prevrow + i2);1722indices.push_back(thisrow + i2 - 2);1723indices.push_back(prevrow + i2);1724indices.push_back(thisrow + i2);1725indices.push_back(thisrow + i2 - 2);17261727/* back */1728indices.push_back(prevrow + i2 - 1);1729indices.push_back(prevrow + i2 + 1);1730indices.push_back(thisrow + i2 - 1);1731indices.push_back(prevrow + i2 + 1);1732indices.push_back(thisrow + i2 + 1);1733indices.push_back(thisrow + i2 - 1);1734}17351736x += scale * size.x / (subdivide_w + 1.0);1737}17381739y += size.y / (subdivide_h + 1.0);1740prevrow = thisrow;1741thisrow = point;1742}17431744/* left + right */1745Vector3 normal_left, normal_right;17461747normal_left = Vector3(-size.y, size.x * left_to_right, 0.0);1748normal_right = Vector3(size.y, size.x * (1.0 - left_to_right), 0.0);1749normal_left.normalize();1750normal_right.normalize();17511752y = start_pos.y;1753thisrow = point;1754prevrow = 0;1755for (j = 0; j <= (subdivide_h + 1); j++) {1756float left, right;1757float scale = j / (subdivide_h + 1.0);17581759left = start_pos.x + (size.x * (1.0 - scale) * left_to_right);1760right = left + (size.x * scale);17611762float v = j;1763float v2 = scale;1764v /= 2.0 * (subdivide_h + 1.0);17651766z = start_pos.z;1767for (i = 0; i <= (subdivide_d + 1); i++) {1768float u = i;1769float u2 = u / (subdivide_d + 1.0);1770u /= (3.0 * (subdivide_d + 1.0));17711772/* right */1773points.push_back(Vector3(right, -y, -z));1774normals.push_back(normal_right);1775ADD_TANGENT(0.0, 0.0, -1.0, 1.0);1776uvs.push_back(Vector2(onethird + u, v));1777if (_add_uv2) {1778uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, v2 * height_v));1779}1780point++;17811782/* left */1783points.push_back(Vector3(left, -y, z));1784normals.push_back(normal_left);1785ADD_TANGENT(0.0, 0.0, 1.0, 1.0);1786uvs.push_back(Vector2(u, 0.5 + v));1787if (_add_uv2) {1788uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, height_v + padding_v + v2 * height_v));1789}1790point++;17911792if (i > 0 && j > 0) {1793int i2 = i * 2;17941795/* right */1796indices.push_back(prevrow + i2 - 2);1797indices.push_back(prevrow + i2);1798indices.push_back(thisrow + i2 - 2);1799indices.push_back(prevrow + i2);1800indices.push_back(thisrow + i2);1801indices.push_back(thisrow + i2 - 2);18021803/* left */1804indices.push_back(prevrow + i2 - 1);1805indices.push_back(prevrow + i2 + 1);1806indices.push_back(thisrow + i2 - 1);1807indices.push_back(prevrow + i2 + 1);1808indices.push_back(thisrow + i2 + 1);1809indices.push_back(thisrow + i2 - 1);1810}18111812z += size.z / (subdivide_d + 1.0);1813}18141815y += size.y / (subdivide_h + 1.0);1816prevrow = thisrow;1817thisrow = point;1818}18191820/* bottom */1821z = start_pos.z;1822thisrow = point;1823prevrow = 0;1824for (j = 0; j <= (subdivide_d + 1); j++) {1825float v = j;1826float v2 = v / (subdivide_d + 1.0);1827v /= (2.0 * (subdivide_d + 1.0));18281829x = start_pos.x;1830for (i = 0; i <= (subdivide_w + 1); i++) {1831float u = i;1832float u2 = u / (subdivide_w + 1.0);1833u /= (3.0 * (subdivide_w + 1.0));18341835/* bottom */1836points.push_back(Vector3(x, start_pos.y, -z));1837normals.push_back(Vector3(0.0, -1.0, 0.0));1838ADD_TANGENT(1.0, 0.0, 0.0, 1.0);1839uvs.push_back(Vector2(twothirds + u, 0.5 + v));1840if (_add_uv2) {1841uv2s.push_back(Vector2(u2 * width_h, 2.0 * (height_v + padding_v) + v2 * depth_v));1842}1843point++;18441845if (i > 0 && j > 0) {1846/* bottom */1847indices.push_back(prevrow + i - 1);1848indices.push_back(prevrow + i);1849indices.push_back(thisrow + i - 1);1850indices.push_back(prevrow + i);1851indices.push_back(thisrow + i);1852indices.push_back(thisrow + i - 1);1853}18541855x += size.x / (subdivide_w + 1.0);1856}18571858z += size.z / (subdivide_d + 1.0);1859prevrow = thisrow;1860thisrow = point;1861}18621863p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);1864p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);1865p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);1866p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);1867if (_add_uv2) {1868p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);1869}1870p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);1871}18721873void PrismMesh::_bind_methods() {1874ClassDB::bind_method(D_METHOD("set_left_to_right", "left_to_right"), &PrismMesh::set_left_to_right);1875ClassDB::bind_method(D_METHOD("get_left_to_right"), &PrismMesh::get_left_to_right);18761877ClassDB::bind_method(D_METHOD("set_size", "size"), &PrismMesh::set_size);1878ClassDB::bind_method(D_METHOD("get_size"), &PrismMesh::get_size);18791880ClassDB::bind_method(D_METHOD("set_subdivide_width", "segments"), &PrismMesh::set_subdivide_width);1881ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PrismMesh::get_subdivide_width);1882ClassDB::bind_method(D_METHOD("set_subdivide_height", "segments"), &PrismMesh::set_subdivide_height);1883ClassDB::bind_method(D_METHOD("get_subdivide_height"), &PrismMesh::get_subdivide_height);1884ClassDB::bind_method(D_METHOD("set_subdivide_depth", "segments"), &PrismMesh::set_subdivide_depth);1885ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PrismMesh::get_subdivide_depth);18861887ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "left_to_right", PROPERTY_HINT_RANGE, "-2.0,2.0,0.1"), "set_left_to_right", "get_left_to_right");1888ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");1889ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");1890ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");1891ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");1892}18931894void PrismMesh::set_left_to_right(const float p_left_to_right) {1895if (Math::is_equal_approx(p_left_to_right, left_to_right)) {1896return;1897}1898left_to_right = p_left_to_right;1899request_update();1900}19011902float PrismMesh::get_left_to_right() const {1903return left_to_right;1904}19051906void PrismMesh::set_size(const Vector3 &p_size) {1907if (p_size.is_equal_approx(size)) {1908return;1909}1910size = p_size;1911_update_lightmap_size();1912request_update();1913}19141915Vector3 PrismMesh::get_size() const {1916return size;1917}19181919void PrismMesh::set_subdivide_width(const int p_divisions) {1920if (p_divisions == subdivide_w || (p_divisions < 0 && subdivide_w == 0)) {1921return;1922}1923subdivide_w = p_divisions > 0 ? p_divisions : 0;1924request_update();1925}19261927int PrismMesh::get_subdivide_width() const {1928return subdivide_w;1929}19301931void PrismMesh::set_subdivide_height(const int p_divisions) {1932if (p_divisions == subdivide_h || (p_divisions < 0 && subdivide_h == 0)) {1933return;1934}1935subdivide_h = p_divisions > 0 ? p_divisions : 0;1936request_update();1937}19381939int PrismMesh::get_subdivide_height() const {1940return subdivide_h;1941}19421943void PrismMesh::set_subdivide_depth(const int p_divisions) {1944if (p_divisions == subdivide_d || (p_divisions < 0 && subdivide_d == 0)) {1945return;1946}1947subdivide_d = p_divisions > 0 ? p_divisions : 0;1948request_update();1949}19501951int PrismMesh::get_subdivide_depth() const {1952return subdivide_d;1953}19541955/**1956SphereMesh1957*/19581959void SphereMesh::_update_lightmap_size() {1960if (get_add_uv2()) {1961// size must have changed, update lightmap size hint1962Size2i _lightmap_size_hint;1963float padding = get_uv2_padding();19641965float _width = radius * Math::TAU;1966_lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);1967float _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..1968_lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);19691970set_lightmap_size_hint(_lightmap_size_hint);1971}1972}19731974void SphereMesh::_create_mesh_array(Array &p_arr) const {1975bool _add_uv2 = get_add_uv2();1976float _uv2_padding = get_uv2_padding() * texel_size;19771978create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding);1979}19801981void 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) {1982int i, j, prevrow, thisrow, point;1983float x, y, z;19841985float scale = height / radius * (is_hemisphere ? 1.0 : 0.5);19861987// Only used if we calculate UV21988float circumference = radius * Math::TAU;1989float horizontal_length = circumference + p_uv2_padding;1990float center_h = 0.5 * circumference / horizontal_length;19911992float height_v = scale * Math::PI / ((scale * Math::PI) + p_uv2_padding / radius);19931994// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't1995// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.1996int num_points = (rings + 2) * (radial_segments + 1);1997LocalVector<Vector3> points;1998points.reserve(num_points);1999LocalVector<Vector3> normals;2000normals.reserve(num_points);2001LocalVector<float> tangents;2002tangents.reserve(num_points * 4);2003LocalVector<Vector2> uvs;2004uvs.reserve(num_points);2005LocalVector<Vector2> uv2s;2006if (p_add_uv2) {2007uv2s.reserve(num_points);2008}2009LocalVector<int> indices;2010indices.reserve((rings + 1) * (radial_segments) * 6);2011point = 0;20122013#define ADD_TANGENT(m_x, m_y, m_z, m_d) \2014tangents.push_back(m_x); \2015tangents.push_back(m_y); \2016tangents.push_back(m_z); \2017tangents.push_back(m_d);20182019thisrow = 0;2020prevrow = 0;2021for (j = 0; j <= (rings + 1); j++) {2022float v = j;2023float w;20242025v /= (rings + 1);2026if (j == (rings + 1)) {2027w = 0.0;2028y = -1.0;2029} else {2030w = Math::sin(Math::PI * v);2031y = Math::cos(Math::PI * v);2032}20332034for (i = 0; i <= radial_segments; i++) {2035float u = i;2036u /= radial_segments;20372038if (i == radial_segments) {2039x = 0.0;2040z = 1.0;2041} else {2042x = Math::sin(u * Math::TAU);2043z = Math::cos(u * Math::TAU);2044}20452046if (is_hemisphere && y < 0.0) {2047points.push_back(Vector3(x * radius * w, 0.0, z * radius * w));2048normals.push_back(Vector3(0.0, -1.0, 0.0));2049} else {2050Vector3 p = Vector3(x * w, y * scale, z * w);2051points.push_back(p * radius);2052Vector3 normal = Vector3(x * w * scale, y, z * w * scale);2053normals.push_back(normal.normalized());2054}2055ADD_TANGENT(z, 0.0, -x, 1.0)2056uvs.push_back(Vector2(u, v));2057if (p_add_uv2) {2058float w_h = w * 2.0 * center_h;2059uv2s.push_back(Vector2(center_h + ((u - 0.5) * w_h), v * height_v));2060}2061point++;20622063if (i > 0 && j > 0) {2064indices.push_back(prevrow + i - 1);2065indices.push_back(prevrow + i);2066indices.push_back(thisrow + i - 1);20672068indices.push_back(prevrow + i);2069indices.push_back(thisrow + i);2070indices.push_back(thisrow + i - 1);2071}2072}20732074prevrow = thisrow;2075thisrow = point;2076}20772078p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);2079p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);2080p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);2081p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);2082if (p_add_uv2) {2083p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);2084}2085p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);2086}20872088void SphereMesh::_bind_methods() {2089ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereMesh::set_radius);2090ClassDB::bind_method(D_METHOD("get_radius"), &SphereMesh::get_radius);2091ClassDB::bind_method(D_METHOD("set_height", "height"), &SphereMesh::set_height);2092ClassDB::bind_method(D_METHOD("get_height"), &SphereMesh::get_height);20932094ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &SphereMesh::set_radial_segments);2095ClassDB::bind_method(D_METHOD("get_radial_segments"), &SphereMesh::get_radial_segments);2096ClassDB::bind_method(D_METHOD("set_rings", "rings"), &SphereMesh::set_rings);2097ClassDB::bind_method(D_METHOD("get_rings"), &SphereMesh::get_rings);20982099ClassDB::bind_method(D_METHOD("set_is_hemisphere", "is_hemisphere"), &SphereMesh::set_is_hemisphere);2100ClassDB::bind_method(D_METHOD("get_is_hemisphere"), &SphereMesh::get_is_hemisphere);21012102ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");2103ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height");2104ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");2105ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");2106ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_hemisphere"), "set_is_hemisphere", "get_is_hemisphere");2107}21082109void SphereMesh::set_radius(const float p_radius) {2110if (Math::is_equal_approx(p_radius, radius)) {2111return;2112}2113radius = p_radius;2114_update_lightmap_size();2115request_update();2116}21172118float SphereMesh::get_radius() const {2119return radius;2120}21212122void SphereMesh::set_height(const float p_height) {2123if (Math::is_equal_approx(height, p_height)) {2124return;2125}2126height = p_height;2127_update_lightmap_size();2128request_update();2129}21302131float SphereMesh::get_height() const {2132return height;2133}21342135void SphereMesh::set_radial_segments(const int p_radial_segments) {2136if (p_radial_segments == radial_segments || (radial_segments == 4 && p_radial_segments < 4)) {2137return;2138}2139radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;2140request_update();2141}21422143int SphereMesh::get_radial_segments() const {2144return radial_segments;2145}21462147void SphereMesh::set_rings(const int p_rings) {2148if (p_rings == rings) {2149return;2150}2151ERR_FAIL_COND(p_rings < 1);2152rings = p_rings;2153request_update();2154}21552156int SphereMesh::get_rings() const {2157return rings;2158}21592160void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) {2161if (p_is_hemisphere == is_hemisphere) {2162return;2163}2164is_hemisphere = p_is_hemisphere;2165_update_lightmap_size();2166request_update();2167}21682169bool SphereMesh::get_is_hemisphere() const {2170return is_hemisphere;2171}21722173/**2174TorusMesh2175*/21762177void TorusMesh::_update_lightmap_size() {2178if (get_add_uv2()) {2179// size must have changed, update lightmap size hint2180Size2i _lightmap_size_hint;2181float padding = get_uv2_padding();21822183float min_radius = inner_radius;2184float max_radius = outer_radius;21852186if (min_radius > max_radius) {2187SWAP(min_radius, max_radius);2188}21892190float radius = (max_radius - min_radius) * 0.5;21912192float _width = max_radius * Math::TAU;2193_lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);2194float _height = radius * Math::TAU;2195_lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);21962197set_lightmap_size_hint(_lightmap_size_hint);2198}2199}22002201void TorusMesh::_create_mesh_array(Array &p_arr) const {2202// set our bounding box22032204bool _add_uv2 = get_add_uv2();22052206// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't2207// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.2208int num_points = (rings + 1) * (ring_segments + 1);2209LocalVector<Vector3> points;2210points.reserve(num_points);2211LocalVector<Vector3> normals;2212normals.reserve(num_points);2213LocalVector<float> tangents;2214tangents.reserve(num_points * 4);2215LocalVector<Vector2> uvs;2216uvs.reserve(num_points);2217LocalVector<Vector2> uv2s;2218if (_add_uv2) {2219uv2s.reserve(num_points);2220}2221LocalVector<int> indices;2222indices.reserve(rings * ring_segments * 6);22232224#define ADD_TANGENT(m_x, m_y, m_z, m_d) \2225tangents.push_back(m_x); \2226tangents.push_back(m_y); \2227tangents.push_back(m_z); \2228tangents.push_back(m_d);22292230ERR_FAIL_COND_MSG(inner_radius == outer_radius, "Inner radius and outer radius cannot be the same.");22312232float min_radius = inner_radius;2233float max_radius = outer_radius;22342235if (min_radius > max_radius) {2236SWAP(min_radius, max_radius);2237}22382239float radius = (max_radius - min_radius) * 0.5;22402241// Only used if we calculate UV22242float _uv2_padding = get_uv2_padding() * texel_size;22432244float horizontal_total = max_radius * Math::TAU + _uv2_padding;2245float max_h = max_radius * Math::TAU / horizontal_total;2246float delta_h = (max_radius - min_radius) * Math::TAU / horizontal_total;22472248float height_v = radius * Math::TAU / (radius * Math::TAU + _uv2_padding);22492250for (int i = 0; i <= rings; i++) {2251int prevrow = (i - 1) * (ring_segments + 1);2252int thisrow = i * (ring_segments + 1);2253float inci = float(i) / rings;2254float angi = inci * Math::TAU;22552256Vector2 normali = (i == rings) ? Vector2(0.0, -1.0) : Vector2(-Math::sin(angi), -Math::cos(angi));22572258for (int j = 0; j <= ring_segments; j++) {2259float incj = float(j) / ring_segments;2260float angj = incj * Math::TAU;22612262Vector2 normalj = (j == ring_segments) ? Vector2(-1.0, 0.0) : Vector2(-Math::cos(angj), Math::sin(angj));2263Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0);22642265float offset_h = 0.5 * (1.0 - normalj.x) * delta_h;2266float adj_h = max_h - offset_h;2267offset_h *= 0.5;22682269points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x));2270normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x));2271ADD_TANGENT(normali.y, 0.0, -normali.x, 1.0);2272uvs.push_back(Vector2(inci, incj));2273if (_add_uv2) {2274uv2s.push_back(Vector2(offset_h + inci * adj_h, incj * height_v));2275}22762277if (i > 0 && j > 0) {2278indices.push_back(thisrow + j - 1);2279indices.push_back(prevrow + j);2280indices.push_back(prevrow + j - 1);22812282indices.push_back(thisrow + j - 1);2283indices.push_back(thisrow + j);2284indices.push_back(prevrow + j);2285}2286}2287}22882289p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);2290p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);2291p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);2292p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);2293if (_add_uv2) {2294p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);2295}2296p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);2297}22982299void TorusMesh::_bind_methods() {2300ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &TorusMesh::set_inner_radius);2301ClassDB::bind_method(D_METHOD("get_inner_radius"), &TorusMesh::get_inner_radius);23022303ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &TorusMesh::set_outer_radius);2304ClassDB::bind_method(D_METHOD("get_outer_radius"), &TorusMesh::get_outer_radius);23052306ClassDB::bind_method(D_METHOD("set_rings", "rings"), &TorusMesh::set_rings);2307ClassDB::bind_method(D_METHOD("get_rings"), &TorusMesh::get_rings);23082309ClassDB::bind_method(D_METHOD("set_ring_segments", "rings"), &TorusMesh::set_ring_segments);2310ClassDB::bind_method(D_METHOD("get_ring_segments"), &TorusMesh::get_ring_segments);23112312ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");2313ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius");2314ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "3,128,1,or_greater"), "set_rings", "get_rings");2315ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_segments", PROPERTY_HINT_RANGE, "3,64,1,or_greater"), "set_ring_segments", "get_ring_segments");2316}23172318void TorusMesh::set_inner_radius(const float p_inner_radius) {2319if (Math::is_equal_approx(p_inner_radius, inner_radius)) {2320return;2321}2322inner_radius = p_inner_radius;2323request_update();2324}23252326float TorusMesh::get_inner_radius() const {2327return inner_radius;2328}23292330void TorusMesh::set_outer_radius(const float p_outer_radius) {2331if (Math::is_equal_approx(p_outer_radius, outer_radius)) {2332return;2333}2334outer_radius = p_outer_radius;2335request_update();2336}23372338float TorusMesh::get_outer_radius() const {2339return outer_radius;2340}23412342void TorusMesh::set_rings(const int p_rings) {2343if (p_rings == rings) {2344return;2345}2346ERR_FAIL_COND(p_rings < 3);2347rings = p_rings;2348request_update();2349}23502351int TorusMesh::get_rings() const {2352return rings;2353}23542355void TorusMesh::set_ring_segments(const int p_ring_segments) {2356if (p_ring_segments == ring_segments) {2357return;2358}2359ERR_FAIL_COND(p_ring_segments < 3);2360ring_segments = p_ring_segments;2361request_update();2362}23632364int TorusMesh::get_ring_segments() const {2365return ring_segments;2366}23672368/**2369PointMesh2370*/23712372void PointMesh::_create_mesh_array(Array &p_arr) const {2373Vector<Vector3> faces;2374faces.resize(1);2375faces.set(0, Vector3(0.0, 0.0, 0.0));23762377p_arr[RS::ARRAY_VERTEX] = faces;2378}23792380PointMesh::PointMesh() {2381primitive_type = PRIMITIVE_POINTS;2382}2383// TUBE TRAIL23842385void TubeTrailMesh::set_radius(const float p_radius) {2386if (Math::is_equal_approx(p_radius, radius)) {2387return;2388}2389radius = p_radius;2390request_update();2391}2392float TubeTrailMesh::get_radius() const {2393return radius;2394}23952396void TubeTrailMesh::set_radial_steps(const int p_radial_steps) {2397if (p_radial_steps == radial_steps) {2398return;2399}2400ERR_FAIL_COND(p_radial_steps < 3 || p_radial_steps > 128);2401radial_steps = p_radial_steps;2402request_update();2403}2404int TubeTrailMesh::get_radial_steps() const {2405return radial_steps;2406}24072408void TubeTrailMesh::set_sections(const int p_sections) {2409if (p_sections == sections) {2410return;2411}2412ERR_FAIL_COND(p_sections < 2 || p_sections > 128);2413sections = p_sections;2414request_update();2415}2416int TubeTrailMesh::get_sections() const {2417return sections;2418}24192420void TubeTrailMesh::set_section_length(float p_section_length) {2421if (p_section_length == section_length) {2422return;2423}2424section_length = p_section_length;2425request_update();2426}2427float TubeTrailMesh::get_section_length() const {2428return section_length;2429}24302431void TubeTrailMesh::set_section_rings(const int p_section_rings) {2432if (p_section_rings == section_rings) {2433return;2434}2435ERR_FAIL_COND(p_section_rings < 1 || p_section_rings > 1024);2436section_rings = p_section_rings;2437request_update();2438}2439int TubeTrailMesh::get_section_rings() const {2440return section_rings;2441}24422443void TubeTrailMesh::set_cap_top(bool p_cap_top) {2444if (p_cap_top == cap_top) {2445return;2446}2447cap_top = p_cap_top;2448request_update();2449}24502451bool TubeTrailMesh::is_cap_top() const {2452return cap_top;2453}24542455void TubeTrailMesh::set_cap_bottom(bool p_cap_bottom) {2456if (p_cap_bottom == cap_bottom) {2457return;2458}2459cap_bottom = p_cap_bottom;2460request_update();2461}24622463bool TubeTrailMesh::is_cap_bottom() const {2464return cap_bottom;2465}24662467void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) {2468if (curve == p_curve) {2469return;2470}2471if (curve.is_valid()) {2472curve->disconnect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed));2473}2474curve = p_curve;2475if (curve.is_valid()) {2476curve->connect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed));2477}2478request_update();2479}2480Ref<Curve> TubeTrailMesh::get_curve() const {2481return curve;2482}24832484void TubeTrailMesh::_curve_changed() {2485request_update();2486}2487int TubeTrailMesh::get_builtin_bind_pose_count() const {2488return sections + 1;2489}24902491Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const {2492float depth = section_length * sections;24932494Transform3D xform;2495xform.origin.y = depth / 2.0 - section_length * float(p_index);2496xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y24972498return xform;2499}25002501void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {2502// Seeing use case for TubeTrailMesh, no need to do anything more then default UV2 calculation25032504int total_rings = section_rings * sections;2505float depth = section_length * sections;25062507// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't2508// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.2509int num_points = (total_rings + 1) * (radial_steps + 1) + 4 + radial_steps * 2;2510LocalVector<Vector3> points;2511points.reserve(num_points);2512LocalVector<Vector3> normals;2513normals.reserve(num_points);2514LocalVector<float> tangents;2515tangents.reserve(num_points * 4);2516LocalVector<Vector2> uvs;2517uvs.reserve(num_points);2518LocalVector<int> bone_indices;2519bone_indices.reserve(num_points * 4);2520LocalVector<float> bone_weights;2521bone_weights.reserve(num_points * 4);2522LocalVector<int> indices;2523indices.reserve(total_rings * radial_steps * 6 + radial_steps * 6);25242525int point = 0;25262527#define ADD_TANGENT(m_x, m_y, m_z, m_d) \2528tangents.push_back(m_x); \2529tangents.push_back(m_y); \2530tangents.push_back(m_z); \2531tangents.push_back(m_d);25322533int thisrow = 0;2534int prevrow = 0;25352536for (int j = 0; j <= total_rings; j++) {2537float v = j;2538v /= total_rings;25392540float y = depth * v;2541y = (depth * 0.5) - y;25422543int bone = j / section_rings;2544float blend = 1.0 - float(j % section_rings) / float(section_rings);25452546for (int i = 0; i <= radial_steps; i++) {2547float u = i;2548u /= radial_steps;25492550float r = radius;2551if (curve.is_valid() && curve->get_point_count() > 0) {2552r *= curve->sample_baked(v);2553}2554float x = 0.0;2555float z = 1.0;2556if (i < radial_steps) {2557x = Math::sin(u * Math::TAU);2558z = Math::cos(u * Math::TAU);2559}25602561Vector3 p = Vector3(x * r, y, z * r);2562points.push_back(p);2563normals.push_back(Vector3(x, 0, z));2564ADD_TANGENT(z, 0.0, -x, 1.0)2565uvs.push_back(Vector2(u, v * 0.5));2566point++;2567{2568bone_indices.push_back(bone);2569bone_indices.push_back(MIN(sections, bone + 1));2570bone_indices.push_back(0);2571bone_indices.push_back(0);25722573bone_weights.push_back(blend);2574bone_weights.push_back(1.0 - blend);2575bone_weights.push_back(0);2576bone_weights.push_back(0);2577}25782579if (i > 0 && j > 0) {2580indices.push_back(prevrow + i - 1);2581indices.push_back(prevrow + i);2582indices.push_back(thisrow + i - 1);25832584indices.push_back(prevrow + i);2585indices.push_back(thisrow + i);2586indices.push_back(thisrow + i - 1);2587}2588}25892590prevrow = thisrow;2591thisrow = point;2592}25932594if (cap_top) {2595// add top2596float scale_pos = 1.0;2597if (curve.is_valid() && curve->get_point_count() > 0) {2598scale_pos = curve->sample_baked(0);2599}26002601if (scale_pos > CMP_EPSILON) {2602float y = depth * 0.5;26032604thisrow = point;2605points.push_back(Vector3(0.0, y, 0));2606normals.push_back(Vector3(0.0, 1.0, 0.0));2607ADD_TANGENT(1.0, 0.0, 0.0, 1.0)2608uvs.push_back(Vector2(0.25, 0.75));2609point++;26102611bone_indices.push_back(0);2612bone_indices.push_back(0);2613bone_indices.push_back(0);2614bone_indices.push_back(0);26152616bone_weights.push_back(1.0);2617bone_weights.push_back(0);2618bone_weights.push_back(0);2619bone_weights.push_back(0);26202621float rm = radius * scale_pos;26222623for (int i = 0; i <= radial_steps; i++) {2624float r = i;2625r /= radial_steps;26262627float x = 0.0;2628float z = 1.0;2629if (i < radial_steps) {2630x = Math::sin(r * Math::TAU);2631z = Math::cos(r * Math::TAU);2632}26332634float u = ((x + 1.0) * 0.25);2635float v = 0.5 + ((z + 1.0) * 0.25);26362637Vector3 p = Vector3(x * rm, y, z * rm);2638points.push_back(p);2639normals.push_back(Vector3(0.0, 1.0, 0.0));2640ADD_TANGENT(1.0, 0.0, 0.0, 1.0)2641uvs.push_back(Vector2(u, v));2642point++;26432644bone_indices.push_back(0);2645bone_indices.push_back(0);2646bone_indices.push_back(0);2647bone_indices.push_back(0);26482649bone_weights.push_back(1.0);2650bone_weights.push_back(0);2651bone_weights.push_back(0);2652bone_weights.push_back(0);26532654if (i > 0) {2655indices.push_back(thisrow);2656indices.push_back(point - 1);2657indices.push_back(point - 2);2658}2659}2660}2661}26622663if (cap_bottom) {2664float scale_neg = 1.0;2665if (curve.is_valid() && curve->get_point_count() > 0) {2666scale_neg = curve->sample_baked(1.0);2667}26682669if (scale_neg > CMP_EPSILON) {2670// add bottom2671float y = depth * -0.5;26722673thisrow = point;2674points.push_back(Vector3(0.0, y, 0.0));2675normals.push_back(Vector3(0.0, -1.0, 0.0));2676ADD_TANGENT(1.0, 0.0, 0.0, 1.0)2677uvs.push_back(Vector2(0.75, 0.75));2678point++;26792680bone_indices.push_back(sections);2681bone_indices.push_back(0);2682bone_indices.push_back(0);2683bone_indices.push_back(0);26842685bone_weights.push_back(1.0);2686bone_weights.push_back(0);2687bone_weights.push_back(0);2688bone_weights.push_back(0);26892690float rm = radius * scale_neg;26912692for (int i = 0; i <= radial_steps; i++) {2693float r = i;2694r /= radial_steps;26952696float x = 0.0;2697float z = 1.0;2698if (i < radial_steps) {2699x = Math::sin(r * Math::TAU);2700z = Math::cos(r * Math::TAU);2701}27022703float u = 0.5 + ((x + 1.0) * 0.25);2704float v = 1.0 - ((z + 1.0) * 0.25);27052706Vector3 p = Vector3(x * rm, y, z * rm);2707points.push_back(p);2708normals.push_back(Vector3(0.0, -1.0, 0.0));2709ADD_TANGENT(1.0, 0.0, 0.0, 1.0)2710uvs.push_back(Vector2(u, v));2711point++;27122713bone_indices.push_back(sections);2714bone_indices.push_back(0);2715bone_indices.push_back(0);2716bone_indices.push_back(0);27172718bone_weights.push_back(1.0);2719bone_weights.push_back(0);2720bone_weights.push_back(0);2721bone_weights.push_back(0);27222723if (i > 0) {2724indices.push_back(thisrow);2725indices.push_back(point - 2);2726indices.push_back(point - 1);2727}2728}2729}2730}27312732p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);2733p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);2734p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);2735p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);2736p_arr[RS::ARRAY_BONES] = Vector<int>(bone_indices);2737p_arr[RS::ARRAY_WEIGHTS] = Vector<float>(bone_weights);2738p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);2739}27402741void TubeTrailMesh::_bind_methods() {2742ClassDB::bind_method(D_METHOD("set_radius", "radius"), &TubeTrailMesh::set_radius);2743ClassDB::bind_method(D_METHOD("get_radius"), &TubeTrailMesh::get_radius);27442745ClassDB::bind_method(D_METHOD("set_radial_steps", "radial_steps"), &TubeTrailMesh::set_radial_steps);2746ClassDB::bind_method(D_METHOD("get_radial_steps"), &TubeTrailMesh::get_radial_steps);27472748ClassDB::bind_method(D_METHOD("set_sections", "sections"), &TubeTrailMesh::set_sections);2749ClassDB::bind_method(D_METHOD("get_sections"), &TubeTrailMesh::get_sections);27502751ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &TubeTrailMesh::set_section_length);2752ClassDB::bind_method(D_METHOD("get_section_length"), &TubeTrailMesh::get_section_length);27532754ClassDB::bind_method(D_METHOD("set_section_rings", "section_rings"), &TubeTrailMesh::set_section_rings);2755ClassDB::bind_method(D_METHOD("get_section_rings"), &TubeTrailMesh::get_section_rings);27562757ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &TubeTrailMesh::set_cap_top);2758ClassDB::bind_method(D_METHOD("is_cap_top"), &TubeTrailMesh::is_cap_top);27592760ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &TubeTrailMesh::set_cap_bottom);2761ClassDB::bind_method(D_METHOD("is_cap_bottom"), &TubeTrailMesh::is_cap_bottom);27622763ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve);2764ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve);27652766ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");27672768ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_steps", PROPERTY_HINT_RANGE, "3,128,1"), "set_radial_steps", "get_radial_steps");2769ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections");27702771ADD_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");27722773ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings");27742775ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top");2776ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom");27772778ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");2779}27802781TubeTrailMesh::TubeTrailMesh() {2782}27832784// RIBBON TRAIL27852786void RibbonTrailMesh::set_shape(Shape p_shape) {2787if (p_shape == shape) {2788return;2789}2790shape = p_shape;2791request_update();2792}2793RibbonTrailMesh::Shape RibbonTrailMesh::get_shape() const {2794return shape;2795}27962797void RibbonTrailMesh::set_size(const float p_size) {2798if (Math::is_equal_approx(p_size, size)) {2799return;2800}2801size = p_size;2802request_update();2803}2804float RibbonTrailMesh::get_size() const {2805return size;2806}28072808void RibbonTrailMesh::set_sections(const int p_sections) {2809if (p_sections == sections) {2810return;2811}2812ERR_FAIL_COND(p_sections < 2 || p_sections > 128);2813sections = p_sections;2814request_update();2815}2816int RibbonTrailMesh::get_sections() const {2817return sections;2818}28192820void RibbonTrailMesh::set_section_length(float p_section_length) {2821if (p_section_length == section_length) {2822return;2823}2824section_length = p_section_length;2825request_update();2826}2827float RibbonTrailMesh::get_section_length() const {2828return section_length;2829}28302831void RibbonTrailMesh::set_section_segments(const int p_section_segments) {2832if (p_section_segments == section_segments) {2833return;2834}2835ERR_FAIL_COND(p_section_segments < 1 || p_section_segments > 1024);2836section_segments = p_section_segments;2837request_update();2838}2839int RibbonTrailMesh::get_section_segments() const {2840return section_segments;2841}28422843void RibbonTrailMesh::set_curve(const Ref<Curve> &p_curve) {2844if (curve == p_curve) {2845return;2846}2847if (curve.is_valid()) {2848curve->disconnect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed));2849}2850curve = p_curve;2851if (curve.is_valid()) {2852curve->connect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed));2853}2854request_update();2855}2856Ref<Curve> RibbonTrailMesh::get_curve() const {2857return curve;2858}28592860void RibbonTrailMesh::_curve_changed() {2861request_update();2862}2863int RibbonTrailMesh::get_builtin_bind_pose_count() const {2864return sections + 1;2865}28662867Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const {2868float depth = section_length * sections;28692870Transform3D xform;2871xform.origin.y = depth / 2.0 - section_length * float(p_index);2872xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y28732874return xform;2875}28762877void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const {2878// Seeing use case of ribbon trail mesh, no need to implement special UV2 calculation28792880int total_segments = section_segments * sections;2881float depth = section_length * sections;28822883// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't2884// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.2885int num_points = (total_segments + 1) * 2;2886num_points *= shape == SHAPE_CROSS ? 2 : 1;2887LocalVector<Vector3> points;2888points.reserve(num_points);2889LocalVector<Vector3> normals;2890normals.reserve(num_points);2891LocalVector<float> tangents;2892tangents.reserve(num_points * 4);2893LocalVector<Vector2> uvs;2894uvs.reserve(num_points);2895LocalVector<int> bone_indices;2896bone_indices.reserve(num_points * 4);2897LocalVector<float> bone_weights;2898bone_weights.reserve(num_points * 4);2899LocalVector<int> indices;2900indices.reserve(total_segments * 6 * (shape == SHAPE_CROSS ? 2 : 1));29012902#define ADD_TANGENT(m_x, m_y, m_z, m_d) \2903tangents.push_back(m_x); \2904tangents.push_back(m_y); \2905tangents.push_back(m_z); \2906tangents.push_back(m_d);29072908for (int j = 0; j <= total_segments; j++) {2909float v = j;2910v /= total_segments;29112912float y = depth * v;2913y = (depth * 0.5) - y;29142915int bone = j / section_segments;2916float blend = 1.0 - float(j % section_segments) / float(section_segments);29172918float s = size;29192920if (curve.is_valid() && curve->get_point_count() > 0) {2921s *= curve->sample_baked(v);2922}29232924points.push_back(Vector3(-s * 0.5, y, 0));2925points.push_back(Vector3(+s * 0.5, y, 0));2926if (shape == SHAPE_CROSS) {2927points.push_back(Vector3(0, y, -s * 0.5));2928points.push_back(Vector3(0, y, +s * 0.5));2929}29302931normals.push_back(Vector3(0, 0, 1));2932normals.push_back(Vector3(0, 0, 1));2933if (shape == SHAPE_CROSS) {2934normals.push_back(Vector3(1, 0, 0));2935normals.push_back(Vector3(1, 0, 0));2936}29372938uvs.push_back(Vector2(0, v));2939uvs.push_back(Vector2(1, v));2940if (shape == SHAPE_CROSS) {2941uvs.push_back(Vector2(0, v));2942uvs.push_back(Vector2(1, v));2943}29442945ADD_TANGENT(0.0, 1.0, 0.0, 1.0)2946ADD_TANGENT(0.0, 1.0, 0.0, 1.0)2947if (shape == SHAPE_CROSS) {2948ADD_TANGENT(0.0, 1.0, 0.0, 1.0)2949ADD_TANGENT(0.0, 1.0, 0.0, 1.0)2950}29512952for (int i = 0; i < (shape == SHAPE_CROSS ? 4 : 2); i++) {2953bone_indices.push_back(bone);2954bone_indices.push_back(MIN(sections, bone + 1));2955bone_indices.push_back(0);2956bone_indices.push_back(0);29572958bone_weights.push_back(blend);2959bone_weights.push_back(1.0 - blend);2960bone_weights.push_back(0);2961bone_weights.push_back(0);2962}29632964if (j > 0) {2965if (shape == SHAPE_CROSS) {2966int base = j * 4 - 4;2967indices.push_back(base + 0);2968indices.push_back(base + 1);2969indices.push_back(base + 4);29702971indices.push_back(base + 1);2972indices.push_back(base + 5);2973indices.push_back(base + 4);29742975indices.push_back(base + 2);2976indices.push_back(base + 3);2977indices.push_back(base + 6);29782979indices.push_back(base + 3);2980indices.push_back(base + 7);2981indices.push_back(base + 6);2982} else {2983int base = j * 2 - 2;2984indices.push_back(base + 0);2985indices.push_back(base + 1);2986indices.push_back(base + 2);29872988indices.push_back(base + 1);2989indices.push_back(base + 3);2990indices.push_back(base + 2);2991}2992}2993}29942995p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);2996p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);2997p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);2998p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);2999p_arr[RS::ARRAY_BONES] = Vector<int>(bone_indices);3000p_arr[RS::ARRAY_WEIGHTS] = Vector<float>(bone_weights);3001p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);3002}30033004void RibbonTrailMesh::_bind_methods() {3005ClassDB::bind_method(D_METHOD("set_size", "size"), &RibbonTrailMesh::set_size);3006ClassDB::bind_method(D_METHOD("get_size"), &RibbonTrailMesh::get_size);30073008ClassDB::bind_method(D_METHOD("set_sections", "sections"), &RibbonTrailMesh::set_sections);3009ClassDB::bind_method(D_METHOD("get_sections"), &RibbonTrailMesh::get_sections);30103011ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &RibbonTrailMesh::set_section_length);3012ClassDB::bind_method(D_METHOD("get_section_length"), &RibbonTrailMesh::get_section_length);30133014ClassDB::bind_method(D_METHOD("set_section_segments", "section_segments"), &RibbonTrailMesh::set_section_segments);3015ClassDB::bind_method(D_METHOD("get_section_segments"), &RibbonTrailMesh::get_section_segments);30163017ClassDB::bind_method(D_METHOD("set_curve", "curve"), &RibbonTrailMesh::set_curve);3018ClassDB::bind_method(D_METHOD("get_curve"), &RibbonTrailMesh::get_curve);30193020ClassDB::bind_method(D_METHOD("set_shape", "shape"), &RibbonTrailMesh::set_shape);3021ClassDB::bind_method(D_METHOD("get_shape"), &RibbonTrailMesh::get_shape);30223023ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Flat,Cross"), "set_shape", "get_shape");3024ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_size", "get_size");3025ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections");3026ADD_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");3027ADD_PROPERTY(PropertyInfo(Variant::INT, "section_segments", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_segments", "get_section_segments");3028ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");30293030BIND_ENUM_CONSTANT(SHAPE_FLAT)3031BIND_ENUM_CONSTANT(SHAPE_CROSS)3032}30333034RibbonTrailMesh::RibbonTrailMesh() {3035}30363037/*************************************************************************/3038/* TextMesh */3039/*************************************************************************/30403041void TextMesh::_generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph &p_gl) const {3042if (cache.has(p_key)) {3043return;3044}30453046GlyphMeshData &gl_data = cache[p_key];30473048Dictionary d = TS->font_get_glyph_contours(p_gl.font_rid, p_gl.font_size, p_gl.index);30493050PackedVector3Array points = d["points"];3051PackedInt32Array contours = d["contours"];3052bool orientation = d["orientation"];30533054if (points.size() < 3 || contours.is_empty()) {3055return; // No full contours, only glyph control points (or nothing), ignore.3056}30573058// Approximate Bezier curves as polygons.3059// See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html, for more info.3060for (int i = 0; i < contours.size(); i++) {3061int32_t start = (i == 0) ? 0 : (contours[i - 1] + 1);3062int32_t end = contours[i];3063Vector<ContourPoint> polygon;30643065for (int32_t j = start; j <= end; j++) {3066if (points[j].z == (real_t)TextServer::CONTOUR_CURVE_TAG_ON) {3067// Point on the curve.3068Vector2 p = Vector2(points[j].x, points[j].y) * pixel_size;3069polygon.push_back(ContourPoint(p, true));3070} else if (points[j].z == (real_t)TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {3071// Conic Bezier arc.3072int32_t next = (j == end) ? start : (j + 1);3073int32_t prev = (j == start) ? end : (j - 1);3074Vector2 p0;3075Vector2 p1 = Vector2(points[j].x, points[j].y);3076Vector2 p2;30773078// For successive conic OFF points add a virtual ON point in the middle.3079if (points[prev].z == (real_t)TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {3080p0 = (Vector2(points[prev].x, points[prev].y) + Vector2(points[j].x, points[j].y)) / 2.0;3081} else if (points[prev].z == (real_t)TextServer::CONTOUR_CURVE_TAG_ON) {3082p0 = Vector2(points[prev].x, points[prev].y);3083} else {3084ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j));3085}3086if (points[next].z == (real_t)TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {3087p2 = (Vector2(points[j].x, points[j].y) + Vector2(points[next].x, points[next].y)) / 2.0;3088} else if (points[next].z == (real_t)TextServer::CONTOUR_CURVE_TAG_ON) {3089p2 = Vector2(points[next].x, points[next].y);3090} else {3091ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j));3092}30933094real_t step = CLAMP(curve_step / (p0 - p2).length(), 0.01, 0.5);3095real_t t = step;3096while (t < 1.0) {3097real_t omt = (1.0 - t);3098real_t omt2 = omt * omt;3099real_t t2 = t * t;31003101Vector2 point = p1 + omt2 * (p0 - p1) + t2 * (p2 - p1);3102Vector2 p = point * pixel_size;3103polygon.push_back(ContourPoint(p, false));3104t += step;3105}3106} else if (points[j].z == (real_t)TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC) {3107// Cubic Bezier arc.3108int32_t cur = j;3109int32_t next1 = (j == end) ? start : (j + 1);3110int32_t next2 = (next1 == end) ? start : (next1 + 1);3111int32_t prev = (j == start) ? end : (j - 1);31123113// There must be exactly two OFF points and two ON points for each cubic arc.3114if (points[prev].z != (real_t)TextServer::CONTOUR_CURVE_TAG_ON) {3115cur = (cur == 0) ? end : cur - 1;3116next1 = (next1 == 0) ? end : next1 - 1;3117next2 = (next2 == 0) ? end : next2 - 1;3118prev = (prev == 0) ? end : prev - 1;3119} else {3120j++;3121}3122ERR_FAIL_COND_MSG(points[prev].z != (real_t)TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, prev));3123ERR_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));3124ERR_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));3125ERR_FAIL_COND_MSG(points[next2].z != (real_t)TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, next2));31263127Vector2 p0 = Vector2(points[prev].x, points[prev].y);3128Vector2 p1 = Vector2(points[cur].x, points[cur].y);3129Vector2 p2 = Vector2(points[next1].x, points[next1].y);3130Vector2 p3 = Vector2(points[next2].x, points[next2].y);31313132real_t step = CLAMP(curve_step / (p0 - p3).length(), 0.01, 0.5);3133real_t t = step;3134while (t < 1.0) {3135Vector2 point = p0.bezier_interpolate(p1, p2, p3, t);3136Vector2 p = point * pixel_size;3137polygon.push_back(ContourPoint(p, false));3138t += step;3139}3140} else {3141ERR_FAIL_MSG(vformat("Unknown point tag at %d:%d", i, j));3142}3143}31443145if (polygon.size() < 3) {3146continue; // Skip glyph control points.3147}31483149if (!orientation) {3150polygon.reverse();3151}31523153gl_data.contours.push_back(polygon);3154}31553156// Calculate bounds.3157List<TPPLPoly> in_poly;3158for (int i = 0; i < gl_data.contours.size(); i++) {3159TPPLPoly inp;3160inp.Init(gl_data.contours[i].size());3161real_t length = 0.0;3162for (int j = 0; j < gl_data.contours[i].size(); j++) {3163int next = (j + 1 == gl_data.contours[i].size()) ? 0 : (j + 1);31643165gl_data.min_p = gl_data.min_p.min(gl_data.contours[i][j].point);3166gl_data.max_p = gl_data.max_p.max(gl_data.contours[i][j].point);3167length += (gl_data.contours[i][next].point - gl_data.contours[i][j].point).length();31683169inp.GetPoint(j) = gl_data.contours[i][j].point;3170}3171TPPLOrientation poly_orient = inp.GetOrientation();3172if (poly_orient == TPPL_ORIENTATION_CW) {3173inp.SetHole(true);3174}3175in_poly.push_back(inp);3176gl_data.contours_info.push_back(ContourInfo(length, poly_orient == TPPL_ORIENTATION_CCW));3177}31783179TPPLPartition tpart;31803181//Decompose and triangulate.3182List<TPPLPoly> out_poly;3183if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) {3184ERR_FAIL_MSG("Convex decomposing failed. Make sure the font doesn't contain self-intersecting lines, as these are not supported in TextMesh.");3185}3186List<TPPLPoly> out_tris;3187for (TPPLPoly &tp : out_poly) {3188if (tpart.Triangulate_OPT(&tp, &out_tris) == 0) {3189ERR_FAIL_MSG("Triangulation failed. Make sure the font doesn't contain self-intersecting lines, as these are not supported in TextMesh.");3190}3191}31923193for (const TPPLPoly &tp : out_tris) {3194ERR_FAIL_COND(tp.GetNumPoints() != 3); // Triangles only.31953196for (int i = 0; i < 3; i++) {3197gl_data.triangles.push_back(Vector2(tp.GetPoint(i).x, tp.GetPoint(i).y));3198}3199}3200}32013202void TextMesh::_create_mesh_array(Array &p_arr) const {3203Ref<Font> font = _get_font_or_default();3204ERR_FAIL_COND(font.is_null());32053206if (dirty_cache) {3207cache.clear();3208dirty_cache = false;3209}32103211// When a shaped text is invalidated by an external source, we want to reshape it.3212if (!TS->shaped_text_is_ready(text_rid)) {3213dirty_text = true;3214}32153216for (const RID &line_rid : lines_rid) {3217if (!TS->shaped_text_is_ready(line_rid)) {3218dirty_lines = true;3219break;3220}3221}32223223// Update text buffer.3224if (dirty_text) {3225TS->shaped_text_clear(text_rid);3226TS->shaped_text_set_direction(text_rid, text_direction);32273228String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;3229TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), language);32303231TypedArray<Vector3i> stt;3232if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {3233GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);3234} else {3235stt = TS->parse_structured_text(st_parser, st_args, txt);3236}3237TS->shaped_text_set_bidi_override(text_rid, stt);32383239dirty_text = false;3240dirty_font = false;3241dirty_lines = true;3242} else if (dirty_font) {3243int spans = TS->shaped_get_span_count(text_rid);3244for (int i = 0; i < spans; i++) {3245TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features());3246}32473248dirty_font = false;3249dirty_lines = true;3250}32513252if (dirty_lines) {3253for (int i = 0; i < lines_rid.size(); i++) {3254TS->free_rid(lines_rid[i]);3255}3256lines_rid.clear();32573258BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;3259switch (autowrap_mode) {3260case TextServer::AUTOWRAP_WORD_SMART:3261autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY;3262break;3263case TextServer::AUTOWRAP_WORD:3264autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;3265break;3266case TextServer::AUTOWRAP_ARBITRARY:3267autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;3268break;3269case TextServer::AUTOWRAP_OFF:3270break;3271}3272PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);32733274float max_line_w = 0.0;3275for (int i = 0; i < line_breaks.size(); i = i + 2) {3276RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);3277max_line_w = MAX(max_line_w, TS->shaped_text_get_width(line));3278lines_rid.push_back(line);3279}32803281if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {3282int jst_to_line = lines_rid.size();3283if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) {3284jst_to_line = lines_rid.size();3285} else {3286if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) {3287jst_to_line = lines_rid.size() - 1;3288}3289if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) {3290for (int i = lines_rid.size() - 1; i >= 0; i--) {3291if (TS->shaped_text_has_visible_chars(lines_rid[i])) {3292jst_to_line = i;3293break;3294}3295}3296}3297}3298for (int i = 0; i < jst_to_line; i++) {3299TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, jst_flags);3300}3301}3302dirty_lines = false;3303}33043305float total_h = 0.0;3306for (int i = 0; i < lines_rid.size(); i++) {3307total_h += (TS->shaped_text_get_size(lines_rid[i]).y + line_spacing) * pixel_size;3308}33093310float vbegin = 0.0;3311switch (vertical_alignment) {3312case VERTICAL_ALIGNMENT_FILL:3313case VERTICAL_ALIGNMENT_TOP: {3314// Nothing.3315} break;3316case VERTICAL_ALIGNMENT_CENTER: {3317vbegin = (total_h - line_spacing * pixel_size) / 2.0;3318} break;3319case VERTICAL_ALIGNMENT_BOTTOM: {3320vbegin = (total_h - line_spacing * pixel_size);3321} break;3322}33233324Vector<Vector3> vertices;3325Vector<Vector3> normals;3326Vector<float> tangents;3327Vector<Vector2> uvs;3328Vector<int32_t> indices;33293330Vector2 min_p = Vector2(Math::INF, Math::INF);3331Vector2 max_p = Vector2(-Math::INF, -Math::INF);33323333int32_t p_size = 0;3334int32_t i_size = 0;33353336Vector2 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size);3337for (int i = 0; i < lines_rid.size(); i++) {3338const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);3339int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);3340float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size;33413342switch (horizontal_alignment) {3343case HORIZONTAL_ALIGNMENT_LEFT:3344offset.x = 0.0;3345break;3346case HORIZONTAL_ALIGNMENT_FILL:3347case HORIZONTAL_ALIGNMENT_CENTER: {3348offset.x = -line_width / 2.0;3349} break;3350case HORIZONTAL_ALIGNMENT_RIGHT: {3351offset.x = -line_width;3352} break;3353}3354offset.x += lbl_offset.x * pixel_size;3355offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size;33563357bool has_depth = !Math::is_zero_approx(depth);33583359for (int j = 0; j < gl_size; j++) {3360if (glyphs[j].index == 0) {3361offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;3362continue;3363}3364if (glyphs[j].font_rid != RID()) {3365GlyphMeshKey key = GlyphMeshKey(glyphs[j].font_rid.get_id(), glyphs[j].index);3366_generate_glyph_mesh_data(key, glyphs[j]);3367GlyphMeshData &gl_data = cache[key];3368const Vector2 gl_of = Vector2(glyphs[j].x_off, glyphs[j].y_off) * pixel_size;33693370p_size += glyphs[j].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1);3371i_size += glyphs[j].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1);33723373if (has_depth) {3374for (int k = 0; k < gl_data.contours.size(); k++) {3375p_size += glyphs[j].repeat * gl_data.contours[k].size() * 4;3376i_size += glyphs[j].repeat * gl_data.contours[k].size() * 6;3377}3378}33793380for (int r = 0; r < glyphs[j].repeat; r++) {3381min_p.x = MIN(gl_data.min_p.x + offset.x + gl_of.x, min_p.x);3382min_p.y = MIN(gl_data.min_p.y - offset.y + gl_of.y, min_p.y);3383max_p.x = MAX(gl_data.max_p.x + offset.x + gl_of.x, max_p.x);3384max_p.y = MAX(gl_data.max_p.y - offset.y + gl_of.y, max_p.y);33853386offset.x += glyphs[j].advance * pixel_size;3387}3388} else {3389p_size += glyphs[j].repeat * 4;3390i_size += glyphs[j].repeat * 6;33913392offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;3393}3394}3395offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size;3396}33973398vertices.resize(p_size);3399normals.resize(p_size);3400uvs.resize(p_size);3401tangents.resize(p_size * 4);3402indices.resize(i_size);34033404Vector3 *vertices_ptr = vertices.ptrw();3405Vector3 *normals_ptr = normals.ptrw();3406float *tangents_ptr = tangents.ptrw();3407Vector2 *uvs_ptr = uvs.ptrw();3408int32_t *indices_ptr = indices.ptrw();34093410// Generate mesh.3411int32_t p_idx = 0;3412int32_t i_idx = 0;34133414offset = Vector2(0, vbegin + lbl_offset.y * pixel_size);3415for (int i = 0; i < lines_rid.size(); i++) {3416const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);3417int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);3418float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size;34193420switch (horizontal_alignment) {3421case HORIZONTAL_ALIGNMENT_LEFT:3422offset.x = 0.0;3423break;3424case HORIZONTAL_ALIGNMENT_FILL:3425case HORIZONTAL_ALIGNMENT_CENTER: {3426offset.x = -line_width / 2.0;3427} break;3428case HORIZONTAL_ALIGNMENT_RIGHT: {3429offset.x = -line_width;3430} break;3431}3432offset.x += lbl_offset.x * pixel_size;3433offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size;34343435bool has_depth = !Math::is_zero_approx(depth);34363437// Generate glyph data, precalculate size of the arrays and mesh bounds for UV.3438for (int j = 0; j < gl_size; j++) {3439if (glyphs[j].index == 0) {3440offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;3441continue;3442}3443if (glyphs[j].font_rid != RID()) {3444GlyphMeshKey key = GlyphMeshKey(glyphs[j].font_rid.get_id(), glyphs[j].index);3445_generate_glyph_mesh_data(key, glyphs[j]);3446const GlyphMeshData &gl_data = cache[key];34473448int64_t ts = gl_data.triangles.size();3449const Vector2 *ts_ptr = gl_data.triangles.ptr();3450const Vector2 gl_of = Vector2(glyphs[j].x_off, glyphs[j].y_off) * pixel_size;34513452for (int r = 0; r < glyphs[j].repeat; r++) {3453for (int k = 0; k < ts; k += 3) {3454// Add front face.3455for (int l = 0; l < 3; l++) {3456Vector3 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);3457vertices_ptr[p_idx] = point;3458normals_ptr[p_idx] = Vector3(0.0, 0.0, 1.0);3459if (has_depth) {3460uvs_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)));3461} else {3462uvs_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)));3463}3464tangents_ptr[p_idx * 4 + 0] = 1.0;3465tangents_ptr[p_idx * 4 + 1] = 0.0;3466tangents_ptr[p_idx * 4 + 2] = 0.0;3467tangents_ptr[p_idx * 4 + 3] = 1.0;3468indices_ptr[i_idx++] = p_idx;3469p_idx++;3470}3471if (has_depth) {3472// Add back face.3473for (int l = 2; l >= 0; l--) {3474Vector3 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);3475vertices_ptr[p_idx] = point;3476normals_ptr[p_idx] = Vector3(0.0, 0.0, -1.0);3477uvs_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)));3478tangents_ptr[p_idx * 4 + 0] = -1.0;3479tangents_ptr[p_idx * 4 + 1] = 0.0;3480tangents_ptr[p_idx * 4 + 2] = 0.0;3481tangents_ptr[p_idx * 4 + 3] = 1.0;3482indices_ptr[i_idx++] = p_idx;3483p_idx++;3484}3485}3486}3487// Add sides.3488if (has_depth) {3489for (int k = 0; k < gl_data.contours.size(); k++) {3490int64_t ps = gl_data.contours[k].size();3491const ContourPoint *ps_ptr = gl_data.contours[k].ptr();3492const ContourInfo &ps_info = gl_data.contours_info[k];3493real_t length = 0.0;3494for (int l = 0; l < ps; l++) {3495int prev = (l == 0) ? (ps - 1) : (l - 1);3496int next = (l + 1 == ps) ? 0 : (l + 1);3497Vector2 d1;3498Vector2 d2 = (ps_ptr[next].point - ps_ptr[l].point).normalized();3499if (ps_ptr[l].sharp) {3500d1 = d2;3501} else {3502d1 = (ps_ptr[l].point - ps_ptr[prev].point).normalized();3503}3504real_t seg_len = (ps_ptr[next].point - ps_ptr[l].point).length();35053506Vector3 quad_faces[4] = {3507Vector3(ps_ptr[l].point.x + offset.x + gl_of.x, -ps_ptr[l].point.y + offset.y - gl_of.y, -depth / 2.0),3508Vector3(ps_ptr[next].point.x + offset.x + gl_of.x, -ps_ptr[next].point.y + offset.y - gl_of.y, -depth / 2.0),3509Vector3(ps_ptr[l].point.x + offset.x + gl_of.x, -ps_ptr[l].point.y + offset.y - gl_of.y, depth / 2.0),3510Vector3(ps_ptr[next].point.x + offset.x + gl_of.x, -ps_ptr[next].point.y + offset.y - gl_of.y, depth / 2.0),3511};3512for (int m = 0; m < 4; m++) {3513const Vector2 &d = ((m % 2) == 0) ? d1 : d2;3514real_t u_pos = ((m % 2) == 0) ? length : length + seg_len;3515vertices_ptr[p_idx + m] = quad_faces[m];3516normals_ptr[p_idx + m] = Vector3(d.y, d.x, 0.0);3517if (m < 2) {3518uvs_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);3519} else {3520uvs_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);3521}3522tangents_ptr[(p_idx + m) * 4 + 0] = d.x;3523tangents_ptr[(p_idx + m) * 4 + 1] = -d.y;3524tangents_ptr[(p_idx + m) * 4 + 2] = 0.0;3525tangents_ptr[(p_idx + m) * 4 + 3] = 1.0;3526}35273528indices_ptr[i_idx++] = p_idx;3529indices_ptr[i_idx++] = p_idx + 1;3530indices_ptr[i_idx++] = p_idx + 2;35313532indices_ptr[i_idx++] = p_idx + 1;3533indices_ptr[i_idx++] = p_idx + 3;3534indices_ptr[i_idx++] = p_idx + 2;35353536length += seg_len;3537p_idx += 4;3538}3539}3540}3541offset.x += glyphs[j].advance * pixel_size;3542}3543} else {3544// Add fallback quad for missing glyphs.3545for (int r = 0; r < glyphs[j].repeat; r++) {3546Size2 sz = TS->get_hex_code_box_size(glyphs[j].font_size, glyphs[j].index) * pixel_size;3547Vector3 quad_faces[4] = {3548Vector3(offset.x, offset.y, 0.0),3549Vector3(offset.x, sz.y + offset.y, 0.0),3550Vector3(sz.x + offset.x, sz.y + offset.y, 0.0),3551Vector3(sz.x + offset.x, offset.y, 0.0),3552};3553for (int k = 0; k < 4; k++) {3554vertices_ptr[p_idx + k] = quad_faces[k];3555normals_ptr[p_idx + k] = Vector3(0.0, 0.0, 1.0);3556if (has_depth) {3557uvs_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)));3558} else {3559uvs_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)));3560}3561tangents_ptr[(p_idx + k) * 4 + 0] = 1.0;3562tangents_ptr[(p_idx + k) * 4 + 1] = 0.0;3563tangents_ptr[(p_idx + k) * 4 + 2] = 0.0;3564tangents_ptr[(p_idx + k) * 4 + 3] = 1.0;3565}35663567indices_ptr[i_idx++] = p_idx;3568indices_ptr[i_idx++] = p_idx + 1;3569indices_ptr[i_idx++] = p_idx + 2;35703571indices_ptr[i_idx++] = p_idx + 0;3572indices_ptr[i_idx++] = p_idx + 2;3573indices_ptr[i_idx++] = p_idx + 3;3574p_idx += 4;35753576offset.x += glyphs[j].advance * pixel_size;3577}3578}3579}3580offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size;3581}35823583if (indices.is_empty()) {3584// If empty, add single triangle to suppress errors.3585vertices.push_back(Vector3());3586normals.push_back(Vector3());3587uvs.push_back(Vector2());3588tangents.push_back(1.0);3589tangents.push_back(0.0);3590tangents.push_back(0.0);3591tangents.push_back(1.0);3592indices.push_back(0);3593indices.push_back(0);3594indices.push_back(0);3595}35963597p_arr[RS::ARRAY_VERTEX] = vertices;3598p_arr[RS::ARRAY_NORMAL] = normals;3599p_arr[RS::ARRAY_TANGENT] = tangents;3600p_arr[RS::ARRAY_TEX_UV] = uvs;3601p_arr[RS::ARRAY_INDEX] = indices;3602}36033604void TextMesh::_bind_methods() {3605ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &TextMesh::set_horizontal_alignment);3606ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &TextMesh::get_horizontal_alignment);36073608ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &TextMesh::set_vertical_alignment);3609ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &TextMesh::get_vertical_alignment);36103611ClassDB::bind_method(D_METHOD("set_text", "text"), &TextMesh::set_text);3612ClassDB::bind_method(D_METHOD("get_text"), &TextMesh::get_text);36133614ClassDB::bind_method(D_METHOD("set_font", "font"), &TextMesh::set_font);3615ClassDB::bind_method(D_METHOD("get_font"), &TextMesh::get_font);36163617ClassDB::bind_method(D_METHOD("set_font_size", "font_size"), &TextMesh::set_font_size);3618ClassDB::bind_method(D_METHOD("get_font_size"), &TextMesh::get_font_size);36193620ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &TextMesh::set_line_spacing);3621ClassDB::bind_method(D_METHOD("get_line_spacing"), &TextMesh::get_line_spacing);36223623ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &TextMesh::set_autowrap_mode);3624ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &TextMesh::get_autowrap_mode);36253626ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &TextMesh::set_justification_flags);3627ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextMesh::get_justification_flags);36283629ClassDB::bind_method(D_METHOD("set_depth", "depth"), &TextMesh::set_depth);3630ClassDB::bind_method(D_METHOD("get_depth"), &TextMesh::get_depth);36313632ClassDB::bind_method(D_METHOD("set_width", "width"), &TextMesh::set_width);3633ClassDB::bind_method(D_METHOD("get_width"), &TextMesh::get_width);36343635ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &TextMesh::set_pixel_size);3636ClassDB::bind_method(D_METHOD("get_pixel_size"), &TextMesh::get_pixel_size);36373638ClassDB::bind_method(D_METHOD("set_offset", "offset"), &TextMesh::set_offset);3639ClassDB::bind_method(D_METHOD("get_offset"), &TextMesh::get_offset);36403641ClassDB::bind_method(D_METHOD("set_curve_step", "curve_step"), &TextMesh::set_curve_step);3642ClassDB::bind_method(D_METHOD("get_curve_step"), &TextMesh::get_curve_step);36433644ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextMesh::set_text_direction);3645ClassDB::bind_method(D_METHOD("get_text_direction"), &TextMesh::get_text_direction);36463647ClassDB::bind_method(D_METHOD("set_language", "language"), &TextMesh::set_language);3648ClassDB::bind_method(D_METHOD("get_language"), &TextMesh::get_language);36493650ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextMesh::set_structured_text_bidi_override);3651ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &TextMesh::get_structured_text_bidi_override);36523653ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &TextMesh::set_structured_text_bidi_override_options);3654ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &TextMesh::get_structured_text_bidi_override_options);36553656ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &TextMesh::set_uppercase);3657ClassDB::bind_method(D_METHOD("is_uppercase"), &TextMesh::is_uppercase);36583659ADD_GROUP("Text", "");3660ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text");3661ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font");3662ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px"), "set_font_size", "get_font_size");3663ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");3664ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment");3665ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");3666ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing");3667ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");3668ADD_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");36693670ADD_GROUP("Mesh", "");3671ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size");3672ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_step", PROPERTY_HINT_RANGE, "0.1,10,0.1,suffix:px"), "set_curve_step", "get_curve_step");3673ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater,suffix:m"), "set_depth", "get_depth");3674ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width");3675ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");36763677ADD_GROUP("BiDi", "");3678ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction");3679ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");3680ADD_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");3681ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");3682}36833684void TextMesh::_notification(int p_what) {3685switch (p_what) {3686case MainLoop::NOTIFICATION_TRANSLATION_CHANGED: {3687String new_text = tr(text);3688if (new_text == xl_text) {3689return; // Nothing new.3690}3691xl_text = new_text;3692dirty_text = true;3693request_update();3694} break;3695}3696}36973698TextMesh::TextMesh() {3699primitive_type = PRIMITIVE_TRIANGLES;3700text_rid = TS->create_shaped_text();3701}37023703TextMesh::~TextMesh() {3704for (int i = 0; i < lines_rid.size(); i++) {3705TS->free_rid(lines_rid[i]);3706}3707lines_rid.clear();37083709TS->free_rid(text_rid);3710}37113712void TextMesh::set_horizontal_alignment(HorizontalAlignment p_alignment) {3713ERR_FAIL_INDEX((int)p_alignment, 4);3714if (horizontal_alignment != p_alignment) {3715if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {3716dirty_lines = true;3717}3718horizontal_alignment = p_alignment;3719request_update();3720}3721}37223723HorizontalAlignment TextMesh::get_horizontal_alignment() const {3724return horizontal_alignment;3725}37263727void TextMesh::set_vertical_alignment(VerticalAlignment p_alignment) {3728ERR_FAIL_INDEX((int)p_alignment, 4);3729if (vertical_alignment != p_alignment) {3730vertical_alignment = p_alignment;3731request_update();3732}3733}37343735VerticalAlignment TextMesh::get_vertical_alignment() const {3736return vertical_alignment;3737}37383739void TextMesh::set_text(const String &p_string) {3740if (text != p_string) {3741text = p_string;3742xl_text = tr(text);3743dirty_text = true;3744request_update();3745}3746}37473748String TextMesh::get_text() const {3749return text;3750}37513752void TextMesh::_font_changed() {3753dirty_font = true;3754dirty_cache = true;3755callable_mp(static_cast<PrimitiveMesh *>(this), &PrimitiveMesh::request_update).call_deferred();3756}37573758void TextMesh::set_font(const Ref<Font> &p_font) {3759if (font_override != p_font) {3760const Callable font_changed = callable_mp(this, &TextMesh::_font_changed);37613762if (font_override.is_valid()) {3763font_override->disconnect_changed(font_changed);3764}3765font_override = p_font;3766dirty_font = true;3767dirty_cache = true;3768if (font_override.is_valid()) {3769font_override->connect_changed(font_changed);3770}3771request_update();3772}3773}37743775Ref<Font> TextMesh::get_font() const {3776return font_override;3777}37783779Ref<Font> TextMesh::_get_font_or_default() const {3780// Similar code taken from `FontVariation::_get_base_font_or_default`.37813782if (font_override.is_valid()) {3783return font_override;3784}37853786StringName theme_name = "font";3787Vector<StringName> theme_types;3788ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), theme_types);37893790ThemeContext *global_context = ThemeDB::get_singleton()->get_default_theme_context();3791Vector<Ref<Theme>> themes = global_context->get_themes();3792if (Engine::get_singleton()->is_editor_hint()) {3793themes.insert(0, ThemeDB::get_singleton()->get_project_theme());3794}37953796for (const Ref<Theme> &theme : themes) {3797if (theme.is_null()) {3798continue;3799}38003801for (const StringName &E : theme_types) {3802if (theme->has_font(theme_name, E)) {3803return theme->get_font(theme_name, E);3804}3805}3806}38073808return global_context->get_fallback_theme()->get_font(theme_name, StringName());3809}38103811void TextMesh::set_font_size(int p_size) {3812if (font_size != p_size) {3813font_size = CLAMP(p_size, 1, 127);3814dirty_font = true;3815dirty_cache = true;3816request_update();3817}3818}38193820int TextMesh::get_font_size() const {3821return font_size;3822}38233824void TextMesh::set_line_spacing(float p_line_spacing) {3825if (line_spacing != p_line_spacing) {3826line_spacing = p_line_spacing;3827request_update();3828}3829}38303831float TextMesh::get_line_spacing() const {3832return line_spacing;3833}38343835void TextMesh::set_autowrap_mode(TextServer::AutowrapMode p_mode) {3836if (autowrap_mode != p_mode) {3837autowrap_mode = p_mode;3838dirty_lines = true;3839request_update();3840}3841}38423843TextServer::AutowrapMode TextMesh::get_autowrap_mode() const {3844return autowrap_mode;3845}38463847void TextMesh::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {3848if (jst_flags != p_flags) {3849jst_flags = p_flags;3850dirty_lines = true;3851request_update();3852}3853}38543855BitField<TextServer::JustificationFlag> TextMesh::get_justification_flags() const {3856return jst_flags;3857}38583859void TextMesh::set_depth(real_t p_depth) {3860if (depth != p_depth) {3861depth = MAX(p_depth, 0.0);3862request_update();3863}3864}38653866real_t TextMesh::get_depth() const {3867return depth;3868}38693870void TextMesh::set_width(real_t p_width) {3871if (width != p_width) {3872width = p_width;3873dirty_lines = true;3874request_update();3875}3876}38773878real_t TextMesh::get_width() const {3879return width;3880}38813882void TextMesh::set_pixel_size(real_t p_amount) {3883if (pixel_size != p_amount) {3884pixel_size = CLAMP(p_amount, 0.0001, 128.0);3885dirty_cache = true;3886request_update();3887}3888}38893890real_t TextMesh::get_pixel_size() const {3891return pixel_size;3892}38933894void TextMesh::set_offset(const Point2 &p_offset) {3895if (lbl_offset != p_offset) {3896lbl_offset = p_offset;3897request_update();3898}3899}39003901Point2 TextMesh::get_offset() const {3902return lbl_offset;3903}39043905void TextMesh::set_curve_step(real_t p_step) {3906if (curve_step != p_step) {3907curve_step = CLAMP(p_step, 0.1, 10.0);3908dirty_cache = true;3909request_update();3910}3911}39123913real_t TextMesh::get_curve_step() const {3914return curve_step;3915}39163917void TextMesh::set_text_direction(TextServer::Direction p_text_direction) {3918ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);3919if (text_direction != p_text_direction) {3920text_direction = p_text_direction;3921dirty_text = true;3922request_update();3923}3924}39253926TextServer::Direction TextMesh::get_text_direction() const {3927return text_direction;3928}39293930void TextMesh::set_language(const String &p_language) {3931if (language != p_language) {3932language = p_language;3933dirty_text = true;3934request_update();3935}3936}39373938String TextMesh::get_language() const {3939return language;3940}39413942void TextMesh::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {3943if (st_parser != p_parser) {3944st_parser = p_parser;3945dirty_text = true;3946request_update();3947}3948}39493950TextServer::StructuredTextParser TextMesh::get_structured_text_bidi_override() const {3951return st_parser;3952}39533954void TextMesh::set_structured_text_bidi_override_options(Array p_args) {3955if (st_args != p_args) {3956st_args = p_args;3957dirty_text = true;3958request_update();3959}3960}39613962Array TextMesh::get_structured_text_bidi_override_options() const {3963return st_args;3964}39653966void TextMesh::set_uppercase(bool p_uppercase) {3967if (uppercase != p_uppercase) {3968uppercase = p_uppercase;3969dirty_text = true;3970request_update();3971}3972}39733974bool TextMesh::is_uppercase() const {3975return uppercase;3976}397739783979