diff options
Diffstat (limited to 'core')
40 files changed, 1023 insertions, 197 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 74c06123e1..6275502378 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -41,7 +41,10 @@ #include "core/os/keyboard.h" #include "core/variant/variant_parser.h" #include "core/version.h" + +#ifdef TOOLS_ENABLED #include "modules/modules_enabled.gen.h" // For mono. +#endif // TOOLS_ENABLED const String ProjectSettings::PROJECT_DATA_DIR_NAME_SUFFIX = "godot"; @@ -75,6 +78,7 @@ String ProjectSettings::get_imported_files_path() const { return get_project_data_path().path_join("imported"); } +#ifdef TOOLS_ENABLED // Returns the features that a project must have when opened with this build of Godot. // This is used by the project manager to provide the initial_settings for config/features. const PackedStringArray ProjectSettings::get_required_features() { @@ -137,6 +141,7 @@ const PackedStringArray ProjectSettings::_trim_to_supported_features(const Packe features.sort(); return features; } +#endif // TOOLS_ENABLED String ProjectSettings::localize_path(const String &p_path) const { if (resource_path.is_empty() || p_path.begins_with("res://") || p_path.begins_with("user://") || @@ -897,6 +902,7 @@ Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other par Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) { ERR_FAIL_COND_V_MSG(p_path.is_empty(), ERR_INVALID_PARAMETER, "Project settings save path cannot be empty."); +#ifdef TOOLS_ENABLED PackedStringArray project_features = get_setting("application/config/features"); // If there is no feature list currently present, force one to generate. if (project_features.is_empty()) { @@ -924,6 +930,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } project_features = _trim_to_supported_features(project_features); set_setting("application/config/features", project_features); +#endif // TOOLS_ENABLED RBSet<_VCSort> vclist; diff --git a/core/config/project_settings.h b/core/config/project_settings.h index c845120a26..a9af63ee39 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -48,8 +48,10 @@ public: //properties that are not for built in values begin from this value, so builtin ones are displayed first NO_BUILTIN_ORDER_BASE = 1 << 16 }; +#ifdef TOOLS_ENABLED const static PackedStringArray get_required_features(); const static PackedStringArray get_unsupported_features(const PackedStringArray &p_project_features); +#endif // TOOLS_ENABLED struct AutoloadInfo { StringName name; @@ -116,8 +118,10 @@ protected: Error _save_custom_bnd(const String &p_file); +#ifdef TOOLS_ENABLED const static PackedStringArray _get_supported_features(); const static PackedStringArray _trim_to_supported_features(const PackedStringArray &p_project_features); +#endif // TOOLS_ENABLED void _convert_to_last_version(int p_from_version); diff --git a/core/input/input.cpp b/core/input/input.cpp index 4e538a85ae..1390a0a7fa 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -169,11 +169,10 @@ void Input::_bind_methods() { void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { String pf = p_function; - if (p_idx == 0 && - (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || - pf == "is_action_just_pressed" || pf == "is_action_just_released" || - pf == "get_action_strength" || pf == "get_action_raw_strength" || - pf == "get_axis" || pf == "get_vector")) { + + if ((p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength" || pf == "get_action_raw_strength")) || + (p_idx < 2 && pf == "get_axis") || + (p_idx < 4 && pf == "get_vector")) { List<PropertyInfo> pinfo; ProjectSettings::get_singleton()->get_property_list(&pinfo); diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index ae421654ca..f84a95347a 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -32,6 +32,7 @@ #include "core/io/file_access_encrypted.h" #include "core/os/keyboard.h" +#include "core/string/string_builder.h" #include "core/variant/variant_parser.h" PackedStringArray ConfigFile::_get_sections() const { @@ -130,6 +131,28 @@ void ConfigFile::erase_section_key(const String &p_section, const String &p_key) } } +String ConfigFile::encode_to_text() const { + StringBuilder sb; + bool first = true; + for (const KeyValue<String, HashMap<String, Variant>> &E : values) { + if (first) { + first = false; + } else { + sb.append("\n"); + } + if (!E.key.is_empty()) { + sb.append("[" + E.key + "]\n\n"); + } + + for (const KeyValue<String, Variant> &F : E.value) { + String vstr; + VariantWriter::write_to_string(F.value, vstr); + sb.append(F.key.property_name_encode() + "=" + vstr + "\n"); + } + } + return sb.as_string(); +} + Error ConfigFile::save(const String &p_path) { Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); @@ -295,6 +318,7 @@ Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) void ConfigFile::clear() { values.clear(); } + void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("set_value", "section", "key", "value"), &ConfigFile::set_value); ClassDB::bind_method(D_METHOD("get_value", "section", "key", "default"), &ConfigFile::get_value, DEFVAL(Variant())); @@ -312,6 +336,8 @@ void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse); ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save); + ClassDB::bind_method(D_METHOD("encode_to_text"), &ConfigFile::encode_to_text); + BIND_METHOD_ERR_RETURN_DOC("load", ERR_FILE_CANT_OPEN); ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted); diff --git a/core/io/config_file.h b/core/io/config_file.h index 3b07ec52f5..f6209492b7 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -68,6 +68,8 @@ public: Error load(const String &p_path); Error parse(const String &p_data); + String encode_to_text() const; // used by exporter + void clear(); Error load_encrypted(const String &p_path, const Vector<uint8_t> &p_key); diff --git a/core/io/dir_access.h b/core/io/dir_access.h index d5318dfb45..2469c2a080 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -55,7 +55,7 @@ private: protected: String _get_root_path() const; - String _get_root_string() const; + virtual String _get_root_string() const; AccessType get_access_type() const; String fix_path(String p_path) const; diff --git a/core/io/image.cpp b/core/io/image.cpp index 2d87523ca4..dee751eec5 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -416,8 +416,8 @@ int Image::get_height() const { return height; } -Vector2i Image::get_size() const { - return Vector2i(width, height); +Size2i Image::get_size() const { + return Size2i(width, height); } bool Image::has_mipmaps() const { diff --git a/core/io/image.h b/core/io/image.h index 9d415423be..fd264a7a38 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -209,7 +209,7 @@ private: public: int get_width() const; ///< Get image width int get_height() const; ///< Get image height - Vector2i get_size() const; + Size2i get_size() const; bool has_mipmaps() const; int get_mipmap_count() const; diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 2f69c10218..b24c49f58d 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -510,7 +510,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += sizeof(double) * 16; } } else { - ERR_FAIL_COND_V((size_t)len < sizeof(float) * 62, ERR_INVALID_DATA); + ERR_FAIL_COND_V((size_t)len < sizeof(float) * 16, ERR_INVALID_DATA); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { val.matrix[i][j] = decode_float(&buf[(i * 4 + j) * sizeof(float)]); diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 0af236f766..1df779d034 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -152,42 +152,24 @@ void PacketPeer::_bind_methods() { /***************/ -int PacketPeerExtension::get_available_packet_count() const { - int count; - if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { - return count; - } - WARN_PRINT_ONCE("PacketPeerExtension::_get_available_packet_count is unimplemented!"); - return -1; -} - Error PacketPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("PacketPeerExtension::_get_packet_native is unimplemented!"); return FAILED; } Error PacketPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("PacketPeerExtension::_put_packet_native is unimplemented!"); return FAILED; } -int PacketPeerExtension::get_max_packet_size() const { - int size; - if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { - return size; - } - WARN_PRINT_ONCE("PacketPeerExtension::_get_max_packet_size is unimplemented!"); - return 0; -} - void PacketPeerExtension::_bind_methods() { GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index ec9d33aa5a..07045e62a6 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -35,6 +35,7 @@ #include "core/object/class_db.h" #include "core/templates/ring_buffer.h" +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" #include "core/object/script_language.h" #include "core/variant/native_ptr.h" @@ -84,16 +85,14 @@ protected: static void _bind_methods(); public: - virtual int get_available_packet_count() const override; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + GDVIRTUAL2R(Error, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const override; + GDVIRTUAL2R(Error, _put_packet, GDNativeConstPtr<const uint8_t>, int); - /* GDExtension */ - GDVIRTUAL0RC(int, _get_available_packet_count); - GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); - GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); - GDVIRTUAL0RC(int, _get_max_packet_size); + EXBIND0RC(int, get_available_packet_count); + EXBIND0RC(int, get_max_packet_size); }; class PacketPeerStream : public PacketPeer { diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index c65968ef03..053ff64069 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -410,48 +410,39 @@ void StreamPeer::_bind_methods() { //////////////////////////////// -int StreamPeerExtension::get_available_bytes() const { - int count; - if (GDVIRTUAL_CALL(_get_available_bytes, count)) { - return count; - } - WARN_PRINT_ONCE("StreamPeerExtension::_get_available_bytes is unimplemented!"); - return -1; -} - Error StreamPeerExtension::get_data(uint8_t *r_buffer, int p_bytes) { - int err; + Error err; int received = 0; if (GDVIRTUAL_CALL(_get_data, r_buffer, p_bytes, &received, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("StreamPeerExtension::_get_data is unimplemented!"); return FAILED; } Error StreamPeerExtension::get_partial_data(uint8_t *r_buffer, int p_bytes, int &r_received) { - int err; + Error err; if (GDVIRTUAL_CALL(_get_partial_data, r_buffer, p_bytes, &r_received, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("StreamPeerExtension::_get_partial_data is unimplemented!"); return FAILED; } Error StreamPeerExtension::put_data(const uint8_t *p_data, int p_bytes) { - int err; + Error err; int sent = 0; if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &sent, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("StreamPeerExtension::_put_data is unimplemented!"); return FAILED; } Error StreamPeerExtension::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - int err; + Error err; if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &r_sent, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("StreamPeerExtension::_put_partial_data is unimplemented!"); return FAILED; diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 4609e52aa2..108a8ce9d9 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -33,6 +33,7 @@ #include "core/object/ref_counted.h" +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" #include "core/object/script_language.h" #include "core/variant/native_ptr.h" @@ -104,16 +105,18 @@ protected: public: virtual Error put_data(const uint8_t *p_data, int p_bytes) override; + GDVIRTUAL3R(Error, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); + virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; + GDVIRTUAL3R(Error, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); + virtual Error get_data(uint8_t *p_buffer, int p_bytes) override; + GDVIRTUAL3R(Error, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); + virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; - virtual int get_available_bytes() const override; + GDVIRTUAL3R(Error, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL3R(int, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL3R(int, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL3R(int, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL3R(int, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL0RC(int, _get_available_bytes); + EXBIND0RC(int, get_available_bytes); }; class StreamPeerBuffer : public StreamPeer { diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp new file mode 100644 index 0000000000..23d7e379ee --- /dev/null +++ b/core/math/a_star_grid_2d.cpp @@ -0,0 +1,589 @@ +/*************************************************************************/ +/* a_star_grid_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "a_star_grid_2d.h" + +static real_t heuristic_manhattan(const Vector2i &p_from, const Vector2i &p_to) { + real_t dx = (real_t)ABS(p_to.x - p_from.x); + real_t dy = (real_t)ABS(p_to.y - p_from.y); + return dx + dy; +} + +static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) { + real_t dx = (real_t)ABS(p_to.x - p_from.x); + real_t dy = (real_t)ABS(p_to.y - p_from.y); + return (real_t)Math::sqrt(dx * dx + dy * dy); +} + +static real_t heuristic_octile(const Vector2i &p_from, const Vector2i &p_to) { + real_t dx = (real_t)ABS(p_to.x - p_from.x); + real_t dy = (real_t)ABS(p_to.y - p_from.y); + real_t F = Math_SQRT2 - 1; + return (dx < dy) ? F * dx + dy : F * dy + dx; +} + +static real_t heuristic_chebyshev(const Vector2i &p_from, const Vector2i &p_to) { + real_t dx = (real_t)ABS(p_to.x - p_from.x); + real_t dy = (real_t)ABS(p_to.y - p_from.y); + return MAX(dx, dy); +} + +static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_manhattan, heuristic_euclidian, heuristic_octile, heuristic_chebyshev }; + +void AStarGrid2D::set_size(const Size2i &p_size) { + ERR_FAIL_COND(p_size.x < 0 || p_size.y < 0); + if (p_size != size) { + size = p_size; + dirty = true; + } +} + +Size2i AStarGrid2D::get_size() const { + return size; +} + +void AStarGrid2D::set_offset(const Vector2 &p_offset) { + if (!offset.is_equal_approx(p_offset)) { + offset = p_offset; + dirty = true; + } +} + +Vector2 AStarGrid2D::get_offset() const { + return offset; +} + +void AStarGrid2D::set_cell_size(const Size2 &p_cell_size) { + if (!cell_size.is_equal_approx(p_cell_size)) { + cell_size = p_cell_size; + dirty = true; + } +} + +Size2 AStarGrid2D::get_cell_size() const { + return cell_size; +} + +void AStarGrid2D::update() { + points.clear(); + for (int64_t y = 0; y < size.y; y++) { + LocalVector<Point> line; + for (int64_t x = 0; x < size.x; x++) { + line.push_back(Point(Vector2i(x, y), offset + Vector2(x, y) * cell_size)); + } + points.push_back(line); + } + dirty = false; +} + +bool AStarGrid2D::is_in_bounds(int p_x, int p_y) const { + return p_x >= 0 && p_x < size.width && p_y >= 0 && p_y < size.height; +} + +bool AStarGrid2D::is_in_boundsv(const Vector2i &p_id) const { + return p_id.x >= 0 && p_id.x < size.width && p_id.y >= 0 && p_id.y < size.height; +} + +bool AStarGrid2D::is_dirty() const { + return dirty; +} + +void AStarGrid2D::set_jumping_enabled(bool p_enabled) { + jumping_enabled = p_enabled; +} + +bool AStarGrid2D::is_jumping_enabled() const { + return jumping_enabled; +} + +void AStarGrid2D::set_diagonal_mode(DiagonalMode p_diagonal_mode) { + ERR_FAIL_INDEX((int)p_diagonal_mode, (int)DIAGONAL_MODE_MAX); + diagonal_mode = p_diagonal_mode; +} + +AStarGrid2D::DiagonalMode AStarGrid2D::get_diagonal_mode() const { + return diagonal_mode; +} + +void AStarGrid2D::set_default_heuristic(Heuristic p_heuristic) { + ERR_FAIL_INDEX((int)p_heuristic, (int)HEURISTIC_MAX); + default_heuristic = p_heuristic; +} + +AStarGrid2D::Heuristic AStarGrid2D::get_default_heuristic() const { + return default_heuristic; +} + +void AStarGrid2D::set_point_solid(const Vector2i &p_id, bool p_solid) { + ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set if point is disabled. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); + points[p_id.y][p_id.x].solid = p_solid; +} + +bool AStarGrid2D::is_point_solid(const Vector2i &p_id) const { + ERR_FAIL_COND_V_MSG(dirty, false, "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), false, vformat("Can't get if point is disabled. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height)); + return points[p_id.y][p_id.x].solid; +} + +AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) { + if (!p_to || p_to->solid) { + return nullptr; + } + if (p_to == end) { + return p_to; + } + + int64_t from_x = p_from->id.x; + int64_t from_y = p_from->id.y; + + int64_t to_x = p_to->id.x; + int64_t to_y = p_to->id.y; + + int64_t dx = to_x - from_x; + int64_t dy = to_y - from_y; + + if (diagonal_mode == DIAGONAL_MODE_ALWAYS || diagonal_mode == DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE) { + if (dx != 0 && dy != 0) { + if ((_is_walkable(to_x - dx, to_y + dy) && !_is_walkable(to_x - dx, to_y)) || (_is_walkable(to_x + dx, to_y - dy) && !_is_walkable(to_x, to_y - dy))) { + return p_to; + } + if (_jump(p_to, _get_point(to_x + dx, to_y)) != nullptr) { + return p_to; + } + if (_jump(p_to, _get_point(to_x, to_y + dy)) != nullptr) { + return p_to; + } + } else { + if (dx != 0) { + if ((_is_walkable(to_x + dx, to_y + 1) && !_is_walkable(to_x, to_y + 1)) || (_is_walkable(to_x + dx, to_y - 1) && !_is_walkable(to_x, to_y - 1))) { + return p_to; + } + } else { + if ((_is_walkable(to_x + 1, to_y + dy) && !_is_walkable(to_x + 1, to_y)) || (_is_walkable(to_x - 1, to_y + dy) && !_is_walkable(to_x - 1, to_y))) { + return p_to; + } + } + } + if (_is_walkable(to_x + dx, to_y + dy) && (diagonal_mode == DIAGONAL_MODE_ALWAYS || (_is_walkable(to_x + dx, to_y) || _is_walkable(to_x, to_y + dy)))) { + return _jump(p_to, _get_point(to_x + dx, to_y + dy)); + } + } else if (diagonal_mode == DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES) { + if (dx != 0 && dy != 0) { + if ((_is_walkable(to_x + dx, to_y + dy) && !_is_walkable(to_x, to_y + dy)) || !_is_walkable(to_x + dx, to_y)) { + return p_to; + } + if (_jump(p_to, _get_point(to_x + dx, to_y)) != nullptr) { + return p_to; + } + if (_jump(p_to, _get_point(to_x, to_y + dy)) != nullptr) { + return p_to; + } + } else { + if (dx != 0) { + if ((_is_walkable(to_x, to_y + 1) && !_is_walkable(to_x - dx, to_y + 1)) || (_is_walkable(to_x, to_y - 1) && !_is_walkable(to_x - dx, to_y - 1))) { + return p_to; + } + } else { + if ((_is_walkable(to_x + 1, to_y) && !_is_walkable(to_x + 1, to_y - dy)) || (_is_walkable(to_x - 1, to_y) && !_is_walkable(to_x - 1, to_y - dy))) { + return p_to; + } + } + } + if (_is_walkable(to_x + dx, to_y + dy) && _is_walkable(to_x + dx, to_y) && _is_walkable(to_x, to_y + dy)) { + return _jump(p_to, _get_point(to_x + dx, to_y + dy)); + } + } else { // DIAGONAL_MODE_NEVER + if (dx != 0) { + if (!_is_walkable(to_x + dx, to_y)) { + return p_to; + } + if (_jump(p_to, _get_point(to_x, to_y + 1)) != nullptr) { + return p_to; + } + if (_jump(p_to, _get_point(to_x, to_y - 1)) != nullptr) { + return p_to; + } + } else { + if (!_is_walkable(to_x, to_y + dy)) { + return p_to; + } + if (_jump(p_to, _get_point(to_x + 1, to_y)) != nullptr) { + return p_to; + } + if (_jump(p_to, _get_point(to_x - 1, to_y)) != nullptr) { + return p_to; + } + } + if (_is_walkable(to_x + dx, to_y + dy) && _is_walkable(to_x + dx, to_y) && _is_walkable(to_x, to_y + dy)) { + return _jump(p_to, _get_point(to_x + dx, to_y + dy)); + } + } + return nullptr; +} + +void AStarGrid2D::_get_nbors(Point *p_point, List<Point *> &r_nbors) { + bool ts0 = false, td0 = false, + ts1 = false, td1 = false, + ts2 = false, td2 = false, + ts3 = false, td3 = false; + + Point *left = nullptr; + Point *right = nullptr; + Point *top = nullptr; + Point *bottom = nullptr; + + Point *top_left = nullptr; + Point *top_right = nullptr; + Point *bottom_left = nullptr; + Point *bottom_right = nullptr; + + { + bool has_left = false; + bool has_right = false; + + if (p_point->id.x - 1 >= 0) { + left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y); + has_left = true; + } + if (p_point->id.x + 1 < size.width) { + right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y); + has_right = true; + } + if (p_point->id.y - 1 >= 0) { + top = _get_point_unchecked(p_point->id.x, p_point->id.y - 1); + if (has_left) { + top_left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y - 1); + } + if (has_right) { + top_right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y - 1); + } + } + if (p_point->id.y + 1 < size.height) { + bottom = _get_point_unchecked(p_point->id.x, p_point->id.y + 1); + if (has_left) { + bottom_left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y + 1); + } + if (has_right) { + bottom_right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y + 1); + } + } + } + + if (top && !top->solid) { + r_nbors.push_back(top); + ts0 = true; + } + if (right && !right->solid) { + r_nbors.push_back(right); + ts1 = true; + } + if (bottom && !bottom->solid) { + r_nbors.push_back(bottom); + ts2 = true; + } + if (left && !left->solid) { + r_nbors.push_back(left); + ts3 = true; + } + + switch (diagonal_mode) { + case DIAGONAL_MODE_ALWAYS: { + td0 = true; + td1 = true; + td2 = true; + td3 = true; + } break; + case DIAGONAL_MODE_NEVER: { + } break; + case DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE: { + td0 = ts3 || ts0; + td1 = ts0 || ts1; + td2 = ts1 || ts2; + td3 = ts2 || ts3; + } break; + case DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES: { + td0 = ts3 && ts0; + td1 = ts0 && ts1; + td2 = ts1 && ts2; + td3 = ts2 && ts3; + } break; + default: + break; + } + + if (td0 && (top_left && !top_left->solid)) { + r_nbors.push_back(top_left); + } + if (td1 && (top_right && !top_right->solid)) { + r_nbors.push_back(top_right); + } + if (td2 && (bottom_right && !bottom_right->solid)) { + r_nbors.push_back(bottom_right); + } + if (td3 && (bottom_left && !bottom_left->solid)) { + r_nbors.push_back(bottom_left); + } +} + +bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { + pass++; + + if (p_end_point->solid) { + return false; + } + + bool found_route = false; + + Vector<Point *> open_list; + SortArray<Point *, SortPoints> sorter; + + p_begin_point->g_score = 0; + p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id); + open_list.push_back(p_begin_point); + end = p_end_point; + + while (!open_list.is_empty()) { + Point *p = open_list[0]; // The currently processed point. + + if (p == p_end_point) { + found_route = true; + break; + } + + sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list. + open_list.remove_at(open_list.size() - 1); + p->closed_pass = pass; // Mark the point as closed. + + List<Point *> nbors; + _get_nbors(p, nbors); + for (List<Point *>::Element *E = nbors.front(); E; E = E->next()) { + Point *e = E->get(); // The neighbour point. + if (jumping_enabled) { + e = _jump(p, e); + if (!e || e->closed_pass == pass) { + continue; + } + } else { + if (e->solid || e->closed_pass == pass) { + continue; + } + } + + real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id); + bool new_point = false; + + if (e->open_pass != pass) { // The point wasn't inside the open list. + e->open_pass = pass; + open_list.push_back(e); + new_point = true; + } else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous. + continue; + } + + e->prev_point = p; + e->g_score = tentative_g_score; + e->f_score = e->g_score + _estimate_cost(e->id, p_end_point->id); + + if (new_point) { // The position of the new points is already known. + sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw()); + } else { + sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw()); + } + } + } + + return found_route; +} + +real_t AStarGrid2D::_estimate_cost(const Vector2i &p_from_id, const Vector2i &p_to_id) { + real_t scost; + if (GDVIRTUAL_CALL(_estimate_cost, p_from_id, p_to_id, scost)) { + return scost; + } + return heuristics[default_heuristic](p_from_id, p_to_id); +} + +real_t AStarGrid2D::_compute_cost(const Vector2i &p_from_id, const Vector2i &p_to_id) { + real_t scost; + if (GDVIRTUAL_CALL(_compute_cost, p_from_id, p_to_id, scost)) { + return scost; + } + return heuristics[default_heuristic](p_from_id, p_to_id); +} + +void AStarGrid2D::clear() { + points.clear(); + size = Vector2i(); +} + +Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { + ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height)); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height)); + + Point *a = _get_point(p_from_id.x, p_from_id.y); + Point *b = _get_point(p_to_id.x, p_to_id.y); + + if (a == b) { + Vector<Vector2> ret; + ret.push_back(a->pos); + return ret; + } + + Point *begin_point = a; + Point *end_point = b; + + bool found_route = _solve(begin_point, end_point); + if (!found_route) { + return Vector<Vector2>(); + } + + Point *p = end_point; + int64_t pc = 1; + while (p != begin_point) { + pc++; + p = p->prev_point; + } + + Vector<Vector2> path; + path.resize(pc); + + { + Vector2 *w = path.ptrw(); + + p = end_point; + int64_t idx = pc - 1; + while (p != begin_point) { + w[idx--] = p->pos; + p = p->prev_point; + } + + w[0] = p->pos; + } + + return path; +} + +Vector<Vector2> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { + ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height)); + ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height)); + + Point *a = _get_point(p_from_id.x, p_from_id.y); + Point *b = _get_point(p_to_id.x, p_to_id.y); + + if (a == b) { + Vector<Vector2> ret; + ret.push_back(Vector2((float)a->id.x, (float)a->id.y)); + return ret; + } + + Point *begin_point = a; + Point *end_point = b; + + bool found_route = _solve(begin_point, end_point); + if (!found_route) { + return Vector<Vector2>(); + } + + Point *p = end_point; + int64_t pc = 1; + while (p != begin_point) { + pc++; + p = p->prev_point; + } + + Vector<Vector2> path; + path.resize(pc); + + { + Vector2 *w = path.ptrw(); + + p = end_point; + int64_t idx = pc - 1; + while (p != begin_point) { + w[idx--] = Vector2((float)p->id.x, (float)p->id.y); + p = p->prev_point; + } + + w[0] = p->id; + } + + return path; +} + +void AStarGrid2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &AStarGrid2D::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &AStarGrid2D::get_size); + ClassDB::bind_method(D_METHOD("set_offset", "offset"), &AStarGrid2D::set_offset); + ClassDB::bind_method(D_METHOD("get_offset"), &AStarGrid2D::get_offset); + ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &AStarGrid2D::set_cell_size); + ClassDB::bind_method(D_METHOD("get_cell_size"), &AStarGrid2D::get_cell_size); + ClassDB::bind_method(D_METHOD("is_in_bounds", "x", "y"), &AStarGrid2D::is_in_bounds); + ClassDB::bind_method(D_METHOD("is_in_boundsv", "id"), &AStarGrid2D::is_in_boundsv); + ClassDB::bind_method(D_METHOD("is_dirty"), &AStarGrid2D::is_dirty); + ClassDB::bind_method(D_METHOD("update"), &AStarGrid2D::update); + ClassDB::bind_method(D_METHOD("set_jumping_enabled", "enabled"), &AStarGrid2D::set_jumping_enabled); + ClassDB::bind_method(D_METHOD("is_jumping_enabled"), &AStarGrid2D::is_jumping_enabled); + ClassDB::bind_method(D_METHOD("set_diagonal_mode", "mode"), &AStarGrid2D::set_diagonal_mode); + ClassDB::bind_method(D_METHOD("get_diagonal_mode"), &AStarGrid2D::get_diagonal_mode); + ClassDB::bind_method(D_METHOD("set_default_heuristic", "heuristic"), &AStarGrid2D::set_default_heuristic); + ClassDB::bind_method(D_METHOD("get_default_heuristic"), &AStarGrid2D::get_default_heuristic); + ClassDB::bind_method(D_METHOD("set_point_solid", "id", "solid"), &AStarGrid2D::set_point_solid, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("is_point_solid", "id"), &AStarGrid2D::is_point_solid); + ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear); + + ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::get_point_path); + ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::get_id_path); + + GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id") + GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id") + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size"), "set_cell_size", "get_cell_size"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "jumping_enabled"), "set_jumping_enabled", "is_jumping_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "default_heuristic", PROPERTY_HINT_ENUM, "Manhattan,Euclidean,Octile,Chebyshev,Max"), "set_default_heuristic", "get_default_heuristic"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "diagonal_mode", PROPERTY_HINT_ENUM, "Never,Always,At Least One Walkable,Only If No Obstacles,Max"), "set_diagonal_mode", "get_diagonal_mode"); + + BIND_ENUM_CONSTANT(HEURISTIC_EUCLIDEAN); + BIND_ENUM_CONSTANT(HEURISTIC_MANHATTAN); + BIND_ENUM_CONSTANT(HEURISTIC_OCTILE); + BIND_ENUM_CONSTANT(HEURISTIC_CHEBYSHEV); + BIND_ENUM_CONSTANT(HEURISTIC_MAX); + + BIND_ENUM_CONSTANT(DIAGONAL_MODE_ALWAYS); + BIND_ENUM_CONSTANT(DIAGONAL_MODE_NEVER); + BIND_ENUM_CONSTANT(DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE); + BIND_ENUM_CONSTANT(DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES); + BIND_ENUM_CONSTANT(DIAGONAL_MODE_MAX); +} diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h new file mode 100644 index 0000000000..bf6363aa01 --- /dev/null +++ b/core/math/a_star_grid_2d.h @@ -0,0 +1,178 @@ +/*************************************************************************/ +/* a_star_grid_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef A_STAR_GRID_2D_H +#define A_STAR_GRID_2D_H + +#include "core/object/gdvirtual.gen.inc" +#include "core/object/ref_counted.h" +#include "core/object/script_language.h" +#include "core/templates/list.h" +#include "core/templates/local_vector.h" + +class AStarGrid2D : public RefCounted { + GDCLASS(AStarGrid2D, RefCounted); + +public: + enum DiagonalMode { + DIAGONAL_MODE_ALWAYS, + DIAGONAL_MODE_NEVER, + DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE, + DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES, + DIAGONAL_MODE_MAX, + }; + + enum Heuristic { + HEURISTIC_EUCLIDEAN, + HEURISTIC_MANHATTAN, + HEURISTIC_OCTILE, + HEURISTIC_CHEBYSHEV, + HEURISTIC_MAX, + }; + +private: + Size2i size; + Vector2 offset; + Size2 cell_size = Size2(1, 1); + bool dirty = false; + + bool jumping_enabled = false; + DiagonalMode diagonal_mode = DIAGONAL_MODE_ALWAYS; + Heuristic default_heuristic = HEURISTIC_EUCLIDEAN; + + struct Point { + Vector2i id; + + bool solid = false; + Vector2 pos; + + // Used for pathfinding. + Point *prev_point = nullptr; + real_t g_score = 0; + real_t f_score = 0; + uint64_t open_pass = 0; + uint64_t closed_pass = 0; + + Point() {} + + Point(const Vector2i &p_id, const Vector2 &p_pos) : + id(p_id), pos(p_pos) {} + }; + + struct SortPoints { + _FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B. + if (A->f_score > B->f_score) { + return true; + } else if (A->f_score < B->f_score) { + return false; + } else { + return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start. + } + } + }; + + LocalVector<LocalVector<Point>> points; + Point *end = nullptr; + + uint64_t pass = 1; + +private: // Internal routines. + _FORCE_INLINE_ bool _is_walkable(int64_t p_x, int64_t p_y) const { + if (p_x >= 0 && p_y >= 0 && p_x < size.width && p_y < size.height) { + return !points[p_y][p_x].solid; + } + return false; + } + + _FORCE_INLINE_ Point *_get_point(int64_t p_x, int64_t p_y) { + if (p_x >= 0 && p_y >= 0 && p_x < size.width && p_y < size.height) { + return &points[p_y][p_x]; + } + return nullptr; + } + + _FORCE_INLINE_ Point *_get_point_unchecked(int64_t p_x, int64_t p_y) { + return &points[p_y][p_x]; + } + + void _get_nbors(Point *p_point, List<Point *> &r_nbors); + Point *_jump(Point *p_from, Point *p_to); + bool _solve(Point *p_begin_point, Point *p_end_point); + +protected: + static void _bind_methods(); + + virtual real_t _estimate_cost(const Vector2i &p_from_id, const Vector2i &p_to_id); + virtual real_t _compute_cost(const Vector2i &p_from_id, const Vector2i &p_to_id); + + GDVIRTUAL2RC(real_t, _estimate_cost, Vector2i, Vector2i) + GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i) + +public: + void set_size(const Size2i &p_size); + Size2i get_size() const; + + void set_offset(const Vector2 &p_offset); + Vector2 get_offset() const; + + void set_cell_size(const Size2 &p_cell_size); + Size2 get_cell_size() const; + + void update(); + + int get_width() const; + int get_height() const; + + bool is_in_bounds(int p_x, int p_y) const; + bool is_in_boundsv(const Vector2i &p_id) const; + bool is_dirty() const; + + void set_jumping_enabled(bool p_enabled); + bool is_jumping_enabled() const; + + void set_diagonal_mode(DiagonalMode p_diagonal_mode); + DiagonalMode get_diagonal_mode() const; + + void set_default_heuristic(Heuristic p_heuristic); + Heuristic get_default_heuristic() const; + + void set_point_solid(const Vector2i &p_id, bool p_solid = true); + bool is_point_solid(const Vector2i &p_id) const; + + void clear(); + + Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to); + Vector<Vector2> get_id_path(const Vector2i &p_from, const Vector2i &p_to); +}; + +VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode); +VARIANT_ENUM_CAST(AStarGrid2D::Heuristic); + +#endif // A_STAR_GRID_2D_H diff --git a/core/math/basis.cpp b/core/math/basis.cpp index bc50d0e64c..0eb6320ac6 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -1033,13 +1033,13 @@ void Basis::rotate_sh(real_t *p_values) { Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(p_target.is_equal_approx(Vector3()), Basis(), "The target vector can't be zero."); - ERR_FAIL_COND_V_MSG(p_up.is_equal_approx(Vector3()), Basis(), "The up vector can't be zero."); + ERR_FAIL_COND_V_MSG(p_target.is_zero_approx(), Basis(), "The target vector can't be zero."); + ERR_FAIL_COND_V_MSG(p_up.is_zero_approx(), Basis(), "The up vector can't be zero."); #endif Vector3 v_z = -p_target.normalized(); Vector3 v_x = p_up.cross(v_z); #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(v_x.is_equal_approx(Vector3()), Basis(), "The target vector and up vector can't be parallel to each other."); + ERR_FAIL_COND_V_MSG(v_x.is_zero_approx(), Basis(), "The target vector and up vector can't be parallel to each other."); #endif v_x.normalize(); Vector3 v_y = v_z.cross(v_x); diff --git a/core/math/basis.h b/core/math/basis.h index 2853947ba7..cc2924f5ff 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -238,10 +238,8 @@ struct _NO_DISCARD_ Basis { Basis(const Vector3 &p_axis, real_t p_angle, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_angle, p_scale); } static Basis from_scale(const Vector3 &p_scale); - _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { - rows[0] = row0; - rows[1] = row1; - rows[2] = row2; + _FORCE_INLINE_ Basis(const Vector3 &p_x_axis, const Vector3 &p_y_axis, const Vector3 &p_z_axis) { + set_columns(p_x_axis, p_y_axis, p_z_axis); } _FORCE_INLINE_ Basis() {} diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index ec96753c79..9238293b48 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -35,6 +35,111 @@ #include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/polypartition.h" +void Geometry3D::get_closest_points_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1, Vector3 &r_ps, Vector3 &r_qt) { + // Based on David Eberly's Computation of Distance Between Line Segments algorithm. + + Vector3 p = p_p1 - p_p0; + Vector3 q = p_q1 - p_q0; + Vector3 r = p_p0 - p_q0; + + real_t a = p.dot(p); + real_t b = p.dot(q); + real_t c = q.dot(q); + real_t d = p.dot(r); + real_t e = q.dot(r); + + real_t s = 0.0f; + real_t t = 0.0f; + + real_t det = a * c - b * b; + if (det > CMP_EPSILON) { + // Non-parallel segments + real_t bte = b * e; + real_t ctd = c * d; + + if (bte <= ctd) { + // s <= 0.0f + if (e <= 0.0f) { + // t <= 0.0f + s = (-d >= a ? 1 : (-d > 0.0f ? -d / a : 0.0f)); + t = 0.0f; + } else if (e < c) { + // 0.0f < t < 1 + s = 0.0f; + t = e / c; + } else { + // t >= 1 + s = (b - d >= a ? 1 : (b - d > 0.0f ? (b - d) / a : 0.0f)); + t = 1; + } + } else { + // s > 0.0f + s = bte - ctd; + if (s >= det) { + // s >= 1 + if (b + e <= 0.0f) { + // t <= 0.0f + s = (-d <= 0.0f ? 0.0f : (-d < a ? -d / a : 1)); + t = 0.0f; + } else if (b + e < c) { + // 0.0f < t < 1 + s = 1; + t = (b + e) / c; + } else { + // t >= 1 + s = (b - d <= 0.0f ? 0.0f : (b - d < a ? (b - d) / a : 1)); + t = 1; + } + } else { + // 0.0f < s < 1 + real_t ate = a * e; + real_t btd = b * d; + + if (ate <= btd) { + // t <= 0.0f + s = (-d <= 0.0f ? 0.0f : (-d >= a ? 1 : -d / a)); + t = 0.0f; + } else { + // t > 0.0f + t = ate - btd; + if (t >= det) { + // t >= 1 + s = (b - d <= 0.0f ? 0.0f : (b - d >= a ? 1 : (b - d) / a)); + t = 1; + } else { + // 0.0f < t < 1 + s /= det; + t /= det; + } + } + } + } + } else { + // Parallel segments + if (e <= 0.0f) { + s = (-d <= 0.0f ? 0.0f : (-d >= a ? 1 : -d / a)); + t = 0.0f; + } else if (e >= c) { + s = (b - d <= 0.0f ? 0.0f : (b - d >= a ? 1 : (b - d) / a)); + t = 1; + } else { + s = 0.0f; + t = e / c; + } + } + + r_ps = (1 - s) * p_p0 + s * p_p1; + r_qt = (1 - t) * p_q0 + t * p_q1; +} + +real_t Geometry3D::get_closest_distance_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1) { + Vector3 ps; + Vector3 qt; + get_closest_points_between_segments(p_p0, p_p1, p_q0, p_q1, ps, qt); + Vector3 st = qt - ps; + return st.length(); +} + void Geometry3D::MeshData::optimize_vertices() { HashMap<int, int> vtx_remap; diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h index 59c56906f4..e5ace9db72 100644 --- a/core/math/geometry_3d.h +++ b/core/math/geometry_3d.h @@ -37,96 +37,8 @@ class Geometry3D { public: - static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) { -// Do the function 'd' as defined by pb. I think it's a dot product of some sort. -#define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z)) - - // Calculate the parametric position on the 2 curves, mua and mub. - real_t mua = (d_of(p1, q1, q2, q1) * d_of(q2, q1, p2, p1) - d_of(p1, q1, p2, p1) * d_of(q2, q1, q2, q1)) / (d_of(p2, p1, p2, p1) * d_of(q2, q1, q2, q1) - d_of(q2, q1, p2, p1) * d_of(q2, q1, p2, p1)); - real_t mub = (d_of(p1, q1, q2, q1) + mua * d_of(q2, q1, p2, p1)) / d_of(q2, q1, q2, q1); - - // Clip the value between [0..1] constraining the solution to lie on the original curves. - if (mua < 0) { - mua = 0; - } - if (mub < 0) { - mub = 0; - } - if (mua > 1) { - mua = 1; - } - if (mub > 1) { - mub = 1; - } - c1 = p1.lerp(p2, mua); - c2 = q1.lerp(q2, mub); - } - - static real_t get_closest_distance_between_segments(const Vector3 &p_from_a, const Vector3 &p_to_a, const Vector3 &p_from_b, const Vector3 &p_to_b) { - Vector3 u = p_to_a - p_from_a; - Vector3 v = p_to_b - p_from_b; - Vector3 w = p_from_a - p_to_a; - real_t a = u.dot(u); // Always >= 0 - real_t b = u.dot(v); - real_t c = v.dot(v); // Always >= 0 - real_t d = u.dot(w); - real_t e = v.dot(w); - real_t D = a * c - b * b; // Always >= 0 - real_t sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0 - real_t tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0 - - // Compute the line parameters of the two closest points. - if (D < (real_t)CMP_EPSILON) { // The lines are almost parallel. - sN = 0.0f; // Force using point P0 on segment S1 - sD = 1.0f; // to prevent possible division by 0.0 later. - tN = e; - tD = c; - } else { // Get the closest points on the infinite lines - sN = (b * e - c * d); - tN = (a * e - b * d); - if (sN < 0.0f) { // sc < 0 => the s=0 edge is visible. - sN = 0.0f; - tN = e; - tD = c; - } else if (sN > sD) { // sc > 1 => the s=1 edge is visible. - sN = sD; - tN = e + b; - tD = c; - } - } - - if (tN < 0.0f) { // tc < 0 => the t=0 edge is visible. - tN = 0.0f; - // Recompute sc for this edge. - if (-d < 0.0f) { - sN = 0.0f; - } else if (-d > a) { - sN = sD; - } else { - sN = -d; - sD = a; - } - } else if (tN > tD) { // tc > 1 => the t=1 edge is visible. - tN = tD; - // Recompute sc for this edge. - if ((-d + b) < 0.0f) { - sN = 0; - } else if ((-d + b) > a) { - sN = sD; - } else { - sN = (-d + b); - sD = a; - } - } - // Finally do the division to get sc and tc. - sc = (Math::is_zero_approx(sN) ? 0.0f : sN / sD); - tc = (Math::is_zero_approx(tN) ? 0.0f : tN / tD); - - // Get the difference of the two closest points. - Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc) - - return dP.length(); // Return the closest distance. - } + static void get_closest_points_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1, Vector3 &r_ps, Vector3 &r_qt); + static real_t get_closest_distance_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1); static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = nullptr) { Vector3 e1 = p_v1 - p_v0; diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp index a634faca9a..2de9e81b38 100644 --- a/core/math/transform_3d.cpp +++ b/core/math/transform_3d.cpp @@ -94,9 +94,7 @@ void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, con origin = p_eye; } -Transform3D Transform3D::spherical_interpolate_with(const Transform3D &p_transform, real_t p_c) const { - /* not sure if very "efficient" but good enough? */ - +Transform3D Transform3D::interpolate_with(const Transform3D &p_transform, real_t p_c) const { Transform3D interp; Vector3 src_scale = basis.get_scale(); @@ -113,15 +111,6 @@ Transform3D Transform3D::spherical_interpolate_with(const Transform3D &p_transfo return interp; } -Transform3D Transform3D::interpolate_with(const Transform3D &p_transform, real_t p_c) const { - Transform3D interp; - - interp.basis = basis.lerp(p_transform.basis, p_c); - interp.origin = origin.lerp(p_transform.origin, p_c); - - return interp; -} - void Transform3D::scale(const Vector3 &p_scale) { basis.scale(p_scale); origin *= p_scale; diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index b572e90859..c62e4a7b0e 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -103,7 +103,6 @@ struct _NO_DISCARD_ Transform3D { void operator*=(const real_t p_val); Transform3D operator*(const real_t p_val) const; - Transform3D spherical_interpolate_with(const Transform3D &p_transform, real_t p_c) const; Transform3D interpolate_with(const Transform3D &p_transform, real_t p_c) const; _FORCE_INLINE_ Transform3D inverse_xform(const Transform3D &t) const { diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index d9b5d55454..56dbba393a 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -182,6 +182,10 @@ bool Vector2::is_equal_approx(const Vector2 &p_v) const { return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y); } +bool Vector2::is_zero_approx() const { + return Math::is_zero_approx(x) && Math::is_zero_approx(y); +} + Vector2::operator String() const { return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")"; } diff --git a/core/math/vector2.h b/core/math/vector2.h index caa6b226e7..9441f84087 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -124,6 +124,7 @@ struct _NO_DISCARD_ Vector2 { Vector2 reflect(const Vector2 &p_normal) const; bool is_equal_approx(const Vector2 &p_v) const; + bool is_zero_approx() const; Vector2 operator+(const Vector2 &p_v) const; void operator+=(const Vector2 &p_v); diff --git a/core/math/vector2i.h b/core/math/vector2i.h index 13b70031bd..0245900a3b 100644 --- a/core/math/vector2i.h +++ b/core/math/vector2i.h @@ -115,7 +115,7 @@ struct _NO_DISCARD_ Vector2i { real_t aspect() const { return width / (real_t)height; } Vector2i sign() const { return Vector2i(SIGN(x), SIGN(y)); } - Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); } + Vector2i abs() const { return Vector2i(Math::abs(x), Math::abs(y)); } Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const; operator String() const; diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index fdbbb8cb5c..4db45fe798 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -134,17 +134,21 @@ Vector3 Vector3::octahedron_tangent_decode(const Vector2 &p_oct, float *sign) { } Basis Vector3::outer(const Vector3 &p_with) const { - Vector3 row0(x * p_with.x, x * p_with.y, x * p_with.z); - Vector3 row1(y * p_with.x, y * p_with.y, y * p_with.z); - Vector3 row2(z * p_with.x, z * p_with.y, z * p_with.z); - - return Basis(row0, row1, row2); + Basis basis; + basis.rows[0] = Vector3(x * p_with.x, x * p_with.y, x * p_with.z); + basis.rows[1] = Vector3(y * p_with.x, y * p_with.y, y * p_with.z); + basis.rows[2] = Vector3(z * p_with.x, z * p_with.y, z * p_with.z); + return basis; } bool Vector3::is_equal_approx(const Vector3 &p_v) const { return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z); } +bool Vector3::is_zero_approx() const { + return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z); +} + Vector3::operator String() const { return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")"; } diff --git a/core/math/vector3.h b/core/math/vector3.h index 7cae6e2481..3944afa92e 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -142,6 +142,7 @@ struct _NO_DISCARD_ Vector3 { _FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const; bool is_equal_approx(const Vector3 &p_v) const; + bool is_zero_approx() const; /* Operators */ diff --git a/core/math/vector3i.h b/core/math/vector3i.h index b49c1142ed..825ce40318 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -128,7 +128,7 @@ double Vector3i::length() const { } Vector3i Vector3i::abs() const { - return Vector3i(ABS(x), ABS(y), ABS(z)); + return Vector3i(Math::abs(x), Math::abs(y), Math::abs(z)); } Vector3i Vector3i::sign() const { diff --git a/core/math/vector4.cpp b/core/math/vector4.cpp index 273a111891..3c25f454a3 100644 --- a/core/math/vector4.cpp +++ b/core/math/vector4.cpp @@ -71,20 +71,35 @@ bool Vector4::is_equal_approx(const Vector4 &p_vec4) const { return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w); } +bool Vector4::is_zero_approx() const { + return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w); +} + real_t Vector4::length() const { return Math::sqrt(length_squared()); } void Vector4::normalize() { - *this /= length(); + real_t lengthsq = length_squared(); + if (lengthsq == 0) { + x = y = z = w = 0; + } else { + real_t length = Math::sqrt(lengthsq); + x /= length; + y /= length; + z /= length; + w /= length; + } } Vector4 Vector4::normalized() const { - return *this / length(); + Vector4 v = *this; + v.normalize(); + return v; } bool Vector4::is_normalized() const { - return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); // Use less epsilon. + return Math::is_equal_approx(length_squared(), (real_t)1, (real_t)UNIT_EPSILON); } real_t Vector4::distance_to(const Vector4 &p_to) const { @@ -183,3 +198,5 @@ Vector4 Vector4::clamp(const Vector4 &p_min, const Vector4 &p_max) const { Vector4::operator String() const { return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ", " + String::num_real(w, false) + ")"; } + +static_assert(sizeof(Vector4) == 4 * sizeof(real_t)); diff --git a/core/math/vector4.h b/core/math/vector4.h index 17d0de18e1..f964264108 100644 --- a/core/math/vector4.h +++ b/core/math/vector4.h @@ -73,6 +73,7 @@ struct _NO_DISCARD_ Vector4 { _FORCE_INLINE_ real_t length_squared() const; bool is_equal_approx(const Vector4 &p_vec4) const; + bool is_zero_approx() const; real_t length() const; void normalize(); Vector4 normalized() const; diff --git a/core/math/vector4i.cpp b/core/math/vector4i.cpp index 2dc5b74202..a89b802675 100644 --- a/core/math/vector4i.cpp +++ b/core/math/vector4i.cpp @@ -84,8 +84,10 @@ Vector4i::operator Vector4() const { } Vector4i::Vector4i(const Vector4 &p_vec4) { - x = p_vec4.x; - y = p_vec4.y; - z = p_vec4.z; - w = p_vec4.w; + x = (int32_t)p_vec4.x; + y = (int32_t)p_vec4.y; + z = (int32_t)p_vec4.z; + w = (int32_t)p_vec4.w; } + +static_assert(sizeof(Vector4i) == 4 * sizeof(int32_t)); diff --git a/core/math/vector4i.h b/core/math/vector4i.h index 37d905878f..d08e40d754 100644 --- a/core/math/vector4i.h +++ b/core/math/vector4i.h @@ -132,7 +132,7 @@ double Vector4i::length() const { } Vector4i Vector4i::abs() const { - return Vector4i(ABS(x), ABS(y), ABS(z), ABS(w)); + return Vector4i(Math::abs(x), Math::abs(y), Math::abs(z), Math::abs(w)); } Vector4i Vector4i::sign() const { diff --git a/core/object/object.cpp b/core/object/object.cpp index 4f7f55c8b6..33208be539 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -811,7 +811,7 @@ String Object::to_string() { _extension->to_string(_extension_instance, &ret); return ret; } - return "[" + get_class() + ":" + itos(get_instance_id()) + "]"; + return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; } void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) { diff --git a/core/object/object.h b/core/object/object.h index 1784c1fe70..97608c7938 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -50,7 +50,7 @@ class TypedArray; enum PropertyHint { PROPERTY_HINT_NONE, ///< no hint provided. - PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,no_slider][,radians][,degrees][,exp][,suffix:<keyword>] range. + PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_less][,no_slider][,radians][,degrees][,exp][,suffix:<keyword>] range. PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "positive_only" to exclude in-out and out-in. (ie: "attenuation,positive_only") diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 9ad6b0ca68..7f734201e7 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -64,6 +64,7 @@ #include "core/io/udp_server.h" #include "core/io/xml_parser.h" #include "core/math/a_star.h" +#include "core/math/a_star_grid_2d.h" #include "core/math/expression.h" #include "core/math/geometry_2d.h" #include "core/math/geometry_3d.h" @@ -236,6 +237,7 @@ void register_core_types() { GDREGISTER_ABSTRACT_CLASS(PackedDataContainerRef); GDREGISTER_CLASS(AStar3D); GDREGISTER_CLASS(AStar2D); + GDREGISTER_CLASS(AStarGrid2D); GDREGISTER_CLASS(EncodedObjectAsID); GDREGISTER_CLASS(RandomNumberGenerator); diff --git a/core/string/ustring.h b/core/string/ustring.h index 31de7cc464..b8ae3c2392 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -376,8 +376,6 @@ public: String path_join(const String &p_file) const; char32_t unicode_at(int p_idx) const; - void erase(int p_pos, int p_chars); - CharString ascii(bool p_allow_extended = false) const; CharString utf8() const; Error parse_utf8(const char *p_utf8, int p_len = -1, bool p_skip_cr = false); diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index d9f4359ee5..c1cb782a57 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -195,6 +195,15 @@ bool Dictionary::has_all(const Array &p_keys) const { return true; } +Variant Dictionary::find_key(const Variant &p_value) const { + for (const KeyValue<Variant, Variant> &E : _p->variant_map) { + if (E.value == p_value) { + return E.key; + } + } + return Variant(); +} + bool Dictionary::erase(const Variant &p_key) { ERR_FAIL_COND_V_MSG(_p->read_only, false, "Dictionary is in read-only state."); if (p_key.get_type() == Variant::STRING_NAME) { diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h index 2632893e8d..d9c9db56cf 100644 --- a/core/variant/dictionary.h +++ b/core/variant/dictionary.h @@ -66,6 +66,7 @@ public: bool has(const Variant &p_key) const; bool has_all(const Array &p_keys) const; + Variant find_key(const Variant &p_value) const; bool erase(const Variant &p_key); diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index a5bc6c229d..b280fc9fe3 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1787,7 +1787,7 @@ String stringify_vector(const T &vec, int recursion_count) { String Variant::stringify(int recursion_count) const { switch (type) { case NIL: - return "null"; + return "<null>"; case BOOL: return _data._bool ? "true" : "false"; case INT: @@ -1904,12 +1904,12 @@ String Variant::stringify(int recursion_count) const { case OBJECT: { if (_get_obj().obj) { if (!_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - return "[Freed Object]"; + return "<Freed Object>"; } return _get_obj().obj->to_string(); } else { - return "[Object:null]"; + return "<Object#null>"; } } break; @@ -1926,7 +1926,7 @@ String Variant::stringify(int recursion_count) const { return "RID(" + itos(s.get_id()) + ")"; } break; default: { - return "[" + get_type_name(type) + "]"; + return "<" + get_type_name(type) + ">"; } } diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 8af2a09111..8f15a62f79 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1605,6 +1605,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector2, normalized, sarray(), varray()); bind_method(Vector2, is_normalized, sarray(), varray()); bind_method(Vector2, is_equal_approx, sarray("to"), varray()); + bind_method(Vector2, is_zero_approx, sarray(), varray()); bind_method(Vector2, posmod, sarray("mod"), varray()); bind_method(Vector2, posmodv, sarray("modv"), varray()); bind_method(Vector2, project, sarray("b"), varray()); @@ -1693,6 +1694,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector3, normalized, sarray(), varray()); bind_method(Vector3, is_normalized, sarray(), varray()); bind_method(Vector3, is_equal_approx, sarray("to"), varray()); + bind_method(Vector3, is_zero_approx, sarray(), varray()); bind_method(Vector3, inverse, sarray(), varray()); bind_method(Vector3, clamp, sarray("min", "max"), varray()); bind_method(Vector3, snapped, sarray("step"), varray()); @@ -1756,6 +1758,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector4, dot, sarray("with"), varray()); bind_method(Vector4, inverse, sarray(), varray()); bind_method(Vector4, is_equal_approx, sarray("with"), varray()); + bind_method(Vector4, is_zero_approx, sarray(), varray()); /* Vector4i */ @@ -1969,7 +1972,6 @@ static void _register_variant_builtin_methods() { bind_method(Transform3D, translated, sarray("offset"), varray()); bind_method(Transform3D, translated_local, sarray("offset"), varray()); bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); - bind_method(Transform3D, spherical_interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, is_equal_approx, sarray("xform"), varray()); @@ -2015,6 +2017,7 @@ static void _register_variant_builtin_methods() { bind_method(Dictionary, merge, sarray("dictionary", "overwrite"), varray(false)); bind_method(Dictionary, has, sarray("key"), varray()); bind_method(Dictionary, has_all, sarray("keys"), varray()); + bind_method(Dictionary, find_key, sarray("value"), varray()); bind_method(Dictionary, erase, sarray("key"), varray()); bind_method(Dictionary, hash, sarray(), varray()); bind_method(Dictionary, keys, sarray(), varray()); diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 34653310b1..8151ff2102 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -1680,7 +1680,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::OBJECT: { - Object *obj = p_variant; + Object *obj = p_variant.get_validated_object(); if (!obj) { p_store_string_func(p_store_string_ud, "null"); |