Path: blob/master/scene/resources/3d/primitive_meshes.cpp
20952 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 "core/os/main_loop.h"35#include "scene/resources/theme.h"36#include "scene/theme/theme_db.h"37#include "servers/rendering/rendering_server.h"38#include "thirdparty/misc/polypartition.h"3940#define PADDING_REF_SIZE 1024.04142/**43PrimitiveMesh44*/45void PrimitiveMesh::_update() const {46Array arr;47if (GDVIRTUAL_CALL(_create_mesh_array, arr)) {48ERR_FAIL_COND_MSG(arr.size() != RS::ARRAY_MAX, "_create_mesh_array must return an array of Mesh.ARRAY_MAX elements.");49} else {50arr.resize(RS::ARRAY_MAX);51_create_mesh_array(arr);52}5354Vector<Vector3> points = arr[RS::ARRAY_VERTEX];5556ERR_FAIL_COND_MSG(points.is_empty(), "_create_mesh_array must return at least a vertex array.");5758aabb = AABB();5960int pc = points.size();61ERR_FAIL_COND(pc == 0);62{63const Vector3 *r = points.ptr();64for (int i = 0; i < pc; i++) {65if (i == 0) {66aabb.position = r[i];67} else {68aabb.expand_to(r[i]);69}70}71}7273Vector<int> indices = arr[RS::ARRAY_INDEX];7475if (flip_faces) {76Vector<Vector3> normals = arr[RS::ARRAY_NORMAL];7778if (normals.size() && indices.size()) {79{80int nc = normals.size();81Vector3 *w = normals.ptrw();82for (int i = 0; i < nc; i++) {83w[i] = -w[i];84}85}8687{88int ic = indices.size();89int *w = indices.ptrw();90for (int i = 0; i < ic; i += 3) {91SWAP(w[i + 0], w[i + 1]);92}93}94arr[RS::ARRAY_NORMAL] = normals;95arr[RS::ARRAY_INDEX] = indices;96}97}9899if (add_uv2) {100// _create_mesh_array should populate our UV2, this is a fallback in case it doesn't.101// As we don't know anything about the geometry we only pad the right and bottom edge102// of our texture.103Vector<Vector2> uv = arr[RS::ARRAY_TEX_UV];104Vector<Vector2> uv2 = arr[RS::ARRAY_TEX_UV2];105106if (uv.size() > 0 && uv2.is_empty()) {107Vector2 uv2_scale = get_uv2_scale();108uv2.resize(uv.size());109110Vector2 *uv2w = uv2.ptrw();111for (int i = 0; i < uv.size(); i++) {112uv2w[i] = uv[i] * uv2_scale;113}114}115116arr[RS::ARRAY_TEX_UV2] = uv2;117}118119array_len = pc;120index_array_len = indices.size();121// in with the new122RenderingServer::get_singleton()->mesh_clear(mesh);123RenderingServer::get_singleton()->mesh_add_surface_from_arrays(mesh, (RenderingServer::PrimitiveType)primitive_type, arr);124RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid());125126pending_request = false;127128clear_cache();129130const_cast<PrimitiveMesh *>(this)->emit_changed();131}132133void PrimitiveMesh::request_update() {134if (pending_request) {135return;136}137_update();138}139140int PrimitiveMesh::get_surface_count() const {141if (pending_request) {142_update();143}144return 1;145}146147int PrimitiveMesh::surface_get_array_len(int p_idx) const {148ERR_FAIL_INDEX_V(p_idx, 1, -1);149if (pending_request) {150_update();151}152153return array_len;154}155156int PrimitiveMesh::surface_get_array_index_len(int p_idx) const {157ERR_FAIL_INDEX_V(p_idx, 1, -1);158if (pending_request) {159_update();160}161162return index_array_len;163}164165Array PrimitiveMesh::surface_get_arrays(int p_surface) const {166ERR_FAIL_INDEX_V(p_surface, 1, Array());167if (pending_request) {168_update();169}170171return RenderingServer::get_singleton()->mesh_surface_get_arrays(mesh, 0);172}173174Dictionary PrimitiveMesh::surface_get_lods(int p_surface) const {175return Dictionary(); //not really supported176}177178TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) const {179return TypedArray<Array>(); //not really supported180}181182BitField<Mesh::ArrayFormat> PrimitiveMesh::surface_get_format(int p_idx) const {183ERR_FAIL_INDEX_V(p_idx, 1, 0);184185uint64_t mesh_format = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;186if (add_uv2) {187mesh_format |= RS::ARRAY_FORMAT_TEX_UV2;188}189190return mesh_format;191}192193Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const {194return primitive_type;195}196197void PrimitiveMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {198ERR_FAIL_INDEX(p_idx, 1);199200set_material(p_material);201}202203Ref<Material> PrimitiveMesh::surface_get_material(int p_idx) const {204ERR_FAIL_INDEX_V(p_idx, 1, nullptr);205206return material;207}208209int PrimitiveMesh::get_blend_shape_count() const {210return 0;211}212213StringName PrimitiveMesh::get_blend_shape_name(int p_index) const {214return StringName();215}216217void PrimitiveMesh::set_blend_shape_name(int p_index, const StringName &p_name) {218}219220AABB PrimitiveMesh::get_aabb() const {221if (pending_request) {222_update();223}224225return aabb;226}227228RID PrimitiveMesh::get_rid() const {229if (pending_request) {230_update();231}232return mesh;233}234235void PrimitiveMesh::_bind_methods() {236ClassDB::bind_method(D_METHOD("set_material", "material"), &PrimitiveMesh::set_material);237ClassDB::bind_method(D_METHOD("get_material"), &PrimitiveMesh::get_material);238239ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays);240241ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &PrimitiveMesh::set_custom_aabb);242ClassDB::bind_method(D_METHOD("get_custom_aabb"), &PrimitiveMesh::get_custom_aabb);243244ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces);245ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces);246247ClassDB::bind_method(D_METHOD("set_add_uv2", "add_uv2"), &PrimitiveMesh::set_add_uv2);248ClassDB::bind_method(D_METHOD("get_add_uv2"), &PrimitiveMesh::get_add_uv2);249250ClassDB::bind_method(D_METHOD("set_uv2_padding", "uv2_padding"), &PrimitiveMesh::set_uv2_padding);251ClassDB::bind_method(D_METHOD("get_uv2_padding"), &PrimitiveMesh::get_uv2_padding);252253ClassDB::bind_method(D_METHOD("request_update"), &PrimitiveMesh::request_update);254255ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");256ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");257ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");258ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_uv2"), "set_add_uv2", "get_add_uv2");259ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv2_padding", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), "set_uv2_padding", "get_uv2_padding");260261GDVIRTUAL_BIND(_create_mesh_array);262}263264void PrimitiveMesh::set_material(const Ref<Material> &p_material) {265if (p_material == material) {266return;267}268material = p_material;269if (!pending_request) {270// just apply it, else it'll happen when _update is called.271RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid());272notify_property_list_changed();273emit_changed();274}275}276277Ref<Material> PrimitiveMesh::get_material() const {278return material;279}280281Array PrimitiveMesh::get_mesh_arrays() const {282return surface_get_arrays(0);283}284285void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) {286if (p_custom.is_equal_approx(custom_aabb)) {287return;288}289custom_aabb = p_custom;290RS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);291emit_changed();292}293294AABB PrimitiveMesh::get_custom_aabb() const {295return custom_aabb;296}297298void PrimitiveMesh::set_flip_faces(bool p_enable) {299if (p_enable == flip_faces) {300return;301}302flip_faces = p_enable;303request_update();304}305306bool PrimitiveMesh::get_flip_faces() const {307return flip_faces;308}309310void PrimitiveMesh::set_add_uv2(bool p_enable) {311if (p_enable == add_uv2) {312return;313}314add_uv2 = p_enable;315_update_lightmap_size();316request_update();317}318319void PrimitiveMesh::set_uv2_padding(float p_padding) {320if (Math::is_equal_approx(p_padding, uv2_padding)) {321return;322}323uv2_padding = p_padding;324_update_lightmap_size();325request_update();326}327328Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const {329Vector2 uv2_scale;330Vector2 lightmap_size = get_lightmap_size_hint();331332// Calculate it as a margin, if no lightmap size hint is given we assume "PADDING_REF_SIZE" as our texture size.333uv2_scale.x = p_margin_scale.x * uv2_padding / (lightmap_size.x == 0.0 ? PADDING_REF_SIZE : lightmap_size.x);334uv2_scale.y = p_margin_scale.y * uv2_padding / (lightmap_size.y == 0.0 ? PADDING_REF_SIZE : lightmap_size.y);335336// Inverse it to turn our margin into a scale337uv2_scale = Vector2(1.0, 1.0) - uv2_scale;338339return uv2_scale;340}341342float PrimitiveMesh::get_lightmap_texel_size() const {343return texel_size;344}345346void PrimitiveMesh::_on_settings_changed() {347float new_texel_size = float(GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"));348if (new_texel_size <= 0.0) {349new_texel_size = 0.2;350}351if (texel_size == new_texel_size) {352return;353}354355texel_size = new_texel_size;356_update_lightmap_size();357request_update();358}359360PrimitiveMesh::PrimitiveMesh() {361ERR_FAIL_NULL(RenderingServer::get_singleton());362mesh = RenderingServer::get_singleton()->mesh_create();363364ERR_FAIL_NULL(ProjectSettings::get_singleton());365texel_size = float(GLOBAL_GET_CACHED(float, "rendering/lightmapping/primitive_meshes/texel_size"));366if (texel_size <= 0.0) {367texel_size = 0.2;368}369ProjectSettings *project_settings = ProjectSettings::get_singleton();370project_settings->connect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed));371}372373PrimitiveMesh::~PrimitiveMesh() {374ERR_FAIL_NULL(RenderingServer::get_singleton());375RenderingServer::get_singleton()->free_rid(mesh);376377ERR_FAIL_NULL(ProjectSettings::get_singleton());378ProjectSettings *project_settings = ProjectSettings::get_singleton();379project_settings->disconnect("settings_changed", callable_mp(this, &PrimitiveMesh::_on_settings_changed));380}381382/**383CapsuleMesh384*/385386void CapsuleMesh::_update_lightmap_size() {387if (get_add_uv2()) {388// size must have changed, update lightmap size hint389Size2i _lightmap_size_hint;390float padding = get_uv2_padding();391392float radial_length = radius * Math::PI * 0.5; // circumference of 90 degree bend393float vertical_length = radial_length * 2 + (height - 2.0 * radius); // total vertical length394395_lightmap_size_hint.x = MAX(1.0, 4.0 * radial_length / texel_size) + padding;396_lightmap_size_hint.y = MAX(1.0, vertical_length / texel_size) + padding;397398set_lightmap_size_hint(_lightmap_size_hint);399}400}401402void CapsuleMesh::_create_mesh_array(Array &p_arr) const {403bool _add_uv2 = get_add_uv2();404float _uv2_padding = get_uv2_padding() * texel_size;405406create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding);407}408409void 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) {410int i, j, prevrow, thisrow, point;411float x, y, z, u, v, w;412float onethird = 1.0 / 3.0;413float twothirds = 2.0 / 3.0;414415// Only used if we calculate UV2416float radial_width = 2.0 * radius * Math::PI;417float radial_h = radial_width / (radial_width + p_uv2_padding);418float radial_length = radius * Math::PI * 0.5; // circumference of 90 degree bend419float vertical_length = radial_length * 2 + (height - 2.0 * radius) + p_uv2_padding; // total vertical length420float radial_v = radial_length / vertical_length; // v size of top and bottom section421float height_v = (height - 2.0 * radius) / vertical_length; // v size of height section422423// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't424// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.425int num_points = (rings + 2) * (radial_segments + 1) * 2;426LocalVector<Vector3> points;427points.reserve(num_points);428LocalVector<Vector3> normals;429normals.reserve(num_points);430LocalVector<float> tangents;431tangents.reserve(num_points * 4);432LocalVector<Vector2> uvs;433uvs.reserve(num_points);434LocalVector<Vector2> uv2s;435if (p_add_uv2) {436uv2s.reserve(num_points);437}438LocalVector<int> indices;439indices.reserve((rings + 1) * (radial_segments) * 6 * 2);440point = 0;441442#define ADD_TANGENT(m_x, m_y, m_z, m_d) \443tangents.push_back(m_x); \444tangents.push_back(m_y); \445tangents.push_back(m_z); \446tangents.push_back(m_d);447448// Note, this has been aligned with our collision shape but I've left the descriptions as top/middle/bottom.449450/* top hemisphere */451thisrow = 0;452prevrow = 0;453for (j = 0; j <= (rings + 1); j++) {454v = j;455456v /= (rings + 1);457if (j == (rings + 1)) {458w = 1.0;459y = 0.0;460} else {461w = Math::sin(0.5 * Math::PI * v);462y = Math::cos(0.5 * Math::PI * v);463}464465for (i = 0; i <= radial_segments; i++) {466u = i;467u /= radial_segments;468469if (i == radial_segments) {470x = 0.0;471z = 1.0;472} else {473x = -Math::sin(u * Math::TAU);474z = Math::cos(u * Math::TAU);475}476477Vector3 p = Vector3(x * w, y, -z * w);478points.push_back(p * radius + Vector3(0.0, 0.5 * height - radius, 0.0));479normals.push_back(p);480ADD_TANGENT(-z, 0.0, -x, 1.0)481uvs.push_back(Vector2(u, v * onethird));482if (p_add_uv2) {483uv2s.push_back(Vector2(u * radial_h, v * radial_v));484}485point++;486487if (i > 0 && j > 0) {488indices.push_back(prevrow + i - 1);489indices.push_back(prevrow + i);490indices.push_back(thisrow + i - 1);491492indices.push_back(prevrow + i);493indices.push_back(thisrow + i);494indices.push_back(thisrow + i - 1);495}496}497498prevrow = thisrow;499thisrow = point;500}501502/* cylinder */503thisrow = point;504prevrow = 0;505for (j = 0; j <= (rings + 1); j++) {506v = j;507v /= (rings + 1);508509y = (height - 2.0 * radius) * v;510y = (0.5 * height - radius) - y;511512for (i = 0; i <= radial_segments; i++) {513u = i;514u /= radial_segments;515516if (i == radial_segments) {517x = 0.0;518z = 1.0;519} else {520x = -Math::sin(u * Math::TAU);521z = Math::cos(u * Math::TAU);522}523524Vector3 p = Vector3(x * radius, y, -z * radius);525points.push_back(p);526normals.push_back(Vector3(x, 0.0, -z));527ADD_TANGENT(-z, 0.0, -x, 1.0)528uvs.push_back(Vector2(u, onethird + (v * onethird)));529if (p_add_uv2) {530uv2s.push_back(Vector2(u * radial_h, radial_v + (v * height_v)));531}532point++;533534if (i > 0 && j > 0) {535indices.push_back(prevrow + i - 1);536indices.push_back(prevrow + i);537indices.push_back(thisrow + i - 1);538539indices.push_back(prevrow + i);540indices.push_back(thisrow + i);541indices.push_back(thisrow + i - 1);542}543}544545prevrow = thisrow;546thisrow = point;547}548549/* bottom hemisphere */550thisrow = point;551prevrow = 0;552for (j = 0; j <= (rings + 1); j++) {553v = j;554555v /= (rings + 1);556if (j == (rings + 1)) {557w = 0.0;558y = -1.0;559} else {560w = Math::cos(0.5 * Math::PI * v);561y = -Math::sin(0.5 * Math::PI * v);562}563564for (i = 0; i <= radial_segments; i++) {565u = i;566u /= radial_segments;567568if (i == radial_segments) {569x = 0.0;570z = 1.0;571} else {572x = -Math::sin(u * Math::TAU);573z = Math::cos(u * Math::TAU);574}575576Vector3 p = Vector3(x * w, y, -z * w);577points.push_back(p * radius + Vector3(0.0, -0.5 * height + radius, 0.0));578normals.push_back(p);579ADD_TANGENT(-z, 0.0, -x, 1.0)580uvs.push_back(Vector2(u, twothirds + v * onethird));581if (p_add_uv2) {582uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + v * radial_v));583}584point++;585586if (i > 0 && j > 0) {587indices.push_back(prevrow + i - 1);588indices.push_back(prevrow + i);589indices.push_back(thisrow + i - 1);590591indices.push_back(prevrow + i);592indices.push_back(thisrow + i);593indices.push_back(thisrow + i - 1);594}595}596597prevrow = thisrow;598thisrow = point;599}600601p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);602p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);603p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);604p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);605if (p_add_uv2) {606p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);607}608p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);609}610611void CapsuleMesh::_bind_methods() {612ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CapsuleMesh::set_radius);613ClassDB::bind_method(D_METHOD("get_radius"), &CapsuleMesh::get_radius);614ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleMesh::set_height);615ClassDB::bind_method(D_METHOD("get_height"), &CapsuleMesh::get_height);616617ClassDB::bind_method(D_METHOD("set_radial_segments", "segments"), &CapsuleMesh::set_radial_segments);618ClassDB::bind_method(D_METHOD("get_radial_segments"), &CapsuleMesh::get_radial_segments);619ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CapsuleMesh::set_rings);620ClassDB::bind_method(D_METHOD("get_rings"), &CapsuleMesh::get_rings);621622ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");623ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height");624ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");625ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings");626627ADD_LINKED_PROPERTY("radius", "height");628ADD_LINKED_PROPERTY("height", "radius");629}630631void CapsuleMesh::set_radius(const float p_radius) {632if (Math::is_equal_approx(radius, p_radius)) {633return;634}635636radius = p_radius;637if (radius > height * 0.5) {638height = radius * 2.0;639}640_update_lightmap_size();641request_update();642}643644float CapsuleMesh::get_radius() const {645return radius;646}647648void CapsuleMesh::set_height(const float p_height) {649if (Math::is_equal_approx(height, p_height)) {650return;651}652653height = p_height;654if (radius > height * 0.5) {655radius = height * 0.5;656}657_update_lightmap_size();658request_update();659}660661float CapsuleMesh::get_height() const {662return height;663}664665void CapsuleMesh::set_radial_segments(const int p_segments) {666if (radial_segments == p_segments) {667return;668}669670radial_segments = p_segments > 4 ? p_segments : 4;671request_update();672}673674int CapsuleMesh::get_radial_segments() const {675return radial_segments;676}677678void CapsuleMesh::set_rings(const int p_rings) {679if (rings == p_rings) {680return;681}682683ERR_FAIL_COND(p_rings < 0);684rings = p_rings;685request_update();686}687688int CapsuleMesh::get_rings() const {689return rings;690}691692/**693BoxMesh694*/695696void BoxMesh::_update_lightmap_size() {697if (get_add_uv2()) {698// size must have changed, update lightmap size hint699Size2i _lightmap_size_hint;700float padding = get_uv2_padding();701702float width = (size.x + size.z) / texel_size;703float length = (size.y + size.y + MAX(size.x, size.z)) / texel_size;704705_lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding;706_lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding;707708set_lightmap_size_hint(_lightmap_size_hint);709}710}711712void BoxMesh::_create_mesh_array(Array &p_arr) const {713// Note about padding, with our box each face of the box faces a different direction so we want a seam714// around every face. We thus add our padding to the right and bottom of each face.715// With 3 faces along the width and 2 along the height of the texture we need to adjust our scale716// accordingly.717bool _add_uv2 = get_add_uv2();718float _uv2_padding = get_uv2_padding() * texel_size;719720BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding);721}722723void 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) {724int i, j, prevrow, thisrow, point;725float x, y, z;726float onethird = 1.0 / 3.0;727float twothirds = 2.0 / 3.0;728729// Only used if we calculate UV2730// 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)731float total_h = (size.x + size.z + (2.0 * p_uv2_padding));732float padding_h = p_uv2_padding / total_h;733float width_h = size.x / total_h;734float depth_h = size.z / total_h;735float total_v = (size.y + size.y + MAX(size.x, size.z) + (3.0 * p_uv2_padding));736float padding_v = p_uv2_padding / total_v;737float width_v = size.x / total_v;738float height_v = size.y / total_v;739float depth_v = size.z / total_v;740741Vector3 start_pos = size * -0.5;742743// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't744// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.745int num_points = (subdivide_h + 2) * (subdivide_w + 2) * 6;746LocalVector<Vector3> points;747points.reserve(num_points);748LocalVector<Vector3> normals;749normals.reserve(num_points);750LocalVector<float> tangents;751tangents.reserve(num_points * 4);752LocalVector<Vector2> uvs;753uvs.reserve(num_points);754LocalVector<Vector2> uv2s;755if (p_add_uv2) {756uv2s.reserve(num_points);757}758LocalVector<int> indices;759indices.reserve((subdivide_h + 1) * (subdivide_w + 1) * 6 * 6);760point = 0;761762#define ADD_TANGENT(m_x, m_y, m_z, m_d) \763tangents.push_back(m_x); \764tangents.push_back(m_y); \765tangents.push_back(m_z); \766tangents.push_back(m_d);767768// front + back769y = start_pos.y;770thisrow = point;771prevrow = 0;772for (j = 0; j <= subdivide_h + 1; j++) {773float v = j;774float v2 = v / (subdivide_w + 1.0);775v /= (2.0 * (subdivide_h + 1.0));776777x = start_pos.x;778for (i = 0; i <= subdivide_w + 1; i++) {779float u = i;780float u2 = u / (subdivide_w + 1.0);781u /= (3.0 * (subdivide_w + 1.0));782783// front784points.push_back(Vector3(x, -y, -start_pos.z)); // double negative on the Z!785normals.push_back(Vector3(0.0, 0.0, 1.0));786ADD_TANGENT(1.0, 0.0, 0.0, 1.0);787uvs.push_back(Vector2(u, v));788if (p_add_uv2) {789uv2s.push_back(Vector2(u2 * width_h, v2 * height_v));790}791point++;792793// back794points.push_back(Vector3(-x, -y, start_pos.z));795normals.push_back(Vector3(0.0, 0.0, -1.0));796ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);797uvs.push_back(Vector2(twothirds + u, v));798if (p_add_uv2) {799uv2s.push_back(Vector2(u2 * width_h, height_v + padding_v + (v2 * height_v)));800}801point++;802803if (i > 0 && j > 0) {804int i2 = i * 2;805806// front807indices.push_back(prevrow + i2 - 2);808indices.push_back(prevrow + i2);809indices.push_back(thisrow + i2 - 2);810indices.push_back(prevrow + i2);811indices.push_back(thisrow + i2);812indices.push_back(thisrow + i2 - 2);813814// back815indices.push_back(prevrow + i2 - 1);816indices.push_back(prevrow + i2 + 1);817indices.push_back(thisrow + i2 - 1);818indices.push_back(prevrow + i2 + 1);819indices.push_back(thisrow + i2 + 1);820indices.push_back(thisrow + i2 - 1);821}822823x += size.x / (subdivide_w + 1.0);824}825826y += size.y / (subdivide_h + 1.0);827prevrow = thisrow;828thisrow = point;829}830831// left + right832y = start_pos.y;833thisrow = point;834prevrow = 0;835for (j = 0; j <= (subdivide_h + 1); j++) {836float v = j;837float v2 = v / (subdivide_h + 1.0);838v /= (2.0 * (subdivide_h + 1.0));839840z = start_pos.z;841for (i = 0; i <= (subdivide_d + 1); i++) {842float u = i;843float u2 = u / (subdivide_d + 1.0);844u /= (3.0 * (subdivide_d + 1.0));845846// right847points.push_back(Vector3(-start_pos.x, -y, -z));848normals.push_back(Vector3(1.0, 0.0, 0.0));849ADD_TANGENT(0.0, 0.0, -1.0, 1.0);850uvs.push_back(Vector2(onethird + u, v));851if (p_add_uv2) {852uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), v2 * height_v));853}854point++;855856// left857points.push_back(Vector3(start_pos.x, -y, z));858normals.push_back(Vector3(-1.0, 0.0, 0.0));859ADD_TANGENT(0.0, 0.0, 1.0, 1.0);860uvs.push_back(Vector2(u, 0.5 + v));861if (p_add_uv2) {862uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), height_v + padding_v + (v2 * height_v)));863}864point++;865866if (i > 0 && j > 0) {867int i2 = i * 2;868869// right870indices.push_back(prevrow + i2 - 2);871indices.push_back(prevrow + i2);872indices.push_back(thisrow + i2 - 2);873indices.push_back(prevrow + i2);874indices.push_back(thisrow + i2);875indices.push_back(thisrow + i2 - 2);876877// left878indices.push_back(prevrow + i2 - 1);879indices.push_back(prevrow + i2 + 1);880indices.push_back(thisrow + i2 - 1);881indices.push_back(prevrow + i2 + 1);882indices.push_back(thisrow + i2 + 1);883indices.push_back(thisrow + i2 - 1);884}885886z += size.z / (subdivide_d + 1.0);887}888889y += size.y / (subdivide_h + 1.0);890prevrow = thisrow;891thisrow = point;892}893894// top + bottom895z = start_pos.z;896thisrow = point;897prevrow = 0;898for (j = 0; j <= (subdivide_d + 1); j++) {899float v = j;900float v2 = v / (subdivide_d + 1.0);901v /= (2.0 * (subdivide_d + 1.0));902903x = start_pos.x;904for (i = 0; i <= (subdivide_w + 1); i++) {905float u = i;906float u2 = u / (subdivide_w + 1.0);907u /= (3.0 * (subdivide_w + 1.0));908909// top910points.push_back(Vector3(-x, -start_pos.y, -z));911normals.push_back(Vector3(0.0, 1.0, 0.0));912ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);913uvs.push_back(Vector2(onethird + u, 0.5 + v));914if (p_add_uv2) {915uv2s.push_back(Vector2(u2 * width_h, ((height_v + padding_v) * 2.0) + (v2 * depth_v)));916}917point++;918919// bottom920points.push_back(Vector3(x, start_pos.y, -z));921normals.push_back(Vector3(0.0, -1.0, 0.0));922ADD_TANGENT(1.0, 0.0, 0.0, 1.0);923uvs.push_back(Vector2(twothirds + u, 0.5 + v));924if (p_add_uv2) {925uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), ((height_v + padding_v) * 2.0) + (v2 * width_v)));926}927point++;928929if (i > 0 && j > 0) {930int i2 = i * 2;931932// top933indices.push_back(prevrow + i2 - 2);934indices.push_back(prevrow + i2);935indices.push_back(thisrow + i2 - 2);936indices.push_back(prevrow + i2);937indices.push_back(thisrow + i2);938indices.push_back(thisrow + i2 - 2);939940// bottom941indices.push_back(prevrow + i2 - 1);942indices.push_back(prevrow + i2 + 1);943indices.push_back(thisrow + i2 - 1);944indices.push_back(prevrow + i2 + 1);945indices.push_back(thisrow + i2 + 1);946indices.push_back(thisrow + i2 - 1);947}948949x += size.x / (subdivide_w + 1.0);950}951952z += size.z / (subdivide_d + 1.0);953prevrow = thisrow;954thisrow = point;955}956957p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);958p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);959p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);960p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);961if (p_add_uv2) {962p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);963}964p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);965}966967void BoxMesh::_bind_methods() {968ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxMesh::set_size);969ClassDB::bind_method(D_METHOD("get_size"), &BoxMesh::get_size);970971ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &BoxMesh::set_subdivide_width);972ClassDB::bind_method(D_METHOD("get_subdivide_width"), &BoxMesh::get_subdivide_width);973ClassDB::bind_method(D_METHOD("set_subdivide_height", "divisions"), &BoxMesh::set_subdivide_height);974ClassDB::bind_method(D_METHOD("get_subdivide_height"), &BoxMesh::get_subdivide_height);975ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &BoxMesh::set_subdivide_depth);976ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &BoxMesh::get_subdivide_depth);977978ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");979ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");980ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");981ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");982}983984void BoxMesh::set_size(const Vector3 &p_size) {985if (p_size.is_equal_approx(size)) {986return;987}988989size = p_size;990_update_lightmap_size();991request_update();992}993994Vector3 BoxMesh::get_size() const {995return size;996}997998void BoxMesh::set_subdivide_width(const int p_divisions) {999if (p_divisions == subdivide_w) {1000return;1001}10021003subdivide_w = p_divisions > 0 ? p_divisions : 0;1004request_update();1005}10061007int BoxMesh::get_subdivide_width() const {1008return subdivide_w;1009}10101011void BoxMesh::set_subdivide_height(const int p_divisions) {1012if (p_divisions == subdivide_h) {1013return;1014}10151016subdivide_h = p_divisions > 0 ? p_divisions : 0;1017request_update();1018}10191020int BoxMesh::get_subdivide_height() const {1021return subdivide_h;1022}10231024void BoxMesh::set_subdivide_depth(const int p_divisions) {1025if (p_divisions == subdivide_d) {1026return;1027}10281029subdivide_d = p_divisions > 0 ? p_divisions : 0;1030request_update();1031}10321033int BoxMesh::get_subdivide_depth() const {1034return subdivide_d;1035}10361037/**1038CylinderMesh1039*/10401041void CylinderMesh::_update_lightmap_size() {1042if (get_add_uv2()) {1043// size must have changed, update lightmap size hint1044Size2i _lightmap_size_hint;1045float padding = get_uv2_padding();10461047float top_circumference = top_radius * Math::PI * 2.0;1048float bottom_circumference = bottom_radius * Math::PI * 2.0;10491050float _width = MAX(top_circumference, bottom_circumference) / texel_size + padding;1051_width = MAX(_width, (((top_radius + bottom_radius) / texel_size) + padding) * 2.0); // this is extremely unlikely to be larger, will only happen if padding is larger then our diameter.1052_lightmap_size_hint.x = MAX(1.0, _width);10531054float _height = ((height + (MAX(top_radius, bottom_radius) * 2.0)) / texel_size) + (2.0 * padding);10551056_lightmap_size_hint.y = MAX(1.0, _height);10571058set_lightmap_size_hint(_lightmap_size_hint);1059}1060}10611062void CylinderMesh::_create_mesh_array(Array &p_arr) const {1063bool _add_uv2 = get_add_uv2();1064float _uv2_padding = get_uv2_padding() * texel_size;10651066create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding);1067}10681069void 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) {1070int i, j, prevrow, thisrow, point;1071float x, y, z, u, v, radius, radius_h;10721073// Only used if we calculate UV21074float top_circumference = top_radius * Math::PI * 2.0;1075float bottom_circumference = bottom_radius * Math::PI * 2.0;1076float vertical_length = height + MAX(2.0 * top_radius, 2.0 * bottom_radius) + (2.0 * p_uv2_padding);1077float height_v = height / vertical_length;1078float padding_v = p_uv2_padding / vertical_length;10791080float horizontal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding);1081float center_h = 0.5 * (horizontal_length - p_uv2_padding) / horizontal_length;1082float top_h = top_circumference / horizontal_length;1083float bottom_h = bottom_circumference / horizontal_length;1084float padding_h = p_uv2_padding / horizontal_length;10851086// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't1087// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.1088int num_points = (rings + 2) * (radial_segments + 1) + 4 + 2 * radial_segments;1089LocalVector<Vector3> points;1090points.reserve(num_points);1091LocalVector<Vector3> normals;1092normals.reserve(num_points);1093LocalVector<float> tangents;1094tangents.reserve(num_points * 4);1095LocalVector<Vector2> uvs;1096uvs.reserve(num_points);1097LocalVector<Vector2> uv2s;1098if (p_add_uv2) {1099uv2s.reserve(num_points);1100}1101LocalVector<int> indices;1102indices.reserve((rings + 1) * (radial_segments) * 6 + 6 * radial_segments);1103point = 0;11041105#define ADD_TANGENT(m_x, m_y, m_z, m_d) \1106tangents.push_back(m_x); \1107tangents.push_back(m_y); \1108tangents.push_back(m_z); \1109tangents.push_back(m_d);11101111thisrow = 0;1112prevrow = 0;1113const real_t side_normal_y = (bottom_radius - top_radius) / height;1114for (j = 0; j <= (rings + 1); j++) {1115v = j;1116v /= (rings + 1);11171118radius = top_radius + ((bottom_radius - top_radius) * v);1119radius_h = top_h + ((bottom_h - top_h) * v);11201121y = height * v;1122y = (height * 0.5) - y;11231124for (i = 0; i <= radial_segments; i++) {1125u = i;1126u /= radial_segments;11271128if (i == radial_segments) {1129x = 0.0;1130z = 1.0;1131} else {1132x = Math::sin(u * Math::TAU);1133z = Math::cos(u * Math::TAU);1134}11351136Vector3 p = Vector3(x * radius, y, z * radius);1137points.push_back(p);1138normals.push_back(Vector3(x, side_normal_y, z).normalized());1139ADD_TANGENT(z, 0.0, -x, 1.0)1140uvs.push_back(Vector2(u, v * 0.5));1141if (p_add_uv2) {1142uv2s.push_back(Vector2(center_h + (u - 0.5) * radius_h, v * height_v));1143}1144point++;11451146if (i > 0 && j > 0) {1147indices.push_back(prevrow + i - 1);1148indices.push_back(prevrow + i);1149indices.push_back(thisrow + i - 1);11501151indices.push_back(prevrow + i);1152indices.push_back(thisrow + i);1153indices.push_back(thisrow + i - 1);1154}1155}11561157prevrow = thisrow;1158thisrow = point;1159}11601161// Adjust for bottom section, only used if we calculate UV2s.1162top_h = top_radius / horizontal_length;1163float top_v = top_radius / vertical_length;1164bottom_h = bottom_radius / horizontal_length;1165float bottom_v = bottom_radius / vertical_length;11661167// Add top.1168if (cap_top && top_radius > 0.0) {1169y = height * 0.5;11701171thisrow = point;1172points.push_back(Vector3(0.0, y, 0.0));1173normals.push_back(Vector3(0.0, 1.0, 0.0));1174ADD_TANGENT(1.0, 0.0, 0.0, 1.0)1175uvs.push_back(Vector2(0.25, 0.75));1176if (p_add_uv2) {1177uv2s.push_back(Vector2(top_h, height_v + padding_v + MAX(top_v, bottom_v)));1178}1179point++;11801181for (i = 0; i <= radial_segments; i++) {1182float r = i;1183r /= radial_segments;11841185if (i == radial_segments) {1186x = 0.0;1187z = 1.0;1188} else {1189x = Math::sin(r * Math::TAU);1190z = Math::cos(r * Math::TAU);1191}11921193u = ((x + 1.0) * 0.25);1194v = 0.5 + ((z + 1.0) * 0.25);11951196Vector3 p = Vector3(x * top_radius, y, z * top_radius);1197points.push_back(p);1198normals.push_back(Vector3(0.0, 1.0, 0.0));1199ADD_TANGENT(1.0, 0.0, 0.0, 1.0)1200uvs.push_back(Vector2(u, v));1201if (p_add_uv2) {1202uv2s.push_back(Vector2(top_h + (x * top_h), height_v + padding_v + MAX(top_v, bottom_v) + (z * top_v)));1203}1204point++;12051206if (i > 0) {1207indices.push_back(thisrow);1208indices.push_back(point - 1);1209indices.push_back(point - 2);1210}1211}1212}12131214// Add bottom.1215if (cap_bottom && bottom_radius > 0.0) {1216y = height * -0.5;12171218thisrow = point;1219points.push_back(Vector3(0.0, y, 0.0));1220normals.push_back(Vector3(0.0, -1.0, 0.0));1221ADD_TANGENT(1.0, 0.0, 0.0, 1.0)1222uvs.push_back(Vector2(0.75, 0.75));1223if (p_add_uv2) {1224uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h, height_v + padding_v + MAX(top_v, bottom_v)));1225}1226point++;12271228for (i = 0; i <= radial_segments; i++) {1229float r = i;1230r /= radial_segments;12311232if (i == radial_segments) {1233x = 0.0;1234z = 1.0;1235} else {1236x = Math::sin(r * Math::TAU);1237z = Math::cos(r * Math::TAU);1238}12391240u = 0.5 + ((x + 1.0) * 0.25);1241v = 1.0 - ((z + 1.0) * 0.25);12421243Vector3 p = Vector3(x * bottom_radius, y, z * bottom_radius);1244points.push_back(p);1245normals.push_back(Vector3(0.0, -1.0, 0.0));1246ADD_TANGENT(1.0, 0.0, 0.0, 1.0)1247uvs.push_back(Vector2(u, v));1248if (p_add_uv2) {1249uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h + (x * bottom_h), height_v + padding_v + MAX(top_v, bottom_v) - (z * bottom_v)));1250}1251point++;12521253if (i > 0) {1254indices.push_back(thisrow);1255indices.push_back(point - 2);1256indices.push_back(point - 1);1257}1258}1259}12601261p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);1262p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);1263p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);1264p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);1265if (p_add_uv2) {1266p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);1267}1268p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);1269}12701271void CylinderMesh::_bind_methods() {1272ClassDB::bind_method(D_METHOD("set_top_radius", "radius"), &CylinderMesh::set_top_radius);1273ClassDB::bind_method(D_METHOD("get_top_radius"), &CylinderMesh::get_top_radius);1274ClassDB::bind_method(D_METHOD("set_bottom_radius", "radius"), &CylinderMesh::set_bottom_radius);1275ClassDB::bind_method(D_METHOD("get_bottom_radius"), &CylinderMesh::get_bottom_radius);1276ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderMesh::set_height);1277ClassDB::bind_method(D_METHOD("get_height"), &CylinderMesh::get_height);12781279ClassDB::bind_method(D_METHOD("set_radial_segments", "segments"), &CylinderMesh::set_radial_segments);1280ClassDB::bind_method(D_METHOD("get_radial_segments"), &CylinderMesh::get_radial_segments);1281ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings);1282ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings);12831284ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &CylinderMesh::set_cap_top);1285ClassDB::bind_method(D_METHOD("is_cap_top"), &CylinderMesh::is_cap_top);12861287ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &CylinderMesh::set_cap_bottom);1288ClassDB::bind_method(D_METHOD("is_cap_bottom"), &CylinderMesh::is_cap_bottom);12891290ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_top_radius", "get_top_radius");1291ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_bottom_radius", "get_bottom_radius");1292ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_height", "get_height");1293ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");1294ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings");1295ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top");1296ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom");1297}12981299void CylinderMesh::set_top_radius(const float p_radius) {1300if (Math::is_equal_approx(p_radius, top_radius)) {1301return;1302}13031304top_radius = p_radius;1305_update_lightmap_size();1306request_update();1307}13081309float CylinderMesh::get_top_radius() const {1310return top_radius;1311}13121313void CylinderMesh::set_bottom_radius(const float p_radius) {1314if (Math::is_equal_approx(p_radius, bottom_radius)) {1315return;1316}13171318bottom_radius = p_radius;1319_update_lightmap_size();1320request_update();1321}13221323float CylinderMesh::get_bottom_radius() const {1324return bottom_radius;1325}13261327void CylinderMesh::set_height(const float p_height) {1328if (Math::is_equal_approx(p_height, height)) {1329return;1330}13311332height = p_height;1333_update_lightmap_size();1334request_update();1335}13361337float CylinderMesh::get_height() const {1338return height;1339}13401341void CylinderMesh::set_radial_segments(const int p_segments) {1342if (p_segments == radial_segments) {1343return;1344}13451346radial_segments = p_segments > 4 ? p_segments : 4;1347request_update();1348}13491350int CylinderMesh::get_radial_segments() const {1351return radial_segments;1352}13531354void CylinderMesh::set_rings(const int p_rings) {1355if (p_rings == rings) {1356return;1357}13581359ERR_FAIL_COND(p_rings < 0);1360rings = p_rings;1361request_update();1362}13631364int CylinderMesh::get_rings() const {1365return rings;1366}13671368void CylinderMesh::set_cap_top(bool p_cap_top) {1369if (p_cap_top == cap_top) {1370return;1371}13721373cap_top = p_cap_top;1374request_update();1375}13761377bool CylinderMesh::is_cap_top() const {1378return cap_top;1379}13801381void CylinderMesh::set_cap_bottom(bool p_cap_bottom) {1382if (p_cap_bottom == cap_bottom) {1383return;1384}13851386cap_bottom = p_cap_bottom;1387request_update();1388}13891390bool CylinderMesh::is_cap_bottom() const {1391return cap_bottom;1392}13931394/**1395PlaneMesh1396*/13971398void PlaneMesh::_update_lightmap_size() {1399if (get_add_uv2()) {1400// size must have changed, update lightmap size hint1401Size2i _lightmap_size_hint;1402float padding = get_uv2_padding();14031404_lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding);1405_lightmap_size_hint.y = MAX(1.0, (size.y / texel_size) + padding);14061407set_lightmap_size_hint(_lightmap_size_hint);1408}1409}14101411void PlaneMesh::_create_mesh_array(Array &p_arr) const {1412int i, j, prevrow, thisrow, point;1413float x, z;14141415// Plane mesh can use default UV2 calculation as implemented in Primitive Mesh14161417Size2 start_pos = size * -0.5;14181419Vector3 normal = Vector3(0.0, 1.0, 0.0);1420if (orientation == FACE_X) {1421normal = Vector3(1.0, 0.0, 0.0);1422} else if (orientation == FACE_Z) {1423normal = Vector3(0.0, 0.0, 1.0);1424}14251426// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't1427// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.1428int num_points = (subdivide_d + 2) * (subdivide_w + 2);1429LocalVector<Vector3> points;1430points.reserve(num_points);1431LocalVector<Vector3> normals;1432normals.reserve(num_points);1433LocalVector<float> tangents;1434tangents.reserve(num_points * 4);1435LocalVector<Vector2> uvs;1436uvs.reserve(num_points);1437LocalVector<int> indices;1438indices.reserve((subdivide_d + 1) * (subdivide_w + 1) * 6);1439point = 0;14401441#define ADD_TANGENT(m_x, m_y, m_z, m_d) \1442tangents.push_back(m_x); \1443tangents.push_back(m_y); \1444tangents.push_back(m_z); \1445tangents.push_back(m_d);14461447/* top + bottom */1448z = start_pos.y;1449thisrow = point;1450prevrow = 0;1451for (j = 0; j <= (subdivide_d + 1); j++) {1452x = start_pos.x;1453for (i = 0; i <= (subdivide_w + 1); i++) {1454float u = i;1455float v = j;1456u /= (subdivide_w + 1.0);1457v /= (subdivide_d + 1.0);14581459if (orientation == FACE_X) {1460points.push_back(Vector3(0.0, z, x) + center_offset);1461} else if (orientation == FACE_Y) {1462points.push_back(Vector3(-x, 0.0, -z) + center_offset);1463} else if (orientation == FACE_Z) {1464points.push_back(Vector3(-x, z, 0.0) + center_offset);1465}1466normals.push_back(normal);1467if (orientation == FACE_X) {1468ADD_TANGENT(0.0, 0.0, -1.0, 1.0);1469} else {1470ADD_TANGENT(1.0, 0.0, 0.0, 1.0);1471}1472uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */1473point++;14741475if (i > 0 && j > 0) {1476indices.push_back(prevrow + i - 1);1477indices.push_back(prevrow + i);1478indices.push_back(thisrow + i - 1);1479indices.push_back(prevrow + i);1480indices.push_back(thisrow + i);1481indices.push_back(thisrow + i - 1);1482}14831484x += size.x / (subdivide_w + 1.0);1485}14861487z += size.y / (subdivide_d + 1.0);1488prevrow = thisrow;1489thisrow = point;1490}14911492p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);1493p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);1494p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);1495p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);1496p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);1497}14981499void PlaneMesh::_bind_methods() {1500ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaneMesh::set_size);1501ClassDB::bind_method(D_METHOD("get_size"), &PlaneMesh::get_size);15021503ClassDB::bind_method(D_METHOD("set_subdivide_width", "subdivide"), &PlaneMesh::set_subdivide_width);1504ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PlaneMesh::get_subdivide_width);1505ClassDB::bind_method(D_METHOD("set_subdivide_depth", "subdivide"), &PlaneMesh::set_subdivide_depth);1506ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PlaneMesh::get_subdivide_depth);15071508ClassDB::bind_method(D_METHOD("set_center_offset", "offset"), &PlaneMesh::set_center_offset);1509ClassDB::bind_method(D_METHOD("get_center_offset"), &PlaneMesh::get_center_offset);15101511ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &PlaneMesh::set_orientation);1512ClassDB::bind_method(D_METHOD("get_orientation"), &PlaneMesh::get_orientation);15131514ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");1515ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");1516ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");1517ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_center_offset", "get_center_offset");1518ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Face X,Face Y,Face Z"), "set_orientation", "get_orientation");15191520BIND_ENUM_CONSTANT(FACE_X)1521BIND_ENUM_CONSTANT(FACE_Y)1522BIND_ENUM_CONSTANT(FACE_Z)1523}15241525void PlaneMesh::set_size(const Size2 &p_size) {1526if (p_size == size) {1527return;1528}1529size = p_size;1530_update_lightmap_size();1531request_update();1532}15331534Size2 PlaneMesh::get_size() const {1535return size;1536}15371538void PlaneMesh::set_subdivide_width(const int p_divisions) {1539if (p_divisions == subdivide_w || (subdivide_w == 0 && p_divisions < 0)) {1540return;1541}1542subdivide_w = p_divisions > 0 ? p_divisions : 0;1543request_update();1544}15451546int PlaneMesh::get_subdivide_width() const {1547return subdivide_w;1548}15491550void PlaneMesh::set_subdivide_depth(const int p_divisions) {1551if (p_divisions == subdivide_d || (subdivide_d == 0 && p_divisions < 0)) {1552return;1553}1554subdivide_d = p_divisions > 0 ? p_divisions : 0;1555request_update();1556}15571558int PlaneMesh::get_subdivide_depth() const {1559return subdivide_d;1560}15611562void PlaneMesh::set_center_offset(const Vector3 p_offset) {1563if (p_offset.is_equal_approx(center_offset)) {1564return;1565}1566center_offset = p_offset;1567request_update();1568}15691570Vector3 PlaneMesh::get_center_offset() const {1571return center_offset;1572}15731574void PlaneMesh::set_orientation(const Orientation p_orientation) {1575if (p_orientation == orientation) {1576return;1577}1578orientation = p_orientation;1579request_update();1580}15811582PlaneMesh::Orientation PlaneMesh::get_orientation() const {1583return orientation;1584}15851586/**1587PrismMesh1588*/15891590void PrismMesh::_update_lightmap_size() {1591if (get_add_uv2()) {1592// size must have changed, update lightmap size hint1593Size2i _lightmap_size_hint;1594float padding = get_uv2_padding();15951596// left_to_right does not effect the surface area of the prism so we ignore that.1597// TODO we could combine the two triangles and save some space but we need to re-align the uv1 and adjust the tangent.15981599float width = (size.x + size.z) / texel_size;1600float length = (size.y + size.y + size.z) / texel_size;16011602_lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding;1603_lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding;16041605set_lightmap_size_hint(_lightmap_size_hint);1606}1607}16081609void PrismMesh::_create_mesh_array(Array &p_arr) const {1610int i, j, prevrow, thisrow, point;1611float x, y, z;1612float onethird = 1.0 / 3.0;1613float twothirds = 2.0 / 3.0;16141615// Only used if we calculate UV21616bool _add_uv2 = get_add_uv2();1617float _uv2_padding = get_uv2_padding() * texel_size;16181619float horizontal_total = size.x + size.z + 2.0 * _uv2_padding;1620float width_h = size.x / horizontal_total;1621float depth_h = size.z / horizontal_total;1622float padding_h = _uv2_padding / horizontal_total;16231624float vertical_total = (size.y + size.y + size.z) + (3.0 * _uv2_padding);1625float height_v = size.y / vertical_total;1626float depth_v = size.z / vertical_total;1627float padding_v = _uv2_padding / vertical_total;16281629// and start building16301631Vector3 start_pos = size * -0.5;16321633// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't1634// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.1635int num_points = (subdivide_h + 2) * (subdivide_w + 2) * 2 + (subdivide_h + 2) * (subdivide_d + 2) * 2 + (subdivide_d + 2) * (subdivide_w + 2);1636LocalVector<Vector3> points;1637points.reserve(num_points);1638LocalVector<Vector3> normals;1639normals.reserve(num_points);1640LocalVector<float> tangents;1641tangents.reserve(num_points * 4);1642LocalVector<Vector2> uvs;1643uvs.reserve(num_points);1644LocalVector<Vector2> uv2s;1645if (_add_uv2) {1646uv2s.reserve(num_points);1647}16481649int num_indices = (subdivide_h + 1) * (subdivide_w + 1) * 12 + (subdivide_w + 1) * 6;1650num_indices += (subdivide_h + 1) * (subdivide_d + 1) * 12;1651num_indices += (subdivide_d + 1) * (subdivide_w + 1) * 6;1652LocalVector<int> indices;1653indices.reserve(num_indices);1654point = 0;16551656#define ADD_TANGENT(m_x, m_y, m_z, m_d) \1657tangents.push_back(m_x); \1658tangents.push_back(m_y); \1659tangents.push_back(m_z); \1660tangents.push_back(m_d);16611662/* front + back */1663y = start_pos.y;1664thisrow = point;1665prevrow = 0;1666for (j = 0; j <= (subdivide_h + 1); j++) {1667float scale = j / (subdivide_h + 1.0);1668float scaled_size_x = size.x * scale;1669float start_x = start_pos.x + (1.0 - scale) * size.x * left_to_right;1670float offset_front = (1.0 - scale) * onethird * left_to_right;1671float offset_back = (1.0 - scale) * onethird * (1.0 - left_to_right);16721673float v = j;1674float v2 = scale;1675v /= 2.0 * (subdivide_h + 1.0);16761677x = 0.0;1678for (i = 0; i <= (subdivide_w + 1); i++) {1679float u = i;1680float u2 = i / (subdivide_w + 1.0);1681u /= (3.0 * (subdivide_w + 1.0));16821683u *= scale;16841685/* front */1686points.push_back(Vector3(start_x + x, -y, -start_pos.z)); // double negative on the Z!1687normals.push_back(Vector3(0.0, 0.0, 1.0));1688ADD_TANGENT(1.0, 0.0, 0.0, 1.0);1689uvs.push_back(Vector2(offset_front + u, v));1690if (_add_uv2) {1691uv2s.push_back(Vector2(u2 * scale * width_h, v2 * height_v));1692}1693point++;16941695/* back */1696points.push_back(Vector3(start_x + scaled_size_x - x, -y, start_pos.z));1697normals.push_back(Vector3(0.0, 0.0, -1.0));1698ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);1699uvs.push_back(Vector2(twothirds + offset_back + u, v));1700if (_add_uv2) {1701uv2s.push_back(Vector2(u2 * scale * width_h, height_v + padding_v + v2 * height_v));1702}1703point++;17041705if (i > 0 && j == 1) {1706int i2 = i * 2;17071708/* front */1709indices.push_back(prevrow + i2);1710indices.push_back(thisrow + i2);1711indices.push_back(thisrow + i2 - 2);17121713/* back */1714indices.push_back(prevrow + i2 + 1);1715indices.push_back(thisrow + i2 + 1);1716indices.push_back(thisrow + i2 - 1);1717} else if (i > 0 && j > 0) {1718int i2 = i * 2;17191720/* front */1721indices.push_back(prevrow + i2 - 2);1722indices.push_back(prevrow + i2);1723indices.push_back(thisrow + i2 - 2);1724indices.push_back(prevrow + i2);1725indices.push_back(thisrow + i2);1726indices.push_back(thisrow + i2 - 2);17271728/* back */1729indices.push_back(prevrow + i2 - 1);1730indices.push_back(prevrow + i2 + 1);1731indices.push_back(thisrow + i2 - 1);1732indices.push_back(prevrow + i2 + 1);1733indices.push_back(thisrow + i2 + 1);1734indices.push_back(thisrow + i2 - 1);1735}17361737x += scale * size.x / (subdivide_w + 1.0);1738}17391740y += size.y / (subdivide_h + 1.0);1741prevrow = thisrow;1742thisrow = point;1743}17441745/* left + right */1746Vector3 normal_left, normal_right;17471748normal_left = Vector3(-size.y, size.x * left_to_right, 0.0);1749normal_right = Vector3(size.y, size.x * (1.0 - left_to_right), 0.0);1750normal_left.normalize();1751normal_right.normalize();17521753y = start_pos.y;1754thisrow = point;1755prevrow = 0;1756for (j = 0; j <= (subdivide_h + 1); j++) {1757float left, right;1758float scale = j / (subdivide_h + 1.0);17591760left = start_pos.x + (size.x * (1.0 - scale) * left_to_right);1761right = left + (size.x * scale);17621763float v = j;1764float v2 = scale;1765v /= 2.0 * (subdivide_h + 1.0);17661767z = start_pos.z;1768for (i = 0; i <= (subdivide_d + 1); i++) {1769float u = i;1770float u2 = u / (subdivide_d + 1.0);1771u /= (3.0 * (subdivide_d + 1.0));17721773/* right */1774points.push_back(Vector3(right, -y, -z));1775normals.push_back(normal_right);1776ADD_TANGENT(0.0, 0.0, -1.0, 1.0);1777uvs.push_back(Vector2(onethird + u, v));1778if (_add_uv2) {1779uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, v2 * height_v));1780}1781point++;17821783/* left */1784points.push_back(Vector3(left, -y, z));1785normals.push_back(normal_left);1786ADD_TANGENT(0.0, 0.0, 1.0, 1.0);1787uvs.push_back(Vector2(u, 0.5 + v));1788if (_add_uv2) {1789uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, height_v + padding_v + v2 * height_v));1790}1791point++;17921793if (i > 0 && j > 0) {1794int i2 = i * 2;17951796/* right */1797indices.push_back(prevrow + i2 - 2);1798indices.push_back(prevrow + i2);1799indices.push_back(thisrow + i2 - 2);1800indices.push_back(prevrow + i2);1801indices.push_back(thisrow + i2);1802indices.push_back(thisrow + i2 - 2);18031804/* left */1805indices.push_back(prevrow + i2 - 1);1806indices.push_back(prevrow + i2 + 1);1807indices.push_back(thisrow + i2 - 1);1808indices.push_back(prevrow + i2 + 1);1809indices.push_back(thisrow + i2 + 1);1810indices.push_back(thisrow + i2 - 1);1811}18121813z += size.z / (subdivide_d + 1.0);1814}18151816y += size.y / (subdivide_h + 1.0);1817prevrow = thisrow;1818thisrow = point;1819}18201821/* bottom */1822z = start_pos.z;1823thisrow = point;1824prevrow = 0;1825for (j = 0; j <= (subdivide_d + 1); j++) {1826float v = j;1827float v2 = v / (subdivide_d + 1.0);1828v /= (2.0 * (subdivide_d + 1.0));18291830x = start_pos.x;1831for (i = 0; i <= (subdivide_w + 1); i++) {1832float u = i;1833float u2 = u / (subdivide_w + 1.0);1834u /= (3.0 * (subdivide_w + 1.0));18351836/* bottom */1837points.push_back(Vector3(x, start_pos.y, -z));1838normals.push_back(Vector3(0.0, -1.0, 0.0));1839ADD_TANGENT(1.0, 0.0, 0.0, 1.0);1840uvs.push_back(Vector2(twothirds + u, 0.5 + v));1841if (_add_uv2) {1842uv2s.push_back(Vector2(u2 * width_h, 2.0 * (height_v + padding_v) + v2 * depth_v));1843}1844point++;18451846if (i > 0 && j > 0) {1847/* bottom */1848indices.push_back(prevrow + i - 1);1849indices.push_back(prevrow + i);1850indices.push_back(thisrow + i - 1);1851indices.push_back(prevrow + i);1852indices.push_back(thisrow + i);1853indices.push_back(thisrow + i - 1);1854}18551856x += size.x / (subdivide_w + 1.0);1857}18581859z += size.z / (subdivide_d + 1.0);1860prevrow = thisrow;1861thisrow = point;1862}18631864p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);1865p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);1866p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);1867p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);1868if (_add_uv2) {1869p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);1870}1871p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);1872}18731874void PrismMesh::_bind_methods() {1875ClassDB::bind_method(D_METHOD("set_left_to_right", "left_to_right"), &PrismMesh::set_left_to_right);1876ClassDB::bind_method(D_METHOD("get_left_to_right"), &PrismMesh::get_left_to_right);18771878ClassDB::bind_method(D_METHOD("set_size", "size"), &PrismMesh::set_size);1879ClassDB::bind_method(D_METHOD("get_size"), &PrismMesh::get_size);18801881ClassDB::bind_method(D_METHOD("set_subdivide_width", "segments"), &PrismMesh::set_subdivide_width);1882ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PrismMesh::get_subdivide_width);1883ClassDB::bind_method(D_METHOD("set_subdivide_height", "segments"), &PrismMesh::set_subdivide_height);1884ClassDB::bind_method(D_METHOD("get_subdivide_height"), &PrismMesh::get_subdivide_height);1885ClassDB::bind_method(D_METHOD("set_subdivide_depth", "segments"), &PrismMesh::set_subdivide_depth);1886ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PrismMesh::get_subdivide_depth);18871888ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "left_to_right", PROPERTY_HINT_RANGE, "-2.0,2.0,0.1"), "set_left_to_right", "get_left_to_right");1889ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");1890ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width");1891ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height");1892ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth");1893}18941895void PrismMesh::set_left_to_right(const float p_left_to_right) {1896if (Math::is_equal_approx(p_left_to_right, left_to_right)) {1897return;1898}1899left_to_right = p_left_to_right;1900request_update();1901}19021903float PrismMesh::get_left_to_right() const {1904return left_to_right;1905}19061907void PrismMesh::set_size(const Vector3 &p_size) {1908if (p_size.is_equal_approx(size)) {1909return;1910}1911size = p_size;1912_update_lightmap_size();1913request_update();1914}19151916Vector3 PrismMesh::get_size() const {1917return size;1918}19191920void PrismMesh::set_subdivide_width(const int p_divisions) {1921if (p_divisions == subdivide_w || (p_divisions < 0 && subdivide_w == 0)) {1922return;1923}1924subdivide_w = p_divisions > 0 ? p_divisions : 0;1925request_update();1926}19271928int PrismMesh::get_subdivide_width() const {1929return subdivide_w;1930}19311932void PrismMesh::set_subdivide_height(const int p_divisions) {1933if (p_divisions == subdivide_h || (p_divisions < 0 && subdivide_h == 0)) {1934return;1935}1936subdivide_h = p_divisions > 0 ? p_divisions : 0;1937request_update();1938}19391940int PrismMesh::get_subdivide_height() const {1941return subdivide_h;1942}19431944void PrismMesh::set_subdivide_depth(const int p_divisions) {1945if (p_divisions == subdivide_d || (p_divisions < 0 && subdivide_d == 0)) {1946return;1947}1948subdivide_d = p_divisions > 0 ? p_divisions : 0;1949request_update();1950}19511952int PrismMesh::get_subdivide_depth() const {1953return subdivide_d;1954}19551956/**1957SphereMesh1958*/19591960void SphereMesh::_update_lightmap_size() {1961if (get_add_uv2()) {1962// size must have changed, update lightmap size hint1963Size2i _lightmap_size_hint;1964float padding = get_uv2_padding();19651966float _width = radius * Math::TAU;1967_lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);1968float _height = (is_hemisphere ? 1.0 : 0.5) * height * Math::PI; // note, with hemisphere height is our radius, while with a full sphere it is the diameter..1969_lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);19701971set_lightmap_size_hint(_lightmap_size_hint);1972}1973}19741975void SphereMesh::_create_mesh_array(Array &p_arr) const {1976bool _add_uv2 = get_add_uv2();1977float _uv2_padding = get_uv2_padding() * texel_size;19781979create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding);1980}19811982void 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) {1983int i, j, prevrow, thisrow, point;1984float x, y, z;19851986float scale = height / radius * (is_hemisphere ? 1.0 : 0.5);19871988// Only used if we calculate UV21989float circumference = radius * Math::TAU;1990float horizontal_length = circumference + p_uv2_padding;1991float center_h = 0.5 * circumference / horizontal_length;19921993float height_v = scale * Math::PI / ((scale * Math::PI) + p_uv2_padding / radius);19941995// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't1996// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.1997int num_points = (rings + 2) * (radial_segments + 1);1998LocalVector<Vector3> points;1999points.reserve(num_points);2000LocalVector<Vector3> normals;2001normals.reserve(num_points);2002LocalVector<float> tangents;2003tangents.reserve(num_points * 4);2004LocalVector<Vector2> uvs;2005uvs.reserve(num_points);2006LocalVector<Vector2> uv2s;2007if (p_add_uv2) {2008uv2s.reserve(num_points);2009}2010LocalVector<int> indices;2011indices.reserve((rings + 1) * (radial_segments) * 6);2012point = 0;20132014#define ADD_TANGENT(m_x, m_y, m_z, m_d) \2015tangents.push_back(m_x); \2016tangents.push_back(m_y); \2017tangents.push_back(m_z); \2018tangents.push_back(m_d);20192020thisrow = 0;2021prevrow = 0;2022for (j = 0; j <= (rings + 1); j++) {2023float v = j;2024float w;20252026v /= (rings + 1);2027if (j == (rings + 1)) {2028w = 0.0;2029y = -1.0;2030} else {2031w = Math::sin(Math::PI * v);2032y = Math::cos(Math::PI * v);2033}20342035for (i = 0; i <= radial_segments; i++) {2036float u = i;2037u /= radial_segments;20382039if (i == radial_segments) {2040x = 0.0;2041z = 1.0;2042} else {2043x = Math::sin(u * Math::TAU);2044z = Math::cos(u * Math::TAU);2045}20462047if (is_hemisphere && y < 0.0) {2048points.push_back(Vector3(x * radius * w, 0.0, z * radius * w));2049normals.push_back(Vector3(0.0, -1.0, 0.0));2050} else {2051Vector3 p = Vector3(x * w, y * scale, z * w);2052points.push_back(p * radius);2053Vector3 normal = Vector3(x * w * scale, y, z * w * scale);2054normals.push_back(normal.normalized());2055}2056ADD_TANGENT(z, 0.0, -x, 1.0)2057uvs.push_back(Vector2(u, v));2058if (p_add_uv2) {2059float w_h = w * 2.0 * center_h;2060uv2s.push_back(Vector2(center_h + ((u - 0.5) * w_h), v * height_v));2061}2062point++;20632064if (i > 0 && j > 0) {2065indices.push_back(prevrow + i - 1);2066indices.push_back(prevrow + i);2067indices.push_back(thisrow + i - 1);20682069indices.push_back(prevrow + i);2070indices.push_back(thisrow + i);2071indices.push_back(thisrow + i - 1);2072}2073}20742075prevrow = thisrow;2076thisrow = point;2077}20782079p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);2080p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);2081p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);2082p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);2083if (p_add_uv2) {2084p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);2085}2086p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);2087}20882089void SphereMesh::_bind_methods() {2090ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereMesh::set_radius);2091ClassDB::bind_method(D_METHOD("get_radius"), &SphereMesh::get_radius);2092ClassDB::bind_method(D_METHOD("set_height", "height"), &SphereMesh::set_height);2093ClassDB::bind_method(D_METHOD("get_height"), &SphereMesh::get_height);20942095ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &SphereMesh::set_radial_segments);2096ClassDB::bind_method(D_METHOD("get_radial_segments"), &SphereMesh::get_radial_segments);2097ClassDB::bind_method(D_METHOD("set_rings", "rings"), &SphereMesh::set_rings);2098ClassDB::bind_method(D_METHOD("get_rings"), &SphereMesh::get_rings);20992100ClassDB::bind_method(D_METHOD("set_is_hemisphere", "is_hemisphere"), &SphereMesh::set_is_hemisphere);2101ClassDB::bind_method(D_METHOD("get_is_hemisphere"), &SphereMesh::get_is_hemisphere);21022103ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");2104ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height");2105ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");2106ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");2107ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_hemisphere"), "set_is_hemisphere", "get_is_hemisphere");2108}21092110void SphereMesh::set_radius(const float p_radius) {2111if (Math::is_equal_approx(p_radius, radius)) {2112return;2113}2114radius = p_radius;2115_update_lightmap_size();2116request_update();2117}21182119float SphereMesh::get_radius() const {2120return radius;2121}21222123void SphereMesh::set_height(const float p_height) {2124if (Math::is_equal_approx(height, p_height)) {2125return;2126}2127height = p_height;2128_update_lightmap_size();2129request_update();2130}21312132float SphereMesh::get_height() const {2133return height;2134}21352136void SphereMesh::set_radial_segments(const int p_radial_segments) {2137if (p_radial_segments == radial_segments || (radial_segments == 4 && p_radial_segments < 4)) {2138return;2139}2140radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;2141request_update();2142}21432144int SphereMesh::get_radial_segments() const {2145return radial_segments;2146}21472148void SphereMesh::set_rings(const int p_rings) {2149if (p_rings == rings) {2150return;2151}2152ERR_FAIL_COND(p_rings < 1);2153rings = p_rings;2154request_update();2155}21562157int SphereMesh::get_rings() const {2158return rings;2159}21602161void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) {2162if (p_is_hemisphere == is_hemisphere) {2163return;2164}2165is_hemisphere = p_is_hemisphere;2166_update_lightmap_size();2167request_update();2168}21692170bool SphereMesh::get_is_hemisphere() const {2171return is_hemisphere;2172}21732174/**2175TorusMesh2176*/21772178void TorusMesh::_update_lightmap_size() {2179if (get_add_uv2()) {2180// size must have changed, update lightmap size hint2181Size2i _lightmap_size_hint;2182float padding = get_uv2_padding();21832184float min_radius = inner_radius;2185float max_radius = outer_radius;21862187if (min_radius > max_radius) {2188SWAP(min_radius, max_radius);2189}21902191float radius = (max_radius - min_radius) * 0.5;21922193float _width = max_radius * Math::TAU;2194_lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);2195float _height = radius * Math::TAU;2196_lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);21972198set_lightmap_size_hint(_lightmap_size_hint);2199}2200}22012202void TorusMesh::_create_mesh_array(Array &p_arr) const {2203// set our bounding box22042205bool _add_uv2 = get_add_uv2();22062207// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't2208// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.2209int num_points = (rings + 1) * (ring_segments + 1);2210LocalVector<Vector3> points;2211points.reserve(num_points);2212LocalVector<Vector3> normals;2213normals.reserve(num_points);2214LocalVector<float> tangents;2215tangents.reserve(num_points * 4);2216LocalVector<Vector2> uvs;2217uvs.reserve(num_points);2218LocalVector<Vector2> uv2s;2219if (_add_uv2) {2220uv2s.reserve(num_points);2221}2222LocalVector<int> indices;2223indices.reserve(rings * ring_segments * 6);22242225#define ADD_TANGENT(m_x, m_y, m_z, m_d) \2226tangents.push_back(m_x); \2227tangents.push_back(m_y); \2228tangents.push_back(m_z); \2229tangents.push_back(m_d);22302231ERR_FAIL_COND_MSG(inner_radius == outer_radius, "Inner radius and outer radius cannot be the same.");22322233float min_radius = inner_radius;2234float max_radius = outer_radius;22352236if (min_radius > max_radius) {2237SWAP(min_radius, max_radius);2238}22392240float radius = (max_radius - min_radius) * 0.5;22412242// Only used if we calculate UV22243float _uv2_padding = get_uv2_padding() * texel_size;22442245float horizontal_total = max_radius * Math::TAU + _uv2_padding;2246float max_h = max_radius * Math::TAU / horizontal_total;2247float delta_h = (max_radius - min_radius) * Math::TAU / horizontal_total;22482249float height_v = radius * Math::TAU / (radius * Math::TAU + _uv2_padding);22502251for (int i = 0; i <= rings; i++) {2252int prevrow = (i - 1) * (ring_segments + 1);2253int thisrow = i * (ring_segments + 1);2254float inci = float(i) / rings;2255float angi = inci * Math::TAU;22562257Vector2 normali = (i == rings) ? Vector2(0.0, -1.0) : Vector2(-Math::sin(angi), -Math::cos(angi));22582259for (int j = 0; j <= ring_segments; j++) {2260float incj = float(j) / ring_segments;2261float angj = incj * Math::TAU;22622263Vector2 normalj = (j == ring_segments) ? Vector2(-1.0, 0.0) : Vector2(-Math::cos(angj), Math::sin(angj));2264Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0);22652266float offset_h = 0.5 * (1.0 - normalj.x) * delta_h;2267float adj_h = max_h - offset_h;2268offset_h *= 0.5;22692270points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x));2271normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x));2272ADD_TANGENT(normali.y, 0.0, -normali.x, 1.0);2273uvs.push_back(Vector2(inci, incj));2274if (_add_uv2) {2275uv2s.push_back(Vector2(offset_h + inci * adj_h, incj * height_v));2276}22772278if (i > 0 && j > 0) {2279indices.push_back(thisrow + j - 1);2280indices.push_back(prevrow + j);2281indices.push_back(prevrow + j - 1);22822283indices.push_back(thisrow + j - 1);2284indices.push_back(thisrow + j);2285indices.push_back(prevrow + j);2286}2287}2288}22892290p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);2291p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);2292p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);2293p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);2294if (_add_uv2) {2295p_arr[RS::ARRAY_TEX_UV2] = Vector<Vector2>(uv2s);2296}2297p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);2298}22992300void TorusMesh::_bind_methods() {2301ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &TorusMesh::set_inner_radius);2302ClassDB::bind_method(D_METHOD("get_inner_radius"), &TorusMesh::get_inner_radius);23032304ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &TorusMesh::set_outer_radius);2305ClassDB::bind_method(D_METHOD("get_outer_radius"), &TorusMesh::get_outer_radius);23062307ClassDB::bind_method(D_METHOD("set_rings", "rings"), &TorusMesh::set_rings);2308ClassDB::bind_method(D_METHOD("get_rings"), &TorusMesh::get_rings);23092310ClassDB::bind_method(D_METHOD("set_ring_segments", "rings"), &TorusMesh::set_ring_segments);2311ClassDB::bind_method(D_METHOD("get_ring_segments"), &TorusMesh::get_ring_segments);23122313ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");2314ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius");2315ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "3,128,1,or_greater"), "set_rings", "get_rings");2316ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_segments", PROPERTY_HINT_RANGE, "3,64,1,or_greater"), "set_ring_segments", "get_ring_segments");2317}23182319void TorusMesh::set_inner_radius(const float p_inner_radius) {2320if (Math::is_equal_approx(p_inner_radius, inner_radius)) {2321return;2322}2323inner_radius = p_inner_radius;2324request_update();2325}23262327float TorusMesh::get_inner_radius() const {2328return inner_radius;2329}23302331void TorusMesh::set_outer_radius(const float p_outer_radius) {2332if (Math::is_equal_approx(p_outer_radius, outer_radius)) {2333return;2334}2335outer_radius = p_outer_radius;2336request_update();2337}23382339float TorusMesh::get_outer_radius() const {2340return outer_radius;2341}23422343void TorusMesh::set_rings(const int p_rings) {2344if (p_rings == rings) {2345return;2346}2347ERR_FAIL_COND(p_rings < 3);2348rings = p_rings;2349request_update();2350}23512352int TorusMesh::get_rings() const {2353return rings;2354}23552356void TorusMesh::set_ring_segments(const int p_ring_segments) {2357if (p_ring_segments == ring_segments) {2358return;2359}2360ERR_FAIL_COND(p_ring_segments < 3);2361ring_segments = p_ring_segments;2362request_update();2363}23642365int TorusMesh::get_ring_segments() const {2366return ring_segments;2367}23682369/**2370PointMesh2371*/23722373void PointMesh::_create_mesh_array(Array &p_arr) const {2374Vector<Vector3> faces;2375faces.resize(1);2376faces.set(0, Vector3(0.0, 0.0, 0.0));23772378p_arr[RS::ARRAY_VERTEX] = faces;2379}23802381PointMesh::PointMesh() {2382primitive_type = PRIMITIVE_POINTS;2383}2384// TUBE TRAIL23852386void TubeTrailMesh::set_radius(const float p_radius) {2387if (Math::is_equal_approx(p_radius, radius)) {2388return;2389}2390radius = p_radius;2391request_update();2392}2393float TubeTrailMesh::get_radius() const {2394return radius;2395}23962397void TubeTrailMesh::set_radial_steps(const int p_radial_steps) {2398if (p_radial_steps == radial_steps) {2399return;2400}2401ERR_FAIL_COND(p_radial_steps < 3 || p_radial_steps > 128);2402radial_steps = p_radial_steps;2403request_update();2404}2405int TubeTrailMesh::get_radial_steps() const {2406return radial_steps;2407}24082409void TubeTrailMesh::set_sections(const int p_sections) {2410if (p_sections == sections) {2411return;2412}2413ERR_FAIL_COND(p_sections < 2 || p_sections > 128);2414sections = p_sections;2415request_update();2416}2417int TubeTrailMesh::get_sections() const {2418return sections;2419}24202421void TubeTrailMesh::set_section_length(float p_section_length) {2422if (p_section_length == section_length) {2423return;2424}2425section_length = p_section_length;2426request_update();2427}2428float TubeTrailMesh::get_section_length() const {2429return section_length;2430}24312432void TubeTrailMesh::set_section_rings(const int p_section_rings) {2433if (p_section_rings == section_rings) {2434return;2435}2436ERR_FAIL_COND(p_section_rings < 1 || p_section_rings > 1024);2437section_rings = p_section_rings;2438request_update();2439}2440int TubeTrailMesh::get_section_rings() const {2441return section_rings;2442}24432444void TubeTrailMesh::set_cap_top(bool p_cap_top) {2445if (p_cap_top == cap_top) {2446return;2447}2448cap_top = p_cap_top;2449request_update();2450}24512452bool TubeTrailMesh::is_cap_top() const {2453return cap_top;2454}24552456void TubeTrailMesh::set_cap_bottom(bool p_cap_bottom) {2457if (p_cap_bottom == cap_bottom) {2458return;2459}2460cap_bottom = p_cap_bottom;2461request_update();2462}24632464bool TubeTrailMesh::is_cap_bottom() const {2465return cap_bottom;2466}24672468void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) {2469if (curve == p_curve) {2470return;2471}2472if (curve.is_valid()) {2473curve->disconnect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed));2474}2475curve = p_curve;2476if (curve.is_valid()) {2477curve->connect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed));2478}2479request_update();2480}2481Ref<Curve> TubeTrailMesh::get_curve() const {2482return curve;2483}24842485void TubeTrailMesh::_curve_changed() {2486request_update();2487}2488int TubeTrailMesh::get_builtin_bind_pose_count() const {2489return sections + 1;2490}24912492Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const {2493float depth = section_length * sections;24942495Transform3D xform;2496xform.origin.y = depth / 2.0 - section_length * float(p_index);2497xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y24982499return xform;2500}25012502void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {2503// Seeing use case for TubeTrailMesh, no need to do anything more then default UV2 calculation25042505int total_rings = section_rings * sections;2506float depth = section_length * sections;25072508// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't2509// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.2510int num_points = (total_rings + 1) * (radial_steps + 1) + 4 + radial_steps * 2;2511LocalVector<Vector3> points;2512points.reserve(num_points);2513LocalVector<Vector3> normals;2514normals.reserve(num_points);2515LocalVector<float> tangents;2516tangents.reserve(num_points * 4);2517LocalVector<Vector2> uvs;2518uvs.reserve(num_points);2519LocalVector<int> bone_indices;2520bone_indices.reserve(num_points * 4);2521LocalVector<float> bone_weights;2522bone_weights.reserve(num_points * 4);2523LocalVector<int> indices;2524indices.reserve(total_rings * radial_steps * 6 + radial_steps * 6);25252526int point = 0;25272528#define ADD_TANGENT(m_x, m_y, m_z, m_d) \2529tangents.push_back(m_x); \2530tangents.push_back(m_y); \2531tangents.push_back(m_z); \2532tangents.push_back(m_d);25332534int thisrow = 0;2535int prevrow = 0;25362537for (int j = 0; j <= total_rings; j++) {2538float v = j;2539v /= total_rings;25402541float y = depth * v;2542y = (depth * 0.5) - y;25432544int bone = j / section_rings;2545float blend = 1.0 - float(j % section_rings) / float(section_rings);25462547for (int i = 0; i <= radial_steps; i++) {2548float u = i;2549u /= radial_steps;25502551float r = radius;2552if (curve.is_valid() && curve->get_point_count() > 0) {2553r *= curve->sample_baked(v);2554}2555float x = 0.0;2556float z = 1.0;2557if (i < radial_steps) {2558x = Math::sin(u * Math::TAU);2559z = Math::cos(u * Math::TAU);2560}25612562Vector3 p = Vector3(x * r, y, z * r);2563points.push_back(p);2564normals.push_back(Vector3(x, 0, z));2565ADD_TANGENT(z, 0.0, -x, 1.0)2566uvs.push_back(Vector2(u, v * 0.5));2567point++;2568{2569bone_indices.push_back(bone);2570bone_indices.push_back(MIN(sections, bone + 1));2571bone_indices.push_back(0);2572bone_indices.push_back(0);25732574bone_weights.push_back(blend);2575bone_weights.push_back(1.0 - blend);2576bone_weights.push_back(0);2577bone_weights.push_back(0);2578}25792580if (i > 0 && j > 0) {2581indices.push_back(prevrow + i - 1);2582indices.push_back(prevrow + i);2583indices.push_back(thisrow + i - 1);25842585indices.push_back(prevrow + i);2586indices.push_back(thisrow + i);2587indices.push_back(thisrow + i - 1);2588}2589}25902591prevrow = thisrow;2592thisrow = point;2593}25942595if (cap_top) {2596// add top2597float scale_pos = 1.0;2598if (curve.is_valid() && curve->get_point_count() > 0) {2599scale_pos = curve->sample_baked(0);2600}26012602if (scale_pos > CMP_EPSILON) {2603float y = depth * 0.5;26042605thisrow = point;2606points.push_back(Vector3(0.0, y, 0));2607normals.push_back(Vector3(0.0, 1.0, 0.0));2608ADD_TANGENT(1.0, 0.0, 0.0, 1.0)2609uvs.push_back(Vector2(0.25, 0.75));2610point++;26112612bone_indices.push_back(0);2613bone_indices.push_back(0);2614bone_indices.push_back(0);2615bone_indices.push_back(0);26162617bone_weights.push_back(1.0);2618bone_weights.push_back(0);2619bone_weights.push_back(0);2620bone_weights.push_back(0);26212622float rm = radius * scale_pos;26232624for (int i = 0; i <= radial_steps; i++) {2625float r = i;2626r /= radial_steps;26272628float x = 0.0;2629float z = 1.0;2630if (i < radial_steps) {2631x = Math::sin(r * Math::TAU);2632z = Math::cos(r * Math::TAU);2633}26342635float u = ((x + 1.0) * 0.25);2636float v = 0.5 + ((z + 1.0) * 0.25);26372638Vector3 p = Vector3(x * rm, y, z * rm);2639points.push_back(p);2640normals.push_back(Vector3(0.0, 1.0, 0.0));2641ADD_TANGENT(1.0, 0.0, 0.0, 1.0)2642uvs.push_back(Vector2(u, v));2643point++;26442645bone_indices.push_back(0);2646bone_indices.push_back(0);2647bone_indices.push_back(0);2648bone_indices.push_back(0);26492650bone_weights.push_back(1.0);2651bone_weights.push_back(0);2652bone_weights.push_back(0);2653bone_weights.push_back(0);26542655if (i > 0) {2656indices.push_back(thisrow);2657indices.push_back(point - 1);2658indices.push_back(point - 2);2659}2660}2661}2662}26632664if (cap_bottom) {2665float scale_neg = 1.0;2666if (curve.is_valid() && curve->get_point_count() > 0) {2667scale_neg = curve->sample_baked(1.0);2668}26692670if (scale_neg > CMP_EPSILON) {2671// add bottom2672float y = depth * -0.5;26732674thisrow = point;2675points.push_back(Vector3(0.0, y, 0.0));2676normals.push_back(Vector3(0.0, -1.0, 0.0));2677ADD_TANGENT(1.0, 0.0, 0.0, 1.0)2678uvs.push_back(Vector2(0.75, 0.75));2679point++;26802681bone_indices.push_back(sections);2682bone_indices.push_back(0);2683bone_indices.push_back(0);2684bone_indices.push_back(0);26852686bone_weights.push_back(1.0);2687bone_weights.push_back(0);2688bone_weights.push_back(0);2689bone_weights.push_back(0);26902691float rm = radius * scale_neg;26922693for (int i = 0; i <= radial_steps; i++) {2694float r = i;2695r /= radial_steps;26962697float x = 0.0;2698float z = 1.0;2699if (i < radial_steps) {2700x = Math::sin(r * Math::TAU);2701z = Math::cos(r * Math::TAU);2702}27032704float u = 0.5 + ((x + 1.0) * 0.25);2705float v = 1.0 - ((z + 1.0) * 0.25);27062707Vector3 p = Vector3(x * rm, y, z * rm);2708points.push_back(p);2709normals.push_back(Vector3(0.0, -1.0, 0.0));2710ADD_TANGENT(1.0, 0.0, 0.0, 1.0)2711uvs.push_back(Vector2(u, v));2712point++;27132714bone_indices.push_back(sections);2715bone_indices.push_back(0);2716bone_indices.push_back(0);2717bone_indices.push_back(0);27182719bone_weights.push_back(1.0);2720bone_weights.push_back(0);2721bone_weights.push_back(0);2722bone_weights.push_back(0);27232724if (i > 0) {2725indices.push_back(thisrow);2726indices.push_back(point - 2);2727indices.push_back(point - 1);2728}2729}2730}2731}27322733p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);2734p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);2735p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);2736p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);2737p_arr[RS::ARRAY_BONES] = Vector<int>(bone_indices);2738p_arr[RS::ARRAY_WEIGHTS] = Vector<float>(bone_weights);2739p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);2740}27412742void TubeTrailMesh::_bind_methods() {2743ClassDB::bind_method(D_METHOD("set_radius", "radius"), &TubeTrailMesh::set_radius);2744ClassDB::bind_method(D_METHOD("get_radius"), &TubeTrailMesh::get_radius);27452746ClassDB::bind_method(D_METHOD("set_radial_steps", "radial_steps"), &TubeTrailMesh::set_radial_steps);2747ClassDB::bind_method(D_METHOD("get_radial_steps"), &TubeTrailMesh::get_radial_steps);27482749ClassDB::bind_method(D_METHOD("set_sections", "sections"), &TubeTrailMesh::set_sections);2750ClassDB::bind_method(D_METHOD("get_sections"), &TubeTrailMesh::get_sections);27512752ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &TubeTrailMesh::set_section_length);2753ClassDB::bind_method(D_METHOD("get_section_length"), &TubeTrailMesh::get_section_length);27542755ClassDB::bind_method(D_METHOD("set_section_rings", "section_rings"), &TubeTrailMesh::set_section_rings);2756ClassDB::bind_method(D_METHOD("get_section_rings"), &TubeTrailMesh::get_section_rings);27572758ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &TubeTrailMesh::set_cap_top);2759ClassDB::bind_method(D_METHOD("is_cap_top"), &TubeTrailMesh::is_cap_top);27602761ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &TubeTrailMesh::set_cap_bottom);2762ClassDB::bind_method(D_METHOD("is_cap_bottom"), &TubeTrailMesh::is_cap_bottom);27632764ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve);2765ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve);27662767ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius");27682769ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_steps", PROPERTY_HINT_RANGE, "3,128,1"), "set_radial_steps", "get_radial_steps");2770ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections");27712772ADD_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");27732774ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings");27752776ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top");2777ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom");27782779ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, Curve::get_class_static()), "set_curve", "get_curve");2780}27812782TubeTrailMesh::TubeTrailMesh() {2783}27842785// RIBBON TRAIL27862787void RibbonTrailMesh::set_shape(Shape p_shape) {2788if (p_shape == shape) {2789return;2790}2791shape = p_shape;2792request_update();2793}2794RibbonTrailMesh::Shape RibbonTrailMesh::get_shape() const {2795return shape;2796}27972798void RibbonTrailMesh::set_size(const float p_size) {2799if (Math::is_equal_approx(p_size, size)) {2800return;2801}2802size = p_size;2803request_update();2804}2805float RibbonTrailMesh::get_size() const {2806return size;2807}28082809void RibbonTrailMesh::set_sections(const int p_sections) {2810if (p_sections == sections) {2811return;2812}2813ERR_FAIL_COND(p_sections < 2 || p_sections > 128);2814sections = p_sections;2815request_update();2816}2817int RibbonTrailMesh::get_sections() const {2818return sections;2819}28202821void RibbonTrailMesh::set_section_length(float p_section_length) {2822if (p_section_length == section_length) {2823return;2824}2825section_length = p_section_length;2826request_update();2827}2828float RibbonTrailMesh::get_section_length() const {2829return section_length;2830}28312832void RibbonTrailMesh::set_section_segments(const int p_section_segments) {2833if (p_section_segments == section_segments) {2834return;2835}2836ERR_FAIL_COND(p_section_segments < 1 || p_section_segments > 1024);2837section_segments = p_section_segments;2838request_update();2839}2840int RibbonTrailMesh::get_section_segments() const {2841return section_segments;2842}28432844void RibbonTrailMesh::set_curve(const Ref<Curve> &p_curve) {2845if (curve == p_curve) {2846return;2847}2848if (curve.is_valid()) {2849curve->disconnect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed));2850}2851curve = p_curve;2852if (curve.is_valid()) {2853curve->connect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed));2854}2855request_update();2856}2857Ref<Curve> RibbonTrailMesh::get_curve() const {2858return curve;2859}28602861void RibbonTrailMesh::_curve_changed() {2862request_update();2863}2864int RibbonTrailMesh::get_builtin_bind_pose_count() const {2865return sections + 1;2866}28672868Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const {2869float depth = section_length * sections;28702871Transform3D xform;2872xform.origin.y = depth / 2.0 - section_length * float(p_index);2873xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y28742875return xform;2876}28772878void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const {2879// Seeing use case of ribbon trail mesh, no need to implement special UV2 calculation28802881int total_segments = section_segments * sections;2882float depth = section_length * sections;28832884// Use LocalVector for operations and copy to Vector at the end to save the cost of CoW semantics which aren't2885// needed here and are very expensive in such a hot loop. Use reserve to avoid repeated memory allocations.2886int num_points = (total_segments + 1) * 2;2887num_points *= shape == SHAPE_CROSS ? 2 : 1;2888LocalVector<Vector3> points;2889points.reserve(num_points);2890LocalVector<Vector3> normals;2891normals.reserve(num_points);2892LocalVector<float> tangents;2893tangents.reserve(num_points * 4);2894LocalVector<Vector2> uvs;2895uvs.reserve(num_points);2896LocalVector<int> bone_indices;2897bone_indices.reserve(num_points * 4);2898LocalVector<float> bone_weights;2899bone_weights.reserve(num_points * 4);2900LocalVector<int> indices;2901indices.reserve(total_segments * 6 * (shape == SHAPE_CROSS ? 2 : 1));29022903#define ADD_TANGENT(m_x, m_y, m_z, m_d) \2904tangents.push_back(m_x); \2905tangents.push_back(m_y); \2906tangents.push_back(m_z); \2907tangents.push_back(m_d);29082909for (int j = 0; j <= total_segments; j++) {2910float v = j;2911v /= total_segments;29122913float y = depth * v;2914y = (depth * 0.5) - y;29152916int bone = j / section_segments;2917float blend = 1.0 - float(j % section_segments) / float(section_segments);29182919float s = size;29202921if (curve.is_valid() && curve->get_point_count() > 0) {2922s *= curve->sample_baked(v);2923}29242925points.push_back(Vector3(-s * 0.5, y, 0));2926points.push_back(Vector3(+s * 0.5, y, 0));2927if (shape == SHAPE_CROSS) {2928points.push_back(Vector3(0, y, -s * 0.5));2929points.push_back(Vector3(0, y, +s * 0.5));2930}29312932normals.push_back(Vector3(0, 0, 1));2933normals.push_back(Vector3(0, 0, 1));2934if (shape == SHAPE_CROSS) {2935normals.push_back(Vector3(1, 0, 0));2936normals.push_back(Vector3(1, 0, 0));2937}29382939uvs.push_back(Vector2(0, v));2940uvs.push_back(Vector2(1, v));2941if (shape == SHAPE_CROSS) {2942uvs.push_back(Vector2(0, v));2943uvs.push_back(Vector2(1, v));2944}29452946ADD_TANGENT(0.0, 1.0, 0.0, 1.0)2947ADD_TANGENT(0.0, 1.0, 0.0, 1.0)2948if (shape == SHAPE_CROSS) {2949ADD_TANGENT(0.0, 1.0, 0.0, 1.0)2950ADD_TANGENT(0.0, 1.0, 0.0, 1.0)2951}29522953for (int i = 0; i < (shape == SHAPE_CROSS ? 4 : 2); i++) {2954bone_indices.push_back(bone);2955bone_indices.push_back(MIN(sections, bone + 1));2956bone_indices.push_back(0);2957bone_indices.push_back(0);29582959bone_weights.push_back(blend);2960bone_weights.push_back(1.0 - blend);2961bone_weights.push_back(0);2962bone_weights.push_back(0);2963}29642965if (j > 0) {2966if (shape == SHAPE_CROSS) {2967int base = j * 4 - 4;2968indices.push_back(base + 0);2969indices.push_back(base + 1);2970indices.push_back(base + 4);29712972indices.push_back(base + 1);2973indices.push_back(base + 5);2974indices.push_back(base + 4);29752976indices.push_back(base + 2);2977indices.push_back(base + 3);2978indices.push_back(base + 6);29792980indices.push_back(base + 3);2981indices.push_back(base + 7);2982indices.push_back(base + 6);2983} else {2984int base = j * 2 - 2;2985indices.push_back(base + 0);2986indices.push_back(base + 1);2987indices.push_back(base + 2);29882989indices.push_back(base + 1);2990indices.push_back(base + 3);2991indices.push_back(base + 2);2992}2993}2994}29952996p_arr[RS::ARRAY_VERTEX] = Vector<Vector3>(points);2997p_arr[RS::ARRAY_NORMAL] = Vector<Vector3>(normals);2998p_arr[RS::ARRAY_TANGENT] = Vector<float>(tangents);2999p_arr[RS::ARRAY_TEX_UV] = Vector<Vector2>(uvs);3000p_arr[RS::ARRAY_BONES] = Vector<int>(bone_indices);3001p_arr[RS::ARRAY_WEIGHTS] = Vector<float>(bone_weights);3002p_arr[RS::ARRAY_INDEX] = Vector<int>(indices);3003}30043005void RibbonTrailMesh::_bind_methods() {3006ClassDB::bind_method(D_METHOD("set_size", "size"), &RibbonTrailMesh::set_size);3007ClassDB::bind_method(D_METHOD("get_size"), &RibbonTrailMesh::get_size);30083009ClassDB::bind_method(D_METHOD("set_sections", "sections"), &RibbonTrailMesh::set_sections);3010ClassDB::bind_method(D_METHOD("get_sections"), &RibbonTrailMesh::get_sections);30113012ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &RibbonTrailMesh::set_section_length);3013ClassDB::bind_method(D_METHOD("get_section_length"), &RibbonTrailMesh::get_section_length);30143015ClassDB::bind_method(D_METHOD("set_section_segments", "section_segments"), &RibbonTrailMesh::set_section_segments);3016ClassDB::bind_method(D_METHOD("get_section_segments"), &RibbonTrailMesh::get_section_segments);30173018ClassDB::bind_method(D_METHOD("set_curve", "curve"), &RibbonTrailMesh::set_curve);3019ClassDB::bind_method(D_METHOD("get_curve"), &RibbonTrailMesh::get_curve);30203021ClassDB::bind_method(D_METHOD("set_shape", "shape"), &RibbonTrailMesh::set_shape);3022ClassDB::bind_method(D_METHOD("get_shape"), &RibbonTrailMesh::get_shape);30233024ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Flat,Cross"), "set_shape", "get_shape");3025ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_size", "get_size");3026ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections");3027ADD_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");3028ADD_PROPERTY(PropertyInfo(Variant::INT, "section_segments", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_segments", "get_section_segments");3029ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, Curve::get_class_static()), "set_curve", "get_curve");30303031BIND_ENUM_CONSTANT(SHAPE_FLAT)3032BIND_ENUM_CONSTANT(SHAPE_CROSS)3033}30343035RibbonTrailMesh::RibbonTrailMesh() {3036}30373038/*************************************************************************/3039/* TextMesh */3040/*************************************************************************/30413042void TextMesh::_generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph &p_gl) const {3043if (cache.has(p_key)) {3044return;3045}30463047GlyphMeshData &gl_data = cache[p_key];30483049Dictionary d = TS->font_get_glyph_contours(p_gl.font_rid, p_gl.font_size, p_gl.index);30503051PackedVector3Array points = d["points"];3052PackedInt32Array contours = d["contours"];3053bool orientation = d["orientation"];30543055if (points.size() < 3 || contours.is_empty()) {3056return; // No full contours, only glyph control points (or nothing), ignore.3057}30583059// Approximate Bezier curves as polygons.3060// See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html, for more info.3061for (int i = 0; i < contours.size(); i++) {3062int32_t start = (i == 0) ? 0 : (contours[i - 1] + 1);3063int32_t end = contours[i];3064Vector<ContourPoint> polygon;30653066for (int32_t j = start; j <= end; j++) {3067if (points[j].z == (real_t)TextServer::CONTOUR_CURVE_TAG_ON) {3068// Point on the curve.3069Vector2 p = Vector2(points[j].x, points[j].y) * pixel_size;3070polygon.push_back(ContourPoint(p, true));3071} else if (points[j].z == (real_t)TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {3072// Conic Bezier arc.3073int32_t next = (j == end) ? start : (j + 1);3074int32_t prev = (j == start) ? end : (j - 1);3075Vector2 p0;3076Vector2 p1 = Vector2(points[j].x, points[j].y);3077Vector2 p2;30783079// For successive conic OFF points add a virtual ON point in the middle.3080if (points[prev].z == (real_t)TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {3081p0 = (Vector2(points[prev].x, points[prev].y) + Vector2(points[j].x, points[j].y)) / 2.0;3082} else if (points[prev].z == (real_t)TextServer::CONTOUR_CURVE_TAG_ON) {3083p0 = Vector2(points[prev].x, points[prev].y);3084} else {3085ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j));3086}3087if (points[next].z == (real_t)TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) {3088p2 = (Vector2(points[j].x, points[j].y) + Vector2(points[next].x, points[next].y)) / 2.0;3089} else if (points[next].z == (real_t)TextServer::CONTOUR_CURVE_TAG_ON) {3090p2 = Vector2(points[next].x, points[next].y);3091} else {3092ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j));3093}30943095real_t step = CLAMP(curve_step / (p0 - p2).length(), 0.01, 0.5);3096real_t t = step;3097while (t < 1.0) {3098real_t omt = (1.0 - t);3099real_t omt2 = omt * omt;3100real_t t2 = t * t;31013102Vector2 point = p1 + omt2 * (p0 - p1) + t2 * (p2 - p1);3103Vector2 p = point * pixel_size;3104polygon.push_back(ContourPoint(p, false));3105t += step;3106}3107} else if (points[j].z == (real_t)TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC) {3108// Cubic Bezier arc.3109int32_t cur = j;3110int32_t next1 = (j == end) ? start : (j + 1);3111int32_t next2 = (next1 == end) ? start : (next1 + 1);3112int32_t prev = (j == start) ? end : (j - 1);31133114// There must be exactly two OFF points and two ON points for each cubic arc.3115if (points[prev].z != (real_t)TextServer::CONTOUR_CURVE_TAG_ON) {3116cur = (cur == 0) ? end : cur - 1;3117next1 = (next1 == 0) ? end : next1 - 1;3118next2 = (next2 == 0) ? end : next2 - 1;3119prev = (prev == 0) ? end : prev - 1;3120} else {3121j++;3122}3123ERR_FAIL_COND_MSG(points[prev].z != (real_t)TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, prev));3124ERR_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));3125ERR_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));3126ERR_FAIL_COND_MSG(points[next2].z != (real_t)TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, next2));31273128Vector2 p0 = Vector2(points[prev].x, points[prev].y);3129Vector2 p1 = Vector2(points[cur].x, points[cur].y);3130Vector2 p2 = Vector2(points[next1].x, points[next1].y);3131Vector2 p3 = Vector2(points[next2].x, points[next2].y);31323133real_t step = CLAMP(curve_step / (p0 - p3).length(), 0.01, 0.5);3134real_t t = step;3135while (t < 1.0) {3136Vector2 point = p0.bezier_interpolate(p1, p2, p3, t);3137Vector2 p = point * pixel_size;3138polygon.push_back(ContourPoint(p, false));3139t += step;3140}3141} else {3142ERR_FAIL_MSG(vformat("Unknown point tag at %d:%d", i, j));3143}3144}31453146if (polygon.size() < 3) {3147continue; // Skip glyph control points.3148}31493150if (!orientation) {3151polygon.reverse();3152}31533154gl_data.contours.push_back(polygon);3155}31563157// Calculate bounds.3158List<TPPLPoly> in_poly;3159for (int i = 0; i < gl_data.contours.size(); i++) {3160TPPLPoly inp;3161inp.Init(gl_data.contours[i].size());3162real_t length = 0.0;3163for (int j = 0; j < gl_data.contours[i].size(); j++) {3164int next = (j + 1 == gl_data.contours[i].size()) ? 0 : (j + 1);31653166gl_data.min_p = gl_data.min_p.min(gl_data.contours[i][j].point);3167gl_data.max_p = gl_data.max_p.max(gl_data.contours[i][j].point);3168length += (gl_data.contours[i][next].point - gl_data.contours[i][j].point).length();31693170inp.GetPoint(j) = gl_data.contours[i][j].point;3171}3172TPPLOrientation poly_orient = inp.GetOrientation();3173if (poly_orient == TPPL_ORIENTATION_CW) {3174inp.SetHole(true);3175}3176in_poly.push_back(inp);3177gl_data.contours_info.push_back(ContourInfo(length, poly_orient == TPPL_ORIENTATION_CCW));3178}31793180TPPLPartition tpart;31813182//Decompose and triangulate.3183List<TPPLPoly> out_poly;3184if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) {3185ERR_FAIL_MSG("Convex decomposing failed. Make sure the font doesn't contain self-intersecting lines, as these are not supported in TextMesh.");3186}3187List<TPPLPoly> out_tris;3188for (TPPLPoly &tp : out_poly) {3189if (tpart.Triangulate_OPT(&tp, &out_tris) == 0) {3190ERR_FAIL_MSG("Triangulation failed. Make sure the font doesn't contain self-intersecting lines, as these are not supported in TextMesh.");3191}3192}31933194for (const TPPLPoly &tp : out_tris) {3195ERR_FAIL_COND(tp.GetNumPoints() != 3); // Triangles only.31963197for (int i = 0; i < 3; i++) {3198gl_data.triangles.push_back(Vector2(tp.GetPoint(i).x, tp.GetPoint(i).y));3199}3200}3201}32023203void TextMesh::_create_mesh_array(Array &p_arr) const {3204Ref<Font> font = _get_font_or_default();3205ERR_FAIL_COND(font.is_null());32063207if (dirty_cache) {3208cache.clear();3209dirty_cache = false;3210}32113212// When a shaped text is invalidated by an external source, we want to reshape it.3213if (!TS->shaped_text_is_ready(text_rid)) {3214dirty_text = true;3215}32163217for (const RID &line_rid : lines_rid) {3218if (!TS->shaped_text_is_ready(line_rid)) {3219dirty_lines = true;3220break;3221}3222}32233224// Update text buffer.3225if (dirty_text) {3226TS->shaped_text_clear(text_rid);3227TS->shaped_text_set_direction(text_rid, text_direction);32283229const String &lang = language.is_empty() ? _get_locale() : language;3230String txt = (uppercase) ? TS->string_to_upper(xl_text, lang) : xl_text;3231TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), lang);32323233TypedArray<Vector3i> stt;3234if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {3235GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);3236} else {3237stt = TS->parse_structured_text(st_parser, st_args, txt);3238}3239TS->shaped_text_set_bidi_override(text_rid, stt);32403241dirty_text = false;3242dirty_font = false;3243dirty_lines = true;3244} else if (dirty_font) {3245int spans = TS->shaped_get_span_count(text_rid);3246for (int i = 0; i < spans; i++) {3247TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features());3248}32493250dirty_font = false;3251dirty_lines = true;3252}32533254if (dirty_lines) {3255for (int i = 0; i < lines_rid.size(); i++) {3256TS->free_rid(lines_rid[i]);3257}3258lines_rid.clear();32593260BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;3261switch (autowrap_mode) {3262case TextServer::AUTOWRAP_WORD_SMART:3263autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY;3264break;3265case TextServer::AUTOWRAP_WORD:3266autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;3267break;3268case TextServer::AUTOWRAP_ARBITRARY:3269autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;3270break;3271case TextServer::AUTOWRAP_OFF:3272break;3273}3274PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);32753276float max_line_w = 0.0;3277for (int i = 0; i < line_breaks.size(); i = i + 2) {3278RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);3279max_line_w = MAX(max_line_w, TS->shaped_text_get_width(line));3280lines_rid.push_back(line);3281}32823283if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {3284int jst_to_line = lines_rid.size();3285if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) {3286jst_to_line = lines_rid.size();3287} else {3288if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) {3289jst_to_line = lines_rid.size() - 1;3290}3291if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) {3292for (int i = lines_rid.size() - 1; i >= 0; i--) {3293if (TS->shaped_text_has_visible_chars(lines_rid[i])) {3294jst_to_line = i;3295break;3296}3297}3298}3299}3300for (int i = 0; i < jst_to_line; i++) {3301TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, jst_flags);3302}3303}3304dirty_lines = false;3305}33063307float total_h = 0.0;3308for (int i = 0; i < lines_rid.size(); i++) {3309total_h += (TS->shaped_text_get_size(lines_rid[i]).y + line_spacing) * pixel_size;3310}33113312float vbegin = 0.0;3313switch (vertical_alignment) {3314case VERTICAL_ALIGNMENT_FILL:3315case VERTICAL_ALIGNMENT_TOP: {3316// Nothing.3317} break;3318case VERTICAL_ALIGNMENT_CENTER: {3319vbegin = (total_h - line_spacing * pixel_size) / 2.0;3320} break;3321case VERTICAL_ALIGNMENT_BOTTOM: {3322vbegin = (total_h - line_spacing * pixel_size);3323} break;3324}33253326Vector<Vector3> vertices;3327Vector<Vector3> normals;3328Vector<float> tangents;3329Vector<Vector2> uvs;3330Vector<int32_t> indices;33313332Vector2 min_p = Vector2(Math::INF, Math::INF);3333Vector2 max_p = Vector2(-Math::INF, -Math::INF);33343335int32_t p_size = 0;3336int32_t i_size = 0;33373338Vector2 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size);3339for (int i = 0; i < lines_rid.size(); i++) {3340const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);3341int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);3342float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size;33433344switch (horizontal_alignment) {3345case HORIZONTAL_ALIGNMENT_LEFT:3346offset.x = 0.0;3347break;3348case HORIZONTAL_ALIGNMENT_FILL:3349case HORIZONTAL_ALIGNMENT_CENTER: {3350offset.x = -line_width / 2.0;3351} break;3352case HORIZONTAL_ALIGNMENT_RIGHT: {3353offset.x = -line_width;3354} break;3355}3356offset.x += lbl_offset.x * pixel_size;3357offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size;33583359bool has_depth = !Math::is_zero_approx(depth);33603361for (int j = 0; j < gl_size; j++) {3362if (glyphs[j].index == 0) {3363offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;3364continue;3365}3366if (glyphs[j].font_rid != RID()) {3367GlyphMeshKey key = GlyphMeshKey(glyphs[j].font_rid.get_id(), glyphs[j].index);3368_generate_glyph_mesh_data(key, glyphs[j]);3369GlyphMeshData &gl_data = cache[key];3370const Vector2 gl_of = Vector2(glyphs[j].x_off, glyphs[j].y_off) * pixel_size;33713372p_size += glyphs[j].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1);3373i_size += glyphs[j].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1);33743375if (has_depth) {3376for (int k = 0; k < gl_data.contours.size(); k++) {3377p_size += glyphs[j].repeat * gl_data.contours[k].size() * 4;3378i_size += glyphs[j].repeat * gl_data.contours[k].size() * 6;3379}3380}33813382for (int r = 0; r < glyphs[j].repeat; r++) {3383min_p.x = MIN(gl_data.min_p.x + offset.x + gl_of.x, min_p.x);3384min_p.y = MIN(gl_data.min_p.y - offset.y + gl_of.y, min_p.y);3385max_p.x = MAX(gl_data.max_p.x + offset.x + gl_of.x, max_p.x);3386max_p.y = MAX(gl_data.max_p.y - offset.y + gl_of.y, max_p.y);33873388offset.x += glyphs[j].advance * pixel_size;3389}3390} else {3391p_size += glyphs[j].repeat * 4;3392i_size += glyphs[j].repeat * 6;33933394offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;3395}3396}3397offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size;3398}33993400vertices.resize(p_size);3401normals.resize(p_size);3402uvs.resize(p_size);3403tangents.resize(p_size * 4);3404indices.resize(i_size);34053406Vector3 *vertices_ptr = vertices.ptrw();3407Vector3 *normals_ptr = normals.ptrw();3408float *tangents_ptr = tangents.ptrw();3409Vector2 *uvs_ptr = uvs.ptrw();3410int32_t *indices_ptr = indices.ptrw();34113412// Generate mesh.3413int32_t p_idx = 0;3414int32_t i_idx = 0;34153416offset = Vector2(0, vbegin + lbl_offset.y * pixel_size);3417for (int i = 0; i < lines_rid.size(); i++) {3418const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);3419int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);3420float line_width = TS->shaped_text_get_width(lines_rid[i]) * pixel_size;34213422switch (horizontal_alignment) {3423case HORIZONTAL_ALIGNMENT_LEFT:3424offset.x = 0.0;3425break;3426case HORIZONTAL_ALIGNMENT_FILL:3427case HORIZONTAL_ALIGNMENT_CENTER: {3428offset.x = -line_width / 2.0;3429} break;3430case HORIZONTAL_ALIGNMENT_RIGHT: {3431offset.x = -line_width;3432} break;3433}3434offset.x += lbl_offset.x * pixel_size;3435offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size;34363437bool has_depth = !Math::is_zero_approx(depth);34383439// Generate glyph data, precalculate size of the arrays and mesh bounds for UV.3440for (int j = 0; j < gl_size; j++) {3441if (glyphs[j].index == 0) {3442offset.x += glyphs[j].advance * pixel_size * glyphs[j].repeat;3443continue;3444}3445if (glyphs[j].font_rid != RID()) {3446GlyphMeshKey key = GlyphMeshKey(glyphs[j].font_rid.get_id(), glyphs[j].index);3447_generate_glyph_mesh_data(key, glyphs[j]);3448const GlyphMeshData &gl_data = cache[key];34493450int64_t ts = gl_data.triangles.size();3451const Vector2 *ts_ptr = gl_data.triangles.ptr();3452const Vector2 gl_of = Vector2(glyphs[j].x_off, glyphs[j].y_off) * pixel_size;34533454for (int r = 0; r < glyphs[j].repeat; r++) {3455for (int k = 0; k < ts; k += 3) {3456// Add front face.3457for (int l = 0; l < 3; l++) {3458Vector3 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);3459vertices_ptr[p_idx] = point;3460normals_ptr[p_idx] = Vector3(0.0, 0.0, 1.0);3461if (has_depth) {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(0.4), real_t(0.0)));3463} else {3464uvs_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)));3465}3466tangents_ptr[p_idx * 4 + 0] = 1.0;3467tangents_ptr[p_idx * 4 + 1] = 0.0;3468tangents_ptr[p_idx * 4 + 2] = 0.0;3469tangents_ptr[p_idx * 4 + 3] = 1.0;3470indices_ptr[i_idx++] = p_idx;3471p_idx++;3472}3473if (has_depth) {3474// Add back face.3475for (int l = 2; l >= 0; l--) {3476Vector3 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);3477vertices_ptr[p_idx] = point;3478normals_ptr[p_idx] = Vector3(0.0, 0.0, -1.0);3479uvs_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)));3480tangents_ptr[p_idx * 4 + 0] = -1.0;3481tangents_ptr[p_idx * 4 + 1] = 0.0;3482tangents_ptr[p_idx * 4 + 2] = 0.0;3483tangents_ptr[p_idx * 4 + 3] = 1.0;3484indices_ptr[i_idx++] = p_idx;3485p_idx++;3486}3487}3488}3489// Add sides.3490if (has_depth) {3491for (int k = 0; k < gl_data.contours.size(); k++) {3492int64_t ps = gl_data.contours[k].size();3493const ContourPoint *ps_ptr = gl_data.contours[k].ptr();3494const ContourInfo &ps_info = gl_data.contours_info[k];3495real_t length = 0.0;3496for (int l = 0; l < ps; l++) {3497int prev = (l == 0) ? (ps - 1) : (l - 1);3498int next = (l + 1 == ps) ? 0 : (l + 1);3499Vector2 d1;3500Vector2 d2 = (ps_ptr[next].point - ps_ptr[l].point).normalized();3501if (ps_ptr[l].sharp) {3502d1 = d2;3503} else {3504d1 = (ps_ptr[l].point - ps_ptr[prev].point).normalized();3505}3506real_t seg_len = (ps_ptr[next].point - ps_ptr[l].point).length();35073508Vector3 quad_faces[4] = {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),3511Vector3(ps_ptr[l].point.x + offset.x + gl_of.x, -ps_ptr[l].point.y + offset.y - gl_of.y, depth / 2.0),3512Vector3(ps_ptr[next].point.x + offset.x + gl_of.x, -ps_ptr[next].point.y + offset.y - gl_of.y, depth / 2.0),3513};3514for (int m = 0; m < 4; m++) {3515const Vector2 &d = ((m % 2) == 0) ? d1 : d2;3516real_t u_pos = ((m % 2) == 0) ? length : length + seg_len;3517vertices_ptr[p_idx + m] = quad_faces[m];3518normals_ptr[p_idx + m] = Vector3(d.y, d.x, 0.0);3519if (m < 2) {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.8 : 0.9);3521} else {3522uvs_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);3523}3524tangents_ptr[(p_idx + m) * 4 + 0] = d.x;3525tangents_ptr[(p_idx + m) * 4 + 1] = -d.y;3526tangents_ptr[(p_idx + m) * 4 + 2] = 0.0;3527tangents_ptr[(p_idx + m) * 4 + 3] = 1.0;3528}35293530indices_ptr[i_idx++] = p_idx;3531indices_ptr[i_idx++] = p_idx + 1;3532indices_ptr[i_idx++] = p_idx + 2;35333534indices_ptr[i_idx++] = p_idx + 1;3535indices_ptr[i_idx++] = p_idx + 3;3536indices_ptr[i_idx++] = p_idx + 2;35373538length += seg_len;3539p_idx += 4;3540}3541}3542}3543offset.x += glyphs[j].advance * pixel_size;3544}3545} else {3546// Add fallback quad for missing glyphs.3547for (int r = 0; r < glyphs[j].repeat; r++) {3548Size2 sz = TS->get_hex_code_box_size(glyphs[j].font_size, glyphs[j].index) * pixel_size;3549Vector3 quad_faces[4] = {3550Vector3(offset.x, offset.y, 0.0),3551Vector3(offset.x, sz.y + offset.y, 0.0),3552Vector3(sz.x + offset.x, sz.y + offset.y, 0.0),3553Vector3(sz.x + offset.x, offset.y, 0.0),3554};3555for (int k = 0; k < 4; k++) {3556vertices_ptr[p_idx + k] = quad_faces[k];3557normals_ptr[p_idx + k] = Vector3(0.0, 0.0, 1.0);3558if (has_depth) {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(0.4), real_t(0.0)));3560} else {3561uvs_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)));3562}3563tangents_ptr[(p_idx + k) * 4 + 0] = 1.0;3564tangents_ptr[(p_idx + k) * 4 + 1] = 0.0;3565tangents_ptr[(p_idx + k) * 4 + 2] = 0.0;3566tangents_ptr[(p_idx + k) * 4 + 3] = 1.0;3567}35683569indices_ptr[i_idx++] = p_idx;3570indices_ptr[i_idx++] = p_idx + 1;3571indices_ptr[i_idx++] = p_idx + 2;35723573indices_ptr[i_idx++] = p_idx + 0;3574indices_ptr[i_idx++] = p_idx + 2;3575indices_ptr[i_idx++] = p_idx + 3;3576p_idx += 4;35773578offset.x += glyphs[j].advance * pixel_size;3579}3580}3581}3582offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size;3583}35843585if (indices.is_empty()) {3586// If empty, add single triangle to suppress errors.3587vertices.push_back(Vector3());3588normals.push_back(Vector3());3589uvs.push_back(Vector2());3590tangents.push_back(1.0);3591tangents.push_back(0.0);3592tangents.push_back(0.0);3593tangents.push_back(1.0);3594indices.push_back(0);3595indices.push_back(0);3596indices.push_back(0);3597}35983599p_arr[RS::ARRAY_VERTEX] = vertices;3600p_arr[RS::ARRAY_NORMAL] = normals;3601p_arr[RS::ARRAY_TANGENT] = tangents;3602p_arr[RS::ARRAY_TEX_UV] = uvs;3603p_arr[RS::ARRAY_INDEX] = indices;3604}36053606void TextMesh::_bind_methods() {3607ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &TextMesh::set_horizontal_alignment);3608ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &TextMesh::get_horizontal_alignment);36093610ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &TextMesh::set_vertical_alignment);3611ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &TextMesh::get_vertical_alignment);36123613ClassDB::bind_method(D_METHOD("set_text", "text"), &TextMesh::set_text);3614ClassDB::bind_method(D_METHOD("get_text"), &TextMesh::get_text);36153616ClassDB::bind_method(D_METHOD("set_font", "font"), &TextMesh::set_font);3617ClassDB::bind_method(D_METHOD("get_font"), &TextMesh::get_font);36183619ClassDB::bind_method(D_METHOD("set_font_size", "font_size"), &TextMesh::set_font_size);3620ClassDB::bind_method(D_METHOD("get_font_size"), &TextMesh::get_font_size);36213622ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &TextMesh::set_line_spacing);3623ClassDB::bind_method(D_METHOD("get_line_spacing"), &TextMesh::get_line_spacing);36243625ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &TextMesh::set_autowrap_mode);3626ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &TextMesh::get_autowrap_mode);36273628ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &TextMesh::set_justification_flags);3629ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextMesh::get_justification_flags);36303631ClassDB::bind_method(D_METHOD("set_depth", "depth"), &TextMesh::set_depth);3632ClassDB::bind_method(D_METHOD("get_depth"), &TextMesh::get_depth);36333634ClassDB::bind_method(D_METHOD("set_width", "width"), &TextMesh::set_width);3635ClassDB::bind_method(D_METHOD("get_width"), &TextMesh::get_width);36363637ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &TextMesh::set_pixel_size);3638ClassDB::bind_method(D_METHOD("get_pixel_size"), &TextMesh::get_pixel_size);36393640ClassDB::bind_method(D_METHOD("set_offset", "offset"), &TextMesh::set_offset);3641ClassDB::bind_method(D_METHOD("get_offset"), &TextMesh::get_offset);36423643ClassDB::bind_method(D_METHOD("set_curve_step", "curve_step"), &TextMesh::set_curve_step);3644ClassDB::bind_method(D_METHOD("get_curve_step"), &TextMesh::get_curve_step);36453646ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextMesh::set_text_direction);3647ClassDB::bind_method(D_METHOD("get_text_direction"), &TextMesh::get_text_direction);36483649ClassDB::bind_method(D_METHOD("set_language", "language"), &TextMesh::set_language);3650ClassDB::bind_method(D_METHOD("get_language"), &TextMesh::get_language);36513652ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextMesh::set_structured_text_bidi_override);3653ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &TextMesh::get_structured_text_bidi_override);36543655ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &TextMesh::set_structured_text_bidi_override_options);3656ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &TextMesh::get_structured_text_bidi_override_options);36573658ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &TextMesh::set_uppercase);3659ClassDB::bind_method(D_METHOD("is_uppercase"), &TextMesh::is_uppercase);36603661ADD_GROUP("Text", "");3662ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text");3663ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, Font::get_class_static()), "set_font", "get_font");3664ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px"), "set_font_size", "get_font_size");3665ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");3666ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment");3667ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");3668ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing");3669ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");3670ADD_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");36713672ADD_GROUP("Mesh", "");3673ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size");3674ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_step", PROPERTY_HINT_RANGE, "0.1,10,0.1,suffix:px"), "set_curve_step", "get_curve_step");3675ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater,suffix:m"), "set_depth", "get_depth");3676ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width");3677ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");36783679ADD_GROUP("BiDi", "");3680ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction");3681ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");3682ADD_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");3683ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");3684}36853686void TextMesh::_notification(int p_what) {3687switch (p_what) {3688case MainLoop::NOTIFICATION_TRANSLATION_CHANGED: {3689// Language update might change the appearance of some characters.3690xl_text = tr(text);3691dirty_text = true;3692request_update();3693} break;3694}3695}36963697TextMesh::TextMesh() {3698primitive_type = PRIMITIVE_TRIANGLES;3699text_rid = TS->create_shaped_text();3700}37013702TextMesh::~TextMesh() {3703for (int i = 0; i < lines_rid.size(); i++) {3704TS->free_rid(lines_rid[i]);3705}3706lines_rid.clear();37073708TS->free_rid(text_rid);3709}37103711void TextMesh::set_horizontal_alignment(HorizontalAlignment p_alignment) {3712ERR_FAIL_INDEX((int)p_alignment, 4);3713if (horizontal_alignment != p_alignment) {3714if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {3715dirty_lines = true;3716}3717horizontal_alignment = p_alignment;3718request_update();3719}3720}37213722HorizontalAlignment TextMesh::get_horizontal_alignment() const {3723return horizontal_alignment;3724}37253726void TextMesh::set_vertical_alignment(VerticalAlignment p_alignment) {3727ERR_FAIL_INDEX((int)p_alignment, 4);3728if (vertical_alignment != p_alignment) {3729vertical_alignment = p_alignment;3730request_update();3731}3732}37333734VerticalAlignment TextMesh::get_vertical_alignment() const {3735return vertical_alignment;3736}37373738void TextMesh::set_text(const String &p_string) {3739if (text != p_string) {3740text = p_string;3741xl_text = tr(text);3742dirty_text = true;3743request_update();3744}3745}37463747String TextMesh::get_text() const {3748return text;3749}37503751void TextMesh::_font_changed() {3752dirty_font = true;3753dirty_cache = true;3754callable_mp(static_cast<PrimitiveMesh *>(this), &PrimitiveMesh::request_update).call_deferred();3755}37563757void TextMesh::set_font(const Ref<Font> &p_font) {3758if (font_override != p_font) {3759const Callable font_changed = callable_mp(this, &TextMesh::_font_changed);37603761if (font_override.is_valid()) {3762font_override->disconnect_changed(font_changed);3763}3764font_override = p_font;3765dirty_font = true;3766dirty_cache = true;3767if (font_override.is_valid()) {3768font_override->connect_changed(font_changed);3769}3770request_update();3771}3772}37733774Ref<Font> TextMesh::get_font() const {3775return font_override;3776}37773778Ref<Font> TextMesh::_get_font_or_default() const {3779// Similar code taken from `FontVariation::_get_base_font_or_default`.37803781if (font_override.is_valid()) {3782return font_override;3783}37843785StringName theme_name = "font";3786Vector<StringName> theme_types;3787ThemeDB::get_singleton()->get_native_type_dependencies(get_class_name(), theme_types);37883789ThemeContext *global_context = ThemeDB::get_singleton()->get_default_theme_context();3790Vector<Ref<Theme>> themes = global_context->get_themes();3791if (Engine::get_singleton()->is_editor_hint()) {3792themes.insert(0, ThemeDB::get_singleton()->get_project_theme());3793}37943795for (const Ref<Theme> &theme : themes) {3796if (theme.is_null()) {3797continue;3798}37993800for (const StringName &E : theme_types) {3801if (theme->has_font(theme_name, E)) {3802return theme->get_font(theme_name, E);3803}3804}3805}38063807return global_context->get_fallback_theme()->get_font(theme_name, StringName());3808}38093810void TextMesh::set_font_size(int p_size) {3811if (font_size != p_size) {3812font_size = CLAMP(p_size, 1, 127);3813dirty_font = true;3814dirty_cache = true;3815request_update();3816}3817}38183819int TextMesh::get_font_size() const {3820return font_size;3821}38223823void TextMesh::set_line_spacing(float p_line_spacing) {3824if (line_spacing != p_line_spacing) {3825line_spacing = p_line_spacing;3826request_update();3827}3828}38293830float TextMesh::get_line_spacing() const {3831return line_spacing;3832}38333834void TextMesh::set_autowrap_mode(TextServer::AutowrapMode p_mode) {3835if (autowrap_mode != p_mode) {3836autowrap_mode = p_mode;3837dirty_lines = true;3838request_update();3839}3840}38413842TextServer::AutowrapMode TextMesh::get_autowrap_mode() const {3843return autowrap_mode;3844}38453846void TextMesh::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {3847if (jst_flags != p_flags) {3848jst_flags = p_flags;3849dirty_lines = true;3850request_update();3851}3852}38533854BitField<TextServer::JustificationFlag> TextMesh::get_justification_flags() const {3855return jst_flags;3856}38573858void TextMesh::set_depth(real_t p_depth) {3859if (depth != p_depth) {3860depth = MAX(p_depth, 0.0);3861request_update();3862}3863}38643865real_t TextMesh::get_depth() const {3866return depth;3867}38683869void TextMesh::set_width(real_t p_width) {3870if (width != p_width) {3871width = p_width;3872dirty_lines = true;3873request_update();3874}3875}38763877real_t TextMesh::get_width() const {3878return width;3879}38803881void TextMesh::set_pixel_size(real_t p_amount) {3882if (pixel_size != p_amount) {3883pixel_size = CLAMP(p_amount, 0.0001, 128.0);3884dirty_cache = true;3885request_update();3886}3887}38883889real_t TextMesh::get_pixel_size() const {3890return pixel_size;3891}38923893void TextMesh::set_offset(const Point2 &p_offset) {3894if (lbl_offset != p_offset) {3895lbl_offset = p_offset;3896request_update();3897}3898}38993900Point2 TextMesh::get_offset() const {3901return lbl_offset;3902}39033904void TextMesh::set_curve_step(real_t p_step) {3905if (curve_step != p_step) {3906curve_step = CLAMP(p_step, 0.1, 10.0);3907dirty_cache = true;3908request_update();3909}3910}39113912real_t TextMesh::get_curve_step() const {3913return curve_step;3914}39153916void TextMesh::set_text_direction(TextServer::Direction p_text_direction) {3917ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);3918if (text_direction != p_text_direction) {3919text_direction = p_text_direction;3920dirty_text = true;3921request_update();3922}3923}39243925TextServer::Direction TextMesh::get_text_direction() const {3926return text_direction;3927}39283929void TextMesh::set_language(const String &p_language) {3930if (language != p_language) {3931language = p_language;3932dirty_text = true;3933request_update();3934}3935}39363937String TextMesh::get_language() const {3938return language;3939}39403941void TextMesh::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {3942if (st_parser != p_parser) {3943st_parser = p_parser;3944dirty_text = true;3945request_update();3946}3947}39483949TextServer::StructuredTextParser TextMesh::get_structured_text_bidi_override() const {3950return st_parser;3951}39523953void TextMesh::set_structured_text_bidi_override_options(const Array &p_args) {3954if (st_args != p_args) {3955st_args = Array(p_args);3956dirty_text = true;3957request_update();3958}3959}39603961Array TextMesh::get_structured_text_bidi_override_options() const {3962return Array(st_args);3963}39643965void TextMesh::set_uppercase(bool p_uppercase) {3966if (uppercase != p_uppercase) {3967uppercase = p_uppercase;3968dirty_text = true;3969request_update();3970}3971}39723973bool TextMesh::is_uppercase() const {3974return uppercase;3975}397639773978