summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp17
-rw-r--r--core/config/project_settings.h2
-rw-r--r--core/debugger/script_debugger.cpp4
-rw-r--r--core/debugger/script_debugger.h1
-rw-r--r--core/io/image_loader.cpp4
-rw-r--r--core/io/image_loader.h2
-rw-r--r--core/io/resource.cpp7
-rw-r--r--core/io/resource.h2
-rw-r--r--core/io/resource_format_binary.cpp4
-rw-r--r--core/io/resource_format_binary.h1
-rw-r--r--core/io/resource_loader.cpp7
-rw-r--r--core/io/resource_loader.h1
-rw-r--r--core/io/resource_saver.cpp7
-rw-r--r--core/io/resource_saver.h1
-rw-r--r--core/math/basis.cpp25
-rw-r--r--core/math/basis.h4
-rw-r--r--core/math/face3.cpp40
-rw-r--r--core/math/face3.h5
-rw-r--r--core/math/geometry_2d.cpp35
-rw-r--r--core/math/geometry_2d.h1
-rw-r--r--core/math/geometry_3d.cpp143
-rw-r--r--core/math/geometry_3d.h2
-rw-r--r--core/math/transform_2d.cpp6
-rw-r--r--core/math/transform_2d.h1
-rw-r--r--core/math/triangle_mesh.cpp196
-rw-r--r--core/math/triangle_mesh.h2
-rw-r--r--core/variant/variant.h1
-rw-r--r--core/variant/variant_setget.cpp17
-rw-r--r--doc/classes/AnimationNode.xml7
-rw-r--r--doc/classes/AnimationNodeOneShot.xml6
-rw-r--r--doc/classes/AnimationNodeStateMachinePlayback.xml11
-rw-r--r--doc/classes/AnimationNodeStateMachineTransition.xml3
-rw-r--r--doc/classes/AnimationNodeTransition.xml8
-rw-r--r--doc/classes/Array.xml2
-rw-r--r--doc/classes/ArrayMesh.xml14
-rw-r--r--doc/classes/Control.xml4
-rw-r--r--doc/classes/ImporterMesh.xml10
-rw-r--r--doc/classes/Mesh.xml7
-rw-r--r--doc/classes/RenderingServer.xml2
-rw-r--r--doc/classes/TextServer.xml11
-rw-r--r--doc/classes/TextServerExtension.xml2
-rw-r--r--editor/code_editor.cpp1
-rw-r--r--editor/doc_tools.cpp1
-rw-r--r--editor/editor_data.cpp18
-rw-r--r--editor/editor_data.h2
-rw-r--r--editor/editor_node.cpp1
-rw-r--r--editor/editor_plugin.cpp3
-rw-r--r--editor/editor_plugin.h3
-rw-r--r--editor/editor_properties.cpp9
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp6
-rw-r--r--editor/plugins/script_editor_plugin.h3
-rw-r--r--modules/ogg/ogg_packet_sequence.cpp2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp44
-rw-r--r--modules/text_server_adv/text_server_adv.h2
-rw-r--r--modules/text_server_fb/text_server_fb.cpp27
-rw-r--r--scene/3d/label_3d.cpp2
-rw-r--r--scene/3d/label_3d.h2
-rw-r--r--scene/animation/animation_blend_tree.cpp162
-rw-r--r--scene/animation/animation_blend_tree.h42
-rw-r--r--scene/animation/animation_node_state_machine.cpp134
-rw-r--r--scene/animation/animation_node_state_machine.h16
-rw-r--r--scene/animation/animation_tree.cpp23
-rw-r--r--scene/animation/animation_tree.h4
-rw-r--r--scene/gui/control.cpp8
-rw-r--r--scene/gui/control.h7
-rw-r--r--scene/gui/item_list.cpp6
-rw-r--r--scene/gui/item_list.h1
-rw-r--r--scene/gui/rich_text_label.cpp4
-rw-r--r--scene/gui/text_edit.cpp10
-rw-r--r--scene/gui/tree.cpp2
-rw-r--r--scene/resources/environment.cpp12
-rw-r--r--scene/resources/mesh.cpp5
-rw-r--r--scene/resources/primitive_meshes.cpp2
-rw-r--r--scene/resources/primitive_meshes.h2
-rw-r--r--scene/resources/resource_format_text.cpp4
-rw-r--r--scene/resources/resource_format_text.h1
-rw-r--r--servers/physics_3d/gjk_epa.cpp6
-rw-r--r--servers/physics_3d/godot_body_pair_3d.cpp14
-rw-r--r--servers/physics_3d/godot_body_pair_3d.h8
-rw-r--r--servers/physics_3d/godot_collision_solver_3d.cpp18
-rw-r--r--servers/physics_3d/godot_collision_solver_3d.h4
-rw-r--r--servers/physics_3d/godot_collision_solver_3d_sat.cpp59
-rw-r--r--servers/physics_3d/godot_physics_server_3d.cpp2
-rw-r--r--servers/physics_3d/godot_physics_server_3d.h2
-rw-r--r--servers/physics_3d/godot_space_3d.cpp6
-rw-r--r--servers/rendering_server.cpp2
-rw-r--r--servers/text/text_server_extension.cpp4
-rw-r--r--servers/text/text_server_extension.h4
-rw-r--r--servers/text_server.cpp116
-rw-r--r--servers/text_server.h7
-rw-r--r--tests/scene/test_arraymesh.h21
-rw-r--r--tests/scene/test_primitives.h2
93 files changed, 628 insertions, 845 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index de27b6d5ac..ed89bc15d3 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1069,23 +1069,6 @@ Variant _GLOBAL_DEF(const PropertyInfo &p_info, const Variant &p_default, bool p
return ret;
}
-Vector<String> ProjectSettings::get_optimizer_presets() const {
- List<PropertyInfo> pi;
- ProjectSettings::get_singleton()->get_property_list(&pi);
- Vector<String> names;
-
- for (const PropertyInfo &E : pi) {
- if (!E.name.begins_with("optimizer_presets/")) {
- continue;
- }
- names.push_back(E.name.get_slicec('/', 1));
- }
-
- names.sort();
-
- return names;
-}
-
void ProjectSettings::_add_property_info_bind(const Dictionary &p_info) {
ERR_FAIL_COND(!p_info.has("name"));
ERR_FAIL_COND(!p_info.has("type"));
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index 29c495c46b..d1704a7c31 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -178,8 +178,6 @@ public:
const HashMap<StringName, PropertyInfo> &get_custom_property_info() const;
uint64_t get_last_saved_time() { return last_save_time; }
- Vector<String> get_optimizer_presets() const;
-
List<String> get_input_presets() const { return input_presets; }
Variant get_setting_with_override(const StringName &p_name) const;
diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp
index 8af1573bff..32725b76c1 100644
--- a/core/debugger/script_debugger.cpp
+++ b/core/debugger/script_debugger.cpp
@@ -73,10 +73,6 @@ bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const
return breakpoints[p_line].has(p_source);
}
-bool ScriptDebugger::is_breakpoint_line(int p_line) const {
- return breakpoints.has(p_line);
-}
-
String ScriptDebugger::breakpoint_find_source(const String &p_source) const {
return p_source;
}
diff --git a/core/debugger/script_debugger.h b/core/debugger/script_debugger.h
index c7aa90027b..edce089179 100644
--- a/core/debugger/script_debugger.h
+++ b/core/debugger/script_debugger.h
@@ -64,7 +64,6 @@ public:
void insert_breakpoint(int p_line, const StringName &p_source);
void remove_breakpoint(int p_line, const StringName &p_source);
bool is_breakpoint(int p_line, const StringName &p_source) const;
- bool is_breakpoint_line(int p_line) const;
void clear_breakpoints();
const HashMap<int, HashSet<StringName>> &get_breakpoints() const { return breakpoints; }
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp
index 17fb199811..c6452f1033 100644
--- a/core/io/image_loader.cpp
+++ b/core/io/image_loader.cpp
@@ -135,10 +135,6 @@ void ImageLoader::remove_image_format_loader(Ref<ImageFormatLoader> p_loader) {
loader.erase(p_loader);
}
-const Vector<Ref<ImageFormatLoader>> &ImageLoader::get_image_format_loaders() {
- return loader;
-}
-
void ImageLoader::cleanup() {
while (loader.size()) {
remove_image_format_loader(loader[0]);
diff --git a/core/io/image_loader.h b/core/io/image_loader.h
index 1473f24186..ac51f13376 100644
--- a/core/io/image_loader.h
+++ b/core/io/image_loader.h
@@ -98,8 +98,6 @@ public:
static void add_image_format_loader(Ref<ImageFormatLoader> p_loader);
static void remove_image_format_loader(Ref<ImageFormatLoader> p_loader);
- static const Vector<Ref<ImageFormatLoader>> &get_image_format_loaders();
-
static void cleanup();
};
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 2d6f09725f..6d3575b9fa 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -385,10 +385,6 @@ void Resource::set_as_translation_remapped(bool p_remapped) {
ResourceCache::lock.unlock();
}
-bool Resource::is_translation_remapped() const {
- return remapped_list.in_list();
-}
-
#ifdef TOOLS_ENABLED
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
void Resource::set_id_for_path(const String &p_path, const String &p_id) {
@@ -481,9 +477,6 @@ void ResourceCache::clear() {
resources.clear();
}
-void ResourceCache::reload_externals() {
-}
-
bool ResourceCache::has(const String &p_path) {
lock.lock();
diff --git a/core/io/resource.h b/core/io/resource.h
index 22ce5cef43..5135664f36 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -136,7 +136,6 @@ public:
#endif
void set_as_translation_remapped(bool p_remapped);
- bool is_translation_remapped() const;
virtual RID get_rid() const; // some resources may offer conversion to RID
@@ -164,7 +163,6 @@ class ResourceCache {
friend void register_core_types();
public:
- static void reload_externals();
static bool has(const String &p_path);
static Ref<Resource> get_ref(const String &p_path);
static void get_cached_resources(List<Ref<Resource>> *p_resources);
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 45e1301930..03beb25b03 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -661,10 +661,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
return OK; //never reach anyway
}
-void ResourceLoaderBinary::set_local_path(const String &p_local_path) {
- res_path = p_local_path;
-}
-
Ref<Resource> ResourceLoaderBinary::get_resource() {
return resource;
}
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index 2e8988005f..9dd208e3cd 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -92,7 +92,6 @@ class ResourceLoaderBinary {
HashMap<String, Ref<Resource>> dependency_cache;
public:
- void set_local_path(const String &p_local_path);
Ref<Resource> get_resource();
Error load();
void set_translation_remapped(bool p_remapped);
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 946c31cf0d..68b9f8b6f7 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -1011,13 +1011,6 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
return true;
}
-void ResourceLoader::remove_custom_resource_format_loader(String script_path) {
- Ref<ResourceFormatLoader> custom_loader = _find_custom_resource_format_loader(script_path);
- if (custom_loader.is_valid()) {
- remove_resource_format_loader(custom_loader);
- }
-}
-
void ResourceLoader::set_create_missing_resources_if_class_unavailable(bool p_enable) {
create_missing_resources_if_class_unavailable = p_enable;
}
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 41ba0dc6e6..e427a2f5fc 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -225,7 +225,6 @@ public:
static ResourceLoaderImport import;
static bool add_custom_resource_format_loader(String script_path);
- static void remove_custom_resource_format_loader(String script_path);
static void add_custom_loaders();
static void remove_custom_loaders();
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 9809b9a48f..b8201cc6b9 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -250,13 +250,6 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
return true;
}
-void ResourceSaver::remove_custom_resource_format_saver(String script_path) {
- Ref<ResourceFormatSaver> custom_saver = _find_custom_resource_format_saver(script_path);
- if (custom_saver.is_valid()) {
- remove_resource_format_saver(custom_saver);
- }
-}
-
void ResourceSaver::add_custom_savers() {
// Custom resource savers exploits global class names
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 2043947963..9e88b2086b 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -101,7 +101,6 @@ public:
static void set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback);
static bool add_custom_resource_format_saver(String script_path);
- static void remove_custom_resource_format_saver(String script_path);
static void add_custom_savers();
static void remove_custom_savers();
};
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 39e383fb49..234a4ddb79 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -36,23 +36,6 @@
#define cofac(row1, col1, row2, col2) \
(rows[row1][col1] * rows[row2][col2] - rows[row1][col2] * rows[row2][col1])
-void Basis::from_z(const Vector3 &p_z) {
- if (Math::abs(p_z.z) > (real_t)Math_SQRT12) {
- // choose p in y-z plane
- real_t a = p_z[1] * p_z[1] + p_z[2] * p_z[2];
- real_t k = 1.0f / Math::sqrt(a);
- rows[0] = Vector3(0, -p_z[2] * k, p_z[1] * k);
- rows[1] = Vector3(a * k, -p_z[0] * rows[0][2], p_z[0] * rows[0][1]);
- } else {
- // choose p in x-y plane
- real_t a = p_z.x * p_z.x + p_z.y * p_z.y;
- real_t k = 1.0f / Math::sqrt(a);
- rows[0] = Vector3(-p_z.y * k, p_z.x * k, 0);
- rows[1] = Vector3(-p_z.z * rows[0].y, p_z.z * rows[0].x, a * k);
- }
- rows[2] = p_z;
-}
-
void Basis::invert() {
real_t co[3] = {
cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1)
@@ -271,14 +254,6 @@ float Basis::get_uniform_scale() const {
return (rows[0].length() + rows[1].length() + rows[2].length()) / 3.0f;
}
-void Basis::make_scale_uniform() {
- float l = (rows[0].length() + rows[1].length() + rows[2].length()) / 3.0f;
- for (int i = 0; i < 3; i++) {
- rows[i].normalize();
- rows[i] *= l;
- }
-}
-
Basis Basis::scaled_local(const Vector3 &p_scale) const {
return (*this) * Basis::from_scale(p_scale);
}
diff --git a/core/math/basis.h b/core/math/basis.h
index b3197dbc84..bbc1d40469 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -56,8 +56,6 @@ struct _NO_DISCARD_ Basis {
_FORCE_INLINE_ real_t determinant() const;
- void from_z(const Vector3 &p_z);
-
void rotate(const Vector3 &p_axis, real_t p_angle);
Basis rotated(const Vector3 &p_axis, real_t p_angle) const;
@@ -101,8 +99,6 @@ struct _NO_DISCARD_ Basis {
void scale_orthogonal(const Vector3 &p_scale);
Basis scaled_orthogonal(const Vector3 &p_scale) const;
-
- void make_scale_uniform();
float get_uniform_scale() const;
Vector3 get_scale() const;
diff --git a/core/math/face3.cpp b/core/math/face3.cpp
index e53bbf872b..1dff0ee4a6 100644
--- a/core/math/face3.cpp
+++ b/core/math/face3.cpp
@@ -120,36 +120,6 @@ bool Face3::is_degenerate() const {
return (normal.length_squared() < (real_t)CMP_EPSILON2);
}
-Face3::Side Face3::get_side_of(const Face3 &p_face, ClockDirection p_clock_dir) const {
- int over = 0, under = 0;
-
- Plane plane = get_plane(p_clock_dir);
-
- for (int i = 0; i < 3; i++) {
- const Vector3 &v = p_face.vertex[i];
-
- if (plane.has_point(v)) { //coplanar, don't bother
- continue;
- }
-
- if (plane.is_point_over(v)) {
- over++;
- } else {
- under++;
- }
- }
-
- if (over > 0 && under == 0) {
- return SIDE_OVER;
- } else if (under > 0 && over == 0) {
- return SIDE_UNDER;
- } else if (under == 0 && over == 0) {
- return SIDE_COPLANAR;
- } else {
- return SIDE_SPANNING;
- }
-}
-
Vector3 Face3::get_random_point_inside() const {
real_t a = Math::random(0.0, 1.0);
real_t b = Math::random(0.0, 1.0);
@@ -164,20 +134,10 @@ Plane Face3::get_plane(ClockDirection p_dir) const {
return Plane(vertex[0], vertex[1], vertex[2], p_dir);
}
-Vector3 Face3::get_median_point() const {
- return (vertex[0] + vertex[1] + vertex[2]) / 3.0f;
-}
-
real_t Face3::get_area() const {
return vec3_cross(vertex[0] - vertex[1], vertex[0] - vertex[2]).length() * 0.5f;
}
-ClockDirection Face3::get_clock_dir() const {
- Vector3 normal = vec3_cross(vertex[0] - vertex[1], vertex[0] - vertex[2]);
- //printf("normal is %g,%g,%g x %g,%g,%g- wtfu is %g\n",tofloat(normal.x),tofloat(normal.y),tofloat(normal.z),tofloat(vertex[0].x),tofloat(vertex[0].y),tofloat(vertex[0].z),tofloat( normal.dot( vertex[0] ) ) );
- return (normal.dot(vertex[0]) >= 0) ? CLOCKWISE : COUNTERCLOCKWISE;
-}
-
bool Face3::intersects_aabb(const AABB &p_aabb) const {
/** TEST PLANE **/
if (!p_aabb.intersects_plane(get_plane())) {
diff --git a/core/math/face3.h b/core/math/face3.h
index 3d87de03dc..3dd47d0226 100644
--- a/core/math/face3.h
+++ b/core/math/face3.h
@@ -57,19 +57,14 @@ struct _NO_DISCARD_ Face3 {
Plane get_plane(ClockDirection p_dir = CLOCKWISE) const;
Vector3 get_random_point_inside() const;
- Side get_side_of(const Face3 &p_face, ClockDirection p_clock_dir = CLOCKWISE) const;
-
bool is_degenerate() const;
real_t get_area() const;
- Vector3 get_median_point() const;
Vector3 get_closest_point_to(const Vector3 &p_point) const;
bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection = nullptr) const;
bool intersects_segment(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *p_intersection = nullptr) const;
- ClockDirection get_clock_dir() const; ///< todo, test if this is returning the proper clockwisity
-
void get_support(const Vector3 &p_normal, const Transform3D &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const;
void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const;
diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp
index a0b76d31cb..74cb92539a 100644
--- a/core/math/geometry_2d.cpp
+++ b/core/math/geometry_2d.cpp
@@ -320,41 +320,6 @@ Vector<Vector<Point2>> Geometry2D::_polypath_offset(const Vector<Point2> &p_poly
return polypaths;
}
-Vector<Point2i> Geometry2D::pack_rects(const Vector<Size2i> &p_sizes, const Size2i &p_atlas_size) {
- Vector<stbrp_node> nodes;
- nodes.resize(p_atlas_size.width);
-
- stbrp_context context;
- stbrp_init_target(&context, p_atlas_size.width, p_atlas_size.height, nodes.ptrw(), p_atlas_size.width);
-
- Vector<stbrp_rect> rects;
- rects.resize(p_sizes.size());
-
- for (int i = 0; i < p_sizes.size(); i++) {
- rects.write[i].id = 0;
- rects.write[i].w = p_sizes[i].width;
- rects.write[i].h = p_sizes[i].height;
- rects.write[i].x = 0;
- rects.write[i].y = 0;
- rects.write[i].was_packed = 0;
- }
-
- int res = stbrp_pack_rects(&context, rects.ptrw(), rects.size());
- if (res == 0) { //pack failed
- return Vector<Point2i>();
- }
-
- Vector<Point2i> ret;
- ret.resize(p_sizes.size());
-
- for (int i = 0; i < p_sizes.size(); i++) {
- Point2i r(rects[i].x, rects[i].y);
- ret.write[i] = r;
- }
-
- return ret;
-}
-
Vector<Vector3i> Geometry2D::partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size) {
Vector<stbrp_node> nodes;
nodes.resize(p_atlas_size.width);
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index b55aecf85e..0e5702e0af 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -464,7 +464,6 @@ public:
static Vector<Vector<Vector2>> decompose_polygon_in_convex(Vector<Point2> polygon);
static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size);
- static Vector<Point2i> pack_rects(const Vector<Size2i> &p_sizes, const Size2i &p_atlas_size);
static Vector<Vector3i> partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size);
private:
diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp
index c04fe7320d..51523ea296 100644
--- a/core/math/geometry_3d.cpp
+++ b/core/math/geometry_3d.cpp
@@ -198,149 +198,6 @@ struct _FaceClassify {
_FaceClassify() {}
};
-static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) {
- // Connect faces, error will occur if an edge is shared between more than 2 faces.
- // Clear connections.
-
- bool error = false;
-
- for (int i = 0; i < len; i++) {
- for (int j = 0; j < 3; j++) {
- p_faces[i].links[j].clear();
- }
- }
-
- for (int i = 0; i < len; i++) {
- if (p_faces[i].group != p_group) {
- continue;
- }
- for (int j = i + 1; j < len; j++) {
- if (p_faces[j].group != p_group) {
- continue;
- }
-
- for (int k = 0; k < 3; k++) {
- Vector3 vi1 = p_faces[i].face.vertex[k];
- Vector3 vi2 = p_faces[i].face.vertex[(k + 1) % 3];
-
- for (int l = 0; l < 3; l++) {
- Vector3 vj2 = p_faces[j].face.vertex[l];
- Vector3 vj1 = p_faces[j].face.vertex[(l + 1) % 3];
-
- if (vi1.distance_to(vj1) < 0.00001f &&
- vi2.distance_to(vj2) < 0.00001f) {
- if (p_faces[i].links[k].face != -1) {
- ERR_PRINT("already linked\n");
- error = true;
- break;
- }
- if (p_faces[j].links[l].face != -1) {
- ERR_PRINT("already linked\n");
- error = true;
- break;
- }
-
- p_faces[i].links[k].face = j;
- p_faces[i].links[k].edge = l;
- p_faces[j].links[l].face = i;
- p_faces[j].links[l].edge = k;
- }
- }
- if (error) {
- break;
- }
- }
- if (error) {
- break;
- }
- }
- if (error) {
- break;
- }
- }
-
- for (int i = 0; i < len; i++) {
- p_faces[i].valid = true;
- for (int j = 0; j < 3; j++) {
- if (p_faces[i].links[j].face == -1) {
- p_faces[i].valid = false;
- }
- }
- }
- return error;
-}
-
-static bool _group_face(_FaceClassify *p_faces, int len, int p_index, int p_group) {
- if (p_faces[p_index].group >= 0) {
- return false;
- }
-
- p_faces[p_index].group = p_group;
-
- for (int i = 0; i < 3; i++) {
- ERR_FAIL_INDEX_V(p_faces[p_index].links[i].face, len, true);
- _group_face(p_faces, len, p_faces[p_index].links[i].face, p_group);
- }
-
- return true;
-}
-
-Vector<Vector<Face3>> Geometry3D::separate_objects(Vector<Face3> p_array) {
- Vector<Vector<Face3>> objects;
-
- int len = p_array.size();
-
- const Face3 *arrayptr = p_array.ptr();
-
- Vector<_FaceClassify> fc;
-
- fc.resize(len);
-
- _FaceClassify *_fcptr = fc.ptrw();
-
- for (int i = 0; i < len; i++) {
- _fcptr[i].face = arrayptr[i];
- }
-
- bool error = _connect_faces(_fcptr, len, -1);
-
- ERR_FAIL_COND_V_MSG(error, Vector<Vector<Face3>>(), "Invalid geometry.");
-
- // Group connected faces in separate objects.
-
- int group = 0;
- for (int i = 0; i < len; i++) {
- if (!_fcptr[i].valid) {
- continue;
- }
- if (_group_face(_fcptr, len, i, group)) {
- group++;
- }
- }
-
- // Group connected faces in separate objects.
-
- for (int i = 0; i < len; i++) {
- _fcptr[i].face = arrayptr[i];
- }
-
- if (group >= 0) {
- objects.resize(group);
- Vector<Face3> *group_faces = objects.ptrw();
-
- for (int i = 0; i < len; i++) {
- if (!_fcptr[i].valid) {
- continue;
- }
- if (_fcptr[i].group >= 0 && _fcptr[i].group < group) {
- group_faces[_fcptr[i].group].push_back(_fcptr[i].face);
- }
- }
- }
-
- return objects;
-}
-
/*** GEOMETRY WRAPPER ***/
enum _CellFlags {
diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h
index 6759db5766..99c554fe05 100644
--- a/core/math/geometry_3d.h
+++ b/core/math/geometry_3d.h
@@ -532,8 +532,6 @@ public:
return clipped;
}
- static Vector<Vector<Face3>> separate_objects(Vector<Face3> p_array);
-
// Create a "wrap" that encloses the given geometry.
static Vector<Face3> wrap_geometry(Vector<Face3> p_array, real_t *p_error = nullptr);
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index 6a7ee32230..910995d717 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -221,12 +221,6 @@ Transform2D Transform2D::operator*(const Transform2D &p_transform) const {
return t;
}
-Transform2D Transform2D::basis_scaled(const Size2 &p_scale) const {
- Transform2D copy = *this;
- copy.scale_basis(p_scale);
- return copy;
-}
-
Transform2D Transform2D::scaled(const Size2 &p_scale) const {
// Equivalent to left multiplication
Transform2D copy = *this;
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index 2a0917c63f..4a17a9db37 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -85,7 +85,6 @@ struct _NO_DISCARD_ Transform2D {
_FORCE_INLINE_ const Vector2 &get_origin() const { return columns[2]; }
_FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { columns[2] = p_origin; }
- Transform2D basis_scaled(const Size2 &p_scale) const;
Transform2D scaled(const Size2 &p_scale) const;
Transform2D scaled_local(const Size2 &p_scale) const;
Transform2D translated(const Vector2 &p_offset) const;
diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp
index 4b6921d38b..0da1b8c7ad 100644
--- a/core/math/triangle_mesh.cpp
+++ b/core/math/triangle_mesh.cpp
@@ -182,90 +182,6 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces, const Vector<int32_t>
valid = true;
}
-Vector3 TriangleMesh::get_area_normal(const AABB &p_aabb) const {
- uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
-
- enum {
- TEST_AABB_BIT = 0,
- VISIT_LEFT_BIT = 1,
- VISIT_RIGHT_BIT = 2,
- VISIT_DONE_BIT = 3,
- VISITED_BIT_SHIFT = 29,
- NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
- VISITED_BIT_MASK = ~NODE_IDX_MASK,
-
- };
-
- int n_count = 0;
- Vector3 n;
-
- int level = 0;
-
- const Triangle *triangleptr = triangles.ptr();
- // const Vector3 *verticesr = vertices.ptr();
- const BVH *bvhptr = bvh.ptr();
-
- int pos = bvh.size() - 1;
-
- stack[0] = pos;
- while (true) {
- uint32_t node = stack[level] & NODE_IDX_MASK;
- const BVH &b = bvhptr[node];
- bool done = false;
-
- switch (stack[level] >> VISITED_BIT_SHIFT) {
- case TEST_AABB_BIT: {
- if (!b.aabb.intersects(p_aabb)) {
- stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
- } else {
- if (b.face_index >= 0) {
- const Triangle &s = triangleptr[b.face_index];
- n += s.normal;
- n_count++;
-
- stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
-
- } else {
- stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
- }
- }
- continue;
- }
- case VISIT_LEFT_BIT: {
- stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
- level++;
- stack[level] = b.left | TEST_AABB_BIT;
- continue;
- }
- case VISIT_RIGHT_BIT: {
- stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
- level++;
- stack[level] = b.right | TEST_AABB_BIT;
- continue;
- }
- case VISIT_DONE_BIT: {
- if (level == 0) {
- done = true;
- break;
- } else {
- level--;
- }
- continue;
- }
- }
-
- if (done) {
- break;
- }
- }
-
- if (n_count > 0) {
- n /= n_count;
- }
-
- return n;
-}
-
bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index) const {
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
@@ -468,118 +384,6 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V
return inters;
}
-bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const {
- uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
-
- //p_fully_inside = true;
-
- enum {
- TEST_AABB_BIT = 0,
- VISIT_LEFT_BIT = 1,
- VISIT_RIGHT_BIT = 2,
- VISIT_DONE_BIT = 3,
- VISITED_BIT_SHIFT = 29,
- NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
- VISITED_BIT_MASK = ~NODE_IDX_MASK,
-
- };
-
- int level = 0;
-
- const Triangle *triangleptr = triangles.ptr();
- const Vector3 *vertexptr = vertices.ptr();
- const BVH *bvhptr = bvh.ptr();
-
- int pos = bvh.size() - 1;
-
- stack[0] = pos;
- while (true) {
- uint32_t node = stack[level] & NODE_IDX_MASK;
- const BVH &b = bvhptr[node];
- bool done = false;
-
- switch (stack[level] >> VISITED_BIT_SHIFT) {
- case TEST_AABB_BIT: {
- if (!b.aabb.intersects_convex_shape(p_planes, p_plane_count, p_points, p_point_count)) {
- stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
- } else {
- if (b.face_index >= 0) {
- const Triangle &s = triangleptr[b.face_index];
-
- for (int j = 0; j < 3; ++j) {
- const Vector3 &point = vertexptr[s.indices[j]];
- const Vector3 &next_point = vertexptr[s.indices[(j + 1) % 3]];
- Vector3 res;
- bool over = true;
- for (int i = 0; i < p_plane_count; i++) {
- const Plane &p = p_planes[i];
-
- if (p.intersects_segment(point, next_point, &res)) {
- bool inisde = true;
- for (int k = 0; k < p_plane_count; k++) {
- if (k == i) {
- continue;
- }
- const Plane &pp = p_planes[k];
- if (pp.is_point_over(res)) {
- inisde = false;
- break;
- }
- }
- if (inisde) {
- return true;
- }
- }
-
- if (p.is_point_over(point)) {
- over = false;
- break;
- }
- }
- if (over) {
- return true;
- }
- }
-
- stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
-
- } else {
- stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
- }
- }
- continue;
- }
- case VISIT_LEFT_BIT: {
- stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
- level++;
- stack[level] = b.left | TEST_AABB_BIT;
- continue;
- }
- case VISIT_RIGHT_BIT: {
- stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
- level++;
- stack[level] = b.right | TEST_AABB_BIT;
- continue;
- }
- case VISIT_DONE_BIT: {
- if (level == 0) {
- done = true;
- break;
- } else {
- level--;
- }
- continue;
- }
- }
-
- if (done) {
- break;
- }
- }
-
- return false;
-}
-
bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, Vector3 p_scale) const {
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h
index 728fd600d5..24fc12dda9 100644
--- a/core/math/triangle_mesh.h
+++ b/core/math/triangle_mesh.h
@@ -84,9 +84,7 @@ public:
bool is_valid() const;
bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index = nullptr) const;
bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index = nullptr) const;
- bool intersect_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const;
bool inside_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, Vector3 p_scale = Vector3(1, 1, 1)) const;
- Vector3 get_area_normal(const AABB &p_aabb) const;
Vector<Face3> get_faces() const;
const Vector<Triangle> &get_triangles() const { return triangles; }
diff --git a/core/variant/variant.h b/core/variant/variant.h
index fff59c43a6..b9294de77d 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -655,6 +655,7 @@ public:
static bool has_indexing(Variant::Type p_type);
static Variant::Type get_indexed_element_type(Variant::Type p_type);
+ static uint32_t get_indexed_element_usage(Variant::Type p_type);
typedef void (*ValidatedIndexedSetter)(Variant *base, int64_t index, const Variant *value, bool *oob);
typedef void (*ValidatedIndexedGetter)(const Variant *base, int64_t index, Variant *value, bool *oob);
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index a74556d88f..ba37e15f31 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -389,6 +389,7 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
v.write[index] = PtrToArg<m_elem_type>::convert(member); \
} \
static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \
+ static uint32_t get_index_usage() { return GetTypeInfo<m_elem_type>::get_class_info().usage; } \
static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); } \
};
@@ -460,6 +461,7 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
v.write[index] = PtrToArg<m_elem_type>::convert(member); \
} \
static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \
+ static uint32_t get_index_usage() { return GetTypeInfo<m_elem_type>::get_class_info().usage; } \
static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); } \
};
@@ -515,6 +517,7 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
v[index] = PtrToArg<m_elem_type>::convert(member); \
} \
static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \
+ static uint32_t get_index_usage() { return GetTypeInfo<m_elem_type>::get_class_info().usage; } \
static uint64_t get_indexed_size(const Variant *base) { return m_max; } \
};
@@ -564,6 +567,7 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
v m_accessor[index] = PtrToArg<m_elem_type>::convert(member); \
} \
static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \
+ static uint32_t get_index_usage() { return GetTypeInfo<m_elem_type>::get_class_info().usage; } \
static uint64_t get_indexed_size(const Variant *base) { return m_max; } \
};
@@ -613,6 +617,7 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
v.m_set(index, PtrToArg<m_elem_type>::convert(member)); \
} \
static Variant::Type get_index_type() { return GetTypeInfo<m_elem_type>::VARIANT_TYPE; } \
+ static uint32_t get_index_usage() { return GetTypeInfo<m_elem_type>::get_class_info().usage; } \
static uint64_t get_indexed_size(const Variant *base) { return m_max; } \
};
@@ -683,6 +688,7 @@ struct VariantIndexedSetGet_Array {
v.set(index, PtrToArg<Variant>::convert(member));
}
static Variant::Type get_index_type() { return Variant::NIL; }
+ static uint32_t get_index_usage() { return PROPERTY_USAGE_NIL_IS_VARIANT; }
static uint64_t get_indexed_size(const Variant *base) { return 0; }
};
@@ -768,6 +774,7 @@ struct VariantIndexedSetGet_String {
}
}
static Variant::Type get_index_type() { return Variant::STRING; }
+ static uint32_t get_index_usage() { return PROPERTY_USAGE_DEFAULT; }
static uint64_t get_indexed_size(const Variant *base) { return VariantInternal::get_string(base)->length(); }
};
@@ -812,6 +819,7 @@ struct VariantIndexedSetGet_String {
v[index] = PtrToArg<Variant>::convert(member); \
} \
static Variant::Type get_index_type() { return Variant::NIL; } \
+ static uint32_t get_index_usage() { return PROPERTY_USAGE_DEFAULT; } \
static uint64_t get_indexed_size(const Variant *base) { return VariantGetInternalPtr<m_base_type>::get_ptr(base)->size(); } \
};
@@ -852,7 +860,8 @@ struct VariantIndexedSetterGetterInfo {
uint64_t (*get_indexed_size)(const Variant *base) = nullptr;
- Variant::Type index_type;
+ Variant::Type index_type = Variant::NIL;
+ uint32_t index_usage = PROPERTY_USAGE_DEFAULT;
bool valid = false;
};
@@ -872,6 +881,7 @@ static void register_indexed_member(Variant::Type p_type) {
sgi.ptr_getter = T::ptr_get;
sgi.index_type = T::get_index_type();
+ sgi.index_usage = T::get_index_usage();
sgi.get_indexed_size = T::get_indexed_size;
sgi.valid = true;
@@ -920,6 +930,11 @@ Variant::Type Variant::get_indexed_element_type(Variant::Type p_type) {
return variant_indexed_setters_getters[p_type].index_type;
}
+uint32_t Variant::get_indexed_element_usage(Variant::Type p_type) {
+ ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, PROPERTY_USAGE_DEFAULT);
+ return variant_indexed_setters_getters[p_type].index_usage;
+}
+
Variant::ValidatedIndexedSetter Variant::get_member_validated_indexed_setter(Variant::Type p_type) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr);
return variant_indexed_setters_getters[p_type].validated_setter;
diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml
index a33ec2f6dc..6e3345b675 100644
--- a/doc/classes/AnimationNode.xml
+++ b/doc/classes/AnimationNode.xml
@@ -49,6 +49,13 @@
When inheriting from [AnimationRootNode], implement this virtual method to return whether the blend tree editor should display filter editing on this node.
</description>
</method>
+ <method name="_is_parameter_read_only" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="parameter" type="StringName" />
+ <description>
+ When inheriting from [AnimationRootNode], implement this virtual method to return whether the [param parameter] is read-only. Parameters are custom local memory used for your nodes, given a resource can be reused in multiple trees.
+ </description>
+ </method>
<method name="_process" qualifiers="virtual const">
<return type="float" />
<param index="0" name="time" type="float" />
diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml
index 14abc34992..9e8193868c 100644
--- a/doc/classes/AnimationNodeOneShot.xml
+++ b/doc/classes/AnimationNodeOneShot.xml
@@ -28,6 +28,12 @@
</member>
</members>
<constants>
+ <constant name="ONE_SHOT_REQUEST_NONE" value="0" enum="OneShotRequest">
+ </constant>
+ <constant name="ONE_SHOT_REQUEST_FIRE" value="1" enum="OneShotRequest">
+ </constant>
+ <constant name="ONE_SHOT_REQUEST_ABORT" value="2" enum="OneShotRequest">
+ </constant>
<constant name="MIX_MODE_BLEND" value="0" enum="MixMode">
</constant>
<constant name="MIX_MODE_ADD" value="1" enum="MixMode">
diff --git a/doc/classes/AnimationNodeStateMachinePlayback.xml b/doc/classes/AnimationNodeStateMachinePlayback.xml
index 8f53ef0dcf..7e01be12c4 100644
--- a/doc/classes/AnimationNodeStateMachinePlayback.xml
+++ b/doc/classes/AnimationNodeStateMachinePlayback.xml
@@ -50,11 +50,19 @@
Returns [code]true[/code] if an animation is playing.
</description>
</method>
+ <method name="next">
+ <return type="void" />
+ <description>
+ If there is a next path by travel or auto advance, immediately transitions from the current state to the next state.
+ </description>
+ </method>
<method name="start">
<return type="void" />
<param index="0" name="node" type="StringName" />
+ <param index="1" name="reset" type="bool" default="true" />
<description>
Starts playing the given animation.
+ If [param reset] is [code]true[/code], the animation is played from the beginning.
</description>
</method>
<method name="stop">
@@ -66,8 +74,11 @@
<method name="travel">
<return type="void" />
<param index="0" name="to_node" type="StringName" />
+ <param index="1" name="reset_on_teleport" type="bool" default="true" />
<description>
Transitions from the current state to another one, following the shortest path.
+ If the path does not connect from the current state, the animation will play after the state teleports.
+ If [param reset_on_teleport] is [code]true[/code], the animation is played from the beginning when the travel cause a teleportation.
</description>
</method>
</methods>
diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml
index 814b2d0052..eee25fad7c 100644
--- a/doc/classes/AnimationNodeStateMachineTransition.xml
+++ b/doc/classes/AnimationNodeStateMachineTransition.xml
@@ -28,6 +28,9 @@
<member name="priority" type="int" setter="set_priority" getter="get_priority" default="1">
Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member advance_mode] is set to [constant ADVANCE_MODE_AUTO].
</member>
+ <member name="reset" type="bool" setter="set_reset" getter="is_reset" default="true">
+ If [code]true[/code], the destination animation is played back from the beginning when switched.
+ </member>
<member name="switch_mode" type="int" setter="set_switch_mode" getter="get_switch_mode" enum="AnimationNodeStateMachineTransition.SwitchMode" default="0">
The transition type.
</member>
diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml
index f6e2fc5eb2..bca94a568a 100644
--- a/doc/classes/AnimationNodeTransition.xml
+++ b/doc/classes/AnimationNodeTransition.xml
@@ -12,6 +12,12 @@
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
<methods>
+ <method name="find_input_caption" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="caption" type="String" />
+ <description>
+ </description>
+ </method>
<method name="get_input_caption" qualifiers="const">
<return type="String" />
<param index="0" name="input" type="int" />
@@ -43,7 +49,7 @@
<member name="enabled_inputs" type="int" setter="set_enabled_inputs" getter="get_enabled_inputs" default="0">
The number of enabled input ports for this node.
</member>
- <member name="from_start" type="bool" setter="set_from_start" getter="is_from_start" default="true">
+ <member name="reset" type="bool" setter="set_reset" getter="is_reset" default="true">
If [code]true[/code], the destination animation is played back from the beginning when switched.
</member>
<member name="xfade_curve" type="Curve" setter="set_xfade_curve" getter="get_xfade_curve">
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 21ccf79fe2..ce4d7693d8 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -679,7 +679,7 @@
</description>
</operator>
<operator name="operator []">
- <return type="void" />
+ <return type="Variant" />
<param index="0" name="index" type="int" />
<description>
Returns a reference to the element of type [Variant] at the specified location. Arrays start at index 0. [param index] can be a zero or positive value to start from the beginning, or a negative value to start from the end. Out-of-bounds array access causes a run-time error, which will result in an error being printed and the project execution pausing if run from the editor.
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index f7764d5e32..7b86afcc4c 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -65,11 +65,15 @@
<param index="1" name="arrays" type="Array" />
<param index="2" name="blend_shapes" type="Array[]" default="[]" />
<param index="3" name="lods" type="Dictionary" default="{}" />
- <param index="4" name="compress_flags" type="int" enum="Mesh.ArrayFormat" default="0" />
- <description>
- Creates a new surface.
- Surfaces are created to be rendered using a [param primitive], which may be any of the types defined in [enum Mesh.PrimitiveType]. (As a note, when using indices, it is recommended to only use points, lines, or triangles.) [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
- The [param arrays] argument is an array of arrays. See [enum Mesh.ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
+ <param index="4" name="flags" type="int" enum="Mesh.ArrayFormat" default="0" />
+ <description>
+ Creates a new surface. [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
+ Surfaces are created to be rendered using a [param primitive], which may be any of the values defined in [enum Mesh.PrimitiveType].
+ The [param arrays] argument is an array of arrays. Each of the [constant Mesh.ARRAY_MAX] elements contains an array with some of the mesh data for this surface as described by the corresponding member of [enum Mesh.ArrayType] or [code]null[/code] if it is not used by the surface. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this surface into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
+ The [param blend_shapes] argument is an array of vertex data for each blend shape. Each element is an array of the same structure as [param arrays], but [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL], and [constant Mesh.ARRAY_TANGENT] are set if and only if they are set in [param arrays] and all other entries are [code]null[/code].
+ The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents a LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of a LOD also increases the distance that the objects has to be from the camera before the LOD is used.
+ The [param flags] argument is the bitwise or of, as required: One value of [enum Mesh.ArrayCustomFormat] left shifted by [code]ARRAY_FORMAT_CUSTOMn_SHIFT[/code] for each custom channel in use, [constant Mesh.ARRAY_FLAG_USE_DYNAMIC_UPDATE], [constant Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS], or [constant Mesh.ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY].
+ [b]Note:[/b] When using indices, it is recommended to only use points, lines, or triangles.
</description>
</method>
<method name="clear_blend_shapes">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 75afb0cdbf..7082eff97d 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -196,12 +196,12 @@
</description>
</method>
<method name="_structured_text_parser" qualifiers="virtual const">
- <return type="Vector2i[]" />
+ <return type="Vector3i[]" />
<param index="0" name="args" type="Array" />
<param index="1" name="text" type="String" />
<description>
User defined BiDi algorithm override function.
- Returns an [Array] of [Vector2i] text ranges, in the left-to-right order. Ranges should cover full source [param text] without overlaps. BiDi algorithm will be used on each range separately.
+ Returns an [Array] of [Vector3i] text ranges and text base directions, in the left-to-right order. Ranges should cover full source [param text] without overlaps. BiDi algorithm will be used on each range separately.
</description>
</method>
<method name="accept_event">
diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml
index b80857a7bf..10479dfcfe 100644
--- a/doc/classes/ImporterMesh.xml
+++ b/doc/classes/ImporterMesh.xml
@@ -27,9 +27,13 @@
<param index="5" name="name" type="String" default="&quot;&quot;" />
<param index="6" name="flags" type="int" default="0" />
<description>
- Creates a new surface, analogous to [method ArrayMesh.add_surface_from_arrays].
- Surfaces are created to be rendered using a [param primitive], which may be any of the types defined in [enum Mesh.PrimitiveType]. (As a note, when using indices, it is recommended to only use points, lines, or triangles.) [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
- The [param arrays] argument is an array of arrays. See [enum Mesh.ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
+ Creates a new surface. [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface.
+ Surfaces are created to be rendered using a [param primitive], which may be any of the values defined in [enum Mesh.PrimitiveType].
+ The [param arrays] argument is an array of arrays. Each of the [constant Mesh.ARRAY_MAX] elements contains an array with some of the mesh data for this surface as described by the corresponding member of [enum Mesh.ArrayType] or [code]null[/code] if it is not used by the surface. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this surface into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
+ The [param blend_shapes] argument is an array of vertex data for each blend shape. Each element is an array of the same structure as [param arrays], but [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL], and [constant Mesh.ARRAY_TANGENT] are set if and only if they are set in [param arrays] and all other entries are [code]null[/code].
+ The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents a LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of a LOD also increases the distance that the objects has to be from the camera before the LOD is used.
+ The [param flags] argument is the bitwise or of, as required: One value of [enum Mesh.ArrayCustomFormat] left shifted by [code]ARRAY_FORMAT_CUSTOMn_SHIFT[/code] for each custom channel in use, [constant Mesh.ARRAY_FLAG_USE_DYNAMIC_UPDATE], [constant Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS], or [constant Mesh.ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY].
+ [b]Note:[/b] When using indices, it is recommended to only use points, lines, or triangles.
</description>
</method>
<method name="clear">
diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml
index 94e80ffb2b..1c1f48588f 100644
--- a/doc/classes/Mesh.xml
+++ b/doc/classes/Mesh.xml
@@ -227,10 +227,10 @@
Contains custom color channel 3. [PackedByteArray] if [code](format &gt;&gt; [constant ARRAY_FORMAT_CUSTOM3_SHIFT]) &amp; [constant ARRAY_FORMAT_CUSTOM_MASK])[/code] is [constant ARRAY_CUSTOM_RGBA8_UNORM], [constant ARRAY_CUSTOM_RGBA8_UNORM], [constant ARRAY_CUSTOM_RG_HALF] or [constant ARRAY_CUSTOM_RGBA_HALF]. [PackedFloat32Array] otherwise.
</constant>
<constant name="ARRAY_BONES" value="10" enum="ArrayType">
- [PackedFloat32Array] or [PackedInt32Array] of bone indices. Each element is a group of 4 numbers.
+ [PackedFloat32Array] or [PackedInt32Array] of bone indices. Contains either 4 or 8 numbers per vertex depending on the presence of the [constant ARRAY_FLAG_USE_8_BONE_WEIGHTS] flag.
</constant>
<constant name="ARRAY_WEIGHTS" value="11" enum="ArrayType">
- [PackedFloat32Array] of bone weights. Each element in groups of 4 floats.
+ [PackedFloat32Array] or [PackedFloat64Array] of bone weights in the range [code]0.0[/code] to [code]1.0[/code] (inclusive). Contains either 4 or 8 numbers per vertex depending on the presence of the [constant ARRAY_FLAG_USE_8_BONE_WEIGHTS] flag.
</constant>
<constant name="ARRAY_INDEX" value="12" enum="ArrayType">
[PackedInt32Array] of integers used as indices referencing vertices, colors, normals, tangents, and textures. All of those arrays must have the same number of elements as the vertex array. No index can be beyond the vertex array size. When this index array is present, it puts the function into "index mode," where the index selects the *i*'th vertex, normal, tangent, color, UV, etc. This means if you want to have different normals or colors along an edge, you have to duplicate the vertices.
@@ -341,6 +341,9 @@
<constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat" is_bitfield="true">
Flag used to mark that the mesh contains up to 8 bone influences per vertex. This flag indicates that [constant ARRAY_BONES] and [constant ARRAY_WEIGHTS] elements will have double length.
</constant>
+ <constant name="ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY" value="268435456" enum="ArrayFormat" is_bitfield="true">
+ Flag used to mark that the mesh intentionally contains no vertex array.
+ </constant>
<constant name="BLEND_SHAPE_MODE_NORMALIZED" value="0" enum="BlendShapeMode">
Blend shapes are normalized.
</constant>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 33170e6606..3d7fb0d445 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3810,6 +3810,8 @@
</constant>
<constant name="ARRAY_FLAG_USE_8_BONE_WEIGHTS" value="134217728" enum="ArrayFormat" is_bitfield="true">
</constant>
+ <constant name="ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY" value="268435456" enum="ArrayFormat" is_bitfield="true">
+ </constant>
<constant name="PRIMITIVE_POINTS" value="0" enum="PrimitiveType">
Primitive to draw consists of points.
</constant>
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index d2c6dee373..711fb89217 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -1042,7 +1042,7 @@
</description>
</method>
<method name="parse_structured_text" qualifiers="const">
- <return type="Vector2i[]" />
+ <return type="Vector3i[]" />
<param index="0" name="parser_type" type="int" enum="TextServer.StructuredTextParser" />
<param index="1" name="args" type="Array" />
<param index="2" name="text" type="String" />
@@ -1634,6 +1634,9 @@
<constant name="DIRECTION_RTL" value="2" enum="Direction">
Text is written from right to left.
</constant>
+ <constant name="DIRECTION_INHERITED" value="3" enum="Direction">
+ Text writing direction is the same as base string writing direction. Used for BiDi override only.
+ </constant>
<constant name="ORIENTATION_HORIZONTAL" value="0" enum="Orientation">
Text is written horizontally.
</constant>
@@ -1881,7 +1884,7 @@
Font have fixed-width characters.
</constant>
<constant name="STRUCTURED_TEXT_DEFAULT" value="0" enum="StructuredTextParser">
- Use default behavior. Same as [constant STRUCTURED_TEXT_NONE] unless specified otherwise in the control description.
+ Use default Unicode BiDi algorithm.
</constant>
<constant name="STRUCTURED_TEXT_URI" value="1" enum="StructuredTextParser">
BiDi override for URI.
@@ -1896,8 +1899,8 @@
BiDi override for lists.
Structured text options: list separator [code]String[/code].
</constant>
- <constant name="STRUCTURED_TEXT_NONE" value="5" enum="StructuredTextParser">
- Use default Unicode BiDi algorithm.
+ <constant name="STRUCTURED_TEXT_GDSCRIPT" value="5" enum="StructuredTextParser">
+ BiDi override for GDScript.
</constant>
<constant name="STRUCTURED_TEXT_CUSTOM" value="6" enum="StructuredTextParser">
User defined structured text BiDi override function.
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index e144b09eb6..f4b306cf96 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -896,7 +896,7 @@
</description>
</method>
<method name="_parse_structured_text" qualifiers="virtual const">
- <return type="Vector2i[]" />
+ <return type="Vector3i[]" />
<param index="0" name="parser_type" type="int" enum="TextServer.StructuredTextParser" />
<param index="1" name="args" type="Array" />
<param index="2" name="text" type="String" />
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index df8adf01e4..644735a4d8 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -2086,6 +2086,7 @@ CodeTextEditor::CodeTextEditor() {
text_editor = memnew(CodeEdit);
add_child(text_editor);
text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ text_editor->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_GDSCRIPT);
int ot_mode = EDITOR_GET("interface/editor/code_font_contextual_ligatures");
Ref<FontVariation> fc = text_editor->get_theme_font(SNAME("font"));
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index c675060b2b..5bdef32c60 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -750,6 +750,7 @@ void DocTools::generate(bool p_basic_types) {
MethodInfo mi;
mi.name = "operator []";
mi.return_val.type = Variant::get_indexed_element_type(Variant::Type(i));
+ mi.return_val.usage = Variant::get_indexed_element_usage(Variant::Type(i));
PropertyInfo arg;
arg.name = "index";
arg.type = Variant::INT;
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 6e66962605..3059ce445c 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -122,12 +122,6 @@ int EditorSelectionHistory::get_history_pos() {
return current_elem_idx;
}
-bool EditorSelectionHistory::is_history_obj_inspector_only(int p_obj) const {
- ERR_FAIL_INDEX_V(p_obj, history.size(), false);
- ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), false);
- return history[p_obj].path[history[p_obj].level].inspector_only;
-}
-
ObjectID EditorSelectionHistory::get_history_obj(int p_obj) const {
ERR_FAIL_INDEX_V(p_obj, history.size(), ObjectID());
ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), ObjectID());
@@ -351,18 +345,6 @@ void EditorData::apply_changes_in_editors() {
}
}
-void EditorData::save_editor_global_states() {
- for (int i = 0; i < editor_plugins.size(); i++) {
- editor_plugins[i]->save_global_state();
- }
-}
-
-void EditorData::restore_editor_global_states() {
- for (int i = 0; i < editor_plugins.size(); i++) {
- editor_plugins[i]->restore_global_state();
- }
-}
-
void EditorData::paste_object_params(Object *p_object) {
ERR_FAIL_NULL(p_object);
undo_redo_manager->create_action(TTR("Paste Params"));
diff --git a/editor/editor_data.h b/editor/editor_data.h
index bce9dd345d..6a89b3572c 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -80,7 +80,6 @@ public:
// Gets an object from the history. The most recent object would be the object with p_obj = get_history_len() - 1.
ObjectID get_history_obj(int p_obj) const;
- bool is_history_obj_inspector_only(int p_obj) const;
bool next();
bool previous();
@@ -177,7 +176,6 @@ public:
Callable get_move_array_element_function(const StringName &p_class) const;
void save_editor_global_states();
- void restore_editor_global_states();
void add_custom_type(const String &p_type, const String &p_inherits, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon);
Variant instantiate_custom_type(const String &p_type, const String &p_inherits);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 7beea04589..b0278030f9 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -7400,7 +7400,6 @@ EditorNode::EditorNode() {
_update_recent_scenes();
- editor_data.restore_editor_global_states();
set_process_shortcut_input(true);
load_errors = memnew(RichTextLabel);
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index d508638acd..7f02148dfc 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -712,9 +712,6 @@ bool EditorPlugin::get_remove_list(List<Node *> *p_list) {
return false;
}
-void EditorPlugin::restore_global_state() {}
-void EditorPlugin::save_global_state() {}
-
void EditorPlugin::add_undo_redo_inspector_hook_callback(Callable p_callable) {
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(p_callable);
}
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index b79d2de035..a5a17acdf1 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -278,9 +278,6 @@ public:
void make_bottom_panel_item_visible(Control *p_item);
void hide_bottom_panel();
- virtual void restore_global_state();
- virtual void save_global_state();
-
void add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser);
void remove_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 3bf320f580..46f52ec4af 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -342,12 +342,17 @@ void EditorPropertyTextEnum::_notification(int p_what) {
}
EditorPropertyTextEnum::EditorPropertyTextEnum() {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ add_child(hb);
+
default_layout = memnew(HBoxContainer);
- add_child(default_layout);
+ default_layout->set_h_size_flags(SIZE_EXPAND_FILL);
+ hb->add_child(default_layout);
edit_custom_layout = memnew(HBoxContainer);
+ edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL);
edit_custom_layout->hide();
- add_child(edit_custom_layout);
+ hb->add_child(edit_custom_layout);
option_button = memnew(OptionButton);
option_button->set_h_size_flags(SIZE_EXPAND_FILL);
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 14e3cb4b97..f5f9ec11b3 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -187,7 +187,7 @@ void AnimationNodeBlendTreeEditor::update_graph() {
String base_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E) + "/" + F.name;
EditorProperty *prop = EditorInspector::instantiate_property_editor(tree, F.type, base_path, F.hint, F.hint_string, F.usage);
if (prop) {
- prop->set_read_only(read_only);
+ prop->set_read_only(read_only || (F.usage & PROPERTY_USAGE_READ_ONLY));
prop->set_object_and_property(tree, base_path);
prop->update_property();
prop->set_name_split_ratio(0);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 188abf1f5c..e515b46b1e 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -4007,12 +4007,6 @@ void ScriptEditorPlugin::apply_changes() {
script_editor->apply_scripts();
}
-void ScriptEditorPlugin::restore_global_state() {
-}
-
-void ScriptEditorPlugin::save_global_state() {
-}
-
void ScriptEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
script_editor->set_window_layout(p_layout);
}
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index d4c80c416b..988d07621c 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -542,9 +542,6 @@ public:
virtual void save_external_data() override;
virtual void apply_changes() override;
- virtual void restore_global_state() override;
- virtual void save_global_state() override;
-
virtual void set_window_layout(Ref<ConfigFile> p_layout) override;
virtual void get_window_layout(Ref<ConfigFile> p_layout) override;
diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp
index 0acaaf5fc9..d473f3b4a0 100644
--- a/modules/ogg/ogg_packet_sequence.cpp
+++ b/modules/ogg/ogg_packet_sequence.cpp
@@ -136,6 +136,8 @@ bool OggPacketSequencePlayback::next_ogg_packet(ogg_packet **p_packet) const {
ERR_FAIL_COND_V(data_version != ogg_packet_sequence->data_version, false);
ERR_FAIL_COND_V(ogg_packet_sequence->page_data.is_empty(), false);
ERR_FAIL_COND_V(ogg_packet_sequence->page_granule_positions.is_empty(), false);
+ ERR_FAIL_COND_V(page_cursor >= ogg_packet_sequence->page_data.size(), false);
+
// Move on to the next page if need be. This happens first to help simplify seek logic.
while (packet_cursor >= ogg_packet_sequence->page_data[page_cursor].size()) {
packet_cursor = 0;
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 8e9ff61ad0..79ca4a7024 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -3654,6 +3654,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
RID TextServerAdvanced::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");
ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
sd->hb_buffer = hb_buffer_create();
@@ -3679,6 +3680,7 @@ void TextServerAdvanced::_shaped_text_clear(const RID &p_shaped) {
void TextServerAdvanced::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -3738,8 +3740,12 @@ void TextServerAdvanced::_shaped_text_set_bidi_override(const RID &p_shaped, con
}
sd->bidi_override.clear();
for (int i = 0; i < p_override.size(); i++) {
- if (p_override[i].get_type() == Variant::VECTOR2I) {
- sd->bidi_override.push_back(p_override[i]);
+ if (p_override[i].get_type() == Variant::VECTOR3I) {
+ const Vector3i &r = p_override[i];
+ sd->bidi_override.push_back(r);
+ } else if (p_override[i].get_type() == Variant::VECTOR2I) {
+ const Vector2i &r = p_override[i];
+ sd->bidi_override.push_back(Vector3i(r.x, r.y, DIRECTION_INHERITED));
}
}
invalidate(sd, false);
@@ -5544,8 +5550,31 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length()));
}
+ int base_para_direction = UBIDI_DEFAULT_LTR;
+ switch (sd->direction) {
+ case DIRECTION_LTR: {
+ sd->para_direction = DIRECTION_LTR;
+ base_para_direction = UBIDI_LTR;
+ } break;
+ case DIRECTION_RTL: {
+ sd->para_direction = DIRECTION_RTL;
+ base_para_direction = UBIDI_RTL;
+ } break;
+ case DIRECTION_INHERITED:
+ case DIRECTION_AUTO: {
+ UBiDiDirection direction = ubidi_getBaseDirection(data, sd->utf16.length());
+ if (direction != UBIDI_NEUTRAL) {
+ sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
+ base_para_direction = direction;
+ } else {
+ sd->para_direction = DIRECTION_LTR;
+ base_para_direction = UBIDI_DEFAULT_LTR;
+ }
+ } break;
+ }
+
if (sd->bidi_override.is_empty()) {
- sd->bidi_override.push_back(Vector2i(sd->start, sd->end));
+ sd->bidi_override.push_back(Vector3i(sd->start, sd->end, DIRECTION_INHERITED));
}
for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
@@ -5561,23 +5590,22 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
UBiDi *bidi_iter = ubidi_openSized(end, 0, &err);
ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
- switch (sd->direction) {
+ switch (static_cast<TextServer::Direction>(sd->bidi_override[ov].z)) {
case DIRECTION_LTR: {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
- sd->para_direction = DIRECTION_LTR;
} break;
case DIRECTION_RTL: {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
- sd->para_direction = DIRECTION_RTL;
+ } break;
+ case DIRECTION_INHERITED: {
+ ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err);
} break;
case DIRECTION_AUTO: {
UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
if (direction != UBIDI_NEUTRAL) {
ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
- sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
} else {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err);
- sd->para_direction = DIRECTION_LTR;
}
} break;
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 5920ddaa50..c7fe46d554 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -499,7 +499,7 @@ class TextServerAdvanced : public TextServerExtension {
/* Intermediate data */
Char16String utf16;
Vector<UBiDi *> bidi_iter;
- Vector<Vector2i> bidi_override;
+ Vector<Vector3i> bidi_override;
ScriptIterator *script_iter = nullptr;
hb_buffer_t *hb_buffer = nullptr;
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index ece34f56d6..b5d7d3a3cf 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -51,7 +51,6 @@ using namespace godot;
#include "core/error/error_macros.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
-#include "core/string/ucaps.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
@@ -2665,6 +2664,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
RID TextServerFallback::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");
ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
sd->direction = p_direction;
@@ -2688,6 +2688,7 @@ void TextServerFallback::_shaped_text_clear(const RID &p_shaped) {
}
void TextServerFallback::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
+ ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
if (p_direction == DIRECTION_RTL) {
ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server.");
}
@@ -4060,31 +4061,11 @@ double TextServerFallback::_shaped_text_get_underline_thickness(const RID &p_sha
}
String TextServerFallback::_string_to_upper(const String &p_string, const String &p_language) const {
- String upper = p_string;
-
- for (int i = 0; i <= upper.length(); i++) {
- const char32_t s = upper[i];
- const char32_t t = _find_upper(s);
- if (s != t) { // avoid copy on write
- upper[i] = t;
- }
- }
-
- return upper;
+ return p_string.to_upper();
}
String TextServerFallback::_string_to_lower(const String &p_string, const String &p_language) const {
- String lower = p_string;
-
- for (int i = 0; i <= lower.length(); i++) {
- const char32_t s = lower[i];
- const char32_t t = _find_lower(s);
- if (s != t) { // avoid copy on write
- lower[i] = t;
- }
- }
-
- return lower;
+ return p_string.to_lower();
}
PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language, int p_chars_per_line) const {
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index f8c54809da..d0f71768d2 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -442,7 +442,7 @@ void Label3D::_shape() {
TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
}
- TypedArray<Vector2i> stt;
+ TypedArray<Vector3i> stt;
if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
} else {
diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h
index 8fc772e4b0..96cc941209 100644
--- a/scene/3d/label_3d.h
+++ b/scene/3d/label_3d.h
@@ -143,7 +143,7 @@ private:
void _generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority = 0, int p_outline_size = 0);
protected:
- GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
+ GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String)
void _notification(int p_what);
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 1ef0774828..e074927b9b 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -229,15 +229,17 @@ AnimationNodeSync::AnimationNodeSync() {
////////////////////////////////////////////////////////
void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const {
- r_list->push_back(PropertyInfo(Variant::BOOL, active));
- r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
+ r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
+ r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort"));
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}
Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const {
- if (p_parameter == active || p_parameter == prev_active) {
+ if (p_parameter == request) {
+ return ONE_SHOT_REQUEST_NONE;
+ } else if (p_parameter == active) {
return false;
} else if (p_parameter == time_to_restart) {
return -1;
@@ -246,6 +248,13 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa
}
}
+bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const {
+ if (p_parameter == active) {
+ return true;
+ }
+ return false;
+}
+
void AnimationNodeOneShot::set_fadein_time(double p_time) {
fade_in = p_time;
}
@@ -303,41 +312,42 @@ bool AnimationNodeOneShot::has_filter() const {
}
double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_external_seeking) {
+ OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request));
bool cur_active = get_parameter(active);
- bool cur_prev_active = get_parameter(prev_active);
double cur_time = get_parameter(time);
double cur_remaining = get_parameter(remaining);
double cur_time_to_restart = get_parameter(time_to_restart);
- if (!cur_active) {
- //make it as if this node doesn't exist, pass input 0 by.
- if (cur_prev_active) {
- set_parameter(prev_active, false);
- }
+ set_parameter(request, ONE_SHOT_REQUEST_NONE);
+
+ bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE;
+ if (cur_request == ONE_SHOT_REQUEST_ABORT) {
+ set_parameter(active, false);
+ set_parameter(time_to_restart, -1);
+ return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
+ } else if (!do_start && !cur_active) {
if (cur_time_to_restart >= 0.0 && !p_seek) {
cur_time_to_restart -= p_time;
if (cur_time_to_restart < 0) {
- //restart
- set_parameter(active, true);
- cur_active = true;
+ do_start = true; // Restart.
}
set_parameter(time_to_restart, cur_time_to_restart);
}
-
- return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
+ if (!do_start) {
+ return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
+ }
}
bool os_seek = p_seek;
-
if (p_seek) {
cur_time = p_time;
}
- bool do_start = !cur_prev_active;
if (do_start) {
cur_time = 0;
os_seek = true;
- set_parameter(prev_active, true);
+ set_parameter(request, ONE_SHOT_REQUEST_NONE);
+ set_parameter(active, true);
}
real_t blend;
@@ -375,7 +385,6 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter
cur_remaining = os_rem;
if (cur_remaining <= 0) {
set_parameter(active, false);
- set_parameter(prev_active, false);
if (autorestart) {
double restart_sec = autorestart_delay + Math::randd() * autorestart_random_delay;
set_parameter(time_to_restart, restart_sec);
@@ -419,6 +428,10 @@ void AnimationNodeOneShot::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_delay", "get_autorestart_delay");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_random_delay", "get_autorestart_random_delay");
+ BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_NONE);
+ BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FIRE);
+ BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_ABORT);
+
BIND_ENUM_CONSTANT(MIX_MODE_BLEND);
BIND_ENUM_CONSTANT(MIX_MODE_ADD);
}
@@ -640,9 +653,10 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con
anims += inputs[i].name;
}
- r_list->push_back(PropertyInfo(Variant::INT, current, PROPERTY_HINT_ENUM, anims));
- r_list->push_back(PropertyInfo(Variant::INT, prev_current, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
- r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
+ r_list->push_back(PropertyInfo(Variant::STRING, current_state, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); // For interface.
+ r_list->push_back(PropertyInfo(Variant::STRING, transition_request, PROPERTY_HINT_ENUM, anims)); // For transition request. It will be cleared after setting the value immediately.
+ r_list->push_back(PropertyInfo(Variant::INT, current_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); // To avoid finding the index every frame, use this internally.
+ r_list->push_back(PropertyInfo(Variant::INT, prev_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}
@@ -650,13 +664,22 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con
Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const {
if (p_parameter == time || p_parameter == prev_xfading) {
return 0.0;
- } else if (p_parameter == prev || p_parameter == prev_current) {
+ } else if (p_parameter == prev_index) {
return -1;
+ } else if (p_parameter == transition_request || p_parameter == current_state) {
+ return String();
} else {
return 0;
}
}
+bool AnimationNodeTransition::is_parameter_read_only(const StringName &p_parameter) const {
+ if (p_parameter == current_state || p_parameter == current_index) {
+ return true;
+ }
+ return false;
+}
+
String AnimationNodeTransition::get_caption() const {
return "Transition";
}
@@ -702,6 +725,17 @@ String AnimationNodeTransition::get_input_caption(int p_input) const {
return inputs[p_input].name;
}
+int AnimationNodeTransition::find_input_caption(const String &p_name) const {
+ int idx = -1;
+ for (int i = 0; i < MAX_INPUTS; i++) {
+ if (inputs[i].name == p_name) {
+ idx = i;
+ break;
+ }
+ }
+ return idx;
+}
+
void AnimationNodeTransition::set_xfade_time(double p_fade) {
xfade_time = p_fade;
}
@@ -718,35 +752,62 @@ Ref<Curve> AnimationNodeTransition::get_xfade_curve() const {
return xfade_curve;
}
-void AnimationNodeTransition::set_from_start(bool p_from_start) {
- from_start = p_from_start;
+void AnimationNodeTransition::set_reset(bool p_reset) {
+ reset = p_reset;
}
-bool AnimationNodeTransition::is_from_start() const {
- return from_start;
+bool AnimationNodeTransition::is_reset() const {
+ return reset;
}
double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_external_seeking) {
- int cur_current = get_parameter(current);
- int cur_prev = get_parameter(prev);
- int cur_prev_current = get_parameter(prev_current);
+ String cur_transition_request = get_parameter(transition_request);
+ int cur_current_index = get_parameter(current_index);
+ int cur_prev_index = get_parameter(prev_index);
double cur_time = get_parameter(time);
double cur_prev_xfading = get_parameter(prev_xfading);
- bool switched = cur_current != cur_prev_current;
+ bool switched = false;
+ bool restart = false;
+
+ if (!cur_transition_request.is_empty()) {
+ int new_idx = find_input_caption(cur_transition_request);
+ if (new_idx >= 0) {
+ if (cur_current_index == new_idx) {
+ // Transition to same state.
+ restart = reset;
+ cur_prev_xfading = 0;
+ set_parameter(prev_xfading, 0);
+ cur_prev_index = -1;
+ set_parameter(prev_index, -1);
+ } else {
+ switched = true;
+ cur_prev_index = cur_current_index;
+ set_parameter(prev_index, cur_current_index);
+ }
+ cur_current_index = new_idx;
+ set_parameter(current_index, cur_current_index);
+ set_parameter(current_state, cur_transition_request);
+ } else {
+ ERR_PRINT("No such input: '" + cur_transition_request + "'");
+ }
+ cur_transition_request = String();
+ set_parameter(transition_request, cur_transition_request);
+ }
- if (switched) {
- set_parameter(prev_current, cur_current);
- set_parameter(prev, cur_prev_current);
+ // Special case for restart.
+ if (restart) {
+ set_parameter(time, 0);
+ return blend_input(cur_current_index, 0, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
+ }
- cur_prev = cur_prev_current;
+ if (switched) {
cur_prev_xfading = xfade_time;
cur_time = 0;
- switched = true;
}
- if (cur_current < 0 || cur_current >= enabled_inputs || cur_prev >= enabled_inputs) {
+ if (cur_current_index < 0 || cur_current_index >= enabled_inputs || cur_prev_index >= enabled_inputs) {
return 0;
}
@@ -754,15 +815,15 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
if (sync) {
for (int i = 0; i < enabled_inputs; i++) {
- if (i != cur_current && i != cur_prev) {
+ if (i != cur_current_index && i != cur_prev_index) {
blend_input(i, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
}
}
}
- if (cur_prev < 0) { // process current animation, check for transition
+ if (cur_prev_index < 0) { // process current animation, check for transition
- rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
+ rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
if (p_seek) {
cur_time = p_time;
@@ -770,8 +831,8 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
cur_time += p_time;
}
- if (inputs[cur_current].auto_advance && rem <= xfade_time) {
- set_parameter(current, (cur_current + 1) % enabled_inputs);
+ if (inputs[cur_current_index].auto_advance && rem <= xfade_time) {
+ set_parameter(transition_request, get_input_caption((cur_current_index + 1) % enabled_inputs));
}
} else { // cross-fading from prev to current
@@ -783,21 +844,21 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
// Blend values must be more than CMP_EPSILON to process discrete keys in edge.
real_t blend_inv = 1.0 - blend;
- if (from_start && !p_seek && switched) { //just switched, seek to start of current
- rem = blend_input(cur_current, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true);
+ if (reset && !p_seek && switched) { //just switched, seek to start of current
+ rem = blend_input(cur_current_index, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true);
} else {
- rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true);
+ rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true);
}
if (p_seek) {
- blend_input(cur_prev, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true);
+ blend_input(cur_prev_index, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true);
cur_time = p_time;
} else {
- blend_input(cur_prev, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true);
+ blend_input(cur_prev_index, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true);
cur_time += p_time;
cur_prev_xfading -= p_time;
if (cur_prev_xfading < 0) {
- set_parameter(prev, -1);
+ set_parameter(prev_index, -1);
}
}
}
@@ -829,6 +890,7 @@ void AnimationNodeTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_input_caption", "input", "caption"), &AnimationNodeTransition::set_input_caption);
ClassDB::bind_method(D_METHOD("get_input_caption", "input"), &AnimationNodeTransition::get_input_caption);
+ ClassDB::bind_method(D_METHOD("find_input_caption", "caption"), &AnimationNodeTransition::find_input_caption);
ClassDB::bind_method(D_METHOD("set_xfade_time", "time"), &AnimationNodeTransition::set_xfade_time);
ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeTransition::get_xfade_time);
@@ -836,13 +898,13 @@ void AnimationNodeTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeTransition::set_xfade_curve);
ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeTransition::get_xfade_curve);
- ClassDB::bind_method(D_METHOD("set_from_start", "from_start"), &AnimationNodeTransition::set_from_start);
- ClassDB::bind_method(D_METHOD("is_from_start"), &AnimationNodeTransition::is_from_start);
+ ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeTransition::set_reset);
+ ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeTransition::is_reset);
ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_inputs", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_xfade_time", "get_xfade_time");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "from_start"), "set_from_start", "is_from_start");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset");
for (int i = 0; i < MAX_INPUTS; i++) {
ADD_PROPERTYI(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_input_caption", "get_input_caption", i);
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index e72471202e..a1969bb621 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -96,6 +96,12 @@ class AnimationNodeOneShot : public AnimationNodeSync {
GDCLASS(AnimationNodeOneShot, AnimationNodeSync);
public:
+ enum OneShotRequest {
+ ONE_SHOT_REQUEST_NONE,
+ ONE_SHOT_REQUEST_FIRE,
+ ONE_SHOT_REQUEST_ABORT,
+ };
+
enum MixMode {
MIX_MODE_BLEND,
MIX_MODE_ADD
@@ -110,13 +116,8 @@ private:
double autorestart_random_delay = 0.0;
MixMode mix = MIX_MODE_BLEND;
- /* bool active;
- bool do_start;
- double time;
- double remaining;*/
-
+ StringName request = PNAME("request");
StringName active = PNAME("active");
- StringName prev_active = "prev_active";
StringName time = "time";
StringName remaining = "remaining";
StringName time_to_restart = "time_to_restart";
@@ -127,6 +128,7 @@ protected:
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
+ virtual bool is_parameter_read_only(const StringName &p_parameter) const override;
virtual String get_caption() const override;
@@ -153,6 +155,7 @@ public:
AnimationNodeOneShot();
};
+VARIANT_ENUM_CAST(AnimationNodeOneShot::OneShotRequest)
VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
class AnimationNodeAdd2 : public AnimationNodeSync {
@@ -284,22 +287,19 @@ class AnimationNodeTransition : public AnimationNodeSync {
InputData inputs[MAX_INPUTS];
int enabled_inputs = 0;
- /*
- double prev_xfading;
- int prev;
- double time;
- int current;
- int prev_current; */
-
- StringName prev_xfading = "prev_xfading";
- StringName prev = "prev";
StringName time = "time";
- StringName current = PNAME("current");
- StringName prev_current = "prev_current";
+ StringName prev_xfading = "prev_xfading";
+ StringName prev_index = "prev_index";
+ StringName current_index = PNAME("current_index");
+ StringName current_state = PNAME("current_state");
+ StringName transition_request = PNAME("transition_request");
+
+ StringName prev_frame_current = "pf_current";
+ StringName prev_frame_current_idx = "pf_current_idx";
double xfade_time = 0.0;
Ref<Curve> xfade_curve;
- bool from_start = true;
+ bool reset = true;
void _update_inputs();
@@ -310,6 +310,7 @@ protected:
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
+ virtual bool is_parameter_read_only(const StringName &p_parameter) const override;
virtual String get_caption() const override;
@@ -321,6 +322,7 @@ public:
void set_input_caption(int p_input, const String &p_name);
String get_input_caption(int p_input) const;
+ int find_input_caption(const String &p_name) const;
void set_xfade_time(double p_fade);
double get_xfade_time() const;
@@ -328,8 +330,8 @@ public:
void set_xfade_curve(const Ref<Curve> &p_curve);
Ref<Curve> get_xfade_curve() const;
- void set_from_start(bool p_from_start);
- bool is_from_start() const;
+ void set_reset(bool p_reset);
+ bool is_reset() const;
double process(double p_time, bool p_seek, bool p_is_external_seeking) override;
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index f5df64dbdd..2c79e5fe06 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -107,6 +107,15 @@ Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const {
return xfade_curve;
}
+void AnimationNodeStateMachineTransition::set_reset(bool p_reset) {
+ reset = p_reset;
+ emit_changed();
+}
+
+bool AnimationNodeStateMachineTransition::is_reset() const {
+ return reset;
+}
+
void AnimationNodeStateMachineTransition::set_priority(int p_priority) {
priority = p_priority;
emit_changed();
@@ -132,6 +141,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve);
ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve);
+ ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeStateMachineTransition::set_reset);
+ ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeStateMachineTransition::is_reset);
+
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);
@@ -140,6 +152,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset");
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
ADD_GROUP("Switch", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode");
@@ -164,18 +179,27 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() {
////////////////////////////////////////////////////////
-void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) {
- start_request_travel = true;
- start_request = p_state;
+void AnimationNodeStateMachinePlayback::travel(const StringName &p_state, bool p_reset_on_teleport) {
+ travel_request = p_state;
+ reset_request_on_teleport = p_reset_on_teleport;
stop_request = false;
}
-void AnimationNodeStateMachinePlayback::start(const StringName &p_state) {
- start_request_travel = false;
+void AnimationNodeStateMachinePlayback::start(const StringName &p_state, bool p_reset) {
+ travel_request = StringName();
+ reset_request = p_reset;
+ _start(p_state);
+}
+
+void AnimationNodeStateMachinePlayback::_start(const StringName &p_state) {
start_request = p_state;
stop_request = false;
}
+void AnimationNodeStateMachinePlayback::next() {
+ next_request = true;
+}
+
void AnimationNodeStateMachinePlayback::stop() {
stop_request = true;
}
@@ -212,7 +236,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
path.clear(); //a new one will be needed
if (current == p_travel) {
- return true; //nothing to do
+ return false; // Will teleport oneself (restart).
}
Vector2 current_pos = p_state_machine->states[current].position;
@@ -323,6 +347,15 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
}
double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) {
+ double rem = _process(p_state_machine, p_time, p_seek, p_is_external_seeking);
+ start_request = StringName();
+ next_request = false;
+ stop_request = false;
+ reset_request_on_teleport = false;
+ return rem;
+}
+
+double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) {
if (p_time == -1) {
Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node;
if (anodesm.is_valid()) {
@@ -335,14 +368,13 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
//if not playing and it can restart, then restart
if (!playing && start_request == StringName()) {
if (!stop_request && p_state_machine->start_node) {
- start(p_state_machine->start_node);
+ _start(p_state_machine->start_node);
} else {
return 0;
}
}
if (playing && stop_request) {
- stop_request = false;
playing = false;
return 0;
}
@@ -350,42 +382,45 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
bool play_start = false;
if (start_request != StringName()) {
- if (start_request_travel) {
- if (!playing) {
- if (!stop_request && p_state_machine->start_node) {
- // can restart, just postpone traveling
- path.clear();
- current = p_state_machine->start_node;
- playing = true;
- play_start = true;
- } else {
- // stopped, invalid state
- String node_name = start_request;
- start_request = StringName(); //clear start request
- ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?");
- }
- } else {
- if (!_travel(p_state_machine, start_request)) {
- // can't travel, then teleport
- path.clear();
- current = start_request;
- play_start = true;
- }
- start_request = StringName(); //clear start request
- }
+ // teleport to start
+ if (p_state_machine->states.has(start_request)) {
+ path.clear();
+ current = start_request;
+ playing = true;
+ play_start = true;
} else {
- // teleport to start
- if (p_state_machine->states.has(start_request)) {
+ StringName node = start_request;
+ ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ }
+ } else if (travel_request != StringName()) {
+ if (!playing) {
+ if (!stop_request && p_state_machine->start_node) {
+ // can restart, just postpone traveling
path.clear();
- current = start_request;
+ current = p_state_machine->start_node;
playing = true;
play_start = true;
- start_request = StringName(); //clear start request
} else {
- StringName node = start_request;
- start_request = StringName(); //clear start request
- ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ // stopped, invalid state
+ String node_name = travel_request;
+ travel_request = StringName();
+ ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?");
}
+ } else {
+ if (!_travel(p_state_machine, travel_request)) {
+ // can't travel, then teleport
+ if (p_state_machine->states.has(travel_request)) {
+ path.clear();
+ current = travel_request;
+ play_start = true;
+ reset_request = reset_request_on_teleport;
+ } else {
+ StringName node = travel_request;
+ travel_request = StringName();
+ ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ }
+ }
+ travel_request = StringName();
}
}
@@ -396,8 +431,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current = p_state_machine->start_node;
}
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true);
- pos_current = 0;
+ if (reset_request) {
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true);
+ pos_current = 0;
+ reset_request = false;
+ }
}
if (!p_state_machine->states.has(current)) {
@@ -421,7 +459,8 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (current_curve.is_valid()) {
fade_blend = current_curve->sample(fade_blend);
}
- double rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
+
+ double rem = do_start ? len_current : p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (fading_from != StringName()) {
double fade_blend_inv = 1.0 - fade_blend;
@@ -457,6 +496,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
next_xfade = p_state_machine->transitions[i].transition->get_xfade_time();
current_curve = p_state_machine->transitions[i].transition->get_xfade_curve();
switch_mode = p_state_machine->transitions[i].transition->get_switch_mode();
+ reset_request = p_state_machine->transitions[i].transition->is_reset();
next = path[0];
}
}
@@ -513,6 +553,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current_curve = p_state_machine->transitions[auto_advance_to].transition->get_xfade_curve();
next_xfade = p_state_machine->transitions[auto_advance_to].transition->get_xfade_time();
switch_mode = p_state_machine->transitions[auto_advance_to].transition->get_switch_mode();
+ reset_request = p_state_machine->transitions[auto_advance_to].transition->is_reset();
}
}
@@ -567,7 +608,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
goto_next = fading_from == StringName();
}
- if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped
+ if (next_request || goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped
if (next_xfade) {
//time to fade, baby
fading_from = current;
@@ -591,7 +632,9 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current = next;
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here.
+ if (reset_request) {
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here.
+ }
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
pos_current = MIN(pos_current, len_current);
p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
@@ -652,8 +695,9 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima
}
void AnimationNodeStateMachinePlayback::_bind_methods() {
- ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel);
- ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start);
+ ClassDB::bind_method(D_METHOD("travel", "to_node", "reset_on_teleport"), &AnimationNodeStateMachinePlayback::travel, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("start", "node", "reset"), &AnimationNodeStateMachinePlayback::start, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("next"), &AnimationNodeStateMachinePlayback::next);
ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop);
ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing);
ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node);
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 116589eb2f..8183d2025a 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -57,6 +57,7 @@ private:
StringName advance_condition_name;
float xfade_time = 0.0;
Ref<Curve> xfade_curve;
+ bool reset = true;
int priority = 1;
String advance_expression;
@@ -84,6 +85,9 @@ public:
void set_xfade_time(float p_xfade);
float get_xfade_time() const;
+ void set_reset(bool p_reset);
+ bool is_reset() const;
+
void set_xfade_curve(const Ref<Curve> &p_curve);
Ref<Curve> get_xfade_curve() const;
@@ -131,10 +135,15 @@ class AnimationNodeStateMachinePlayback : public Resource {
bool playing = false;
StringName start_request;
- bool start_request_travel = false;
+ StringName travel_request;
+ bool reset_request = false;
+ bool reset_request_on_teleport = false;
+ bool next_request = false;
bool stop_request = false;
bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel);
+ void _start(const StringName &p_state);
+ double _process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking);
double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking);
@@ -144,8 +153,9 @@ protected:
static void _bind_methods();
public:
- void travel(const StringName &p_state);
- void start(const StringName &p_state);
+ void travel(const StringName &p_state, bool p_reset_on_teleport = true);
+ void start(const StringName &p_state, bool p_reset = true);
+ void next();
void stop();
bool is_playing() const;
StringName get_current_node() const;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index ab341797c7..dd00897422 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -54,13 +54,19 @@ Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter
return ret;
}
+bool AnimationNode::is_parameter_read_only(const StringName &p_parameter) const {
+ bool ret = false;
+ GDVIRTUAL_CALL(_is_parameter_read_only, p_parameter, ret);
+ return ret;
+}
+
void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->tree->property_parent_map.has(base_path));
ERR_FAIL_COND(!state->tree->property_parent_map[base_path].has(p_name));
StringName path = state->tree->property_parent_map[base_path][p_name];
- state->tree->property_map[path] = p_value;
+ state->tree->property_map[path].first = p_value;
}
Variant AnimationNode::get_parameter(const StringName &p_name) const {
@@ -69,7 +75,7 @@ Variant AnimationNode::get_parameter(const StringName &p_name) const {
ERR_FAIL_COND_V(!state->tree->property_parent_map[base_path].has(p_name), Variant());
StringName path = state->tree->property_parent_map[base_path][p_name];
- return state->tree->property_map[path];
+ return state->tree->property_map[path].first;
}
void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
@@ -427,6 +433,7 @@ void AnimationNode::_bind_methods() {
GDVIRTUAL_BIND(_get_parameter_list);
GDVIRTUAL_BIND(_get_child_by_name, "name");
GDVIRTUAL_BIND(_get_parameter_default_value, "parameter");
+ GDVIRTUAL_BIND(_is_parameter_read_only, "parameter");
GDVIRTUAL_BIND(_process, "time", "seek", "is_external_seeking");
GDVIRTUAL_BIND(_get_caption);
GDVIRTUAL_BIND(_has_filter);
@@ -1889,7 +1896,10 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<A
StringName key = pinfo.name;
if (!property_map.has(p_base_path + key)) {
- property_map[p_base_path + key] = node->get_parameter_default_value(key);
+ Pair<Variant, bool> param;
+ param.first = node->get_parameter_default_value(key);
+ param.second = node->is_parameter_read_only(key);
+ property_map[p_base_path + key] = param;
}
property_parent_map[p_base_path][key] = p_base_path + key;
@@ -1931,7 +1941,10 @@ bool AnimationTree::_set(const StringName &p_name, const Variant &p_value) {
}
if (property_map.has(p_name)) {
- property_map[p_name] = p_value;
+ if (is_inside_tree() && property_map[p_name].second) {
+ return false; // Prevent to set property by user.
+ }
+ property_map[p_name].first = p_value;
return true;
}
@@ -1944,7 +1957,7 @@ bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const {
}
if (property_map.has(p_name)) {
- r_ret = property_map[p_name];
+ r_ret = property_map[p_name].first;
return true;
}
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 2c1be6199c..52d3e1bd41 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -117,6 +117,7 @@ protected:
GDVIRTUAL0RC(Array, _get_parameter_list)
GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName)
+ GDVIRTUAL1RC(bool, _is_parameter_read_only, StringName)
GDVIRTUAL3RC(double, _process, double, bool, bool)
GDVIRTUAL0RC(String, _get_caption)
GDVIRTUAL0RC(bool, _has_filter)
@@ -124,6 +125,7 @@ protected:
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const;
+ virtual bool is_parameter_read_only(const StringName &p_parameter) const;
void set_parameter(const StringName &p_name, const Variant &p_value);
Variant get_parameter(const StringName &p_name) const;
@@ -304,7 +306,7 @@ private:
void _update_properties();
List<PropertyInfo> properties;
HashMap<StringName, HashMap<StringName, StringName>> property_parent_map;
- HashMap<StringName, Variant> property_map;
+ HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
struct Activity {
uint64_t last_pass = 0;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 5930818763..fead0878fb 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1582,10 +1582,6 @@ void Control::set_block_minimum_size_adjust(bool p_block) {
data.block_minimum_size_adjust = p_block;
}
-bool Control::is_minimum_size_adjust_blocked() const {
- return data.block_minimum_size_adjust;
-}
-
Size2 Control::get_minimum_size() const {
Vector2 ms;
GDVIRTUAL_CALL(_get_minimum_size, ms);
@@ -2769,9 +2765,9 @@ void Control::end_bulk_theme_override() {
// Internationalization.
-TypedArray<Vector2i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
+TypedArray<Vector3i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) {
- TypedArray<Vector2i> ret;
+ TypedArray<Vector3i> ret;
GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret);
return ret;
} else {
diff --git a/scene/gui/control.h b/scene/gui/control.h
index aaab9f530c..a93a88e5b4 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -145,7 +145,7 @@ public:
TEXT_DIRECTION_AUTO = TextServer::DIRECTION_AUTO,
TEXT_DIRECTION_LTR = TextServer::DIRECTION_LTR,
TEXT_DIRECTION_RTL = TextServer::DIRECTION_RTL,
- TEXT_DIRECTION_INHERITED,
+ TEXT_DIRECTION_INHERITED = TextServer::DIRECTION_INHERITED,
};
private:
@@ -330,7 +330,7 @@ protected:
// Internationalization.
- virtual TypedArray<Vector2i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
+ virtual TypedArray<Vector3i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
// Base object overrides.
@@ -340,7 +340,7 @@ protected:
// Exposed virtual methods.
GDVIRTUAL1RC(bool, _has_point, Vector2)
- GDVIRTUAL2RC(TypedArray<Vector2i>, _structured_text_parser, Array, String)
+ GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String)
GDVIRTUAL0RC(Vector2, _get_minimum_size)
GDVIRTUAL1RC(Variant, _get_drag_data, Vector2)
@@ -464,7 +464,6 @@ public:
void update_minimum_size();
void set_block_minimum_size_adjust(bool p_block);
- bool is_minimum_size_adjust_blocked() const;
virtual Size2 get_minimum_size() const;
virtual Size2 get_combined_minimum_size() const;
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 372baeadae..25a27d5e1a 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -309,12 +309,6 @@ void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon) {
shape_changed = true;
}
-Ref<Texture2D> ItemList::get_item_tag_icon(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<Texture2D>());
-
- return items[p_idx].tag_icon;
-}
-
void ItemList::set_item_selectable(int p_idx, bool p_selectable) {
if (p_idx < 0) {
p_idx += get_item_count();
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 848f9a2ba9..934318dbb4 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -191,7 +191,6 @@ public:
Variant get_item_metadata(int p_idx) const;
void set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon);
- Ref<Texture2D> get_item_tag_icon(int p_idx) const;
void set_item_tooltip_enabled(int p_idx, const bool p_enabled);
bool is_item_tooltip_enabled(int p_idx) const;
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 5ab64b35fd..a7e50a765e 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -4074,8 +4074,8 @@ void RichTextLabel::append_text(const String &p_bbcode) {
st_parser_type = TextServer::STRUCTURED_TEXT_EMAIL;
} else if (subtag_a[1] == "l" || subtag_a[1] == "list") {
st_parser_type = TextServer::STRUCTURED_TEXT_LIST;
- } else if (subtag_a[1] == "n" || subtag_a[1] == "none") {
- st_parser_type = TextServer::STRUCTURED_TEXT_NONE;
+ } else if (subtag_a[1] == "n" || subtag_a[1] == "gdscript") {
+ st_parser_type = TextServer::STRUCTURED_TEXT_GDSCRIPT;
} else if (subtag_a[1] == "c" || subtag_a[1] == "custom") {
st_parser_type = TextServer::STRUCTURED_TEXT_CUSTOM;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 108a533a74..8ffaa9e81f 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1208,7 +1208,15 @@ void TextEdit::_notification(int p_what) {
char_ofs = 0;
}
for (int j = 0; j < gl_size; j++) {
- const Variant *color_data = color_map.getptr(glyphs[j].start);
+ int64_t color_start = -1;
+ for (const Variant *key = color_map.next(nullptr); key; key = color_map.next(key)) {
+ if (int64_t(*key) <= glyphs[j].start) {
+ color_start = *key;
+ } else {
+ break;
+ }
+ }
+ const Variant *color_data = (color_start >= 0) ? color_map.getptr(color_start) : nullptr;
if (color_data != nullptr) {
current_color = (color_data->operator Dictionary()).get("color", font_color);
if (!editable && current_color.a > font_readonly_color.a) {
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 2138f10ad0..2d985c2324 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -332,7 +332,7 @@ void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::Struc
}
TextServer::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const {
- ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_NONE);
+ ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_DEFAULT);
return cells[p_column].st_parser;
}
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 746f2f8f9b..8b4656414d 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -1043,6 +1043,18 @@ void Environment::_validate_property(PropertyInfo &p_property) const {
}
}
+ if (p_property.name == "ambient_light_color" || p_property.name == "ambient_light_energy") {
+ if (ambient_source == AMBIENT_SOURCE_DISABLED) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+ }
+
+ if (p_property.name == "ambient_light_sky_contribution") {
+ if (ambient_source == AMBIENT_SOURCE_DISABLED || ambient_source == AMBIENT_SOURCE_COLOR) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+ }
+
if (p_property.name == "fog_aerial_perspective") {
if (bg_mode != BG_SKY) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 5e18b5df37..cedf4319f8 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -687,6 +687,7 @@ void Mesh::_bind_methods() {
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY);
BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED);
BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE);
@@ -1555,6 +1556,7 @@ void ArrayMesh::_recompute_aabb() {
// TODO: Need to add binding to add_surface using future MeshSurfaceData object.
void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
+ ERR_FAIL_COND(surfaces.size() == RS::MAX_MESH_SURFACES);
_create_if_empty();
Surface s;
@@ -1590,6 +1592,7 @@ void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_prim
}
void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, BitField<ArrayFormat> p_flags) {
+ ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size());
ERR_FAIL_COND(p_arrays.size() != ARRAY_MAX);
RS::SurfaceData surface;
@@ -2058,7 +2061,7 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ArrayMesh::set_blend_shape_mode);
ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode);
- ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0));
ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces);
ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region);
ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region);
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 5ef66a22b6..86ed0001dd 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -2901,7 +2901,7 @@ void TextMesh::_create_mesh_array(Array &p_arr) const {
TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
}
- Array stt;
+ TypedArray<Vector3i> stt;
if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
} else {
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 22cd12b004..e62f26b17c 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -622,7 +622,7 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
- GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
+ GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String)
TextMesh();
~TextMesh();
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index c85c213c5d..2e8b4f93be 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -49,10 +49,6 @@
///
-void ResourceLoaderText::set_local_path(const String &p_local_path) {
- res_path = p_local_path;
-}
-
Ref<Resource> ResourceLoaderText::get_resource() {
return resource;
}
diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h
index 0f95e2fbfd..0cced3d20c 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -115,7 +115,6 @@ class ResourceLoaderText {
Ref<PackedScene> _parse_node_tag(VariantParser::ResourceParser &parser);
public:
- void set_local_path(const String &p_local_path);
Ref<Resource> get_resource();
Error load();
Error set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid);
diff --git a/servers/physics_3d/gjk_epa.cpp b/servers/physics_3d/gjk_epa.cpp
index 88f2040d17..e5678914fe 100644
--- a/servers/physics_3d/gjk_epa.cpp
+++ b/servers/physics_3d/gjk_epa.cpp
@@ -1011,9 +1011,11 @@ bool gjk_epa_calculate_penetration(const GodotShape3D *p_shape_A, const Transfor
if (GjkEpa2::Penetration(p_shape_A, p_transform_A, p_margin_A, p_shape_B, p_transform_B, p_margin_B, p_transform_B.origin - p_transform_A.origin, res)) {
if (p_result_callback) {
if (p_swap) {
- p_result_callback(res.witnesses[1], 0, res.witnesses[0], 0, p_userdata);
+ Vector3 normal = (res.witnesses[1] - res.witnesses[0]).normalized();
+ p_result_callback(res.witnesses[1], 0, res.witnesses[0], 0, normal, p_userdata);
} else {
- p_result_callback(res.witnesses[0], 0, res.witnesses[1], 0, p_userdata);
+ Vector3 normal = (res.witnesses[0] - res.witnesses[1]).normalized();
+ p_result_callback(res.witnesses[0], 0, res.witnesses[1], 0, normal, p_userdata);
}
}
return true;
diff --git a/servers/physics_3d/godot_body_pair_3d.cpp b/servers/physics_3d/godot_body_pair_3d.cpp
index ce3da390cb..619e6c00be 100644
--- a/servers/physics_3d/godot_body_pair_3d.cpp
+++ b/servers/physics_3d/godot_body_pair_3d.cpp
@@ -38,12 +38,12 @@
#define MIN_VELOCITY 0.0001
#define MAX_BIAS_ROTATION (Math_PI / 8)
-void GodotBodyPair3D::_contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata) {
+void GodotBodyPair3D::_contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata) {
GodotBodyPair3D *pair = static_cast<GodotBodyPair3D *>(p_userdata);
- pair->contact_added_callback(p_point_A, p_index_A, p_point_B, p_index_B);
+ pair->contact_added_callback(p_point_A, p_index_A, p_point_B, p_index_B, normal);
}
-void GodotBodyPair3D::contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B) {
+void GodotBodyPair3D::contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal) {
Vector3 local_A = A->get_inv_transform().basis.xform(p_point_A);
Vector3 local_B = B->get_inv_transform().basis.xform(p_point_B - offset_B);
@@ -577,12 +577,12 @@ GodotBodyPair3D::~GodotBodyPair3D() {
B->remove_constraint(this);
}
-void GodotBodySoftBodyPair3D::_contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata) {
+void GodotBodySoftBodyPair3D::_contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata) {
GodotBodySoftBodyPair3D *pair = static_cast<GodotBodySoftBodyPair3D *>(p_userdata);
- pair->contact_added_callback(p_point_A, p_index_A, p_point_B, p_index_B);
+ pair->contact_added_callback(p_point_A, p_index_A, p_point_B, p_index_B, normal);
}
-void GodotBodySoftBodyPair3D::contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B) {
+void GodotBodySoftBodyPair3D::contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal) {
Vector3 local_A = body->get_inv_transform().xform(p_point_A);
Vector3 local_B = p_point_B - soft_body->get_node_position(p_index_B);
@@ -591,7 +591,7 @@ void GodotBodySoftBodyPair3D::contact_added_callback(const Vector3 &p_point_A, i
contact.index_B = p_index_B;
contact.local_A = local_A;
contact.local_B = local_B;
- contact.normal = (p_point_A - p_point_B).normalized();
+ contact.normal = (normal.dot((p_point_A - p_point_B)) < 0 ? -normal : normal);
contact.used = true;
// Attempt to determine if the contact will be reused.
diff --git a/servers/physics_3d/godot_body_pair_3d.h b/servers/physics_3d/godot_body_pair_3d.h
index c3165c7fcf..a8f5180dd5 100644
--- a/servers/physics_3d/godot_body_pair_3d.h
+++ b/servers/physics_3d/godot_body_pair_3d.h
@@ -97,9 +97,9 @@ class GodotBodyPair3D : public GodotBodyContact3D {
Contact contacts[MAX_CONTACTS];
int contact_count = 0;
- static void _contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata);
+ static void _contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata);
- void contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B);
+ void contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal);
void validate_contacts();
bool _test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, const Transform3D &p_xform_A, GodotBody3D *p_B, int p_shape_B, const Transform3D &p_xform_B);
@@ -126,9 +126,9 @@ class GodotBodySoftBodyPair3D : public GodotBodyContact3D {
LocalVector<Contact> contacts;
- static void _contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata);
+ static void _contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata);
- void contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B);
+ void contact_added_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal);
void validate_contacts();
diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/servers/physics_3d/godot_collision_solver_3d.cpp
index 0b92b745fe..fb5a67c008 100644
--- a/servers/physics_3d/godot_collision_solver_3d.cpp
+++ b/servers/physics_3d/godot_collision_solver_3d.cpp
@@ -81,9 +81,11 @@ bool GodotCollisionSolver3D::solve_static_world_boundary(const GodotShape3D *p_s
if (p_result_callback) {
if (p_swap_result) {
- p_result_callback(supports[i], 0, support_A, 0, p_userdata);
+ Vector3 normal = (support_A - supports[i]).normalized();
+ p_result_callback(supports[i], 0, support_A, 0, normal, p_userdata);
} else {
- p_result_callback(support_A, 0, supports[i], 0, p_userdata);
+ Vector3 normal = (supports[i] - support_A).normalized();
+ p_result_callback(support_A, 0, supports[i], 0, normal, p_userdata);
}
}
}
@@ -126,9 +128,11 @@ bool GodotCollisionSolver3D::solve_separation_ray(const GodotShape3D *p_shape_A,
if (p_result_callback) {
if (p_swap_result) {
- p_result_callback(support_B, 0, support_A, 0, p_userdata);
+ Vector3 normal = (support_B - support_A).normalized();
+ p_result_callback(support_B, 0, support_A, 0, normal, p_userdata);
} else {
- p_result_callback(support_A, 0, support_B, 0, p_userdata);
+ Vector3 normal = (support_A - support_B).normalized();
+ p_result_callback(support_A, 0, support_B, 0, normal, p_userdata);
}
}
return true;
@@ -142,7 +146,7 @@ struct _SoftBodyContactCollisionInfo {
int contact_count = 0;
};
-void GodotCollisionSolver3D::soft_body_contact_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata) {
+void GodotCollisionSolver3D::soft_body_contact_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata) {
_SoftBodyContactCollisionInfo &cinfo = *(static_cast<_SoftBodyContactCollisionInfo *>(p_userdata));
++cinfo.contact_count;
@@ -152,9 +156,9 @@ void GodotCollisionSolver3D::soft_body_contact_callback(const Vector3 &p_point_A
}
if (cinfo.swap_result) {
- cinfo.result_callback(p_point_B, cinfo.node_index, p_point_A, p_index_A, cinfo.userdata);
+ cinfo.result_callback(p_point_B, cinfo.node_index, p_point_A, p_index_A, -normal, cinfo.userdata);
} else {
- cinfo.result_callback(p_point_A, p_index_A, p_point_B, cinfo.node_index, cinfo.userdata);
+ cinfo.result_callback(p_point_A, p_index_A, p_point_B, cinfo.node_index, normal, cinfo.userdata);
}
}
diff --git a/servers/physics_3d/godot_collision_solver_3d.h b/servers/physics_3d/godot_collision_solver_3d.h
index 7ef0dc97ac..36ea79576e 100644
--- a/servers/physics_3d/godot_collision_solver_3d.h
+++ b/servers/physics_3d/godot_collision_solver_3d.h
@@ -35,11 +35,11 @@
class GodotCollisionSolver3D {
public:
- typedef void (*CallbackResult)(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata);
+ typedef void (*CallbackResult)(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata);
private:
static bool soft_body_query_callback(uint32_t p_node_index, void *p_userdata);
- static void soft_body_contact_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata);
+ static void soft_body_contact_callback(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata);
static bool soft_body_concave_callback(void *p_userdata, GodotShape3D *p_convex);
static bool concave_callback(void *p_userdata, GodotShape3D *p_convex);
static bool solve_static_world_boundary(const GodotShape3D *p_shape_A, const Transform3D &p_transform_A, const GodotShape3D *p_shape_B, const Transform3D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin = 0);
diff --git a/servers/physics_3d/godot_collision_solver_3d_sat.cpp b/servers/physics_3d/godot_collision_solver_3d_sat.cpp
index d13f4ee801..66d1811abb 100644
--- a/servers/physics_3d/godot_collision_solver_3d_sat.cpp
+++ b/servers/physics_3d/godot_collision_solver_3d_sat.cpp
@@ -75,11 +75,13 @@ struct _CollectorCallback {
Vector3 normal;
Vector3 *prev_axis = nullptr;
- _FORCE_INLINE_ void call(const Vector3 &p_point_A, const Vector3 &p_point_B) {
+ _FORCE_INLINE_ void call(const Vector3 &p_point_A, const Vector3 &p_point_B, Vector3 p_normal) {
+ if (p_normal.dot(p_point_B - p_point_A) < 0)
+ p_normal = -p_normal;
if (swap) {
- callback(p_point_B, 0, p_point_A, 0, userdata);
+ callback(p_point_B, 0, p_point_A, 0, -p_normal, userdata);
} else {
- callback(p_point_A, 0, p_point_B, 0, userdata);
+ callback(p_point_A, 0, p_point_B, 0, p_normal, userdata);
}
}
};
@@ -92,7 +94,7 @@ static void _generate_contacts_point_point(const Vector3 *p_points_A, int p_poin
ERR_FAIL_COND(p_point_count_B != 1);
#endif
- p_callback->call(*p_points_A, *p_points_B);
+ p_callback->call(*p_points_A, *p_points_B, p_callback->normal);
}
static void _generate_contacts_point_edge(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {
@@ -102,7 +104,7 @@ static void _generate_contacts_point_edge(const Vector3 *p_points_A, int p_point
#endif
Vector3 closest_B = Geometry3D::get_closest_point_to_segment_uncapped(*p_points_A, p_points_B);
- p_callback->call(*p_points_A, closest_B);
+ p_callback->call(*p_points_A, closest_B, p_callback->normal);
}
static void _generate_contacts_point_face(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {
@@ -111,9 +113,9 @@ static void _generate_contacts_point_face(const Vector3 *p_points_A, int p_point
ERR_FAIL_COND(p_point_count_B < 3);
#endif
- Vector3 closest_B = Plane(p_points_B[0], p_points_B[1], p_points_B[2]).project(*p_points_A);
-
- p_callback->call(*p_points_A, closest_B);
+ Plane plane(p_points_B[0], p_points_B[1], p_points_B[2]);
+ Vector3 closest_B = plane.project(*p_points_A);
+ p_callback->call(*p_points_A, closest_B, plane.get_normal());
}
static void _generate_contacts_point_circle(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {
@@ -122,9 +124,9 @@ static void _generate_contacts_point_circle(const Vector3 *p_points_A, int p_poi
ERR_FAIL_COND(p_point_count_B != 3);
#endif
- Vector3 closest_B = Plane(p_points_B[0], p_points_B[1], p_points_B[2]).project(*p_points_A);
-
- p_callback->call(*p_points_A, closest_B);
+ Plane plane(p_points_B[0], p_points_B[1], p_points_B[2]);
+ Vector3 closest_B = plane.project(*p_points_A);
+ p_callback->call(*p_points_A, closest_B, plane.get_normal());
}
static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {
@@ -154,8 +156,8 @@ static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_
sa.sort(dvec, 4);
//use the middle ones as contacts
- p_callback->call(base_A + axis * dvec[1], base_B + axis * dvec[1]);
- p_callback->call(base_A + axis * dvec[2], base_B + axis * dvec[2]);
+ p_callback->call(base_A + axis * dvec[1], base_B + axis * dvec[1], p_callback->normal);
+ p_callback->call(base_A + axis * dvec[2], base_B + axis * dvec[2], p_callback->normal);
return;
}
@@ -170,7 +172,14 @@ static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_
Vector3 closest_A = p_points_A[0] + rel_A * d;
Vector3 closest_B = Geometry3D::get_closest_point_to_segment_uncapped(closest_A, p_points_B);
- p_callback->call(closest_A, closest_B);
+ // The normal should be perpendicular to both edges.
+ Vector3 normal = rel_A.cross(rel_B);
+ real_t normal_len = normal.length();
+ if (normal_len > 1e-3)
+ normal /= normal_len;
+ else
+ normal = p_callback->normal;
+ p_callback->call(closest_A, closest_B, normal);
}
static void _generate_contacts_edge_circle(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {
@@ -267,7 +276,7 @@ static void _generate_contacts_edge_circle(const Vector3 *p_points_A, int p_poin
continue;
}
- p_callback->call(contact_point_A, closest_B);
+ p_callback->call(contact_point_A, closest_B, circle_plane.get_normal());
}
}
@@ -352,7 +361,7 @@ static void _generate_contacts_face_face(const Vector3 *p_points_A, int p_point_
continue;
}
- p_callback->call(clipbuf_src[i], closest_B);
+ p_callback->call(clipbuf_src[i], closest_B, plane_B.get_normal());
}
}
@@ -431,7 +440,7 @@ static void _generate_contacts_face_circle(const Vector3 *p_points_A, int p_poin
continue;
}
- p_callback->call(contact_point_A, closest_B);
+ p_callback->call(contact_point_A, closest_B, circle_plane.get_normal());
}
}
@@ -534,7 +543,7 @@ static void _generate_contacts_circle_circle(const Vector3 *p_points_A, int p_po
continue;
}
- p_callback->call(contact_point_A, closest_B);
+ p_callback->call(contact_point_A, closest_B, circle_B_plane.get_normal());
}
}
@@ -678,7 +687,7 @@ public:
return true;
}
- static _FORCE_INLINE_ void test_contact_points(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata) {
+ static _FORCE_INLINE_ void test_contact_points(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata) {
SeparatorAxisTest<ShapeA, ShapeB, withMargin> *separator = (SeparatorAxisTest<ShapeA, ShapeB, withMargin> *)p_userdata;
Vector3 axis = (p_point_B - p_point_A);
real_t depth = axis.length();
@@ -802,11 +811,11 @@ static void analytic_sphere_collision(const Vector3 &p_origin_a, real_t p_radius
if (p_radius_a < p_radius_b) {
Vector3 point_a = p_origin_a - b_to_a * p_radius_a;
Vector3 point_b = point_a + b_to_a * overlap;
- p_collector->call(point_a, point_b); // Consider adding b_to_a vector
+ p_collector->call(point_a, point_b, b_to_a); // Consider adding b_to_a vector
} else {
Vector3 point_b = p_origin_b + b_to_a * p_radius_b;
Vector3 point_a = point_b - b_to_a * overlap;
- p_collector->call(point_a, point_b); // Consider adding b_to_a vector
+ p_collector->call(point_a, point_b, b_to_a); // Consider adding b_to_a vector
}
}
@@ -859,8 +868,8 @@ static void _collision_sphere_box(const GodotShape3D *p_a, const Transform3D &p_
axis = delta / length;
}
Vector3 point_a = p_transform_a.origin + (sphere_A->get_radius() + p_margin_a) * axis;
- Vector3 point_b = (withMargin ? nearest + p_margin_b * axis : nearest);
- p_collector->call(point_a, point_b);
+ Vector3 point_b = (withMargin ? nearest - p_margin_b * axis : nearest);
+ p_collector->call(point_a, point_b, axis);
}
template <bool withMargin>
@@ -926,8 +935,8 @@ static void analytic_sphere_cylinder_collision(real_t p_radius_a, real_t p_radiu
axis = delta / length;
}
Vector3 point_a = p_transform_a.origin + (p_radius_a + p_margin_a) * axis;
- Vector3 point_b = (withMargin ? nearest + p_margin_b * axis : nearest);
- p_collector->call(point_a, point_b);
+ Vector3 point_b = (withMargin ? nearest - p_margin_b * axis : nearest);
+ p_collector->call(point_a, point_b, axis);
}
template <bool withMargin>
diff --git a/servers/physics_3d/godot_physics_server_3d.cpp b/servers/physics_3d/godot_physics_server_3d.cpp
index e8250acb45..b6d8acfbf3 100644
--- a/servers/physics_3d/godot_physics_server_3d.cpp
+++ b/servers/physics_3d/godot_physics_server_3d.cpp
@@ -1737,7 +1737,7 @@ void GodotPhysicsServer3D::_update_shapes() {
}
}
-void GodotPhysicsServer3D::_shape_col_cbk(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata) {
+void GodotPhysicsServer3D::_shape_col_cbk(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata) {
CollCbkData *cbk = static_cast<CollCbkData *>(p_userdata);
if (cbk->max == 0) {
diff --git a/servers/physics_3d/godot_physics_server_3d.h b/servers/physics_3d/godot_physics_server_3d.h
index 3da0c6debe..040e673dcd 100644
--- a/servers/physics_3d/godot_physics_server_3d.h
+++ b/servers/physics_3d/godot_physics_server_3d.h
@@ -77,7 +77,7 @@ public:
Vector3 *ptr = nullptr;
};
- static void _shape_col_cbk(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata);
+ static void _shape_col_cbk(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata);
virtual RID world_boundary_shape_create() override;
virtual RID separation_ray_shape_create() override;
diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp
index c3aad22932..93572965d2 100644
--- a/servers/physics_3d/godot_space_3d.cpp
+++ b/servers/physics_3d/godot_space_3d.cpp
@@ -445,7 +445,7 @@ struct _RestCallbackData {
_RestResultData *other_results = nullptr;
};
-static void _rest_cbk_result(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, void *p_userdata) {
+static void _rest_cbk_result(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata) {
_RestCallbackData *rd = static_cast<_RestCallbackData *>(p_userdata);
Vector3 contact_rel = p_point_B - p_point_A;
@@ -480,7 +480,7 @@ static void _rest_cbk_result(const Vector3 &p_point_A, int p_index_A, const Vect
// Keep this result as separate result.
result.len = len;
result.contact = p_point_B;
- result.normal = contact_rel / len;
+ result.normal = normal;
result.object = rd->object;
result.shape = rd->shape;
result.local_shape = rd->local_shape;
@@ -499,7 +499,7 @@ static void _rest_cbk_result(const Vector3 &p_point_A, int p_index_A, const Vect
rd->best_result.len = len;
rd->best_result.contact = p_point_B;
- rd->best_result.normal = contact_rel / len;
+ rd->best_result.normal = normal;
rd->best_result.object = rd->object;
rd->best_result.shape = rd->shape;
rd->best_result.local_shape = rd->local_shape;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index e24b2af976..c3bd3d277f 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -996,6 +996,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
if (index_array_len) {
List<Variant> keys;
p_lods.get_key_list(&keys);
+ keys.sort(); // otherwise lod levels may get skipped
for (const Variant &E : keys) {
float distance = E;
ERR_CONTINUE(distance <= 0.0);
@@ -1826,6 +1827,7 @@ void RenderingServer::_bind_methods() {
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
+ BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY);
BIND_ENUM_CONSTANT(PRIMITIVE_POINTS);
BIND_ENUM_CONSTANT(PRIMITIVE_LINES);
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index 997b83e32d..cbf37f25d6 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -1373,8 +1373,8 @@ String TextServerExtension::string_to_lower(const String &p_string, const String
return p_string;
}
-TypedArray<Vector2i> TextServerExtension::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
- TypedArray<Vector2i> ret;
+TypedArray<Vector3i> TextServerExtension::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
+ TypedArray<Vector3i> ret;
GDVIRTUAL_CALL(_parse_structured_text, p_parser_type, p_args, p_text, ret);
return ret;
}
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index fb784f5471..8536836983 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -521,8 +521,8 @@ public:
GDVIRTUAL2RC(String, _string_to_upper, const String &, const String &);
GDVIRTUAL2RC(String, _string_to_lower, const String &, const String &);
- TypedArray<Vector2i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
- GDVIRTUAL3RC(TypedArray<Vector2i>, _parse_structured_text, StructuredTextParser, const Array &, const String &);
+ TypedArray<Vector3i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
+ GDVIRTUAL3RC(TypedArray<Vector3i>, _parse_structured_text, StructuredTextParser, const Array &, const String &);
virtual int64_t is_confusable(const String &p_string, const PackedStringArray &p_dict) const override;
virtual bool spoof_check(const String &p_string) const override;
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index d339533688..027109b67d 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -483,6 +483,7 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(DIRECTION_AUTO);
BIND_ENUM_CONSTANT(DIRECTION_LTR);
BIND_ENUM_CONSTANT(DIRECTION_RTL);
+ BIND_ENUM_CONSTANT(DIRECTION_INHERITED);
/* Orientation */
BIND_ENUM_CONSTANT(ORIENTATION_HORIZONTAL);
@@ -599,7 +600,7 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_FILE);
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_EMAIL);
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST);
- BIND_ENUM_CONSTANT(STRUCTURED_TEXT_NONE);
+ BIND_ENUM_CONSTANT(STRUCTURED_TEXT_GDSCRIPT);
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM);
}
@@ -1692,22 +1693,22 @@ String TextServer::strip_diacritics(const String &p_string) const {
return result;
}
-TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
- TypedArray<Vector2i> ret;
+TypedArray<Vector3i> TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
+ TypedArray<Vector3i> ret;
switch (p_parser_type) {
case STRUCTURED_TEXT_URI: {
int prev = 0;
for (int i = 0; i < p_text.length(); i++) {
if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == '.') || (p_text[i] == ':') || (p_text[i] == '&') || (p_text[i] == '=') || (p_text[i] == '@') || (p_text[i] == '?') || (p_text[i] == '#')) {
if (prev != i) {
- ret.push_back(Vector2i(prev, i));
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
- ret.push_back(Vector2i(i, i + 1));
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
prev = i + 1;
}
}
if (prev != p_text.length()) {
- ret.push_back(Vector2i(prev, p_text.length()));
+ ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
}
} break;
case STRUCTURED_TEXT_FILE: {
@@ -1715,14 +1716,14 @@ TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_pa
for (int i = 0; i < p_text.length(); i++) {
if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == ':')) {
if (prev != i) {
- ret.push_back(Vector2i(prev, i));
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
- ret.push_back(Vector2i(i, i + 1));
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
prev = i + 1;
}
}
if (prev != p_text.length()) {
- ret.push_back(Vector2i(prev, p_text.length()));
+ ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
}
} break;
case STRUCTURED_TEXT_EMAIL: {
@@ -1731,19 +1732,19 @@ TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_pa
for (int i = 0; i < p_text.length(); i++) {
if ((p_text[i] == '@') && local) { // Add full "local" as single context.
local = false;
- ret.push_back(Vector2i(prev, i));
- ret.push_back(Vector2i(i, i + 1));
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
prev = i + 1;
} else if (!local && (p_text[i] == '.')) { // Add each dot separated "domain" part as context.
if (prev != i) {
- ret.push_back(Vector2i(prev, i));
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
- ret.push_back(Vector2i(i, i + 1));
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
prev = i + 1;
}
}
if (prev != p_text.length()) {
- ret.push_back(Vector2i(prev, p_text.length()));
+ ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
}
} break;
case STRUCTURED_TEXT_LIST: {
@@ -1752,18 +1753,97 @@ TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_pa
int prev = 0;
for (int i = 0; i < tags.size(); i++) {
if (prev != i) {
- ret.push_back(Vector2i(prev, prev + tags[i].length()));
+ ret.push_back(Vector3i(prev, prev + tags[i].length(), TextServer::DIRECTION_INHERITED));
}
- ret.push_back(Vector2i(prev + tags[i].length(), prev + tags[i].length() + 1));
+ ret.push_back(Vector3i(prev + tags[i].length(), prev + tags[i].length() + 1, TextServer::DIRECTION_INHERITED));
prev = prev + tags[i].length() + 1;
}
}
} break;
+ case STRUCTURED_TEXT_GDSCRIPT: {
+ bool in_string_literal = false;
+ bool in_string_literal_single = false;
+ bool in_id = false;
+
+ int prev = 0;
+ for (int i = 0; i < p_text.length(); i++) {
+ char32_t c = p_text[i];
+ if (in_string_literal) {
+ if (c == '\\') {
+ i++;
+ continue; // Skip escaped chars.
+ } else if (c == '\"') {
+ // String literal end, push string and ".
+ if (prev != i) {
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
+ }
+ prev = i + 1;
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
+ in_string_literal = false;
+ }
+ } else if (in_string_literal_single) {
+ if (c == '\\') {
+ i++;
+ continue; // Skip escaped chars.
+ } else if (c == '\'') {
+ // String literal end, push string and '.
+ if (prev != i) {
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
+ }
+ prev = i + 1;
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
+ in_string_literal_single = false;
+ }
+ } else if (in_id) {
+ if (!is_unicode_identifier_continue(c)) {
+ // End of id, push id.
+ if (prev != i) {
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
+ }
+ prev = i;
+ in_id = false;
+ }
+ } else if (is_unicode_identifier_start(c)) {
+ // Start of new id, push prev element.
+ if (prev != i) {
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
+ }
+ prev = i;
+ in_id = true;
+ } else if (c == '\"') {
+ // String literal start, push prev element and ".
+ if (prev != i) {
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
+ }
+ prev = i + 1;
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
+ in_string_literal = true;
+ } else if (c == '\'') {
+ // String literal start, push prev element and '.
+ if (prev != i) {
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
+ }
+ prev = i + 1;
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
+ in_string_literal_single = true;
+ } else if (c == '#') {
+ // Start of comment, push prev element and #, skip the rest of the text.
+ if (prev != i) {
+ ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
+ }
+ prev = i + 1;
+ ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
+ break;
+ }
+ }
+ if (prev < p_text.length()) {
+ ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
+ }
+ } break;
case STRUCTURED_TEXT_CUSTOM:
- case STRUCTURED_TEXT_NONE:
case STRUCTURED_TEXT_DEFAULT:
default: {
- ret.push_back(Vector2i(0, p_text.length()));
+ ret.push_back(Vector3i(0, p_text.length(), TextServer::DIRECTION_INHERITED));
}
}
return ret;
diff --git a/servers/text_server.h b/servers/text_server.h
index a56c7d8b23..a91d367e97 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -65,7 +65,8 @@ public:
enum Direction {
DIRECTION_AUTO,
DIRECTION_LTR,
- DIRECTION_RTL
+ DIRECTION_RTL,
+ DIRECTION_INHERITED,
};
enum Orientation {
@@ -198,7 +199,7 @@ public:
STRUCTURED_TEXT_FILE,
STRUCTURED_TEXT_EMAIL,
STRUCTURED_TEXT_LIST,
- STRUCTURED_TEXT_NONE,
+ STRUCTURED_TEXT_GDSCRIPT,
STRUCTURED_TEXT_CUSTOM
};
@@ -505,7 +506,7 @@ public:
virtual String string_to_upper(const String &p_string, const String &p_language = "") const = 0;
virtual String string_to_lower(const String &p_string, const String &p_language = "") const = 0;
- TypedArray<Vector2i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
+ TypedArray<Vector3i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
virtual void cleanup() {}
diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h
index 4d9feeb4fa..b2a2ecc3bf 100644
--- a/tests/scene/test_arraymesh.h
+++ b/tests/scene/test_arraymesh.h
@@ -114,6 +114,17 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
CHECK(mesh->get_blend_shape_count() == 0);
}
+ SUBCASE("Can't add surface with incorrect number of blend shapes.") {
+ mesh->add_blend_shape(name_a);
+ mesh->add_blend_shape(name_b);
+ Ref<CylinderMesh> cylinder = memnew(CylinderMesh);
+ Array cylinder_array{};
+ ERR_PRINT_OFF
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
+ ERR_PRINT_ON
+ CHECK(mesh->get_surface_count() == 0);
+ }
+
SUBCASE("Can't clear blend shapes after surface had been added.") {
mesh->add_blend_shape(name_a);
mesh->add_blend_shape(name_b);
@@ -121,7 +132,15 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
Array cylinder_array{};
cylinder_array.resize(Mesh::ARRAY_MAX);
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
- mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
+ Array blend_shape{};
+ blend_shape.resize(Mesh::ARRAY_MAX);
+ blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX];
+ blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL];
+ blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT];
+ Array blend_shapes{};
+ blend_shapes.push_back(blend_shape);
+ blend_shapes.push_back(blend_shape);
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes);
ERR_PRINT_OFF
mesh->clear_blend_shapes();
diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h
index 6cdb5fb0a5..9232a3020d 100644
--- a/tests/scene/test_primitives.h
+++ b/tests/scene/test_primitives.h
@@ -734,7 +734,7 @@ TEST_CASE("[SceneTree][Primitive][Text] Text Primitive") {
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_FILE ||
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_EMAIL ||
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_LIST ||
- text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_NONE ||
+ text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_GDSCRIPT ||
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_CUSTOM));
CHECK(text->get_structured_text_bidi_override_options().size() >= 0);
CHECK(text->get_width() > 0);