diff options
646 files changed, 13721 insertions, 7338 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index ce4f757a19..bfda7c72f8 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -82,10 +82,8 @@ jobs: sudo cp -f misc/ci/sources.list /etc/apt/sources.list sudo apt-get update # The actual dependencies - sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \ - libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev \ - libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm xvfb wget unzip \ - llvm libspeechd-dev speech-dispatcher fontconfig libfontconfig-dev libxkbcommon-dev + sudo apt-get install build-essential pkg-config libgl1-mesa-dev libglu-dev \ + xvfb wget unzip llvm - name: Setup Godot build cache uses: ./.github/actions/godot-cache diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 96e1da9dde..9de901754a 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1105,8 +1105,8 @@ void Semaphore::wait() { semaphore.wait(); } -Error Semaphore::try_wait() { - return semaphore.try_wait() ? OK : ERR_BUSY; +bool Semaphore::try_wait() { + return semaphore.try_wait(); } void Semaphore::post() { @@ -1125,7 +1125,7 @@ void Mutex::lock() { mutex.lock(); } -Error Mutex::try_lock() { +bool Mutex::try_lock() { return mutex.try_lock(); } diff --git a/core/core_bind.h b/core/core_bind.h index c0c87fd009..8852463234 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -361,7 +361,7 @@ class Mutex : public RefCounted { public: void lock(); - Error try_lock(); + bool try_lock(); void unlock(); }; @@ -373,7 +373,7 @@ class Semaphore : public RefCounted { public: void wait(); - Error try_wait(); + bool try_wait(); void post(); }; diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp index cbb18a277f..939c1c298f 100644 --- a/core/crypto/crypto.cpp +++ b/core/crypto/crypto.cpp @@ -65,6 +65,45 @@ void X509Certificate::_bind_methods() { ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load); } +/// TLSOptions + +Ref<TLSOptions> TLSOptions::client(Ref<X509Certificate> p_trusted_chain, const String &p_common_name_override) { + Ref<TLSOptions> opts; + opts.instantiate(); + opts->trusted_ca_chain = p_trusted_chain; + opts->common_name = p_common_name_override; + opts->verify_mode = TLS_VERIFY_FULL; + return opts; +} + +Ref<TLSOptions> TLSOptions::client_unsafe(Ref<X509Certificate> p_trusted_chain) { + Ref<TLSOptions> opts; + opts.instantiate(); + opts->trusted_ca_chain = p_trusted_chain; + if (p_trusted_chain.is_null()) { + opts->verify_mode = TLS_VERIFY_NONE; + } else { + opts->verify_mode = TLS_VERIFY_CERT; + } + return opts; +} + +Ref<TLSOptions> TLSOptions::server(Ref<CryptoKey> p_own_key, Ref<X509Certificate> p_own_certificate) { + Ref<TLSOptions> opts; + opts.instantiate(); + opts->server_mode = true; + opts->own_certificate = p_own_certificate; + opts->private_key = p_own_key; + opts->verify_mode = TLS_VERIFY_NONE; + return opts; +} + +void TLSOptions::_bind_methods() { + ClassDB::bind_static_method("TLSOptions", D_METHOD("client", "trusted_chain", "common_name_override"), &TLSOptions::client, DEFVAL(Ref<X509Certificate>()), DEFVAL(String())); + ClassDB::bind_static_method("TLSOptions", D_METHOD("client_unsafe", "trusted_chain"), &TLSOptions::client_unsafe, DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_static_method("TLSOptions", D_METHOD("server", "key", "certificate"), &TLSOptions::server); +} + /// HMACContext void HMACContext::_bind_methods() { diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h index 981f67883c..999fe076d6 100644 --- a/core/crypto/crypto.h +++ b/core/crypto/crypto.h @@ -67,6 +67,40 @@ public: virtual Error save(String p_path) = 0; }; +class TLSOptions : public RefCounted { + GDCLASS(TLSOptions, RefCounted); + +public: + enum TLSVerifyMode { + TLS_VERIFY_NONE = 0, + TLS_VERIFY_CERT = 1, + TLS_VERIFY_FULL = 2, + }; + +private: + bool server_mode = false; + String common_name; + TLSVerifyMode verify_mode = TLS_VERIFY_FULL; + Ref<X509Certificate> trusted_ca_chain; + Ref<X509Certificate> own_certificate; + Ref<CryptoKey> private_key; + +protected: + static void _bind_methods(); + +public: + static Ref<TLSOptions> client(Ref<X509Certificate> p_trusted_chain = Ref<X509Certificate>(), const String &p_common_name_override = String()); + static Ref<TLSOptions> client_unsafe(Ref<X509Certificate> p_trusted_chain); + static Ref<TLSOptions> server(Ref<CryptoKey> p_own_key, Ref<X509Certificate> p_own_certificate); + + TLSVerifyMode get_verify_mode() const { return verify_mode; } + String get_common_name() const { return common_name; } + Ref<X509Certificate> get_trusted_ca_chain() const { return trusted_ca_chain; } + Ref<X509Certificate> get_own_certificate() const { return own_certificate; } + Ref<CryptoKey> get_private_key() const { return private_key; } + bool is_server() const { return server_mode; } +}; + class HMACContext : public RefCounted { GDCLASS(HMACContext, RefCounted); diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 7be14b741d..e26ead6d8c 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -82,6 +82,11 @@ static String get_property_info_type_name(const PropertyInfo &p_info) { return get_builtin_or_variant_type_name(p_info.type); } +static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) { + static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" }; + return argmeta[metadata]; +} + Dictionary GDExtensionAPIDump::generate_extension_api() { Dictionary api_dump; @@ -840,6 +845,10 @@ Dictionary GDExtensionAPIDump::generate_extension_api() { d3["type"] = get_property_info_type_name(pinfo); + if (mi.get_argument_meta(i) > 0) { + d3["meta"] = get_type_meta_name((GodotTypeInfo::Metadata)mi.get_argument_meta(i)); + } + if (i == -1) { d2["return_value"] = d3; } else { @@ -884,8 +893,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api() { d3["type"] = get_property_info_type_name(pinfo); if (method->get_argument_meta(i) > 0) { - static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" }; - d3["meta"] = argmeta[method->get_argument_meta(i)]; + d3["meta"] = get_type_meta_name(method->get_argument_meta(i)); } if (i >= 0 && i >= (method->get_argument_count() - default_args.size())) { @@ -929,6 +937,9 @@ Dictionary GDExtensionAPIDump::generate_extension_api() { Dictionary d3; d3["name"] = F.arguments[i].name; d3["type"] = get_property_info_type_name(F.arguments[i]); + if (F.get_argument_meta(i) > 0) { + d3["meta"] = get_type_meta_name((GodotTypeInfo::Metadata)F.get_argument_meta(i)); + } arguments.push_back(d3); } if (arguments.size()) { diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index a9063c8b27..63ed60a710 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -856,6 +856,13 @@ static GDExtensionVariantPtr gdextension_array_operator_index_const(GDExtensionC return (GDExtensionVariantPtr)&self->operator[](p_index); } +void gdextension_array_set_typed(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script) { + Array *self = reinterpret_cast<Array *>(p_self); + const StringName *class_name = reinterpret_cast<const StringName *>(p_class_name); + const Variant *script = reinterpret_cast<const Variant *>(p_script); + self->set_typed(p_type, *class_name, *script); +} + /* Dictionary functions */ static GDExtensionVariantPtr gdextension_dictionary_operator_index(GDExtensionTypePtr p_self, GDExtensionConstVariantPtr p_key) { @@ -1129,6 +1136,7 @@ void gdextension_setup_interface(GDExtensionInterface *p_interface) { gde_interface.array_operator_index = gdextension_array_operator_index; gde_interface.array_operator_index_const = gdextension_array_operator_index_const; + gde_interface.array_set_typed = gdextension_array_set_typed; /* Dictionary functions */ diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 6e5dee8265..7f8f7374e9 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -551,6 +551,7 @@ typedef struct { GDExtensionVariantPtr (*array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr GDExtensionVariantPtr (*array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr + void (*array_set_typed)(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script); // p_self should be an Array ptr /* Dictionary functions */ diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 74c0812f43..5a9ec74184 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -1499,7 +1499,18 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool p_exact } String InputEventAction::as_text() const { - return vformat(RTR("Input Action %s was %s"), action, pressed ? "pressed" : "released"); + const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(action); + if (!events) { + return String(); + } + + for (const Ref<InputEvent> &E : *events) { + if (E.is_valid()) { + return E->as_text(); + } + } + + return String(); } String InputEventAction::to_string() { diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp index c542e394a1..07d62d3a8d 100644 --- a/core/io/dtls_server.cpp +++ b/core/io/dtls_server.cpp @@ -48,6 +48,6 @@ bool DTLSServer::is_available() { } void DTLSServer::_bind_methods() { - ClassDB::bind_method(D_METHOD("setup", "key", "certificate", "chain"), &DTLSServer::setup, DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("setup", "server_options"), &DTLSServer::setup); ClassDB::bind_method(D_METHOD("take_connection", "udp_peer"), &DTLSServer::take_connection); } diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h index e749e6968b..f3fbde3c15 100644 --- a/core/io/dtls_server.h +++ b/core/io/dtls_server.h @@ -47,7 +47,7 @@ public: static bool is_available(); static DTLSServer *create(); - virtual Error setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0; + virtual Error setup(Ref<TLSOptions> p_options) = 0; virtual void stop() = 0; virtual Ref<PacketPeerDTLS> take_connection(Ref<PacketPeerUDP> p_peer) = 0; diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 829abdc614..190edbfb82 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -138,7 +138,7 @@ PackedStringArray HTTPClient::_get_response_headers() { } void HTTPClient::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_tls", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "tls_options"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(Ref<TLSOptions>())); ClassDB::bind_method(D_METHOD("set_connection", "connection"), &HTTPClient::set_connection); ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection); ClassDB::bind_method(D_METHOD("request_raw", "method", "url", "headers", "body"), &HTTPClient::_request_raw); diff --git a/core/io/http_client.h b/core/io/http_client.h index 853ea7f472..9e018182e3 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -31,6 +31,7 @@ #ifndef HTTP_CLIENT_H #define HTTP_CLIENT_H +#include "core/crypto/crypto.h" #include "core/io/ip.h" #include "core/io/stream_peer.h" #include "core/io/stream_peer_tcp.h" @@ -168,7 +169,7 @@ public: Error verify_headers(const Vector<String> &p_headers); virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0; - virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) = 0; + virtual Error connect_to_host(const String &p_host, int p_port = -1, Ref<TLSOptions> p_tls_options = Ref<TLSOptions>()) = 0; virtual void set_connection(const Ref<StreamPeer> &p_connection) = 0; virtual Ref<StreamPeer> get_connection() const = 0; diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index 5cdb13fa06..3788fa501e 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -39,29 +39,31 @@ HTTPClient *HTTPClientTCP::_create_func() { return memnew(HTTPClientTCP); } -Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_tls, bool p_verify_host) { +Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, Ref<TLSOptions> p_options) { close(); conn_port = p_port; conn_host = p_host; + tls_options = p_options; ip_candidates.clear(); - tls = p_tls; - tls_verify_host = p_verify_host; - String host_lower = conn_host.to_lower(); if (host_lower.begins_with("http://")) { conn_host = conn_host.substr(7, conn_host.length() - 7); + tls_options.unref(); } else if (host_lower.begins_with("https://")) { - tls = true; + if (tls_options.is_null()) { + tls_options = TLSOptions::client(); + } conn_host = conn_host.substr(8, conn_host.length() - 8); } + ERR_FAIL_COND_V(tls_options.is_valid() && tls_options->is_server(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER); if (conn_port < 0) { - if (tls) { + if (tls_options.is_valid()) { conn_port = PORT_HTTPS; } else { conn_port = PORT_HTTP; @@ -70,11 +72,11 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_tl connection = tcp_connection; - if (tls && https_proxy_port != -1) { + if (tls_options.is_valid() && https_proxy_port != -1) { proxy_client.instantiate(); // Needs proxy negotiation. server_host = https_proxy_host; server_port = https_proxy_port; - } else if (!tls && http_proxy_port != -1) { + } else if (tls_options.is_null() && http_proxy_port != -1) { server_host = http_proxy_host; server_port = http_proxy_port; } else { @@ -107,7 +109,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_tl void HTTPClientTCP::set_connection(const Ref<StreamPeer> &p_connection) { ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object."); - if (tls) { + if (tls_options.is_valid()) { ERR_FAIL_NULL_MSG(Object::cast_to<StreamPeerTLS>(p_connection.ptr()), "Connection is not a reference to a valid StreamPeerTLS object."); } @@ -156,7 +158,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< } String uri = p_url; - if (!tls && http_proxy_port != -1) { + if (tls_options.is_null() && http_proxy_port != -1) { uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url); } @@ -181,7 +183,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< } } if (add_host) { - if ((tls && conn_port == PORT_HTTPS) || (!tls && conn_port == PORT_HTTP)) { + if ((tls_options.is_valid() && conn_port == PORT_HTTPS) || (tls_options.is_null() && conn_port == PORT_HTTP)) { // Don't append the standard ports. request += "Host: " + conn_host + "\r\n"; } else { @@ -316,7 +318,7 @@ Error HTTPClientTCP::poll() { return OK; } break; case StreamPeerTCP::STATUS_CONNECTED: { - if (tls && proxy_client.is_valid()) { + if (tls_options.is_valid() && proxy_client.is_valid()) { Error err = proxy_client->poll(); if (err == ERR_UNCONFIGURED) { proxy_client->set_connection(tcp_connection); @@ -357,13 +359,12 @@ Error HTTPClientTCP::poll() { return ERR_CANT_CONNECT; } break; } - } else if (tls) { + } else if (tls_options.is_valid()) { Ref<StreamPeerTLS> tls_conn; if (!handshaking) { // Connect the StreamPeerTLS and start handshaking. tls_conn = Ref<StreamPeerTLS>(StreamPeerTLS::create()); - tls_conn->set_blocking_handshake_enabled(false); - Error err = tls_conn->connect_to_stream(tcp_connection, tls_verify_host, conn_host); + Error err = tls_conn->connect_to_stream(tcp_connection, conn_host, tls_options); if (err != OK) { close(); status = STATUS_TLS_HANDSHAKE_ERROR; @@ -421,7 +422,7 @@ Error HTTPClientTCP::poll() { case STATUS_BODY: case STATUS_CONNECTED: { // Check if we are still connected. - if (tls) { + if (tls_options.is_valid()) { Ref<StreamPeerTLS> tmp = connection; tmp->poll(); if (tmp->get_status() != StreamPeerTLS::STATUS_CONNECTED) { diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h index 97ac2d76a7..6060c975bc 100644 --- a/core/io/http_client_tcp.h +++ b/core/io/http_client_tcp.h @@ -33,6 +33,8 @@ #include "http_client.h" +#include "core/crypto/crypto.h" + class HTTPClientTCP : public HTTPClient { private: Status status = STATUS_DISCONNECTED; @@ -46,11 +48,10 @@ private: String http_proxy_host; int https_proxy_port = -1; // Proxy server for https requests. String https_proxy_host; - bool tls = false; - bool tls_verify_host = false; bool blocking = false; bool handshaking = false; bool head_request = false; + Ref<TLSOptions> tls_options; Vector<uint8_t> response_str; @@ -79,7 +80,7 @@ public: Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override; - Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) override; + Error connect_to_host(const String &p_host, int p_port = -1, Ref<TLSOptions> p_tls_options = Ref<TLSOptions>()) override; void set_connection(const Ref<StreamPeer> &p_connection) override; Ref<StreamPeer> get_connection() const override; void close() override; diff --git a/core/io/image.cpp b/core/io/image.cpp index 9408a9c103..736a3ec82e 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -78,6 +78,10 @@ const char *Image::format_names[Image::FORMAT_MAX] = { "ETC2_RGB8A1", "ETC2_RA_AS_RG", "FORMAT_DXT5_RA_AS_RG", + "ASTC_4x4", + "ASTC_4x4_HDR", + "ASTC_8x8", + "ASTC_8x8_HDR", }; SavePNGFunc Image::save_png_func = nullptr; @@ -2187,16 +2191,16 @@ void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Forma int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); if (unlikely(p_data.size() != size)) { - String description_mipmaps; + String description_mipmaps = get_format_name(p_format) + " "; if (p_use_mipmaps) { const int num_mipmaps = get_image_required_mipmaps(p_width, p_height, p_format); if (num_mipmaps != 1) { - description_mipmaps = vformat("with %d mipmaps", num_mipmaps); + description_mipmaps += vformat("with %d mipmaps", num_mipmaps); } else { - description_mipmaps = "with 1 mipmap"; + description_mipmaps += "with 1 mipmap"; } } else { - description_mipmaps = "without mipmaps"; + description_mipmaps += "without mipmaps"; } const String description = vformat("%dx%dx%d (%s)", p_width, p_height, get_format_pixel_size(p_format), description_mipmaps); ERR_FAIL_MSG(vformat("Expected Image data size of %s = %d bytes, got %d bytes instead.", description, size, p_data.size())); @@ -2618,35 +2622,35 @@ Error Image::decompress() { return OK; } -Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality, ASTCFormat p_astc_format) { +Error Image::compress(CompressMode p_mode, CompressSource p_source, ASTCFormat p_astc_format) { ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode."); ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source."); - return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality, p_astc_format); + return compress_from_channels(p_mode, detect_used_channels(p_source), p_astc_format); } -Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality, ASTCFormat p_astc_format) { +Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format) { ERR_FAIL_COND_V(data.is_empty(), ERR_INVALID_DATA); switch (p_mode) { case COMPRESS_S3TC: { ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); - _image_compress_bc_func(this, p_lossy_quality, p_channels); + _image_compress_bc_func(this, p_channels); } break; case COMPRESS_ETC: { ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE); - _image_compress_etc1_func(this, p_lossy_quality); + _image_compress_etc1_func(this); } break; case COMPRESS_ETC2: { ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE); - _image_compress_etc2_func(this, p_lossy_quality, p_channels); + _image_compress_etc2_func(this, p_channels); } break; case COMPRESS_BPTC: { ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); - _image_compress_bptc_func(this, p_lossy_quality, p_channels); + _image_compress_bptc_func(this, p_channels); } break; case COMPRESS_ASTC: { ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); - _image_compress_astc_func(this, p_lossy_quality, p_astc_format); + _image_compress_astc_func(this, p_astc_format); } break; case COMPRESS_MAX: { ERR_FAIL_V(ERR_INVALID_PARAMETER); @@ -3000,11 +3004,11 @@ ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; -void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nullptr; -void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr; -void (*Image::_image_compress_etc1_func)(Image *, float) = nullptr; -void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = nullptr; -void (*Image::_image_compress_astc_func)(Image *, float, Image::ASTCFormat) = nullptr; +void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr; +void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr; +void (*Image::_image_compress_etc1_func)(Image *) = nullptr; +void (*Image::_image_compress_etc2_func)(Image *, Image::UsedChannels) = nullptr; +void (*Image::_image_compress_astc_func)(Image *, Image::ASTCFormat) = nullptr; void (*Image::_image_decompress_bc)(Image *) = nullptr; void (*Image::_image_decompress_bptc)(Image *) = nullptr; void (*Image::_image_decompress_etc1)(Image *) = nullptr; @@ -3426,8 +3430,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible); ClassDB::bind_method(D_METHOD("detect_used_channels", "source"), &Image::detect_used_channels, DEFVAL(COMPRESS_SOURCE_GENERIC)); - ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality", "astc_format"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(0.7), DEFVAL(ASTC_FORMAT_4x4)); - ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "lossy_quality", "astc_format"), &Image::compress_from_channels, DEFVAL(0.7), DEFVAL(ASTC_FORMAT_4x4)); + ClassDB::bind_method(D_METHOD("compress", "mode", "source", "astc_format"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(ASTC_FORMAT_4x4)); + ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "astc_format"), &Image::compress_from_channels, DEFVAL(ASTC_FORMAT_4x4)); ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress); ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed); @@ -3547,11 +3551,11 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(ASTC_FORMAT_8x8); } -void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { +void Image::set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels)) { _image_compress_bc_func = p_compress_func; } -void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { +void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels)) { _image_compress_bptc_func = p_compress_func; } diff --git a/core/io/image.h b/core/io/image.h index 29ceb9478f..8e353a8bb7 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -149,11 +149,11 @@ public: static ImageMemLoadFunc _tga_mem_loader_func; static ImageMemLoadFunc _bmp_mem_loader_func; - static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels); - static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels); - static void (*_image_compress_etc1_func)(Image *, float); - static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels); - static void (*_image_compress_astc_func)(Image *, float, ASTCFormat p_format); + static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels); + static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels); + static void (*_image_compress_etc1_func)(Image *); + static void (*_image_compress_etc2_func)(Image *, UsedChannels p_channels); + static void (*_image_compress_astc_func)(Image *, ASTCFormat p_format); static void (*_image_decompress_bc)(Image *); static void (*_image_decompress_bptc)(Image *); @@ -368,8 +368,8 @@ public: COMPRESS_SOURCE_MAX, }; - Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7, ASTCFormat p_astc_format = ASTC_FORMAT_4x4); - Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality = 0.7, ASTCFormat p_astc_format = ASTC_FORMAT_4x4); + Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, ASTCFormat p_astc_format = ASTC_FORMAT_4x4); + Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format = ASTC_FORMAT_4x4); Error decompress(); bool is_compressed() const; @@ -391,8 +391,8 @@ public: Rect2i get_used_rect() const; Ref<Image> get_region(const Rect2i &p_area) const; - static void set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)); - static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)); + static void set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels)); + static void set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels)); static String get_format_name(Format p_format); Error load_png_from_buffer(const Vector<uint8_t> &p_array); diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp index c0998f10bc..18bef3ff3c 100644 --- a/core/io/packet_peer_dtls.cpp +++ b/core/io/packet_peer_dtls.cpp @@ -48,7 +48,7 @@ bool PacketPeerDTLS::is_available() { void PacketPeerDTLS::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &PacketPeerDTLS::poll); - ClassDB::bind_method(D_METHOD("connect_to_peer", "packet_peer", "validate_certs", "for_hostname", "valid_certificate"), &PacketPeerDTLS::connect_to_peer, DEFVAL(true), DEFVAL(String()), DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("connect_to_peer", "packet_peer", "hostname", "client_options"), &PacketPeerDTLS::connect_to_peer, DEFVAL(Ref<TLSOptions>())); ClassDB::bind_method(D_METHOD("get_status"), &PacketPeerDTLS::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_peer"), &PacketPeerDTLS::disconnect_from_peer); diff --git a/core/io/packet_peer_dtls.h b/core/io/packet_peer_dtls.h index 5ba1faed7c..3990a851f7 100644 --- a/core/io/packet_peer_dtls.h +++ b/core/io/packet_peer_dtls.h @@ -53,7 +53,7 @@ public: }; virtual void poll() = 0; - virtual Error connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_validate_certs = true, const String &p_for_hostname = String(), Ref<X509Certificate> p_ca_certs = Ref<X509Certificate>()) = 0; + virtual Error connect_to_peer(Ref<PacketPeerUDP> p_base, const String &p_hostname, Ref<TLSOptions> p_options = Ref<TLSOptions>()) = 0; virtual void disconnect_from_peer() = 0; virtual Status get_status() const = 0; diff --git a/core/io/resource.cpp b/core/io/resource.cpp index e44bbc246b..4abcbffb61 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -260,15 +260,35 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const { } Variant p = get(E.name); - if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) { - r->set(E.name, p.duplicate(p_subresources)); - } else if (p.get_type() == Variant::OBJECT && !(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) { - Ref<Resource> sr = p; - if (sr.is_valid()) { - r->set(E.name, sr->duplicate(p_subresources)); + switch (p.get_type()) { + case Variant::Type::DICTIONARY: + case Variant::Type::ARRAY: + case Variant::Type::PACKED_BYTE_ARRAY: + case Variant::Type::PACKED_COLOR_ARRAY: + case Variant::Type::PACKED_INT32_ARRAY: + case Variant::Type::PACKED_INT64_ARRAY: + case Variant::Type::PACKED_FLOAT32_ARRAY: + case Variant::Type::PACKED_FLOAT64_ARRAY: + case Variant::Type::PACKED_STRING_ARRAY: + case Variant::Type::PACKED_VECTOR2_ARRAY: + case Variant::Type::PACKED_VECTOR3_ARRAY: { + r->set(E.name, p.duplicate(p_subresources)); + } break; + + case Variant::Type::OBJECT: { + if (!(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) { + Ref<Resource> sr = p; + if (sr.is_valid()) { + r->set(E.name, sr->duplicate(p_subresources)); + } + } else { + r->set(E.name, p); + } + } break; + + default: { + r->set(E.name, p); } - } else { - r->set(E.name, p); } } diff --git a/core/io/stream_peer_tls.cpp b/core/io/stream_peer_tls.cpp index 71fadd1d30..69877974e6 100644 --- a/core/io/stream_peer_tls.cpp +++ b/core/io/stream_peer_tls.cpp @@ -41,31 +41,17 @@ StreamPeerTLS *StreamPeerTLS::create() { return nullptr; } -bool StreamPeerTLS::available = false; - bool StreamPeerTLS::is_available() { - return available; -} - -void StreamPeerTLS::set_blocking_handshake_enabled(bool p_enabled) { - blocking_handshake = p_enabled; -} - -bool StreamPeerTLS::is_blocking_handshake_enabled() const { - return blocking_handshake; + return _create != nullptr; } void StreamPeerTLS::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerTLS::poll); - ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerTLS::accept_stream, DEFVAL(Ref<X509Certificate>())); - ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerTLS::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("accept_stream", "stream", "server_options"), &StreamPeerTLS::accept_stream); + ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "common_name", "client_options"), &StreamPeerTLS::connect_to_stream, DEFVAL(Ref<TLSOptions>())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTLS::get_status); ClassDB::bind_method(D_METHOD("get_stream"), &StreamPeerTLS::get_stream); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerTLS::disconnect_from_stream); - ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerTLS::set_blocking_handshake_enabled); - ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerTLS::is_blocking_handshake_enabled); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled"); BIND_ENUM_CONSTANT(STATUS_DISCONNECTED); BIND_ENUM_CONSTANT(STATUS_HANDSHAKING); diff --git a/core/io/stream_peer_tls.h b/core/io/stream_peer_tls.h index 6666107ad8..5894abb7a4 100644 --- a/core/io/stream_peer_tls.h +++ b/core/io/stream_peer_tls.h @@ -41,10 +41,6 @@ protected: static StreamPeerTLS *(*_create)(); static void _bind_methods(); - static bool available; - - bool blocking_handshake = true; - public: enum Status { STATUS_DISCONNECTED, @@ -54,12 +50,9 @@ public: STATUS_ERROR_HOSTNAME_MISMATCH }; - void set_blocking_handshake_enabled(bool p_enabled); - bool is_blocking_handshake_enabled() const; - virtual void poll() = 0; - virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0; - virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()) = 0; + virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<TLSOptions> p_options) = 0; + virtual Error connect_to_stream(Ref<StreamPeer> p_base, const String &p_common_name, Ref<TLSOptions> p_options) = 0; virtual Status get_status() const = 0; virtual Ref<StreamPeer> get_stream() const = 0; diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 646bdea758..f0f160940d 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -327,7 +327,7 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) { bool found_route = false; - Vector<Point *> open_list; + LocalVector<Point *> open_list; SortArray<Point *, SortPoints> sorter; begin_point->g_score = 0; @@ -335,19 +335,19 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) { open_list.push_back(begin_point); while (!open_list.is_empty()) { - Point *p = open_list[0]; // The currently processed point + Point *p = open_list[0]; // The currently processed point. if (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 + sorter.pop_heap(0, open_list.size(), open_list.ptr()); // 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 + p->closed_pass = pass; // Mark the point as closed. for (OAHashMap<int64_t, Point *>::Iterator it = p->neighbors.iter(); it.valid; it = p->neighbors.next_iter(it)) { - Point *e = *(it.value); // The neighbor point + Point *e = *(it.value); // The neighbor point. if (!e->enabled || e->closed_pass == pass) { continue; @@ -370,9 +370,9 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) { e->f_score = e->g_score + _estimate_cost(e->id, 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()); + sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr()); } else { - sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw()); + sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptr()); } } } diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 677e609763..139dc3afb1 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -265,7 +265,7 @@ AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) { return nullptr; } -void AStarGrid2D::_get_nbors(Point *p_point, List<Point *> &r_nbors) { +void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) { bool ts0 = false, td0 = false, ts1 = false, td1 = false, ts2 = false, td2 = false, @@ -378,7 +378,7 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { bool found_route = false; - Vector<Point *> open_list; + LocalVector<Point *> open_list; SortArray<Point *, SortPoints> sorter; p_begin_point->g_score = 0; @@ -394,14 +394,14 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { break; } - sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list. + sorter.pop_heap(0, open_list.size(), open_list.ptr()); // 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; + LocalVector<Point *> nbors; _get_nbors(p, nbors); - for (List<Point *>::Element *E = nbors.front(); E; E = E->next()) { - Point *e = E->get(); // The neighbor point. + + for (Point *e : nbors) { real_t weight_scale = 1.0; if (jumping_enabled) { @@ -433,9 +433,9 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { 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()); + sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr()); } else { - sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw()); + sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptr()); } } } diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index 6b9012a5e3..e4e62ec360 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -124,7 +124,7 @@ private: // Internal routines. return &points[p_y][p_x]; } - void _get_nbors(Point *p_point, List<Point *> &r_nbors); + void _get_nbors(Point *p_point, LocalVector<Point *> &r_nbors); Point *_jump(Point *p_from, Point *p_to); bool _solve(Point *p_begin_point, Point *p_end_point); diff --git a/core/math/bvh.h b/core/math/bvh.h index 357d483375..ea8289607e 100644 --- a/core/math/bvh.h +++ b/core/math/bvh.h @@ -780,7 +780,7 @@ private: if (p_thread_safe) { _mutex = p_mutex; - if (_mutex->try_lock() != OK) { + if (!_mutex->try_lock()) { WARN_PRINT("Info : multithread BVH access detected (benign)"); _mutex->lock(); } diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index 4786110054..590483bf20 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -151,7 +151,7 @@ void Geometry3D::MeshData::optimize_vertices() { } } - for (MeshData::Edge edge : edges) { + for (MeshData::Edge &edge : edges) { int a = edge.vertex_a; int b = edge.vertex_b; diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 910995d717..96010b4096 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -263,39 +263,12 @@ real_t Transform2D::basis_determinant() const { return columns[0].x * columns[1].y - columns[0].y * columns[1].x; } -Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_c) const { - //extract parameters - Vector2 p1 = get_origin(); - Vector2 p2 = p_transform.get_origin(); - - real_t r1 = get_rotation(); - real_t r2 = p_transform.get_rotation(); - - Size2 s1 = get_scale(); - Size2 s2 = p_transform.get_scale(); - - //slerp rotation - Vector2 v1(Math::cos(r1), Math::sin(r1)); - Vector2 v2(Math::cos(r2), Math::sin(r2)); - - real_t dot = v1.dot(v2); - - dot = CLAMP(dot, (real_t)-1.0, (real_t)1.0); - - Vector2 v; - - if (dot > 0.9995f) { - v = v1.lerp(v2, p_c).normalized(); //linearly interpolate to avoid numerical precision issues - } else { - real_t angle = p_c * Math::acos(dot); - Vector2 v3 = (v2 - v1 * dot).normalized(); - v = v1 * Math::cos(angle) + v3 * Math::sin(angle); - } - - //construct matrix - Transform2D res(v.angle(), p1.lerp(p2, p_c)); - res.scale_basis(s1.lerp(s2, p_c)); - return res; +Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_weight) const { + return Transform2D( + Math::lerp_angle(get_rotation(), p_transform.get_rotation(), p_weight), + get_scale().lerp(p_transform.get_scale(), p_weight), + Math::lerp_angle(get_skew(), p_transform.get_skew(), p_weight), + get_origin().lerp(p_transform.get_origin(), p_weight)); } void Transform2D::operator*=(const real_t p_val) { diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 0ee95835a6..18f27ae4a4 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -72,6 +72,7 @@ def generate_version(argcount, const=False, returns=False): s = s.replace("$RVOID", "(void)r_ret;") # If required, may lead to uninitialized errors s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;") method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n" + method_info += "\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;\\\n" else: s = s.replace("$RET", "") s = s.replace("$RVOID", "") @@ -113,6 +114,9 @@ def generate_version(argcount, const=False, returns=False): ) callptrargsptr += "&argval" + str(i + 1) method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n" + method_info += ( + "\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::METADATA);\\\n" + ) if argcount: callsiargs += "};\\\n" diff --git a/core/object/object.cpp b/core/object/object.cpp index a8b9e00c96..57aa1339ec 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1549,7 +1549,9 @@ void Object::_bind_methods() { #define BIND_OBJ_CORE_METHOD(m_method) \ ::ClassDB::add_virtual_method(get_class_static(), m_method, true, Vector<String>(), true); - BIND_OBJ_CORE_METHOD(MethodInfo("_notification", PropertyInfo(Variant::INT, "what"))); + MethodInfo notification_mi("_notification", PropertyInfo(Variant::INT, "what")); + notification_mi.arguments_metadata.push_back(GodotTypeInfo::Metadata::METADATA_INT_IS_INT32); + BIND_OBJ_CORE_METHOD(notification_mi); BIND_OBJ_CORE_METHOD(MethodInfo(Variant::BOOL, "_set", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value"))); #ifdef TOOLS_ENABLED MethodInfo miget("_get", PropertyInfo(Variant::STRING_NAME, "property")); diff --git a/core/object/object.h b/core/object/object.h index ec77da4ee1..5ec69a371b 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -223,6 +223,16 @@ struct MethodInfo { int id = 0; List<PropertyInfo> arguments; Vector<Variant> default_arguments; + int return_val_metadata = 0; + Vector<int> arguments_metadata; + + int get_argument_meta(int p_arg) const { + ERR_FAIL_COND_V(p_arg < -1 || p_arg > arguments.size(), 0); + if (p_arg == -1) { + return return_val_metadata; + } + return arguments_metadata.size() > p_arg ? arguments_metadata[p_arg] : 0; + } inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id; } inline bool operator<(const MethodInfo &p_method) const { return id == p_method.id ? (name < p_method.name) : (id < p_method.id); } diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 6315356510..84017e89a6 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -124,8 +124,6 @@ enum class Key { KP_7 = SPECIAL | 0x8D, KP_8 = SPECIAL | 0x8E, KP_9 = SPECIAL | 0x8F, - SUPER_L = SPECIAL | 0x40, - SUPER_R = SPECIAL | 0x41, MENU = SPECIAL | 0x42, HYPER = SPECIAL | 0x43, HELP = SPECIAL | 0x45, diff --git a/core/os/mutex.h b/core/os/mutex.h index c91917a9a1..ceedcb235a 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -31,7 +31,6 @@ #ifndef MUTEX_H #define MUTEX_H -#include "core/error/error_list.h" #include "core/typedefs.h" #include <mutex> @@ -49,8 +48,8 @@ public: mutex.unlock(); } - _ALWAYS_INLINE_ Error try_lock() const { - return mutex.try_lock() ? OK : ERR_BUSY; + _ALWAYS_INLINE_ bool try_lock() const { + return mutex.try_lock(); } }; diff --git a/core/os/os.cpp b/core/os/os.cpp index 86469852e3..ef7d860d19 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -553,6 +553,16 @@ void OS::add_frame_delay(bool p_can_draw) { } } +OS::PreferredTextureFormat OS::get_preferred_texture_format() const { +#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) + return PREFERRED_TEXTURE_FORMAT_ETC2_ASTC; // By rule, ARM hardware uses ETC texture compression. +#elif defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) + return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // By rule, X86 hardware prefers S3TC and derivatives. +#else + return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // Override in platform if needed. +#endif +} + OS::OS() { singleton = this; diff --git a/core/os/os.h b/core/os/os.h index 436a83eae3..d77890d89d 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -290,6 +290,14 @@ public: virtual Vector<String> get_granted_permissions() const { return Vector<String>(); } virtual void process_and_drop_events() {} + + enum PreferredTextureFormat { + PREFERRED_TEXTURE_FORMAT_S3TC_BPTC, + PREFERRED_TEXTURE_FORMAT_ETC2_ASTC + }; + + virtual PreferredTextureFormat get_preferred_texture_format() const; + OS(); virtual ~OS(); }; diff --git a/core/os/rw_lock.h b/core/os/rw_lock.h index e290b7c00b..a232fcb1ce 100644 --- a/core/os/rw_lock.h +++ b/core/os/rw_lock.h @@ -31,7 +31,7 @@ #ifndef RW_LOCK_H #define RW_LOCK_H -#include "core/error/error_list.h" +#include "core/typedefs.h" #include <shared_mutex> @@ -39,34 +39,34 @@ class RWLock { mutable std::shared_timed_mutex mutex; public: - // Lock the rwlock, block if locked by someone else - void read_lock() const { + // Lock the RWLock, block if locked by someone else. + _ALWAYS_INLINE_ void read_lock() const { mutex.lock_shared(); } - // Unlock the rwlock, let other threads continue - void read_unlock() const { + // Unlock the RWLock, let other threads continue. + _ALWAYS_INLINE_ void read_unlock() const { mutex.unlock_shared(); } - // Attempt to lock the rwlock, OK on success, ERR_BUSY means it can't lock. - Error read_try_lock() const { - return mutex.try_lock_shared() ? OK : ERR_BUSY; + // Attempt to lock the RWLock for reading. True on success, false means it can't lock. + _ALWAYS_INLINE_ bool read_try_lock() const { + return mutex.try_lock_shared(); } - // Lock the rwlock, block if locked by someone else - void write_lock() { + // Lock the RWLock, block if locked by someone else. + _ALWAYS_INLINE_ void write_lock() { mutex.lock(); } - // Unlock the rwlock, let other thwrites continue - void write_unlock() { + // Unlock the RWLock, let other threads continue. + _ALWAYS_INLINE_ void write_unlock() { mutex.unlock(); } - // Attempt to lock the rwlock, OK on success, ERR_BUSY means it can't lock. - Error write_try_lock() { - return mutex.try_lock() ? OK : ERR_BUSY; + // Attempt to lock the RWLock for writing. True on success, false means it can't lock. + _ALWAYS_INLINE_ bool write_try_lock() { + return mutex.try_lock(); } }; @@ -74,11 +74,11 @@ class RWLockRead { const RWLock &lock; public: - RWLockRead(const RWLock &p_lock) : + _ALWAYS_INLINE_ RWLockRead(const RWLock &p_lock) : lock(p_lock) { lock.read_lock(); } - ~RWLockRead() { + _ALWAYS_INLINE_ ~RWLockRead() { lock.read_unlock(); } }; @@ -87,11 +87,11 @@ class RWLockWrite { RWLock &lock; public: - RWLockWrite(RWLock &p_lock) : + _ALWAYS_INLINE_ RWLockWrite(RWLock &p_lock) : lock(p_lock) { lock.write_lock(); } - ~RWLockWrite() { + _ALWAYS_INLINE_ ~RWLockWrite() { lock.write_unlock(); } }; diff --git a/core/os/semaphore.h b/core/os/semaphore.h index 6a290f21c6..a992a4587d 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -39,32 +39,33 @@ class Semaphore { private: - mutable std::mutex mutex_; - mutable std::condition_variable condition_; - mutable unsigned long count_ = 0; // Initialized as locked. + mutable std::mutex mutex; + mutable std::condition_variable condition; + mutable uint32_t count = 0; // Initialized as locked. public: _ALWAYS_INLINE_ void post() const { - std::lock_guard<decltype(mutex_)> lock(mutex_); - ++count_; - condition_.notify_one(); + std::lock_guard lock(mutex); + count++; + condition.notify_one(); } _ALWAYS_INLINE_ void wait() const { - std::unique_lock<decltype(mutex_)> lock(mutex_); - while (!count_) { // Handle spurious wake-ups. - condition_.wait(lock); + std::unique_lock lock(mutex); + while (!count) { // Handle spurious wake-ups. + condition.wait(lock); } - --count_; + count--; } _ALWAYS_INLINE_ bool try_wait() const { - std::lock_guard<decltype(mutex_)> lock(mutex_); - if (count_) { - --count_; + std::lock_guard lock(mutex); + if (count) { + count--; return true; + } else { + return false; } - return false; } }; diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 700174bdae..a374e7c009 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -209,6 +209,7 @@ void register_core_types() { GDREGISTER_CLASS(AESContext); ClassDB::register_custom_instance_class<X509Certificate>(); ClassDB::register_custom_instance_class<CryptoKey>(); + GDREGISTER_ABSTRACT_CLASS(TLSOptions); ClassDB::register_custom_instance_class<HMACContext>(); ClassDB::register_custom_instance_class<Crypto>(); ClassDB::register_custom_instance_class<StreamPeerTLS>(); diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 9e468f7075..b34d9f3271 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -4342,6 +4342,9 @@ bool String::is_valid_html_color() const { return Color::html_is_valid(*this); } +// Changes made to the set of invalid filename characters must also be reflected in the String documentation for is_valid_filename. +static const char *invalid_filename_characters = ": / \\ ? * \" | % < >"; + bool String::is_valid_filename() const { String stripped = strip_edges(); if (*this != stripped) { @@ -4352,7 +4355,22 @@ bool String::is_valid_filename() const { return false; } - return !(find(":") != -1 || find("/") != -1 || find("\\") != -1 || find("?") != -1 || find("*") != -1 || find("\"") != -1 || find("|") != -1 || find("%") != -1 || find("<") != -1 || find(">") != -1); + Vector<String> chars = String(invalid_filename_characters).split(" "); + for (const String &ch : chars) { + if (contains(ch)) { + return false; + } + } + return true; +} + +String String::validate_filename() const { + Vector<String> chars = String(invalid_filename_characters).split(" "); + String name = strip_edges(); + for (int i = 0; i < chars.size(); i++) { + name = name.replace(chars[i], "_"); + } + return name; } bool String::is_valid_ip_address() const { diff --git a/core/string/ustring.h b/core/string/ustring.h index 6338f1d3cd..1582504c57 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -433,6 +433,7 @@ public: static const String invalid_node_name_characters; String validate_node_name() const; String validate_identifier() const; + String validate_filename() const; bool is_valid_identifier() const; bool is_valid_int() const; diff --git a/core/variant/array.cpp b/core/variant/array.cpp index f8af78f3c1..94d1596514 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -775,15 +775,9 @@ Variant Array::get_typed_script() const { return _p->typed.script; } -void Array::set_read_only(bool p_enable) { - if (p_enable == bool(_p->read_only != nullptr)) { - return; - } - if (p_enable) { +void Array::make_read_only() { + if (_p->read_only == nullptr) { _p->read_only = memnew(Variant); - } else { - memdelete(_p->read_only); - _p->read_only = nullptr; } } diff --git a/core/variant/array.h b/core/variant/array.h index 3728c22fe0..d9ca3278fb 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -127,7 +127,7 @@ public: StringName get_typed_class_name() const; Variant get_typed_script() const; - void set_read_only(bool p_enable); + void make_read_only(); bool is_read_only() const; Array(const Array &p_base, uint32_t p_type, const StringName &p_class_name, const Variant &p_script); diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index f87064a0d1..0429508cc5 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -333,15 +333,9 @@ Dictionary Dictionary::duplicate(bool p_deep) const { return recursive_duplicate(p_deep, 0); } -void Dictionary::set_read_only(bool p_enable) { - if (p_enable == bool(_p->read_only != nullptr)) { - return; - } - if (p_enable) { +void Dictionary::make_read_only() { + if (_p->read_only == nullptr) { _p->read_only = memnew(Variant); - } else { - memdelete(_p->read_only); - _p->read_only = nullptr; } } bool Dictionary::is_read_only() const { diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h index e6d8ebe25b..8935d35ed9 100644 --- a/core/variant/dictionary.h +++ b/core/variant/dictionary.h @@ -86,7 +86,7 @@ public: Dictionary duplicate(bool p_deep = false) const; Dictionary recursive_duplicate(bool p_deep, int recursion_count) const; - void set_read_only(bool p_enable); + void make_read_only(); bool is_read_only() const; const void *id() const; diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index ca42738b05..672b030806 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3492,6 +3492,46 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const } } +bool Variant::identity_compare(const Variant &p_variant) const { + if (type != p_variant.type) { + return false; + } + + switch (type) { + case OBJECT: { + return _get_obj().obj == p_variant._get_obj().obj; + } break; + + case DICTIONARY: { + const Dictionary &l = *(reinterpret_cast<const Dictionary *>(_data._mem)); + const Dictionary &r = *(reinterpret_cast<const Dictionary *>(p_variant._data._mem)); + return l.id() == r.id(); + } break; + + case ARRAY: { + const Array &l = *(reinterpret_cast<const Array *>(_data._mem)); + const Array &r = *(reinterpret_cast<const Array *>(p_variant._data._mem)); + return l.id() == r.id(); + } break; + + case PACKED_BYTE_ARRAY: + case PACKED_INT32_ARRAY: + case PACKED_INT64_ARRAY: + case PACKED_FLOAT32_ARRAY: + case PACKED_FLOAT64_ARRAY: + case PACKED_STRING_ARRAY: + case PACKED_VECTOR2_ARRAY: + case PACKED_VECTOR3_ARRAY: + case PACKED_COLOR_ARRAY: { + return _data.packed_array == p_variant._data.packed_array; + } break; + + default: { + return hash_compare(p_variant); + } + } +} + bool StringLikeVariantComparator::compare(const Variant &p_lhs, const Variant &p_rhs) { if (p_lhs.hash_compare(p_rhs)) { return true; diff --git a/core/variant/variant.h b/core/variant/variant.h index b9294de77d..b2f31a6d57 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -748,6 +748,7 @@ public: uint32_t recursive_hash(int recursion_count) const; bool hash_compare(const Variant &p_variant, int recursion_count = 0) const; + bool identity_compare(const Variant &p_variant) const; bool booleanize() const; String stringify(int recursion_count = 0) const; String to_json_string() const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 9e8c6fccb3..e7d1a2478f 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1695,6 +1695,7 @@ static void _register_variant_builtin_methods() { bind_string_method(json_escape, sarray(), varray()); bind_string_method(validate_node_name, sarray(), varray()); + bind_string_method(validate_filename, sarray(), varray()); bind_string_method(is_valid_identifier, sarray(), varray()); bind_string_method(is_valid_int, sarray(), varray()); @@ -2179,6 +2180,8 @@ static void _register_variant_builtin_methods() { bind_method(Dictionary, values, sarray(), varray()); bind_method(Dictionary, duplicate, sarray("deep"), varray(false)); bind_method(Dictionary, get, sarray("key", "default"), varray(Variant())); + bind_method(Dictionary, make_read_only, sarray(), varray()); + bind_method(Dictionary, is_read_only, sarray(), varray()); /* Array */ @@ -2221,12 +2224,11 @@ static void _register_variant_builtin_methods() { bind_method(Array, max, sarray(), varray()); bind_method(Array, min, sarray(), varray()); bind_method(Array, typed_assign, sarray("array"), varray()); - bind_method(Array, set_typed, sarray("type", "class_name", "script"), varray()); bind_method(Array, is_typed, sarray(), varray()); bind_method(Array, get_typed_builtin, sarray(), varray()); bind_method(Array, get_typed_class_name, sarray(), varray()); bind_method(Array, get_typed_script, sarray(), varray()); - bind_method(Array, set_read_only, sarray("enable"), varray()); + bind_method(Array, make_read_only, sarray(), varray()); bind_method(Array, is_read_only, sarray(), varray()); /* Byte Array */ diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index fe7150bca9..042ebe368a 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -1007,9 +1007,14 @@ struct VariantUtilityFunctions { static inline uint64_t rid_allocate_id() { return RID_AllocBase::_gen_id(); } + static inline RID rid_from_int64(uint64_t p_base) { return RID::from_uint64(p_base); } + + static inline bool is_same(const Variant &p_a, const Variant &p_b) { + return p_a.identity_compare(p_b); + } }; #ifdef DEBUG_METHODS_ENABLED @@ -1601,6 +1606,8 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(rid_allocate_id, Vector<String>(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(rid_from_int64, sarray("base"), Variant::UTILITY_FUNC_TYPE_GENERAL); + + FUNCBINDR(is_same, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_GENERAL); } void Variant::_unregister_variant_utility_functions() { diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 4fdb7d82c5..0d6524ccbe 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -442,9 +442,14 @@ <param index="0" name="variable" type="Variant" /> <description> Returns the integer hash of the passed [param variable]. - [codeblock] + [codeblocks] + [gdscript] print(hash("a")) # Prints 177670 - [/codeblock] + [/gdscript] + [csharp] + GD.Print(GD.Hash("a")); // Prints 177670 + [/csharp] + [/codeblocks] </description> </method> <method name="instance_from_id"> @@ -452,13 +457,29 @@ <param index="0" name="instance_id" type="int" /> <description> Returns the [Object] that corresponds to [param instance_id]. All Objects have a unique instance ID. See also [method Object.get_instance_id]. - [codeblock] + [codeblocks] + [gdscript] var foo = "bar" + func _ready(): var id = get_instance_id() var inst = instance_from_id(id) print(inst.foo) # Prints bar - [/codeblock] + [/gdscript] + [csharp] + public partial class MyNode : Node + { + public string Foo { get; set; } = "bar"; + + public override void _Ready() + { + ulong id = GetInstanceId(); + var inst = (MyNode)InstanceFromId(Id); + GD.Print(inst.Foo); // Prints bar + } + } + [/csharp] + [/codeblocks] </description> </method> <method name="inverse_lerp"> @@ -525,6 +546,31 @@ Returns [code]true[/code] if [param x] is a NaN ("Not a Number" or invalid) value. </description> </method> + <method name="is_same"> + <return type="bool" /> + <param index="0" name="a" type="Variant" /> + <param index="1" name="b" type="Variant" /> + <description> + Returns [code]true[/code], for value types, if [param a] and [param b] share the same value. Returns [code]true[/code], for reference types, if the references of [param a] and [param b] are the same. + [codeblock] + # Vector2 is a value type + var vec2_a = Vector2(0, 0) + var vec2_b = Vector2(0, 0) + var vec2_c = Vector2(1, 1) + is_same(vec2_a, vec2_a) # true + is_same(vec2_a, vec2_b) # true + is_same(vec2_a, vec2_c) # false + + # Array is a reference type + var arr_a = [] + var arr_b = [] + is_same(arr_a, arr_a) # true + is_same(arr_a, arr_b) # false + [/codeblock] + These are [Variant] value types: [code]null[/code], [bool], [int], [float], [String], [StringName], [Vector2], [Vector2i], [Vector3], [Vector3i], [Vector4], [Vector4i], [Rect2], [Rect2i], [Transform2D], [Transform3D], [Plane], [Quaternion], [AABB], [Basis], [Projection], [Color], [NodePath], [RID], [Callable] and [Signal]. + These are [Variant] reference types: [Object], [Dictionary], [Array], [PackedByteArray], [PackedInt32Array], [PackedInt64Array], [PackedFloat32Array], [PackedFloat64Array], [PackedStringArray], [PackedVector2Array], [PackedVector3Array] and [PackedColorArray]. + </description> + </method> <method name="is_zero_approx"> <return type="bool" /> <param index="0" name="x" type="float" /> @@ -764,10 +810,16 @@ <method name="print" qualifiers="vararg"> <description> Converts one or more arguments of any type to string in the best way possible and prints them to the console. - [codeblock] + [codeblocks] + [gdscript] var a = [1, 2, 3] print("a", "b", a) # Prints ab[1, 2, 3] - [/codeblock] + [/gdscript] + [csharp] + var a = new Godot.Collections.Array { 1, 2, 3 }; + GD.Print("a", "b", a); // Prints ab[1, 2, 3] + [/csharp] + [/codeblocks] [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print] or [method print_rich]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. </description> </method> @@ -775,9 +827,14 @@ <description> Converts one or more arguments of any type to string in the best way possible and prints them to the console. The following BBCode tags are supported: b, i, u, s, indent, code, url, center, right, color, bgcolor, fgcolor. Color tags only support named colors such as [code]red[/code], [i]not[/i] hexadecimal color codes. Unsupported tags will be left as-is in standard output. When printing to standard output, the supported subset of BBCode is converted to ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes is currently only supported on Linux and macOS. Support for ANSI escape codes may vary across terminal emulators, especially for italic and strikethrough. - [codeblock] + [codeblocks] + [gdscript] print_rich("[code][b]Hello world![/b][/code]") # Prints out: [b]Hello world![/b] - [/codeblock] + [/gdscript] + [csharp] + GD.PrintRich("[code][b]Hello world![/b][/code]"); // Prints out: [b]Hello world![/b] + [/csharp] + [/codeblocks] [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print] or [method print_rich]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. </description> </method> @@ -789,53 +846,86 @@ <method name="printerr" qualifiers="vararg"> <description> Prints one or more arguments to strings in the best way possible to standard error line. - [codeblock] + [codeblocks] + [gdscript] printerr("prints to stderr") - [/codeblock] + [/gdscript] + [csharp] + GD.PrintErr("prints to stderr"); + [/csharp] + [/codeblocks] </description> </method> <method name="printraw" qualifiers="vararg"> <description> Prints one or more arguments to strings in the best way possible to the OS terminal. Unlike [method print], no newline is automatically added at the end. - [codeblock] + [codeblocks] + [gdscript] printraw("A") printraw("B") printraw("C") # Prints ABC to terminal - [/codeblock] + [/gdscript] + [csharp] + GD.PrintRaw("A"); + GD.PrintRaw("B"); + GD.PrintRaw("C"); + // Prints ABC to terminal + [/csharp] + [/codeblocks] </description> </method> <method name="prints" qualifiers="vararg"> <description> Prints one or more arguments to the console with a space between each argument. - [codeblock] + [codeblocks] + [gdscript] prints("A", "B", "C") # Prints A B C - [/codeblock] + [/gdscript] + [csharp] + GD.PrintS("A", "B", "C"); // Prints A B C + [/csharp] + [/codeblocks] </description> </method> <method name="printt" qualifiers="vararg"> <description> Prints one or more arguments to the console with a tab between each argument. - [codeblock] + [codeblocks] + [gdscript] printt("A", "B", "C") # Prints A B C - [/codeblock] + [/gdscript] + [csharp] + GD.PrintT("A", "B", "C"); // Prints A B C + [/csharp] + [/codeblocks] </description> </method> <method name="push_error" qualifiers="vararg"> <description> Pushes an error message to Godot's built-in debugger and to the OS terminal. - [codeblock] + [codeblocks] + [gdscript] push_error("test error") # Prints "test error" to debugger and terminal as error call - [/codeblock] + [/gdscript] + [csharp] + GD.PushError("test error"); // Prints "test error" to debugger and terminal as error call + [/csharp] + [/codeblocks] [b]Note:[/b] This function does not pause project execution. To print an error message and pause project execution in debug builds, use [code]assert(false, "test error")[/code] instead. </description> </method> <method name="push_warning" qualifiers="vararg"> <description> Pushes a warning message to Godot's built-in debugger and to the OS terminal. - [codeblock] + [codeblocks] + [gdscript] push_warning("test warning") # Prints "test warning" to debugger and terminal as warning call - [/codeblock] + [/gdscript] + [csharp] + GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call + [/csharp] + [/codeblocks] </description> </method> <method name="rad_to_deg"> @@ -868,9 +958,14 @@ <return type="float" /> <description> Returns a random floating point value between [code]0.0[/code] and [code]1.0[/code] (inclusive). - [codeblock] + [codeblocks] + [gdscript] randf() # Returns e.g. 0.375671 - [/codeblock] + [/gdscript] + [csharp] + GD.Randf(); // Returns e.g. 0.375671 + [/csharp] + [/codeblocks] </description> </method> <method name="randf_range"> @@ -879,10 +974,16 @@ <param index="1" name="to" type="float" /> <description> Returns a random floating point value between [param from] and [param to] (inclusive). - [codeblock] + [codeblocks] + [gdscript] randf_range(0, 20.5) # Returns e.g. 7.45315 randf_range(-10, 10) # Returns e.g. -3.844535 - [/codeblock] + [/gdscript] + [csharp] + GD.RandRange(0.0, 20.5); // Returns e.g. 7.45315 + GD.RandRange(-10.0, 10.0); // Returns e.g. -3.844535 + [/csharp] + [/codeblocks] </description> </method> <method name="randfn"> @@ -897,12 +998,20 @@ <return type="int" /> <description> Returns a random unsigned 32-bit integer. Use remainder to obtain a random value in the interval [code][0, N - 1][/code] (where N is smaller than 2^32). - [codeblock] + [codeblocks] + [gdscript] randi() # Returns random integer between 0 and 2^32 - 1 randi() % 20 # Returns random integer between 0 and 19 randi() % 100 # Returns random integer between 0 and 99 randi() % 100 + 1 # Returns random integer between 1 and 100 - [/codeblock] + [/gdscript] + [csharp] + GD.Randi(); // Returns random integer between 0 and 2^32 - 1 + GD.Randi() % 20; // Returns random integer between 0 and 19 + GD.Randi() % 100; // Returns random integer between 0 and 99 + GD.Randi() % 100 + 1; // Returns random integer between 1 and 100 + [/csharp] + [/codeblocks] </description> </method> <method name="randi_range"> @@ -911,10 +1020,16 @@ <param index="1" name="to" type="int" /> <description> Returns a random signed 32-bit integer between [param from] and [param to] (inclusive). If [param to] is lesser than [param from], they are swapped. - [codeblock] + [codeblocks] + [gdscript] randi_range(0, 1) # Returns either 0 or 1 randi_range(-10, 1000) # Returns random integer between -10 and 1000 - [/codeblock] + [/gdscript] + [csharp] + GD.RandRange(0, 1); // Returns either 0 or 1 + GD.RandRange(-10, 1000); // Returns random integer between -10 and 1000 + [/csharp] + [/codeblocks] </description> </method> <method name="randomize"> @@ -985,14 +1100,24 @@ <param index="0" name="base" type="int" /> <description> Sets the seed for the random number generator to [param base]. Setting the seed manually can ensure consistent, repeatable results for most random functions. - [codeblock] + [codeblocks] + [gdscript] var my_seed = "Godot Rocks".hash() seed(my_seed) var a = randf() + randi() seed(my_seed) var b = randf() + randi() # a and b are now identical - [/codeblock] + [/gdscript] + [csharp] + ulong mySeed = (ulong)GD.Hash("Godot Rocks"); + GD.Seed(mySeed); + var a = GD.Randf() + GD.Randi(); + GD.Seed(mySeed); + var b = GD.Randf() + GD.Randi(); + // a and b are now identical + [/csharp] + [/codeblocks] </description> </method> <method name="sign"> @@ -1154,11 +1279,18 @@ <param index="0" name="string" type="String" /> <description> Converts a formatted [param string] that was returned by [method var_to_str] to the original [Variant]. - [codeblock] + [codeblocks] + [gdscript] var a = '{ "a": 1, "b": 2 }' # a is a String var b = str_to_var(a) # b is a Dictionary print(b["a"]) # Prints 1 - [/codeblock] + [/gdscript] + [csharp] + string a = "{ \"a\": 1, \"b\": 2 }"; // a is a string + var b = GD.StrToVar(a).AsGodotDictionary(); // b is a Dictionary + GD.Print(b["a"]); // Prints 1 + [/csharp] + [/codeblocks] </description> </method> <method name="tan"> @@ -1218,15 +1350,21 @@ <param index="0" name="variable" type="Variant" /> <description> Converts a [Variant] [param variable] to a formatted [String] that can then be parsed using [method str_to_var]. - [codeblock] - a = { "a": 1, "b": 2 } + [codeblocks] + [gdscript] + var a = { "a": 1, "b": 2 } print(var_to_str(a)) - [/codeblock] + [/gdscript] + [csharp] + var a = new Godot.Collections.Dictionary { ["a"] = 1, ["b"] = 2 }; + GD.Print(GD.VarToStr(a)); + [/csharp] + [/codeblocks] Prints: [codeblock] { - "a": 1, - "b": 2 + "a": 1, + "b": 2 } [/codeblock] </description> @@ -2509,7 +2647,7 @@ Additionally, other keywords can be included: [code]"exp"[/code] for exponential range editing, [code]"radians"[/code] for editing radian angles in degrees, [code]"degrees"[/code] to hint at an angle and [code]"hide_slider"[/code] to hide the slider. </constant> <constant name="PROPERTY_HINT_ENUM" value="2" enum="PropertyHint"> - Hints that an [int], [float], or [String] property is an enumerated value to pick in a list specified via a hint string. + Hints that an [int] or [String] property is an enumerated value to pick in a list specified via a hint string. The hint string is a comma separated list of names such as [code]"Hello,Something,Else"[/code]. Whitespaces are [b]not[/b] removed from either end of a name. For integer and float properties, the first name in the list has value 0, the next 1, and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"Zero,One,Three:3,Four,Six:6"[/code]. </constant> <constant name="PROPERTY_HINT_ENUM_SUGGESTION" value="3" enum="PropertyHint"> diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index ca454cafa3..2c5337eea3 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -66,7 +66,7 @@ [/gdscript] [csharp] // position (-3, 2, 0), size (1, 1, 1) - var box = new AABB(new Vector3(-3, 2, 0), new Vector3(1, 1, 1)); + var box = new Aabb(new Vector3(-3, 2, 0), new Vector3(1, 1, 1)); // position (-3, -1, 0), size (3, 4, 2), so we fit both the original AABB and Vector3(0, -1, 2) var box2 = box.Expand(new Vector3(0, -1, 2)); [/csharp] diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml index 32599d7f7d..b253a33377 100644 --- a/doc/classes/AStarGrid2D.xml +++ b/doc/classes/AStarGrid2D.xml @@ -17,11 +17,11 @@ [/gdscript] [csharp] AStarGrid2D astarGrid = new AStarGrid2D(); - astarGrid.Size = new Vector2i(32, 32); - astarGrid.CellSize = new Vector2i(16, 16); + astarGrid.Size = new Vector2I(32, 32); + astarGrid.CellSize = new Vector2I(16, 16); astarGrid.Update(); - GD.Print(astarGrid.GetIdPath(Vector2i.Zero, new Vector2i(3, 4))); // prints (0, 0), (1, 1), (2, 2), (3, 3), (3, 4) - GD.Print(astarGrid.GetPointPath(Vector2i.Zero, new Vector2i(3, 4))); // prints (0, 0), (16, 16), (32, 32), (48, 48), (48, 64) + GD.Print(astarGrid.GetIdPath(Vector2I.Zero, new Vector2I(3, 4))); // prints (0, 0), (1, 1), (2, 2), (3, 3), (3, 4) + GD.Print(astarGrid.GetPointPath(Vector2I.Zero, new Vector2I(3, 4))); // prints (0, 0), (16, 16), (32, 32), (48, 48), (48, 64) [/csharp] [/codeblocks] </description> diff --git a/doc/classes/AnimatedSprite2D.xml b/doc/classes/AnimatedSprite2D.xml index cd9c0cee98..9872c59990 100644 --- a/doc/classes/AnimatedSprite2D.xml +++ b/doc/classes/AnimatedSprite2D.xml @@ -5,34 +5,82 @@ </brief_description> <description> [AnimatedSprite2D] is similar to the [Sprite2D] node, except it carries multiple textures as animation frames. Animations are created using a [SpriteFrames] resource, which allows you to import image files (or a folder containing said files) to provide the animation frames for the sprite. The [SpriteFrames] resource can be configured in the editor via the SpriteFrames bottom panel. - After setting up [member frames], [method play] may be called. It's also possible to select an [member animation] and toggle [member playing], even within the editor. - To pause the current animation, set [member playing] to [code]false[/code]. Alternatively, setting [member speed_scale] to [code]0[/code] also preserves the current frame's elapsed time. </description> <tutorials> <link title="2D Sprite animation">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link> <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> </tutorials> <methods> + <method name="get_playing_speed" qualifiers="const"> + <return type="float" /> + <description> + Returns the actual playing speed of current animation or [code]0[/code] if not playing. This speed is the [member speed_scale] property multiplied by [code]custom_speed[/code] argument specified when calling the [method play] method. + Returns a negative value if the current animation is playing backwards. + </description> + </method> + <method name="is_playing" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if an animation is currently playing (even if [member speed_scale] and/or [code]custom_speed[/code] are [code]0[/code]). + </description> + </method> + <method name="pause"> + <return type="void" /> + <description> + Pauses the currently playing animation. The [member frame] and [member frame_progress] will be kept and calling [method play] or [method play_backwards] without arguments will resume the animation from the current playback position. + See also [method stop]. + </description> + </method> <method name="play"> <return type="void" /> - <param index="0" name="anim" type="StringName" default="&""" /> - <param index="1" name="backwards" type="bool" default="false" /> + <param index="0" name="name" type="StringName" default="&""" /> + <param index="1" name="custom_speed" type="float" default="1.0" /> + <param index="2" name="from_end" type="bool" default="false" /> + <description> + Plays the animation with key [param name]. If [param custom_speed] is negative and [param from_end] is [code]true[/code], the animation will play backwards (which is equivalent to calling [method play_backwards]). + If this method is called with that same animation [param name], or with no [param name] parameter, the assigned animation will resume playing if it was paused. + </description> + </method> + <method name="play_backwards"> + <return type="void" /> + <param index="0" name="name" type="StringName" default="&""" /> + <description> + Plays the animation with key [param name] in reverse. + This method is a shorthand for [method play] with [code]custom_speed = -1.0[/code] and [code]from_end = true[/code], so see its description for more information. + </description> + </method> + <method name="set_frame_and_progress"> + <return type="void" /> + <param index="0" name="frame" type="int" /> + <param index="1" name="progress" type="float" /> <description> - Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played. If [param backwards] is [code]true[/code], the animation is played in reverse. - [b]Note:[/b] If [member speed_scale] is negative, the animation direction specified by [param backwards] will be inverted. + The setter of [member frame] resets the [member frame_progress] to [code]0.0[/code] implicitly, but this method avoids that. + This is useful when you want to carry over the current [member frame_progress] to another [member frame]. + [b]Example:[/b] + [codeblocks] + [gdscript] + # Change the animation with keeping the frame index and progress. + var current_frame = animated_sprite.get_frame() + var current_progress = animated_sprite.get_frame_progress() + animated_sprite.play("walk_another_skin") + animated_sprite.set_frame_and_progress(current_frame, current_progress) + [/gdscript] + [/codeblocks] </description> </method> <method name="stop"> <return type="void" /> <description> - Stops the current [member animation] at the current [member frame]. - [b]Note:[/b] This method resets the current frame's elapsed time and removes the [code]backwards[/code] flag from the current [member animation] (if it was previously set by [method play]). If this behavior is undesired, set [member playing] to [code]false[/code] instead. + Stops the currently playing animation. The animation position is reset to [code]0[/code] and the [code]custom_speed[/code] is reset to [code]1.0[/code]. See also [method pause]. </description> </method> </methods> <members> <member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&"default""> - The current animation from the [member frames] resource. If this value changes, the [code]frame[/code] counter is reset. + The current animation from the [member sprite_frames] resource. If this value is changed, the [member frame] counter and the [member frame_progress] are reset. + </member> + <member name="autoplay" type="String" setter="set_autoplay" getter="get_autoplay" default=""""> + The key of the animation to play when the scene loads. </member> <member name="centered" type="bool" setter="set_centered" getter="is_centered" default="true"> If [code]true[/code], texture will be centered. @@ -44,32 +92,46 @@ If [code]true[/code], texture is flipped vertically. </member> <member name="frame" type="int" setter="set_frame" getter="get_frame" default="0"> - The displayed animation frame's index. + The displayed animation frame's index. Setting this property also resets [member frame_progress]. If this is not desired, use [method set_frame_and_progress]. </member> - <member name="frames" type="SpriteFrames" setter="set_sprite_frames" getter="get_sprite_frames"> - The [SpriteFrames] resource containing the animation(s). Allows you the option to load, edit, clear, make unique and save the states of the [SpriteFrames] resource. + <member name="frame_progress" type="float" setter="set_frame_progress" getter="get_frame_progress" default="0.0"> + The progress value between [code]0.0[/code] and [code]1.0[/code] until the current frame transitions to the next frame. If the animation is playing backwards, the value transitions from [code]1.0[/code] to [code]0.0[/code]. </member> <member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)"> The texture's drawing offset. </member> - <member name="playing" type="bool" setter="set_playing" getter="is_playing" default="false"> - If [code]true[/code], the [member animation] is currently playing. Setting this property to [code]false[/code] pauses the current animation. Use [method stop] to stop the animation at the current frame instead. - [b]Note:[/b] Unlike [method stop], changing this property to [code]false[/code] preserves the current frame's elapsed time and the [code]backwards[/code] flag of the current [member animation] (if it was previously set by [method play]). - [b]Note:[/b] After a non-looping animation finishes, the property still remains [code]true[/code]. - </member> <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0"> - The animation speed is multiplied by this value. If set to a negative value, the animation is played in reverse. If set to [code]0[/code], the animation is paused, preserving the current frame's elapsed time. + The speed scaling ratio. For example, if this value is [code]1[/code], then the animation plays at normal speed. If it's [code]0.5[/code], then it plays at half speed. If it's [code]2[/code], then it plays at double speed. + If set to a negative value, the animation is played in reverse. If set to [code]0[/code], the animation will not advance. + </member> + <member name="sprite_frames" type="SpriteFrames" setter="set_sprite_frames" getter="get_sprite_frames"> + The [SpriteFrames] resource containing the animation(s). Allows you the option to load, edit, clear, make unique and save the states of the [SpriteFrames] resource. </member> </members> <signals> + <signal name="animation_changed"> + <description> + Emitted when [member animation] changes. + </description> + </signal> <signal name="animation_finished"> <description> - Emitted when the animation reaches the end, or the start if it is played in reverse. If the animation is looping, this signal is emitted at the end of each loop. + Emitted when the animation reaches the end, or the start if it is played in reverse. When the animation finishes, it pauses the playback. + </description> + </signal> + <signal name="animation_looped"> + <description> + Emitted when the animation loops. </description> </signal> <signal name="frame_changed"> <description> - Emitted when [member frame] changed. + Emitted when [member frame] changes. + </description> + </signal> + <signal name="sprite_frames_changed"> + <description> + Emitted when [member sprite_frames] changes. </description> </signal> </signals> diff --git a/doc/classes/AnimatedSprite3D.xml b/doc/classes/AnimatedSprite3D.xml index 4837ae715f..c39bb99827 100644 --- a/doc/classes/AnimatedSprite3D.xml +++ b/doc/classes/AnimatedSprite3D.xml @@ -4,59 +4,121 @@ 2D sprite node in 3D world, that can use multiple 2D textures for animation. </brief_description> <description> - [AnimatedSprite3D] is similar to the [Sprite3D] node, except it carries multiple textures as animation [member frames]. Animations are created using a [SpriteFrames] resource, which allows you to import image files (or a folder containing said files) to provide the animation frames for the sprite. The [SpriteFrames] resource can be configured in the editor via the SpriteFrames bottom panel. - After setting up [member frames], [method play] may be called. It's also possible to select an [member animation] and toggle [member playing], even within the editor. - To pause the current animation, set [member playing] to [code]false[/code]. Alternatively, setting [member speed_scale] to [code]0[/code] also preserves the current frame's elapsed time. + [AnimatedSprite3D] is similar to the [Sprite3D] node, except it carries multiple textures as animation [member sprite_frames]. Animations are created using a [SpriteFrames] resource, which allows you to import image files (or a folder containing said files) to provide the animation frames for the sprite. The [SpriteFrames] resource can be configured in the editor via the SpriteFrames bottom panel. </description> <tutorials> <link title="2D Sprite animation (also applies to 3D)">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link> </tutorials> <methods> + <method name="get_playing_speed" qualifiers="const"> + <return type="float" /> + <description> + Returns the actual playing speed of current animation or [code]0[/code] if not playing. This speed is the [member speed_scale] property multiplied by [code]custom_speed[/code] argument specified when calling the [method play] method. + Returns a negative value if the current animation is playing backwards. + </description> + </method> + <method name="is_playing" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if an animation is currently playing (even if [member speed_scale] and/or [code]custom_speed[/code] are [code]0[/code]). + </description> + </method> + <method name="pause"> + <return type="void" /> + <description> + Pauses the currently playing animation. The [member frame] and [member frame_progress] will be kept and calling [method play] or [method play_backwards] without arguments will resume the animation from the current playback position. + See also [method stop]. + </description> + </method> <method name="play"> <return type="void" /> - <param index="0" name="anim" type="StringName" default="&""" /> - <param index="1" name="backwards" type="bool" default="false" /> + <param index="0" name="name" type="StringName" default="&""" /> + <param index="1" name="custom_speed" type="float" default="1.0" /> + <param index="2" name="from_end" type="bool" default="false" /> + <description> + Plays the animation with key [param name]. If [param custom_speed] is negative and [param from_end] is [code]true[/code], the animation will play backwards (which is equivalent to calling [method play_backwards]). + If this method is called with that same animation [param name], or with no [param name] parameter, the assigned animation will resume playing if it was paused. + </description> + </method> + <method name="play_backwards"> + <return type="void" /> + <param index="0" name="name" type="StringName" default="&""" /> + <description> + Plays the animation with key [param name] in reverse. + This method is a shorthand for [method play] with [code]custom_speed = -1.0[/code] and [code]from_end = true[/code], so see its description for more information. + </description> + </method> + <method name="set_frame_and_progress"> + <return type="void" /> + <param index="0" name="frame" type="int" /> + <param index="1" name="progress" type="float" /> <description> - Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played. If [param backwards] is [code]true[/code], the animation is played in reverse. - [b]Note:[/b] If [member speed_scale] is negative, the animation direction specified by [param backwards] will be inverted. + The setter of [member frame] resets the [member frame_progress] to [code]0.0[/code] implicitly, but this method avoids that. + This is useful when you want to carry over the current [member frame_progress] to another [member frame]. + [b]Example:[/b] + [codeblocks] + [gdscript] + # Change the animation with keeping the frame index and progress. + var current_frame = animated_sprite.get_frame() + var current_progress = animated_sprite.get_frame_progress() + animated_sprite.play("walk_another_skin") + animated_sprite.set_frame_and_progress(current_frame, current_progress) + [/gdscript] + [/codeblocks] </description> </method> <method name="stop"> <return type="void" /> <description> - Stops the current [member animation] at the current [member frame]. - [b]Note:[/b] This method resets the current frame's elapsed time and removes the [code]backwards[/code] flag from the current [member animation] (if it was previously set by [method play]). If this behavior is undesired, set [member playing] to [code]false[/code] instead. + Stops the currently playing animation. The animation position is reset to [code]0[/code] and the [code]custom_speed[/code] is reset to [code]1.0[/code]. See also [method pause]. </description> </method> </methods> <members> <member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&"default""> - The current animation from the [code]frames[/code] resource. If this value changes, the [code]frame[/code] counter is reset. + The current animation from the [member sprite_frames] resource. If this value is changed, the [member frame] counter and the [member frame_progress] are reset. </member> - <member name="frame" type="int" setter="set_frame" getter="get_frame" default="0"> - The displayed animation frame's index. + <member name="autoplay" type="String" setter="set_autoplay" getter="get_autoplay" default=""""> + The key of the animation to play when the scene loads. </member> - <member name="frames" type="SpriteFrames" setter="set_sprite_frames" getter="get_sprite_frames"> - The [SpriteFrames] resource containing the animation(s). + <member name="frame" type="int" setter="set_frame" getter="get_frame" default="0"> + The displayed animation frame's index. Setting this property also resets [member frame_progress]. If this is not desired, use [method set_frame_and_progress]. </member> - <member name="playing" type="bool" setter="set_playing" getter="is_playing" default="false"> - If [code]true[/code], the [member animation] is currently playing. Setting this property to [code]false[/code] pauses the current animation. Use [method stop] to stop the animation at the current frame instead. - [b]Note:[/b] Unlike [method stop], changing this property to [code]false[/code] preserves the current frame's elapsed time and the [code]backwards[/code] flag of the current [member animation] (if it was previously set by [method play]). - [b]Note:[/b] After a non-looping animation finishes, the property still remains [code]true[/code]. + <member name="frame_progress" type="float" setter="set_frame_progress" getter="get_frame_progress" default="0.0"> + The progress value between [code]0.0[/code] and [code]1.0[/code] until the current frame transitions to the next frame. If the animation is playing backwards, the value transitions from [code]1.0[/code] to [code]0.0[/code]. </member> <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0"> - The animation speed is multiplied by this value. If set to a negative value, the animation is played in reverse. If set to [code]0[/code], the animation is paused, preserving the current frame's elapsed time. + The speed scaling ratio. For example, if this value is [code]1[/code], then the animation plays at normal speed. If it's [code]0.5[/code], then it plays at half speed. If it's [code]2[/code], then it plays at double speed. + If set to a negative value, the animation is played in reverse. If set to [code]0[/code], the animation will not advance. + </member> + <member name="sprite_frames" type="SpriteFrames" setter="set_sprite_frames" getter="get_sprite_frames"> + The [SpriteFrames] resource containing the animation(s). Allows you the option to load, edit, clear, make unique and save the states of the [SpriteFrames] resource. </member> </members> <signals> + <signal name="animation_changed"> + <description> + Emitted when [member animation] changes. + </description> + </signal> <signal name="animation_finished"> <description> - Emitted when the animation reaches the end, or the start if it is played in reverse. If the animation is looping, this signal is emitted at the end of each loop. + Emitted when the animation reaches the end, or the start if it is played in reverse. When the animation finishes, it pauses the playback. + </description> + </signal> + <signal name="animation_looped"> + <description> + Emitted when the animation loops. </description> </signal> <signal name="frame_changed"> <description> - Emitted when [member frame] changed. + Emitted when [member frame] changes. + </description> + </signal> + <signal name="sprite_frames_changed"> + <description> + Emitted when [member sprite_frames] changes. </description> </signal> </signals> diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index c0626dcfe4..74ee13a3d2 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -104,6 +104,13 @@ [param stream] is the [AudioStream] resource to play. [param start_offset] is the number of seconds cut off at the beginning of the audio stream, while [param end_offset] is at the ending. </description> </method> + <method name="audio_track_is_use_blend" qualifiers="const"> + <return type="bool" /> + <param index="0" name="track_idx" type="int" /> + <description> + Returns [code]true[/code] if the track at [code]idx[/code] will be blended with other animations. + </description> + </method> <method name="audio_track_set_key_end_offset"> <return type="void" /> <param index="0" name="track_idx" type="int" /> @@ -131,6 +138,14 @@ Sets the stream of the key identified by [param key_idx] to value [param stream]. The [param track_idx] must be the index of an Audio Track. </description> </method> + <method name="audio_track_set_use_blend"> + <return type="void" /> + <param index="0" name="track_idx" type="int" /> + <param index="1" name="enable" type="bool" /> + <description> + Sets whether the track will be blended with other animations. If [code]true[/code], the audio playback volume changes depending on the blend value. + </description> + </method> <method name="bezier_track_get_key_in_handle" qualifiers="const"> <return type="Vector2" /> <param index="0" name="track_idx" type="int" /> diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml index 6e3345b675..bc65e6013b 100644 --- a/doc/classes/AnimationNode.xml +++ b/doc/classes/AnimationNode.xml @@ -68,10 +68,10 @@ </description> </method> <method name="add_input"> - <return type="void" /> + <return type="bool" /> <param index="0" name="name" type="String" /> <description> - Adds an input to the node. This is only useful for nodes created for use in an [AnimationNodeBlendTree]. + Adds an input to the node. This is only useful for nodes created for use in an [AnimationNodeBlendTree]. If the addition fails, returns [code]false[/code]. </description> </method> <method name="blend_animation"> @@ -115,13 +115,20 @@ Blend another animation node (in case this node contains children animation nodes). This function is only useful if you inherit from [AnimationRootNode] instead, else editors will not display your node for addition. </description> </method> + <method name="find_input" qualifiers="const"> + <return type="int" /> + <param index="0" name="name" type="String" /> + <description> + Returns the input index which corresponds to [param name]. If not found, returns [code]-1[/code]. + </description> + </method> <method name="get_input_count" qualifiers="const"> <return type="int" /> <description> Amount of inputs in this node, only useful for nodes that go into [AnimationNodeBlendTree]. </description> </method> - <method name="get_input_name"> + <method name="get_input_name" qualifiers="const"> <return type="String" /> <param index="0" name="input" type="int" /> <description> @@ -157,6 +164,14 @@ Adds or removes a path for the filter. </description> </method> + <method name="set_input_name"> + <return type="bool" /> + <param index="0" name="input" type="int" /> + <param index="1" name="name" type="String" /> + <description> + Sets the name of the input at the given [param input] index. If the setting fails, returns [code]false[/code]. + </description> + </method> <method name="set_parameter"> <return type="void" /> <param index="0" name="name" type="StringName" /> diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml index eee25fad7c..bccab4613a 100644 --- a/doc/classes/AnimationNodeStateMachineTransition.xml +++ b/doc/classes/AnimationNodeStateMachineTransition.xml @@ -15,7 +15,7 @@ $animation_tree.set("parameters/conditions/idle", is_on_floor and (linear_velocity.x == 0)) [/gdscript] [csharp] - GetNode<AnimationTree>("animation_tree").Set("parameters/conditions/idle", IsOnFloor && (LinearVelocity.x == 0)); + GetNode<AnimationTree>("animation_tree").Set("parameters/conditions/idle", IsOnFloor && (LinearVelocity.X == 0)); [/csharp] [/codeblocks] </member> diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml index 90bae41586..a067b0a9ba 100644 --- a/doc/classes/AnimationNodeTransition.xml +++ b/doc/classes/AnimationNodeTransition.xml @@ -12,18 +12,11 @@ <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> - Returns the input index which corresponds to [param caption]. If not found, returns [code]-1[/code]. - </description> - </method> - <method name="get_input_caption" qualifiers="const"> - <return type="String" /> + <method name="is_input_reset" qualifiers="const"> + <return type="bool" /> <param index="0" name="input" type="int" /> <description> - Returns the name of the input at the given [param input] index. This name is displayed in the editor next to the node input. + Returns whether the animation restarts when the animation transitions from the other animation. </description> </method> <method name="is_input_set_as_auto_advance" qualifiers="const"> @@ -41,21 +34,18 @@ Enables or disables auto-advance for the given [param input] index. If enabled, state changes to the next input after playing the animation once. If enabled for the last input state, it loops to the first. </description> </method> - <method name="set_input_caption"> + <method name="set_input_reset"> <return type="void" /> <param index="0" name="input" type="int" /> - <param index="1" name="caption" type="String" /> + <param index="1" name="enable" type="bool" /> <description> - Sets the name of the input at the given [param input] index. This name is displayed in the editor next to the node input. + If [code]true[/code], the destination animation is restarted when the animation transitions. </description> </method> </methods> <members> - <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. The maximum is [code]31[/code]. - </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 name="input_count" type="int" setter="set_input_count" getter="get_input_count" default="0"> + The number of enabled input ports for this node. </member> <member name="xfade_curve" type="Curve" setter="set_xfade_curve" getter="get_xfade_curve"> Determines how cross-fading between animations is eased. If empty, the transition will be linear. diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index 304caeef43..25e4a4549d 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -15,6 +15,17 @@ <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> + <method name="_post_process_key_value" qualifiers="virtual const"> + <return type="Variant" /> + <param index="0" name="animation" type="Animation" /> + <param index="1" name="track" type="int" /> + <param index="2" name="value" type="Variant" /> + <param index="3" name="object" type="Object" /> + <param index="4" name="object_idx" type="int" /> + <description> + A virtual function for processing after key getting during playback. + </description> + </method> <method name="add_animation_library"> <return type="int" enum="Error" /> <param index="0" name="name" type="StringName" /> @@ -102,13 +113,14 @@ <param index="0" name="anim_from" type="StringName" /> <param index="1" name="anim_to" type="StringName" /> <description> - Gets the blend time (in seconds) between two animations, referenced by their keys. + Returns the blend time (in seconds) between two animations, referenced by their keys. </description> </method> <method name="get_playing_speed" qualifiers="const"> <return type="float" /> <description> - Gets the actual playing speed of current animation or 0 if not playing. This speed is the [member playback_speed] property multiplied by [code]custom_speed[/code] argument specified when calling the [method play] method. + Returns the actual playing speed of current animation or [code]0[/code] if not playing. This speed is the [member speed_scale] property multiplied by [code]custom_speed[/code] argument specified when calling the [method play] method. + Returns a negative value if the current animation is playing backwards. </description> </method> <method name="get_queue"> @@ -134,7 +146,7 @@ <method name="is_playing" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if playing an animation. + Returns [code]true[/code] if an animation is currently playing (even if [member speed_scale] and/or [code]custom_speed[/code] are [code]0[/code]). </description> </method> <method name="pause"> @@ -152,7 +164,7 @@ <param index="3" name="from_end" type="bool" default="false" /> <description> Plays the animation with key [param name]. Custom blend times and speed can be set. If [param custom_speed] is negative and [param from_end] is [code]true[/code], the animation will play backwards (which is equivalent to calling [method play_backwards]). - The [AnimationPlayer] keeps track of its current or last played animation with [member assigned_animation]. If this method is called with that same animation [param name], or with no [param name] parameter, the assigned animation will resume playing if it was paused, or restart if it was stopped (see [method stop] for both pause and stop). If the animation was already playing, it will keep playing. + The [AnimationPlayer] keeps track of its current or last played animation with [member assigned_animation]. If this method is called with that same animation [param name], or with no [param name] parameter, the assigned animation will resume playing if it was paused. [b]Note:[/b] The animation will be updated the next time the [AnimationPlayer] is processed. If other variables are updated at the same time this is called, they may be updated too early. To perform the update immediately, call [code]advance(0)[/code]. </description> </method> @@ -210,7 +222,7 @@ <return type="void" /> <param index="0" name="keep_state" type="bool" default="false" /> <description> - Stops the currently playing animation. The animation position is reset to [code]0[/code] and the playback speed is reset to [code]1.0[/code]. See also [method pause]. + Stops the currently playing animation. The animation position is reset to [code]0[/code] and the [code]custom_speed[/code] is reset to [code]1.0[/code]. See also [method pause]. If [param keep_state] is [code]true[/code], the animation state is not updated visually. [b]Note:[/b] The method / audio / animation playback tracks will not be processed by this method. </description> @@ -220,6 +232,10 @@ <member name="assigned_animation" type="String" setter="set_assigned_animation" getter="get_assigned_animation"> If playing, the the current animation's key, otherwise, the animation last played. When set, this changes the animation, but will not play it unless already playing. See also [member current_animation]. </member> + <member name="audio_max_polyphony" type="int" setter="set_audio_max_polyphony" getter="get_audio_max_polyphony" default="32"> + The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers. + For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each. + </member> <member name="autoplay" type="String" setter="set_autoplay" getter="get_autoplay" default=""""> The key of the animation to play when the scene loads. </member> @@ -249,9 +265,6 @@ <member name="playback_process_mode" type="int" setter="set_process_callback" getter="get_process_callback" enum="AnimationPlayer.AnimationProcessCallback" default="1"> The process notification in which to update animations. </member> - <member name="playback_speed" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0"> - The speed scaling ratio. For example, if this value is 1, then the animation plays at normal speed. If it's 0.5, then it plays at half speed. If it's 2, then it plays at double speed. - </member> <member name="reset_on_save" type="bool" setter="set_reset_on_save_enabled" getter="is_reset_on_save_enabled" default="true"> This is used by the editor. If set to [code]true[/code], the scene will be saved with the effects of the reset animation (the animation with the key [code]"RESET"[/code]) applied as if it had been seeked to time 0, with the editor keeping the values that the scene had before saving. This makes it more convenient to preview and edit animations in the editor, as changes to the scene will not be saved as long as they are set in the reset animation. @@ -259,6 +272,10 @@ <member name="root_node" type="NodePath" setter="set_root" getter="get_root" default="NodePath("..")"> The node from which node path references will travel. </member> + <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0"> + The speed scaling ratio. For example, if this value is [code]1[/code], then the animation plays at normal speed. If it's [code]0.5[/code], then it plays at half speed. If it's [code]2[/code], then it plays at double speed. + If set to a negative value, the animation is played in reverse. If set to [code]0[/code], the animation will not advance. + </member> </members> <signals> <signal name="animation_changed"> diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 49bc4ee6af..86562c340d 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -12,6 +12,17 @@ <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> + <method name="_post_process_key_value" qualifiers="virtual const"> + <return type="Variant" /> + <param index="0" name="animation" type="Animation" /> + <param index="1" name="track" type="int" /> + <param index="2" name="value" type="Variant" /> + <param index="3" name="object" type="Object" /> + <param index="4" name="object_idx" type="int" /> + <description> + A virtual function for processing after key getting during playback. + </description> + </method> <method name="advance"> <return type="void" /> <param index="0" name="delta" type="float" /> @@ -99,6 +110,10 @@ <member name="anim_player" type="NodePath" setter="set_animation_player" getter="get_animation_player" default="NodePath("")"> The path to the [AnimationPlayer] used for animating. </member> + <member name="audio_max_polyphony" type="int" setter="set_audio_max_polyphony" getter="get_audio_max_polyphony" default="32"> + The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers. + For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each. + </member> <member name="process_callback" type="int" setter="set_process_callback" getter="get_process_callback" enum="AnimationTree.AnimationProcessCallback" default="1"> The process mode of this [AnimationTree]. See [enum AnimationProcessCallback] for available modes. </member> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 3f76cc16ec..8a98921a60 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -87,8 +87,9 @@ <member name="gravity_point_center" type="Vector2" setter="set_gravity_point_center" getter="get_gravity_point_center" default="Vector2(0, 1)"> If gravity is a point (see [member gravity_point]), this will be the point of attraction. </member> - <member name="gravity_point_distance_scale" type="float" setter="set_gravity_point_distance_scale" getter="get_gravity_point_distance_scale" default="0.0"> - The falloff factor for point gravity. The greater the value, the faster gravity decreases with distance. + <member name="gravity_point_unit_distance" type="float" setter="set_gravity_point_unit_distance" getter="get_gravity_point_unit_distance" default="0.0"> + The distance at which the gravity strength is equal to [member gravity]. For example, on a planet 100 pixels in radius with a surface gravity of 4.0 px/s², set the [member gravity] to 4.0 and the unit distance to 100.0. The gravity will have falloff according to the inverse square law, so in the example, at 200 pixels from the center the gravity will be 1.0 px/s² (twice the distance, 1/4th the gravity), at 50 pixels it will be 16.0 px/s² (half the distance, 4x the gravity), and so on. + The above is true only when the unit distance is a positive number. When this is set to 0.0, the gravity will be constant regardless of distance. </member> <member name="gravity_space_override" type="int" setter="set_gravity_space_override_mode" getter="get_gravity_space_override_mode" enum="Area2D.SpaceOverride" default="0"> Override mode for gravity calculations within this area. See [enum SpaceOverride] for possible values. diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index d40bca99d8..bd046b7cb8 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -86,8 +86,9 @@ <member name="gravity_point_center" type="Vector3" setter="set_gravity_point_center" getter="get_gravity_point_center" default="Vector3(0, -1, 0)"> If gravity is a point (see [member gravity_point]), this will be the point of attraction. </member> - <member name="gravity_point_distance_scale" type="float" setter="set_gravity_point_distance_scale" getter="get_gravity_point_distance_scale" default="0.0"> - The falloff factor for point gravity. The greater the value, the faster gravity decreases with distance. + <member name="gravity_point_unit_distance" type="float" setter="set_gravity_point_unit_distance" getter="get_gravity_point_unit_distance" default="0.0"> + The distance at which the gravity strength is equal to [member gravity]. For example, on a planet 100 meters in radius with a surface gravity of 4.0 m/s², set the [member gravity] to 4.0 and the unit distance to 100.0. The gravity will have falloff according to the inverse square law, so in the example, at 200 meters from the center the gravity will be 1.0 m/s² (twice the distance, 1/4th the gravity), at 50 meters it will be 16.0 m/s² (half the distance, 4x the gravity), and so on. + The above is true only when the unit distance is a positive number. When this is set to 0.0, the gravity will be constant regardless of distance. </member> <member name="gravity_space_override" type="int" setter="set_gravity_space_override_mode" getter="get_gravity_space_override_mode" enum="Area3D.SpaceOverride" default="0"> Override mode for gravity calculations within this area. See [enum SpaceOverride] for possible values. diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index ce4d7693d8..9be5e60f4a 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -59,7 +59,7 @@ <param index="2" name="class_name" type="StringName" /> <param index="3" name="script" type="Variant" /> <description> - Creates a typed array from the [param base] array. The base array can't be already typed. See [method set_typed] for more details. + Creates a typed array from the [param base] array. The base array can't be already typed. </description> </constructor> <constructor name="Array"> @@ -392,7 +392,7 @@ <method name="is_read_only" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if the array is read-only. See [method set_read_only]. Arrays are automatically read-only if declared with [code]const[/code] keyword. + Returns [code]true[/code] if the array is read-only. See [method make_read_only]. Arrays are automatically read-only if declared with [code]const[/code] keyword. </description> </method> <method name="is_typed" qualifiers="const"> @@ -401,6 +401,12 @@ Returns [code]true[/code] if the array is typed. Typed arrays can only store elements of their associated type and provide type safety for the [code][][/code] operator. Methods of typed array still return [Variant]. </description> </method> + <method name="make_read_only"> + <return type="void" /> + <description> + Makes the array read-only, i.e. disabled modifying of the array's elements. Does not apply to nested content, e.g. content of nested arrays. + </description> + </method> <method name="map" qualifiers="const"> <return type="Array" /> <param index="0" name="method" type="Callable" /> @@ -524,23 +530,6 @@ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array. </description> </method> - <method name="set_read_only"> - <return type="void" /> - <param index="0" name="enable" type="bool" /> - <description> - Makes the [Array] read-only, i.e. disabled modifying of the array's elements. Does not apply to nested content, e.g. content of nested arrays. - </description> - </method> - <method name="set_typed"> - <return type="void" /> - <param index="0" name="type" type="int" /> - <param index="1" name="class_name" type="StringName" /> - <param index="2" name="script" type="Variant" /> - <description> - Makes the [Array] typed. The [param type] should be one of the [enum Variant.Type] constants. [param class_name] is optional and can only be provided for [constant TYPE_OBJECT]. [param script] can only be provided if [param class_name] is not empty. - The method fails if an array is already typed. - </description> - </method> <method name="shuffle"> <return type="void" /> <description> diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 7b86afcc4c..6dc66194b8 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -206,6 +206,7 @@ Overrides the [AABB] with one defined by user for use with frustum culling. Especially useful to avoid unexpected culling when using a shader to offset vertices. </member> <member name="shadow_mesh" type="ArrayMesh" setter="set_shadow_mesh" getter="get_shadow_mesh"> + An optional mesh which is used for rendering shadows and can be used for the depth prepass. Can be used to increase performance of shadow rendering by using a mesh that only contains vertex position data (without normals, UVs, colors, etc.). </member> </members> </class> diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml index 06e183f4e2..9b3a4f7bba 100644 --- a/doc/classes/AudioStreamPlayer.xml +++ b/doc/classes/AudioStreamPlayer.xml @@ -28,6 +28,12 @@ Returns the [AudioStreamPlayback] object associated with this [AudioStreamPlayer]. </description> </method> + <method name="has_stream_playback"> + <return type="bool" /> + <description> + Returns whether the [AudioStreamPlayer] can return the [AudioStreamPlayback] object or not. + </description> + </method> <method name="play"> <return type="void" /> <param index="0" name="from_position" type="float" default="0.0" /> diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml index 81755b580f..18869e87cb 100644 --- a/doc/classes/AudioStreamPlayer2D.xml +++ b/doc/classes/AudioStreamPlayer2D.xml @@ -25,6 +25,12 @@ Returns the [AudioStreamPlayback] object associated with this [AudioStreamPlayer2D]. </description> </method> + <method name="has_stream_playback"> + <return type="bool" /> + <description> + Returns whether the [AudioStreamPlayer] can return the [AudioStreamPlayback] object or not. + </description> + </method> <method name="play"> <return type="void" /> <param index="0" name="from_position" type="float" default="0.0" /> diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml index f711210ca1..d5a06dcad6 100644 --- a/doc/classes/AudioStreamPlayer3D.xml +++ b/doc/classes/AudioStreamPlayer3D.xml @@ -25,6 +25,12 @@ Returns the [AudioStreamPlayback] object associated with this [AudioStreamPlayer3D]. </description> </method> + <method name="has_stream_playback"> + <return type="bool" /> + <description> + Returns whether the [AudioStreamPlayer] can return the [AudioStreamPlayback] object or not. + </description> + </method> <method name="play"> <return type="void" /> <param index="0" name="from_position" type="float" default="0.0" /> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 7082eff97d..0d675112b8 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -383,7 +383,9 @@ <method name="get_global_rect" qualifiers="const"> <return type="Rect2" /> <description> - Returns the position and size of the control relative to the [CanvasLayer]. See [member global_position] and [member size]. + Returns the position and size of the control relative to the containing canvas. See [member global_position] and [member size]. + [b]Note:[/b] If the node itself or any parent [CanvasItem] between the node and the canvas have a non default rotation or skew, the resulting size is likely not meaningful. + [b]Note:[/b] Setting [member Viewport.gui_snap_controls_to_pixels] to [code]true[/code] can lead to rounding inaccuracies between the displayed control and the returned [Rect2]. </description> </method> <method name="get_minimum_size" qualifiers="const"> @@ -414,7 +416,9 @@ <method name="get_rect" qualifiers="const"> <return type="Rect2" /> <description> - Returns the position and size of the control relative to the top-left corner of the parent Control. See [member position] and [member size]. + Returns the position and size of the control in the coordinate system of the containing node. See [member position], [member scale] and [member size]. + [b]Note:[/b] If [member rotation] is not the default rotation, the resulting size is not meaningful. + [b]Note:[/b] Setting [member Viewport.gui_snap_controls_to_pixels] to [code]true[/code] can lead to rounding inaccuracies between the displayed control and the returned [Rect2]. </description> </method> <method name="get_screen_position" qualifiers="const"> @@ -895,6 +899,7 @@ <param index="0" name="position" type="Vector2" /> <description> Moves the mouse cursor to [param position], relative to [member position] of this [Control]. + [b]Note:[/b] [method warp_mouse] is only supported on Windows, macOS and Linux. It has no effect on Android, iOS and Web. </description> </method> </methods> @@ -991,7 +996,7 @@ By default, the node's pivot is its top-left corner. When you change its [member rotation] or [member scale], it will rotate or scale around this pivot. Set this property to [member size] / 2 to pivot around the Control's center. </member> <member name="position" type="Vector2" setter="_set_position" getter="get_position" default="Vector2(0, 0)"> - The node's position, relative to its parent. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset]. + The node's position, relative to its containing node. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset]. </member> <member name="rotation" type="float" setter="set_rotation" getter="get_rotation" default="0.0"> The node's rotation around its pivot, in radians. See [member pivot_offset] to change the pivot's position. @@ -1008,7 +1013,7 @@ The [Node] which must be a parent of the focused [Control] for the shortcut to be activated. If [code]null[/code], the shortcut can be activated when any control is focused (a global shortcut). This allows shortcuts to be accepted only when the user has a certain area of the GUI focused. </member> <member name="size" type="Vector2" setter="_set_size" getter="get_size" default="Vector2(0, 0)"> - The size of the node's bounding rectangle, in pixels. [Container] nodes update this property automatically. + The size of the node's bounding rectangle, in the node's coordinate system. [Container] nodes update this property automatically. </member> <member name="size_flags_horizontal" type="int" setter="set_h_size_flags" getter="get_h_size_flags" enum="Control.SizeFlags" default="1"> Tells the parent [Container] nodes how they should resize and place the node on the X axis. Use one of the [enum SizeFlags] constants to change the flags. See the constants to learn what each does. diff --git a/doc/classes/DTLSServer.xml b/doc/classes/DTLSServer.xml index 457513b8aa..a3a0b0456c 100644 --- a/doc/classes/DTLSServer.xml +++ b/doc/classes/DTLSServer.xml @@ -148,11 +148,9 @@ <methods> <method name="setup"> <return type="int" enum="Error" /> - <param index="0" name="key" type="CryptoKey" /> - <param index="1" name="certificate" type="X509Certificate" /> - <param index="2" name="chain" type="X509Certificate" default="null" /> + <param index="0" name="server_options" type="TLSOptions" /> <description> - Setup the DTLS server to use the given [param key] and provide the given [param certificate] to clients. You can pass the optional [param chain] parameter to provide additional CA chain information along with the certificate. + Setup the DTLS server to use the given [param server_options]. See [method TLSOptions.server]. </description> </method> <method name="take_connection"> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 5f99ba82b8..776a9f6fc1 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -42,7 +42,7 @@ You can access a dictionary's value by referencing its corresponding key. In the above example, [code]points_dict["White"][/code] will return [code]50[/code]. You can also write [code]points_dict.White[/code], which is equivalent. However, you'll have to use the bracket syntax if the key you're accessing the dictionary with isn't a fixed string (such as a number or variable). [codeblocks] [gdscript] - @export(String, "White", "Yellow", "Orange") var my_color + @export_enum("White", "Yellow", "Orange") var my_color: String var points_dict = {"White": 50, "Yellow": 75, "Orange": 100} func _ready(): # We can't use dot syntax here as `my_color` is a variable. @@ -271,12 +271,24 @@ Returns [code]true[/code] if the dictionary is empty (its size is [code]0[/code]). See also [method size]. </description> </method> + <method name="is_read_only" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the dictionary is read-only. See [method make_read_only]. Dictionaries are automatically read-only if declared with [code]const[/code] keyword. + </description> + </method> <method name="keys" qualifiers="const"> <return type="Array" /> <description> Returns the list of keys in the dictionary. </description> </method> + <method name="make_read_only"> + <return type="void" /> + <description> + Makes the dictionary read-only, i.e. disables modification of the dictionary's contents. Does not apply to nested content, e.g. content of nested dictionaries. + </description> + </method> <method name="merge"> <return type="void" /> <param index="0" name="dictionary" type="Dictionary" /> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index b77ec4c517..6f4a7fc13d 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -190,6 +190,7 @@ <description> Adds a new checkable item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] [codeblock] @@ -211,6 +212,7 @@ <description> Adds a new checkable item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] [codeblock] @@ -232,6 +234,7 @@ <description> Adds a new item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] [codeblock] @@ -254,6 +257,7 @@ Adds a new radio-checkable item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. [b]Note:[/b] Radio-checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. See [method global_menu_set_item_checked] for more info on how to control it. + [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] [codeblock] @@ -274,6 +278,7 @@ <description> Adds a new item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] [codeblock] @@ -285,7 +290,7 @@ <method name="global_menu_add_multistate_item"> <return type="int" /> <param index="0" name="menu_root" type="String" /> - <param index="1" name="labe" type="String" /> + <param index="1" name="label" type="String" /> <param index="2" name="max_states" type="int" /> <param index="3" name="default_state" type="int" /> <param index="4" name="callback" type="Callable" /> @@ -294,10 +299,11 @@ <param index="7" name="accelerator" type="int" enum="Key" default="0" /> <param index="8" name="index" type="int" default="-1" /> <description> - Adds a new item with text [param labe] to the global menu with ID [param menu_root]. + Adds a new item with text [param label] to the global menu with ID [param menu_root]. Contrarily to normal binary items, multistate items can have more than two states, as defined by [param max_states]. Each press or activate of the item will increase the state by one. The default value is defined by [param default_state]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. [b]Note:[/b] By default, there's no indication of the current item state, it should be changed manually. + [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] [codeblock] @@ -319,6 +325,7 @@ Adds a new radio-checkable item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. [b]Note:[/b] Radio-checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. See [method global_menu_set_item_checked] for more info on how to control it. + [b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag]. [b]Note:[/b] This method is implemented on macOS. [b]Supported system menu IDs:[/b] [codeblock] @@ -562,6 +569,7 @@ <param index="2" name="callback" type="Callable" /> <description> Sets the callback of the item at index [param idx]. Callback is emitted when an item is pressed. + [b]Note:[/b] The [param callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the tag parameter when the menu item was created. [b]Note:[/b] This method is implemented on macOS. </description> </method> @@ -623,6 +631,7 @@ <param index="2" name="key_callback" type="Callable" /> <description> Sets the callback of the item at index [param idx]. Callback is emitted when its accelerator is activated. + [b]Note:[/b] The [param key_callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the tag parameter when the menu item was created. [b]Note:[/b] This method is implemented on macOS. </description> </method> @@ -1090,6 +1099,7 @@ <param index="0" name="position" type="Vector2i" /> <description> Sets the mouse cursor position to the given [param position] relative to an origin at the upper left corner of the currently focused game Window Manager window. + [b]Note:[/b] [method warp_mouse] is only supported on Windows, macOS and Linux. It has no effect on Android, iOS and Web. </description> </method> <method name="window_can_draw" qualifiers="const"> diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index c395815117..ec9efcc9c4 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -37,8 +37,8 @@ return [{"name": "my_option", "default_value": false}] func _import(source_file, save_path, options, platform_variants, gen_files): - var file = File.new() - if file.open(source_file, File.READ) != OK: + var file = FileAccess.open(source_file, FileAccess.READ) + if file == null: return FAILED var mesh = ArrayMesh.new() # Fill the Mesh with data read in "file", left as an exercise to the reader. diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 326c4f6456..c097c8f685 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -409,6 +409,7 @@ <description> Adds a custom type, which will appear in the list of nodes or resources. An icon can be optionally passed. When a given node or resource is selected, the base type will be instantiated (e.g. "Node3D", "Control", "Resource"), then the script will be loaded and set to this object. + [b]Note:[/b] The base type is the base engine class which this type's class hierarchy inherits, not any custom type parent classes. You can use the virtual method [method _handles] to check if your custom object is being edited by checking the script or using the [code]is[/code] keyword. During run-time, this will be a simple object with a script so this function does not need to be called then. [b]Note:[/b] Custom types added this way are not true classes. They are just a helper to create a node with specific script. diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 9015a12b43..72843eb157 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -607,6 +607,10 @@ <member name="interface/theme/draw_extra_borders" type="bool" setter="" getter=""> If [code]true[/code], draws additional borders around interactive UI elements in the editor. This is automatically enabled when using the [b]Black (OLED)[/b] theme preset, as this theme preset uses a fully black background. </member> + <member name="interface/theme/enable_touchscreen_touch_area" type="bool" setter="" getter=""> + If [code]true[/code], increases the touch area for the UI elements to improve usability on touchscreen devices. + [b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices. + </member> <member name="interface/theme/icon_and_font_color" type="int" setter="" getter=""> The icon and font color scheme to use in the editor. - [b]Auto[/b] determines the color scheme to use automatically based on [member interface/theme/base_color]. diff --git a/doc/classes/EditorTranslationParserPlugin.xml b/doc/classes/EditorTranslationParserPlugin.xml index df10c645ef..89746363d8 100644 --- a/doc/classes/EditorTranslationParserPlugin.xml +++ b/doc/classes/EditorTranslationParserPlugin.xml @@ -15,8 +15,7 @@ extends EditorTranslationParserPlugin func _parse_file(path, msgids, msgids_context_plural): - var file = File.new() - file.open(path, File.READ) + var file = FileAccess.open(path, FileAccess.READ) var text = file.get_as_text() var split_strs = text.split(",", false) for s in split_strs: diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index b3ed38d250..b7a5cff694 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -30,13 +30,10 @@ <return type="int" enum="Error" /> <param index="0" name="host" type="String" /> <param index="1" name="port" type="int" default="-1" /> - <param index="2" name="use_tls" type="bool" default="false" /> - <param index="3" name="verify_host" type="bool" default="true" /> + <param index="2" name="tls_options" type="TLSOptions" default="null" /> <description> Connects to a host. This needs to be done before any requests are sent. - The host should not have http:// prepended but will strip the protocol identifier if provided. - If no [param port] is specified (or [code]-1[/code] is used), it is automatically set to 80 for HTTP and 443 for HTTPS (if [param use_tls] is enabled). - [param verify_host] will check the TLS identity of the host if set to [code]true[/code]. + If no [param port] is specified (or [code]-1[/code] is used), it is automatically set to 80 for HTTP and 443 for HTTPS. You can pass the optional [param tls_options] parameter to customize the trusted certification authorities, or the common name verification when using HTTPS. See [method TLSOptions.client] and [method TLSOptions.client_unsafe]. </description> </method> <method name="get_response_body_length" qualifiers="const"> diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index 3dbc024b14..d403acf90c 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -187,9 +187,8 @@ <return type="int" enum="Error" /> <param index="0" name="url" type="String" /> <param index="1" name="custom_headers" type="PackedStringArray" default="PackedStringArray()" /> - <param index="2" name="tls_validate_domain" type="bool" default="true" /> - <param index="3" name="method" type="int" enum="HTTPClient.Method" default="0" /> - <param index="4" name="request_data" type="String" default="""" /> + <param index="2" name="method" type="int" enum="HTTPClient.Method" default="0" /> + <param index="3" name="request_data" type="String" default="""" /> <description> Creates request on the underlying [HTTPClient]. If there is no configuration errors, it tries to connect using [method HTTPClient.connect_to_host] and passes parameters onto [method HTTPClient.request]. Returns [constant OK] if request is successfully created. (Does not imply that the server has responded), [constant ERR_UNCONFIGURED] if not in the tree, [constant ERR_BUSY] if still processing previous request, [constant ERR_INVALID_PARAMETER] if given string is not a valid URL format, or [constant ERR_CANT_CONNECT] if not using thread and the [HTTPClient] cannot connect to host. @@ -201,9 +200,8 @@ <return type="int" enum="Error" /> <param index="0" name="url" type="String" /> <param index="1" name="custom_headers" type="PackedStringArray" default="PackedStringArray()" /> - <param index="2" name="tls_validate_domain" type="bool" default="true" /> - <param index="3" name="method" type="int" enum="HTTPClient.Method" default="0" /> - <param index="4" name="request_data_raw" type="PackedByteArray" default="PackedByteArray()" /> + <param index="2" name="method" type="int" enum="HTTPClient.Method" default="0" /> + <param index="3" name="request_data_raw" type="PackedByteArray" default="PackedByteArray()" /> <description> Creates request on the underlying [HTTPClient] using a raw array of bytes for the request body. If there is no configuration errors, it tries to connect using [method HTTPClient.connect_to_host] and passes parameters onto [method HTTPClient.request]. Returns [constant OK] if request is successfully created. (Does not imply that the server has responded), [constant ERR_UNCONFIGURED] if not in the tree, [constant ERR_BUSY] if still processing previous request, [constant ERR_INVALID_PARAMETER] if given string is not a valid URL format, or [constant ERR_CANT_CONNECT] if not using thread and the [HTTPClient] cannot connect to host. @@ -227,6 +225,13 @@ The proxy server is unset if [param host] is empty or [param port] is -1. </description> </method> + <method name="set_tls_options"> + <return type="void" /> + <param index="0" name="client_options" type="TLSOptions" /> + <description> + Sets the [TLSOptions] to be used when connecting to an HTTPS server. See [method TLSOptions.client]. + </description> + </method> </methods> <members> <member name="accept_gzip" type="bool" setter="set_accept_gzip" getter="is_accepting_gzip" default="true"> diff --git a/doc/classes/HashingContext.xml b/doc/classes/HashingContext.xml index 6e3092e618..7c2be6f817 100644 --- a/doc/classes/HashingContext.xml +++ b/doc/classes/HashingContext.xml @@ -11,15 +11,14 @@ const CHUNK_SIZE = 102 func hash_file(path): - var ctx = HashingContext.new() - var file = File.new() - # Start a SHA-256 context. - ctx.start(HashingContext.HASH_SHA256) # Check that file exists. - if not file.file_exists(path): + if not FileAccess.file_exists(path): return + # Start a SHA-256 context. + var ctx = HashingContext.new() + ctx.start(HashingContext.HASH_SHA256) # Open the file to hash. - file.open(path, File.READ) + var file = FileAccess.open(path, FileAccess.READ) # Update the context after reading each chunk. while not file.eof_reached(): ctx.update(file.get_buffer(CHUNK_SIZE)) diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 051e087611..5b07124b91 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -75,12 +75,10 @@ <return type="int" enum="Error" /> <param index="0" name="mode" type="int" enum="Image.CompressMode" /> <param index="1" name="source" type="int" enum="Image.CompressSource" default="0" /> - <param index="2" name="lossy_quality" type="float" default="0.7" /> - <param index="3" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" /> + <param index="2" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" /> <description> Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. The [param mode] parameter helps to pick the best compression method for DXT and ETC2 formats. It is ignored for ASTC compression. - The [param lossy_quality] parameter is optional for compressors that support it. For ASTC compression, the [param astc_format] parameter must be supplied. </description> </method> @@ -88,12 +86,10 @@ <return type="int" enum="Error" /> <param index="0" name="mode" type="int" enum="Image.CompressMode" /> <param index="1" name="channels" type="int" enum="Image.UsedChannels" /> - <param index="2" name="lossy_quality" type="float" default="0.7" /> - <param index="3" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" /> + <param index="2" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" /> <description> Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. This is an alternative to [method compress] that lets the user supply the channels used in order for the compressor to pick the best DXT and ETC2 formats. For other formats (non DXT or ETC2), this argument is ignored. - The [param lossy_quality] parameter is optional for compressors that support it. For ASTC compression, the [param astc_format] parameter must be supplied. </description> </method> @@ -525,7 +521,7 @@ var img = new Image(); img.Create(imgWidth, imgHeight, false, Image.Format.Rgba8); - img.SetPixelv(new Vector2i(1, 2), Colors.Red); // Sets the color at (1, 2) to red. + img.SetPixelv(new Vector2I(1, 2), Colors.Red); // Sets the color at (1, 2) to red. [/csharp] [/codeblocks] This is the same as [method set_pixel], but with a [Vector2i] argument instead of two integer arguments. diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 3b0cfb3825..70e629974d 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -375,6 +375,7 @@ <description> Sets the mouse position to the specified vector, provided in pixels and relative to an origin at the upper left corner of the currently focused Window Manager game window. Mouse position is clipped to the limits of the screen resolution, or to the limits of the game window if [enum MouseMode] is set to [constant MOUSE_MODE_CONFINED] or [constant MOUSE_MODE_CONFINED_HIDDEN]. + [b]Note:[/b] [method warp_mouse] is only supported on Windows, macOS and Linux. It has no effect on Android, iOS and Web. </description> </method> </methods> diff --git a/doc/classes/Label3D.xml b/doc/classes/Label3D.xml index 0cbca2224e..cfb47ff4c3 100644 --- a/doc/classes/Label3D.xml +++ b/doc/classes/Label3D.xml @@ -32,9 +32,18 @@ </method> </methods> <members> + <member name="alpha_antialiasing_edge" type="float" setter="set_alpha_antialiasing_edge" getter="get_alpha_antialiasing_edge" default="0.0"> + Threshold at which antialiasing will be applied on the alpha channel. + </member> + <member name="alpha_antialiasing_mode" type="int" setter="set_alpha_antialiasing" getter="get_alpha_antialiasing" enum="BaseMaterial3D.AlphaAntiAliasing" default="0"> + The type of alpha antialiasing to apply. See [enum BaseMaterial3D.AlphaAntiAliasing]. + </member> <member name="alpha_cut" type="int" setter="set_alpha_cut_mode" getter="get_alpha_cut_mode" enum="Label3D.AlphaCutMode" default="0"> The alpha cutting mode to use for the sprite. See [enum AlphaCutMode] for possible values. </member> + <member name="alpha_hash_scale" type="float" setter="set_alpha_hash_scale" getter="get_alpha_hash_scale" default="1.0"> + The hashing scale for Alpha Hash. Recommended values between [code]0[/code] and [code]2[/code]. + </member> <member name="alpha_scissor_threshold" type="float" setter="set_alpha_scissor_threshold" getter="get_alpha_scissor_threshold" default="0.5"> Threshold at which the alpha scissor will discard values. </member> @@ -152,5 +161,8 @@ This mode draws fully opaque pixels in the depth prepass. This is slower than [constant ALPHA_CUT_DISABLED] or [constant ALPHA_CUT_DISCARD], but it allows displaying translucent areas and smooth edges while using proper sorting. [b]Note:[/b] When using text with overlapping glyphs (e.g., cursive scripts), this mode might have transparency sorting issues between the main text and the outline. </constant> + <constant name="ALPHA_CUT_HASH" value="3" enum="AlphaCutMode"> + This mode draws cuts off all values below a spatially-deterministic threshold, the rest will remain opaque. + </constant> </constants> </class> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index b8383aaed9..8ed8622030 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -61,6 +61,45 @@ <return type="PopupMenu" /> <description> Returns the [PopupMenu] of this [LineEdit]. By default, this menu is displayed when right-clicking on the [LineEdit]. + You can add custom menu items or remove standard ones. Make sure your IDs don't conflict with the standard ones (see [enum MenuItems]). For example: + [codeblocks] + [gdscript] + func _ready(): + var menu = get_menu() + # Remove all items after "Redo". + menu.item_count = menu.get_item_index(MENU_REDO) + 1 + # Add custom items. + menu.add_separator() + menu.add_item("Insert Date", MENU_MAX + 1) + # Connect callback. + menu.id_pressed.connect(_on_item_pressed) + + func _on_item_pressed(id): + if id == MENU_MAX + 1: + insert_text_at_caret(Time.get_date_string_from_system()) + [/gdscript] + [csharp] + public override void _Ready() + { + var menu = GetMenu(); + // Remove all items after "Redo". + menu.ItemCount = menu.GetItemIndex(LineEdit.MenuItems.Redo) + 1; + // Add custom items. + menu.AddSeparator(); + menu.AddItem("Insert Date", LineEdit.MenuItems.Max + 1); + // Add event handler. + menu.IdPressed += OnItemPressed; + } + + public void OnItemPressed(int id) + { + if (id == LineEdit.MenuItems.Max + 1) + { + InsertTextAtCaret(Time.GetDateStringFromSystem()); + } + } + [/csharp] + [/codeblocks] [b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member Window.visible] property. </description> </method> @@ -296,70 +335,76 @@ <constant name="MENU_REDO" value="6" enum="MenuItems"> Reverse the last undo action. </constant> - <constant name="MENU_DIR_INHERITED" value="7" enum="MenuItems"> + <constant name="MENU_SUBMENU_TEXT_DIR" value="7" enum="MenuItems"> + ID of "Text Writing Direction" submenu. + </constant> + <constant name="MENU_DIR_INHERITED" value="8" enum="MenuItems"> Sets text direction to inherited. </constant> - <constant name="MENU_DIR_AUTO" value="8" enum="MenuItems"> + <constant name="MENU_DIR_AUTO" value="9" enum="MenuItems"> Sets text direction to automatic. </constant> - <constant name="MENU_DIR_LTR" value="9" enum="MenuItems"> + <constant name="MENU_DIR_LTR" value="10" enum="MenuItems"> Sets text direction to left-to-right. </constant> - <constant name="MENU_DIR_RTL" value="10" enum="MenuItems"> + <constant name="MENU_DIR_RTL" value="11" enum="MenuItems"> Sets text direction to right-to-left. </constant> - <constant name="MENU_DISPLAY_UCC" value="11" enum="MenuItems"> + <constant name="MENU_DISPLAY_UCC" value="12" enum="MenuItems"> Toggles control character display. </constant> - <constant name="MENU_INSERT_LRM" value="12" enum="MenuItems"> + <constant name="MENU_SUBMENU_INSERT_UCC" value="13" enum="MenuItems"> + ID of "Insert Control Character" submenu. + </constant> + <constant name="MENU_INSERT_LRM" value="14" enum="MenuItems"> Inserts left-to-right mark (LRM) character. </constant> - <constant name="MENU_INSERT_RLM" value="13" enum="MenuItems"> + <constant name="MENU_INSERT_RLM" value="15" enum="MenuItems"> Inserts right-to-left mark (RLM) character. </constant> - <constant name="MENU_INSERT_LRE" value="14" enum="MenuItems"> + <constant name="MENU_INSERT_LRE" value="16" enum="MenuItems"> Inserts start of left-to-right embedding (LRE) character. </constant> - <constant name="MENU_INSERT_RLE" value="15" enum="MenuItems"> + <constant name="MENU_INSERT_RLE" value="17" enum="MenuItems"> Inserts start of right-to-left embedding (RLE) character. </constant> - <constant name="MENU_INSERT_LRO" value="16" enum="MenuItems"> + <constant name="MENU_INSERT_LRO" value="18" enum="MenuItems"> Inserts start of left-to-right override (LRO) character. </constant> - <constant name="MENU_INSERT_RLO" value="17" enum="MenuItems"> + <constant name="MENU_INSERT_RLO" value="19" enum="MenuItems"> Inserts start of right-to-left override (RLO) character. </constant> - <constant name="MENU_INSERT_PDF" value="18" enum="MenuItems"> + <constant name="MENU_INSERT_PDF" value="20" enum="MenuItems"> Inserts pop direction formatting (PDF) character. </constant> - <constant name="MENU_INSERT_ALM" value="19" enum="MenuItems"> + <constant name="MENU_INSERT_ALM" value="21" enum="MenuItems"> Inserts Arabic letter mark (ALM) character. </constant> - <constant name="MENU_INSERT_LRI" value="20" enum="MenuItems"> + <constant name="MENU_INSERT_LRI" value="22" enum="MenuItems"> Inserts left-to-right isolate (LRI) character. </constant> - <constant name="MENU_INSERT_RLI" value="21" enum="MenuItems"> + <constant name="MENU_INSERT_RLI" value="23" enum="MenuItems"> Inserts right-to-left isolate (RLI) character. </constant> - <constant name="MENU_INSERT_FSI" value="22" enum="MenuItems"> + <constant name="MENU_INSERT_FSI" value="24" enum="MenuItems"> Inserts first strong isolate (FSI) character. </constant> - <constant name="MENU_INSERT_PDI" value="23" enum="MenuItems"> + <constant name="MENU_INSERT_PDI" value="25" enum="MenuItems"> Inserts pop direction isolate (PDI) character. </constant> - <constant name="MENU_INSERT_ZWJ" value="24" enum="MenuItems"> + <constant name="MENU_INSERT_ZWJ" value="26" enum="MenuItems"> Inserts zero width joiner (ZWJ) character. </constant> - <constant name="MENU_INSERT_ZWNJ" value="25" enum="MenuItems"> + <constant name="MENU_INSERT_ZWNJ" value="27" enum="MenuItems"> Inserts zero width non-joiner (ZWNJ) character. </constant> - <constant name="MENU_INSERT_WJ" value="26" enum="MenuItems"> + <constant name="MENU_INSERT_WJ" value="28" enum="MenuItems"> Inserts word joiner (WJ) character. </constant> - <constant name="MENU_INSERT_SHY" value="27" enum="MenuItems"> + <constant name="MENU_INSERT_SHY" value="29" enum="MenuItems"> Inserts soft hyphen (SHY) character. </constant> - <constant name="MENU_MAX" value="28" enum="MenuItems"> + <constant name="MENU_MAX" value="30" enum="MenuItems"> Represents the size of the [enum MenuItems] enum. </constant> <constant name="KEYBOARD_TYPE_DEFAULT" value="0" enum="VirtualKeyboardType"> diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index 3ce6ce41b4..020ce4f468 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -138,10 +138,10 @@ Used with [method Node.rpc_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods. </constant> <constant name="RPC_MODE_ANY_PEER" value="1" enum="RPCMode"> - Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc(any)[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not. + Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc("any")[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not. </constant> <constant name="RPC_MODE_AUTHORITY" value="2" enum="RPCMode"> - Used with [method Node.rpc_config] to set a method to be callable remotely only by the current multiplayer authority (which is the server by default). Analogous to the [code]@rpc(authority)[/code] annotation. See [method Node.set_multiplayer_authority]. + Used with [method Node.rpc_config] to set a method to be callable remotely only by the current multiplayer authority (which is the server by default). Analogous to the [code]@rpc("authority")[/code] annotation. See [method Node.set_multiplayer_authority]. </constant> </constants> </class> diff --git a/doc/classes/Mutex.xml b/doc/classes/Mutex.xml index 74f29bdc48..78694ce813 100644 --- a/doc/classes/Mutex.xml +++ b/doc/classes/Mutex.xml @@ -18,9 +18,9 @@ </description> </method> <method name="try_lock"> - <return type="int" enum="Error" /> + <return type="bool" /> <description> - Tries locking this [Mutex], but does not block. Returns [constant OK] on success, [constant ERR_BUSY] otherwise. + Tries locking this [Mutex], but does not block. Returns [code]true[/code] on success, [code]false[/code] otherwise. [b]Note:[/b] This function returns [constant OK] if the thread already has ownership of the mutex. </description> </method> diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index b561748b30..6ae4dc4177 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -4,8 +4,8 @@ 2D Agent used in navigation for collision avoidance. </brief_description> <description> - 2D Agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent2D] is physics safe. - [b]Note:[/b] After setting [member target_location] it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. + 2D Agent that is used in navigation to reach a position while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent2D] is physics safe. + [b]Note:[/b] After setting [member target_position] it is required to use the [method get_next_path_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. </description> <tutorials> <link title="Using NavigationAgents">$DOCS_URL/tutorials/navigation/navigation_using_navigationagents.html</link> @@ -14,13 +14,13 @@ <method name="distance_to_target" qualifiers="const"> <return type="float" /> <description> - Returns the distance to the target location, using the agent's global position. The user must set [member target_location] in order for this to be accurate. + Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate. </description> </method> <method name="get_current_navigation_path" qualifiers="const"> <return type="PackedVector2Array" /> <description> - Returns this agent's current path from start to finish in global coordinates. The path only updates when the target location is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_location] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. + Returns this agent's current path from start to finish in global coordinates. The path only updates when the target position is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_path_position] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. </description> </method> <method name="get_current_navigation_path_index" qualifiers="const"> @@ -35,10 +35,10 @@ Returns the path query result for the path the agent is currently following. </description> </method> - <method name="get_final_location"> + <method name="get_final_position"> <return type="Vector2" /> <description> - Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. + Returns the reachable final position in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. </description> </method> <method name="get_navigation_layer_value" qualifiers="const"> @@ -54,10 +54,10 @@ Returns the [RID] of the navigation map for this NavigationAgent node. This function returns always the map set on the NavigationAgent node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationAgent node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationAgent and also update the agent on the NavigationServer. </description> </method> - <method name="get_next_location"> + <method name="get_next_path_position"> <return type="Vector2" /> <description> - Returns the next location in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. + Returns the next position in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. </description> </method> <method name="get_rid" qualifiers="const"> @@ -69,19 +69,19 @@ <method name="is_navigation_finished"> <return type="bool" /> <description> - Returns true if the navigation path's final location has been reached. + Returns true if the navigation path's final position has been reached. </description> </method> <method name="is_target_reachable"> <return type="bool" /> <description> - Returns true if [member target_location] is reachable. + Returns true if [member target_position] is reachable. </description> </method> <method name="is_target_reached" qualifiers="const"> <return type="bool" /> <description> - Returns true if [member target_location] is reached. It may not always be possible to reach the target location. It should always be possible to reach the final location though. See [method get_final_location]. + Returns true if [member target_position] is reached. It may not always be possible to reach the target position. It should always be possible to reach the final position though. See [method get_final_position]. </description> </method> <method name="set_navigation_layer_value"> @@ -127,7 +127,7 @@ The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update. </member> <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="100.0"> - The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. + The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. </member> <member name="path_metadata_flags" type="int" setter="set_path_metadata_flags" getter="get_path_metadata_flags" enum="NavigationPathQueryParameters2D.PathMetadataFlags" default="7"> Additional information to return with the navigation path. @@ -139,8 +139,8 @@ <member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="10.0"> The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update. </member> - <member name="target_location" type="Vector2" setter="set_target_location" getter="get_target_location" default="Vector2(0, 0)"> - The user-defined target location. Setting this property will clear the current navigation path. + <member name="target_position" type="Vector2" setter="set_target_position" getter="get_target_position" default="Vector2(0, 0)"> + The user-defined target position. Setting this property will clear the current navigation path. </member> <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="1.0"> The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. Must be positive. @@ -152,7 +152,7 @@ <description> Notifies when a navigation link has been reached. The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: - - [code]location[/code]: The start location of the link that was reached. + - [code]position[/code]: The start position of the link that was reached. - [code]type[/code]: Always [constant NavigationPathQueryResult2D.PATH_SEGMENT_TYPE_LINK]. - [code]rid[/code]: The [RID] of the link. - [code]owner[/code]: The object which manages the link (usually [NavigationLink2D]). @@ -160,7 +160,7 @@ </signal> <signal name="navigation_finished"> <description> - Notifies when the final location is reached. + Notifies when the final position is reached. </description> </signal> <signal name="path_changed"> @@ -170,7 +170,7 @@ </signal> <signal name="target_reached"> <description> - Notifies when the player-defined [member target_location] is reached. + Notifies when the player-defined [member target_position] is reached. </description> </signal> <signal name="velocity_computed"> @@ -184,7 +184,7 @@ <description> Notifies when a waypoint along the path has been reached. The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: - - [code]location[/code]: The location of the waypoint that was reached. + - [code]position[/code]: The position of the waypoint that was reached. - [code]type[/code]: The type of navigation primitive (region or link) that contains this waypoint. - [code]rid[/code]: The [RID] of the containing navigation primitive (region or link). - [code]owner[/code]: The object which manages the containing navigation primitive (region or link). diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml index a1b007ee56..a22cd6dd46 100644 --- a/doc/classes/NavigationAgent3D.xml +++ b/doc/classes/NavigationAgent3D.xml @@ -4,8 +4,8 @@ 3D Agent used in navigation for collision avoidance. </brief_description> <description> - 3D Agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent3D] is physics safe. - [b]Note:[/b] After setting [member target_location] it is required to use the [method get_next_location] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. + 3D Agent that is used in navigation to reach a position while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. [NavigationAgent3D] is physics safe. + [b]Note:[/b] After setting [member target_position] it is required to use the [method get_next_path_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node. </description> <tutorials> <link title="Using NavigationAgents">$DOCS_URL/tutorials/navigation/navigation_using_navigationagents.html</link> @@ -14,13 +14,13 @@ <method name="distance_to_target" qualifiers="const"> <return type="float" /> <description> - Returns the distance to the target location, using the agent's global position. The user must set [member target_location] in order for this to be accurate. + Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate. </description> </method> <method name="get_current_navigation_path" qualifiers="const"> <return type="PackedVector3Array" /> <description> - Returns this agent's current path from start to finish in global coordinates. The path only updates when the target location is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_location] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. + Returns this agent's current path from start to finish in global coordinates. The path only updates when the target position is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_path_position] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic. </description> </method> <method name="get_current_navigation_path_index" qualifiers="const"> @@ -35,10 +35,10 @@ Returns the path query result for the path the agent is currently following. </description> </method> - <method name="get_final_location"> + <method name="get_final_position"> <return type="Vector3" /> <description> - Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. + Returns the reachable final position in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame. </description> </method> <method name="get_navigation_layer_value" qualifiers="const"> @@ -54,10 +54,10 @@ Returns the [RID] of the navigation map for this NavigationAgent node. This function returns always the map set on the NavigationAgent node and not the map of the abstract agent on the NavigationServer. If the agent map is changed directly with the NavigationServer API the NavigationAgent node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationAgent and also update the agent on the NavigationServer. </description> </method> - <method name="get_next_location"> + <method name="get_next_path_position"> <return type="Vector3" /> <description> - Returns the next location in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. + Returns the next position in global coordinates that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent. The use of this function once every physics frame is required to update the internal path logic of the NavigationAgent. </description> </method> <method name="get_rid" qualifiers="const"> @@ -69,19 +69,19 @@ <method name="is_navigation_finished"> <return type="bool" /> <description> - Returns true if the navigation path's final location has been reached. + Returns true if the navigation path's final position has been reached. </description> </method> <method name="is_target_reachable"> <return type="bool" /> <description> - Returns true if [member target_location] is reachable. + Returns true if [member target_position] is reachable. </description> </method> <method name="is_target_reached" qualifiers="const"> <return type="bool" /> <description> - Returns true if [member target_location] is reached. It may not always be possible to reach the target location. It should always be possible to reach the final location though. See [method get_final_location]. + Returns true if [member target_position] is reached. It may not always be possible to reach the target position. It should always be possible to reach the final position though. See [method get_final_position]. </description> </method> <method name="set_navigation_layer_value"> @@ -133,7 +133,7 @@ The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update. </member> <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0"> - The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. + The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. </member> <member name="path_metadata_flags" type="int" setter="set_path_metadata_flags" getter="get_path_metadata_flags" enum="NavigationPathQueryParameters3D.PathMetadataFlags" default="7"> Additional information to return with the navigation path. @@ -145,8 +145,8 @@ <member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="1.0"> The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update. </member> - <member name="target_location" type="Vector3" setter="set_target_location" getter="get_target_location" default="Vector3(0, 0, 0)"> - The user-defined target location. Setting this property will clear the current navigation path. + <member name="target_position" type="Vector3" setter="set_target_position" getter="get_target_position" default="Vector3(0, 0, 0)"> + The user-defined target position. Setting this property will clear the current navigation path. </member> <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="5.0"> The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. Must be positive. @@ -158,7 +158,7 @@ <description> Notifies when a navigation link has been reached. The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: - - [code]location[/code]: The start location of the link that was reached. + - [code]position[/code]: The start position of the link that was reached. - [code]type[/code]: Always [constant NavigationPathQueryResult3D.PATH_SEGMENT_TYPE_LINK]. - [code]rid[/code]: The [RID] of the link. - [code]owner[/code]: The object which manages the link (usually [NavigationLink3D]). @@ -166,7 +166,7 @@ </signal> <signal name="navigation_finished"> <description> - Notifies when the final location is reached. + Notifies when the final position is reached. </description> </signal> <signal name="path_changed"> @@ -176,7 +176,7 @@ </signal> <signal name="target_reached"> <description> - Notifies when the player-defined [member target_location] is reached. + Notifies when the player-defined [member target_position] is reached. </description> </signal> <signal name="velocity_computed"> @@ -190,7 +190,7 @@ <description> Notifies when a waypoint along the path has been reached. The details dictionary may contain the following keys depending on the value of [member path_metadata_flags]: - - [code]location[/code]: The location of the waypoint that was reached. + - [code]position[/code]: The position of the waypoint that was reached. - [code]type[/code]: The type of navigation primitive (region or link) that contains this waypoint. - [code]rid[/code]: The [RID] of the containing navigation primitive (region or link). - [code]owner[/code]: The object which manages the containing navigation primitive (region or link). diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml index 44d2110a7c..b3f4367675 100644 --- a/doc/classes/NavigationLink2D.xml +++ b/doc/classes/NavigationLink2D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="NavigationLink2D" inherits="Node2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Creates a link between two locations that [NavigationServer2D] can route agents through. + Creates a link between two positions that [NavigationServer2D] can route agents through. </brief_description> <description> - Creates a link between two locations that [NavigationServer2D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. + Creates a link between two positions that [NavigationServer2D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. </description> <tutorials> <link title="Using NavigationLinks">$DOCS_URL/tutorials/navigation/navigation_using_navigationlinks.html</link> @@ -28,12 +28,12 @@ </methods> <members> <member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true"> - Whether this link can be traveled in both directions or only from [member start_location] to [member end_location]. + Whether this link can be traveled in both directions or only from [member start_position] to [member end_position]. </member> <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> Whether this link is currently active. If [code]false[/code], [method NavigationServer2D.map_get_path] will ignore this link. </member> - <member name="end_location" type="Vector2" setter="set_end_location" getter="get_end_location" default="Vector2(0, 0)"> + <member name="end_position" type="Vector2" setter="set_end_position" getter="get_end_position" default="Vector2(0, 0)"> Ending position of the link. This position will search out the nearest polygon in the navigation mesh to attach to. The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius]. @@ -44,7 +44,7 @@ <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer2D.map_get_path]. </member> - <member name="start_location" type="Vector2" setter="set_start_location" getter="get_start_location" default="Vector2(0, 0)"> + <member name="start_position" type="Vector2" setter="set_start_position" getter="get_start_position" default="Vector2(0, 0)"> Starting position of the link. This position will search out the nearest polygon in the navigation mesh to attach to. The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius]. diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml index 4aa5801afb..4dff226042 100644 --- a/doc/classes/NavigationLink3D.xml +++ b/doc/classes/NavigationLink3D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="NavigationLink3D" inherits="Node3D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Creates a link between two locations that [NavigationServer3D] can route agents through. + Creates a link between two positions that [NavigationServer3D] can route agents through. </brief_description> <description> - Creates a link between two locations that [NavigationServer3D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. + Creates a link between two positions that [NavigationServer3D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. </description> <tutorials> <link title="Using NavigationLinks">$DOCS_URL/tutorials/navigation/navigation_using_navigationlinks.html</link> @@ -28,12 +28,12 @@ </methods> <members> <member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true"> - Whether this link can be traveled in both directions or only from [member start_location] to [member end_location]. + Whether this link can be traveled in both directions or only from [member start_position] to [member end_position]. </member> <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> Whether this link is currently active. If [code]false[/code], [method NavigationServer3D.map_get_path] will ignore this link. </member> - <member name="end_location" type="Vector3" setter="set_end_location" getter="get_end_location" default="Vector3(0, 0, 0)"> + <member name="end_position" type="Vector3" setter="set_end_position" getter="get_end_position" default="Vector3(0, 0, 0)"> Ending position of the link. This position will search out the nearest polygon in the navigation mesh to attach to. The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius]. @@ -44,7 +44,7 @@ <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer3D.map_get_path]. </member> - <member name="start_location" type="Vector3" setter="set_start_location" getter="get_start_location" default="Vector3(0, 0, 0)"> + <member name="start_position" type="Vector3" setter="set_start_position" getter="get_start_position" default="Vector3(0, 0, 0)"> Starting position of the link. This position will search out the nearest polygon in the navigation mesh to attach to. The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius]. diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 1ba949b294..16f6de5238 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -41,12 +41,10 @@ <method name="agent_set_callback"> <return type="void" /> <param index="0" name="agent" type="RID" /> - <param index="1" name="object_id" type="int" /> - <param index="2" name="method" type="StringName" /> - <param index="3" name="userdata" type="Variant" default="null" /> + <param index="1" name="callback" type="Callable" /> <description> - Sets the callback [param object_id] and [param method] that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations. - [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [param object_id]. + Sets the callback that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be passed as the first parameter just before the physics calculations. + [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with an empty [Callable]. </description> </method> <method name="agent_set_map"> @@ -137,14 +135,14 @@ <method name="link_create"> <return type="RID" /> <description> - Create a new link between two locations on a map. + Create a new link between two positions on a map. </description> </method> - <method name="link_get_end_location" qualifiers="const"> + <method name="link_get_end_position" qualifiers="const"> <return type="Vector2" /> <param index="0" name="link" type="RID" /> <description> - Returns the ending location of this [code]link[/code]. + Returns the ending position of this [code]link[/code]. </description> </method> <method name="link_get_enter_cost" qualifiers="const"> @@ -175,11 +173,11 @@ Returns the [code]ObjectID[/code] of the object which manages this link. </description> </method> - <method name="link_get_start_location" qualifiers="const"> + <method name="link_get_start_position" qualifiers="const"> <return type="Vector2" /> <param index="0" name="link" type="RID" /> <description> - Returns the starting location of this [code]link[/code]. + Returns the starting position of this [code]link[/code]. </description> </method> <method name="link_get_travel_cost" qualifiers="const"> @@ -204,12 +202,12 @@ Sets whether this [code]link[/code] can be travelled in both directions. </description> </method> - <method name="link_set_end_location"> + <method name="link_set_end_position"> <return type="void" /> <param index="0" name="link" type="RID" /> - <param index="1" name="location" type="Vector2" /> + <param index="1" name="position" type="Vector2" /> <description> - Sets the exit location for the [code]link[/code]. + Sets the exit position for the [code]link[/code]. </description> </method> <method name="link_set_enter_cost"> @@ -244,12 +242,12 @@ Set the [code]ObjectID[/code] of the object which manages this link. </description> </method> - <method name="link_set_start_location"> + <method name="link_set_start_position"> <return type="void" /> <param index="0" name="link" type="RID" /> - <param index="1" name="location" type="Vector2" /> + <param index="1" name="position" type="Vector2" /> <description> - Sets the entry location for this [code]link[/code]. + Sets the entry position for this [code]link[/code]. </description> </method> <method name="link_set_travel_cost"> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index e007c71342..340821d41e 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -41,12 +41,10 @@ <method name="agent_set_callback"> <return type="void" /> <param index="0" name="agent" type="RID" /> - <param index="1" name="object_id" type="int" /> - <param index="2" name="method" type="StringName" /> - <param index="3" name="userdata" type="Variant" default="null" /> + <param index="1" name="callback" type="Callable" /> <description> - Sets the callback [param object_id] and [param method] that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations. - [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [param object_id]. + Sets the callback that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be passed as the first parameter just before the physics calculations. + [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with an empty [Callable]. </description> </method> <method name="agent_set_map"> @@ -144,14 +142,14 @@ <method name="link_create"> <return type="RID" /> <description> - Create a new link between two locations on a map. + Create a new link between two positions on a map. </description> </method> - <method name="link_get_end_location" qualifiers="const"> + <method name="link_get_end_position" qualifiers="const"> <return type="Vector3" /> <param index="0" name="link" type="RID" /> <description> - Returns the ending location of this [code]link[/code]. + Returns the ending position of this [code]link[/code]. </description> </method> <method name="link_get_enter_cost" qualifiers="const"> @@ -182,11 +180,11 @@ Returns the [code]ObjectID[/code] of the object which manages this link. </description> </method> - <method name="link_get_start_location" qualifiers="const"> + <method name="link_get_start_position" qualifiers="const"> <return type="Vector3" /> <param index="0" name="link" type="RID" /> <description> - Returns the starting location of this [code]link[/code]. + Returns the starting position of this [code]link[/code]. </description> </method> <method name="link_get_travel_cost" qualifiers="const"> @@ -211,12 +209,12 @@ Sets whether this [code]link[/code] can be travelled in both directions. </description> </method> - <method name="link_set_end_location"> + <method name="link_set_end_position"> <return type="void" /> <param index="0" name="link" type="RID" /> - <param index="1" name="location" type="Vector3" /> + <param index="1" name="position" type="Vector3" /> <description> - Sets the exit location for the [code]link[/code]. + Sets the exit position for the [code]link[/code]. </description> </method> <method name="link_set_enter_cost"> @@ -251,12 +249,12 @@ Set the [code]ObjectID[/code] of the object which manages this link. </description> </method> - <method name="link_set_start_location"> + <method name="link_set_start_position"> <return type="void" /> <param index="0" name="link" type="RID" /> - <param index="1" name="location" type="Vector3" /> + <param index="1" name="position" type="Vector3" /> <description> - Sets the entry location for this [code]link[/code]. + Sets the entry position for this [code]link[/code]. </description> </method> <method name="link_set_travel_cost"> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 7b27f16d82..7c40c189c0 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -666,7 +666,7 @@ channel = 0, } [/codeblock] - See [enum MultiplayerAPI.RPCMode] and [enum MultiplayerPeer.TransferMode]. An alternative is annotating methods and properties with the corresponding annotation ([code]@rpc(any)[/code], [code]@rpc(authority)[/code]). By default, methods are not exposed to networking (and RPCs). + See [enum MultiplayerAPI.RPCMode] and [enum MultiplayerPeer.TransferMode]. An alternative is annotating methods and properties with the corresponding annotation ([code]@rpc("any")[/code], [code]@rpc("authority")[/code]). By default, methods are not exposed to networking (and RPCs). </description> </method> <method name="rpc_id" qualifiers="vararg"> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 0058bf8a2f..04895c28a8 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -498,7 +498,7 @@ <description> Returns [code]true[/code] if the Godot binary used to run the project is a [i]debug[/i] export template, or when running in the editor. Returns [code]false[/code] if the Godot binary used to run the project is a [i]release[/i] export template. - To check whether the Godot binary used to run the project is an export template (debug or release), use [code]OS.has_feature("standalone")[/code] instead. + To check whether the Godot binary used to run the project is an export template (debug or release), use [code]OS.has_feature("template")[/code] instead. </description> </method> <method name="is_keycode_unicode" qualifiers="const"> diff --git a/doc/classes/PacketPeerDTLS.xml b/doc/classes/PacketPeerDTLS.xml index db8403a56b..19c5d0e287 100644 --- a/doc/classes/PacketPeerDTLS.xml +++ b/doc/classes/PacketPeerDTLS.xml @@ -14,11 +14,10 @@ <method name="connect_to_peer"> <return type="int" enum="Error" /> <param index="0" name="packet_peer" type="PacketPeerUDP" /> - <param index="1" name="validate_certs" type="bool" default="true" /> - <param index="2" name="for_hostname" type="String" default="""" /> - <param index="3" name="valid_certificate" type="X509Certificate" default="null" /> + <param index="1" name="hostname" type="String" /> + <param index="2" name="client_options" type="TLSOptions" default="null" /> <description> - Connects a [param packet_peer] beginning the DTLS handshake using the underlying [PacketPeerUDP] which must be connected (see [method PacketPeerUDP.connect_to_host]). If [param validate_certs] is [code]true[/code], [PacketPeerDTLS] will validate that the certificate presented by the remote peer and match it with the [param for_hostname] argument. You can specify a custom [X509Certificate] to use for validation via the [param valid_certificate] argument. + Connects a [param packet_peer] beginning the DTLS handshake using the underlying [PacketPeerUDP] which must be connected (see [method PacketPeerUDP.connect_to_host]). You can optionally specify the [param client_options] to be used while verifying the TLS connections. See [method TLSOptions.client] and [method TLSOptions.client_unsafe]. </description> </method> <method name="disconnect_from_peer"> diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index f1316fa991..93e88347d4 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -984,25 +984,23 @@ <constant name="AREA_PARAM_GRAVITY_IS_POINT" value="3" enum="AreaParameter"> Constant to set/get whether the gravity vector of an area is a direction, or a center point. </constant> - <constant name="AREA_PARAM_GRAVITY_DISTANCE_SCALE" value="4" enum="AreaParameter"> - Constant to set/get the falloff factor for point gravity of an area. The greater this value is, the faster the strength of gravity decreases with the square of distance. + <constant name="AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE" value="4" enum="AreaParameter"> + Constant to set/get the distance at which the gravity strength is equal to the gravity controlled by [constant AREA_PARAM_GRAVITY]. For example, on a planet 100 pixels in radius with a surface gravity of 4.0 px/s², set the gravity to 4.0 and the unit distance to 100.0. The gravity will have falloff according to the inverse square law, so in the example, at 200 pixels from the center the gravity will be 1.0 px/s² (twice the distance, 1/4th the gravity), at 50 pixels it will be 16.0 px/s² (half the distance, 4x the gravity), and so on. + The above is true only when the unit distance is a positive number. When the unit distance is set to 0.0, the gravity will be constant regardless of distance. </constant> - <constant name="AREA_PARAM_GRAVITY_POINT_ATTENUATION" value="5" enum="AreaParameter"> - This constant was used to set/get the falloff factor for point gravity. It has been superseded by [constant AREA_PARAM_GRAVITY_DISTANCE_SCALE]. - </constant> - <constant name="AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE" value="6" enum="AreaParameter"> + <constant name="AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE" value="5" enum="AreaParameter"> Constant to set/get linear damping override mode in an area. See [enum AreaSpaceOverrideMode] for possible values. </constant> - <constant name="AREA_PARAM_LINEAR_DAMP" value="7" enum="AreaParameter"> + <constant name="AREA_PARAM_LINEAR_DAMP" value="6" enum="AreaParameter"> Constant to set/get the linear damping factor of an area. </constant> - <constant name="AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE" value="8" enum="AreaParameter"> + <constant name="AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE" value="7" enum="AreaParameter"> Constant to set/get angular damping override mode in an area. See [enum AreaSpaceOverrideMode] for possible values. </constant> - <constant name="AREA_PARAM_ANGULAR_DAMP" value="9" enum="AreaParameter"> + <constant name="AREA_PARAM_ANGULAR_DAMP" value="8" enum="AreaParameter"> Constant to set/get the angular damping factor of an area. </constant> - <constant name="AREA_PARAM_PRIORITY" value="10" enum="AreaParameter"> + <constant name="AREA_PARAM_PRIORITY" value="9" enum="AreaParameter"> Constant to set/get the priority (order of processing) of an area. </constant> <constant name="AREA_SPACE_OVERRIDE_DISABLED" value="0" enum="AreaSpaceOverrideMode"> diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml index e62bda0dd3..5b261d8414 100644 --- a/doc/classes/PhysicsServer3D.xml +++ b/doc/classes/PhysicsServer3D.xml @@ -1315,37 +1315,35 @@ <constant name="AREA_PARAM_GRAVITY_IS_POINT" value="3" enum="AreaParameter"> Constant to set/get whether the gravity vector of an area is a direction, or a center point. </constant> - <constant name="AREA_PARAM_GRAVITY_DISTANCE_SCALE" value="4" enum="AreaParameter"> - Constant to set/get the falloff factor for point gravity of an area. The greater this value is, the faster the strength of gravity decreases with the square of distance. + <constant name="AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE" value="4" enum="AreaParameter"> + Constant to set/get the distance at which the gravity strength is equal to the gravity controlled by [constant AREA_PARAM_GRAVITY]. For example, on a planet 100 meters in radius with a surface gravity of 4.0 m/s², set the gravity to 4.0 and the unit distance to 100.0. The gravity will have falloff according to the inverse square law, so in the example, at 200 meters from the center the gravity will be 1.0 m/s² (twice the distance, 1/4th the gravity), at 50 meters it will be 16.0 m/s² (half the distance, 4x the gravity), and so on. + The above is true only when the unit distance is a positive number. When this is set to 0.0, the gravity will be constant regardless of distance. </constant> - <constant name="AREA_PARAM_GRAVITY_POINT_ATTENUATION" value="5" enum="AreaParameter"> - This constant was used to set/get the falloff factor for point gravity. It has been superseded by [constant AREA_PARAM_GRAVITY_DISTANCE_SCALE]. - </constant> - <constant name="AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE" value="6" enum="AreaParameter"> + <constant name="AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE" value="5" enum="AreaParameter"> Constant to set/get linear damping override mode in an area. See [enum AreaSpaceOverrideMode] for possible values. </constant> - <constant name="AREA_PARAM_LINEAR_DAMP" value="7" enum="AreaParameter"> + <constant name="AREA_PARAM_LINEAR_DAMP" value="6" enum="AreaParameter"> Constant to set/get the linear damping factor of an area. </constant> - <constant name="AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE" value="8" enum="AreaParameter"> + <constant name="AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE" value="7" enum="AreaParameter"> Constant to set/get angular damping override mode in an area. See [enum AreaSpaceOverrideMode] for possible values. </constant> - <constant name="AREA_PARAM_ANGULAR_DAMP" value="9" enum="AreaParameter"> + <constant name="AREA_PARAM_ANGULAR_DAMP" value="8" enum="AreaParameter"> Constant to set/get the angular damping factor of an area. </constant> - <constant name="AREA_PARAM_PRIORITY" value="10" enum="AreaParameter"> + <constant name="AREA_PARAM_PRIORITY" value="9" enum="AreaParameter"> Constant to set/get the priority (order of processing) of an area. </constant> - <constant name="AREA_PARAM_WIND_FORCE_MAGNITUDE" value="11" enum="AreaParameter"> + <constant name="AREA_PARAM_WIND_FORCE_MAGNITUDE" value="10" enum="AreaParameter"> Constant to set/get the magnitude of area-specific wind force. </constant> - <constant name="AREA_PARAM_WIND_SOURCE" value="12" enum="AreaParameter"> + <constant name="AREA_PARAM_WIND_SOURCE" value="11" enum="AreaParameter"> Constant to set/get the 3D vector that specifies the origin from which an area-specific wind blows. </constant> - <constant name="AREA_PARAM_WIND_DIRECTION" value="13" enum="AreaParameter"> + <constant name="AREA_PARAM_WIND_DIRECTION" value="12" enum="AreaParameter"> Constant to set/get the 3D vector that specifies the direction in which an area-specific wind blows. </constant> - <constant name="AREA_PARAM_WIND_ATTENUATION_FACTOR" value="14" enum="AreaParameter"> + <constant name="AREA_PARAM_WIND_ATTENUATION_FACTOR" value="13" enum="AreaParameter"> Constant to set/get the exponential rate at which wind force decreases with distance from its origin. </constant> <constant name="AREA_SPACE_OVERRIDE_DISABLED" value="0" enum="AreaSpaceOverrideMode"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 430f1c60fd..f1ada58e0d 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -408,8 +408,11 @@ <member name="debug/gdscript/warnings/incompatible_ternary" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a ternary operator may emit values with incompatible types. </member> - <member name="debug/gdscript/warnings/int_assigned_to_enum" type="int" setter="" getter="" default="1"> - When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when trying to assign an integer to a variable that expects an enum value. + <member name="debug/gdscript/warnings/int_as_enum_without_cast" type="int" setter="" getter="" default="1"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when trying to use an integer as an enum without an explicit cast. + </member> + <member name="debug/gdscript/warnings/int_as_enum_without_match" type="int" setter="" getter="" default="1"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when trying to use an integer as an enum when there is no matching enum member for that numeric value. </member> <member name="debug/gdscript/warnings/integer_division" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when dividing an integer by another integer (the decimal part will be discarded). @@ -2282,20 +2285,12 @@ <member name="rendering/textures/lossless_compression/force_png" type="bool" setter="" getter="" default="false"> If [code]true[/code], the texture importer will import lossless textures using the PNG format. Otherwise, it will default to using WebP. </member> - <member name="rendering/textures/vram_compression/import_bptc" type="bool" setter="" getter="" default="false"> - If [code]true[/code], the texture importer will import VRAM-compressed textures using the BPTC algorithm. This texture compression algorithm is only supported on desktop platforms, and only when using the Vulkan renderer. - [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]). - </member> - <member name="rendering/textures/vram_compression/import_etc" type="bool" setter="" getter="" default="false"> - If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression algorithm. This algorithm doesn't support alpha channels in textures. - [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]). - </member> - <member name="rendering/textures/vram_compression/import_etc2" type="bool" setter="" getter="" default="true"> - If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression 2 algorithm. This texture compression algorithm is only supported when using the Vulkan renderer. + <member name="rendering/textures/vram_compression/import_etc2_astc" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression 2 algorithm for lower quality textures and normalmaps and Adaptable Scalable Texture Compression algorithm for high quality textures (in 4x4 block size). [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]). </member> - <member name="rendering/textures/vram_compression/import_s3tc" type="bool" setter="" getter="" default="true"> - If [code]true[/code], the texture importer will import VRAM-compressed textures using the S3 Texture Compression algorithm. This algorithm is only supported on desktop platforms and consoles. + <member name="rendering/textures/vram_compression/import_s3tc_bptc" type="bool" setter="" getter="" default="true"> + If [code]true[/code], the texture importer will import VRAM-compressed textures using the S3 Texture Compression algorithm (DXT1-5) for lower quality textures and the the BPTC algorithm (BC6H and BC7) for high quality textures. This algorithm is only supported on PC desktop platforms and consoles. [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]). </member> <member name="rendering/textures/webp_compression/compression_method" type="int" setter="" getter="" default="2"> diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml index 325ead0cfa..80a9b40605 100644 --- a/doc/classes/Rect2i.xml +++ b/doc/classes/Rect2i.xml @@ -80,9 +80,9 @@ [/gdscript] [csharp] // position (-3, 2), size (1, 1) - var rect = new Rect2i(new Vector2i(-3, 2), new Vector2i(1, 1)); - // position (-3, -1), size (3, 4), so we fit both rect and Vector2i(0, -1) - var rect2 = rect.Expand(new Vector2i(0, -1)); + var rect = new Rect2I(new Vector2I(-3, 2), new Vector2I(1, 1)); + // position (-3, -1), size (3, 4), so we fit both rect and Vector2I(0, -1) + var rect2 = rect.Expand(new Vector2I(0, -1)); [/csharp] [/codeblocks] </description> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index fc08a16619..8bb3073000 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3245,12 +3245,12 @@ <description> </description> </method> - <method name="viewport_set_disable_environment"> + <method name="viewport_set_environment_mode"> <return type="void" /> <param index="0" name="viewport" type="RID" /> - <param index="1" name="disabled" type="bool" /> + <param index="1" name="mode" type="int" enum="RenderingServer.ViewportEnvironmentMode" /> <description> - If [code]true[/code], rendering of a viewport's environment is disabled. + Sets the viewport's environment mode which allows enabling or disabling rendering of 3D environment over 2D canvas. When disabled, 2D will not be affected by the environment. When enabled, 2D will be affected by the environment if the environment background mode is [constant ENV_BG_CANVAS]. The default behaviour is to inherit the setting from the viewport's parent. If the topmost parent is also set to [constant VIEWPORT_ENVIRONMENT_INHERIT], then the behavior will be the same as if it was set to [constant VIEWPORT_ENVIRONMENT_ENABLED]. </description> </method> <method name="viewport_set_fsr_sharpness"> @@ -4135,6 +4135,18 @@ <constant name="VIEWPORT_CLEAR_ONLY_NEXT_FRAME" value="2" enum="ViewportClearMode"> The viewport is cleared once, then the clear mode is set to [constant VIEWPORT_CLEAR_NEVER]. </constant> + <constant name="VIEWPORT_ENVIRONMENT_DISABLED" value="0" enum="ViewportEnvironmentMode"> + Disable rendering of 3D environment over 2D canvas. + </constant> + <constant name="VIEWPORT_ENVIRONMENT_ENABLED" value="1" enum="ViewportEnvironmentMode"> + Enable rendering of 3D environment over 2D canvas. + </constant> + <constant name="VIEWPORT_ENVIRONMENT_INHERIT" value="2" enum="ViewportEnvironmentMode"> + Inherit enable/disable value from parent. If topmost parent is also set to inherit, then this has the same behavior as [constant VIEWPORT_ENVIRONMENT_ENABLED]. + </constant> + <constant name="VIEWPORT_ENVIRONMENT_MAX" value="3" enum="ViewportEnvironmentMode"> + Max value of [enum ViewportEnvironmentMode] enum. + </constant> <constant name="VIEWPORT_SDF_OVERSIZE_100_PERCENT" value="0" enum="ViewportSDFOversize"> </constant> <constant name="VIEWPORT_SDF_OVERSIZE_120_PERCENT" value="1" enum="ViewportSDFOversize"> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 8c0b16c76f..1ecc8a1d4e 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -251,6 +251,14 @@ Adds a [code][color][/code] tag to the tag stack. </description> </method> + <method name="push_customfx"> + <return type="void" /> + <param index="0" name="effect" type="RichTextEffect" /> + <param index="1" name="env" type="Dictionary" /> + <description> + Adds a custom effect tag to the tag stack. The effect does not need to be in [member custom_effects]. The environment is directly passed to the effect. + </description> + </method> <method name="push_dropcap"> <return type="void" /> <param index="0" name="string" type="String" /> diff --git a/doc/classes/Semaphore.xml b/doc/classes/Semaphore.xml index 6b2007363e..d1d126c5cb 100644 --- a/doc/classes/Semaphore.xml +++ b/doc/classes/Semaphore.xml @@ -17,9 +17,9 @@ </description> </method> <method name="try_wait"> - <return type="int" enum="Error" /> + <return type="bool" /> <description> - Like [method wait], but won't block, so if the value is zero, fails immediately and returns [constant ERR_BUSY]. If non-zero, it returns [constant OK] to report success. + Like [method wait], but won't block, so if the value is zero, fails immediately and returns [code]false[/code]. If non-zero, it returns [code]true[/code] to report success. </description> </method> <method name="wait"> diff --git a/doc/classes/SpriteBase3D.xml b/doc/classes/SpriteBase3D.xml index 5fa984e7a0..e1f3a52a1d 100644 --- a/doc/classes/SpriteBase3D.xml +++ b/doc/classes/SpriteBase3D.xml @@ -38,9 +38,21 @@ </method> </methods> <members> + <member name="alpha_antialiasing_edge" type="float" setter="set_alpha_antialiasing_edge" getter="get_alpha_antialiasing_edge" default="0.0"> + Threshold at which antialiasing will be applied on the alpha channel. + </member> + <member name="alpha_antialiasing_mode" type="int" setter="set_alpha_antialiasing" getter="get_alpha_antialiasing" enum="BaseMaterial3D.AlphaAntiAliasing" default="0"> + The type of alpha antialiasing to apply. See [enum BaseMaterial3D.AlphaAntiAliasing]. + </member> <member name="alpha_cut" type="int" setter="set_alpha_cut_mode" getter="get_alpha_cut_mode" enum="SpriteBase3D.AlphaCutMode" default="0"> The alpha cutting mode to use for the sprite. See [enum AlphaCutMode] for possible values. </member> + <member name="alpha_hash_scale" type="float" setter="set_alpha_hash_scale" getter="get_alpha_hash_scale" default="1.0"> + The hashing scale for Alpha Hash. Recommended values between [code]0[/code] and [code]2[/code]. + </member> + <member name="alpha_scissor_threshold" type="float" setter="set_alpha_scissor_threshold" getter="get_alpha_scissor_threshold" default="0.5"> + Threshold at which the alpha scissor will discard values. + </member> <member name="axis" type="int" setter="set_axis" getter="get_axis" enum="Vector3.Axis" default="2"> The direction in which the front of the texture faces. </member> @@ -118,5 +130,8 @@ <constant name="ALPHA_CUT_OPAQUE_PREPASS" value="2" enum="AlphaCutMode"> This mode draws fully opaque pixels in the depth prepass. This is slower than [constant ALPHA_CUT_DISABLED] or [constant ALPHA_CUT_DISCARD], but it allows displaying translucent areas and smooth edges while using proper sorting. </constant> + <constant name="ALPHA_CUT_HASH" value="3" enum="AlphaCutMode"> + This mode draws cuts off all values below a spatially-deterministic threshold, the rest will remain opaque. + </constant> </constants> </class> diff --git a/doc/classes/StreamPeerTLS.xml b/doc/classes/StreamPeerTLS.xml index d1ddb3d441..9e16dc8914 100644 --- a/doc/classes/StreamPeerTLS.xml +++ b/doc/classes/StreamPeerTLS.xml @@ -14,22 +14,18 @@ <method name="accept_stream"> <return type="int" enum="Error" /> <param index="0" name="stream" type="StreamPeer" /> - <param index="1" name="private_key" type="CryptoKey" /> - <param index="2" name="certificate" type="X509Certificate" /> - <param index="3" name="chain" type="X509Certificate" default="null" /> + <param index="1" name="server_options" type="TLSOptions" /> <description> - Accepts a peer connection as a server using the given [param private_key] and providing the given [param certificate] to the client. You can pass the optional [param chain] parameter to provide additional CA chain information along with the certificate. + Accepts a peer connection as a server using the given [param server_options]. See [method TLSOptions.server]. </description> </method> <method name="connect_to_stream"> <return type="int" enum="Error" /> <param index="0" name="stream" type="StreamPeer" /> - <param index="1" name="validate_certs" type="bool" default="false" /> - <param index="2" name="for_hostname" type="String" default="""" /> - <param index="3" name="valid_certificate" type="X509Certificate" default="null" /> + <param index="1" name="common_name" type="String" /> + <param index="2" name="client_options" type="TLSOptions" default="null" /> <description> - Connects to a peer using an underlying [StreamPeer] [param stream]. If [param validate_certs] is [code]true[/code], [StreamPeerTLS] will validate that the certificate presented by the peer matches the [param for_hostname]. - [b]Note:[/b] Specifying a custom [param valid_certificate] is not supported in Web exports due to browsers restrictions. + Connects to a peer using an underlying [StreamPeer] [param stream] and verifying the remote certificate is correctly signed for the given [param common_name]. You can pass the optional [param client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe]. </description> </method> <method name="disconnect_from_stream"> @@ -57,10 +53,6 @@ </description> </method> </methods> - <members> - <member name="blocking_handshake" type="bool" setter="set_blocking_handshake_enabled" getter="is_blocking_handshake_enabled" default="true"> - </member> - </members> <constants> <constant name="STATUS_DISCONNECTED" value="0" enum="Status"> A status representing a [StreamPeerTLS] that is disconnected. diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 97466e7860..143e1f23e9 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -1002,10 +1002,16 @@ [/codeblocks] </description> </method> + <method name="validate_filename" qualifiers="const"> + <return type="String" /> + <description> + Returns a copy of the string with all characters that are not allowed in [method is_valid_filename] replaced with underscores. + </description> + </method> <method name="validate_node_name" qualifiers="const"> <return type="String" /> <description> - Removes all characters that are not allowed in [member Node.name] from the string ([code].[/code] [code]:[/code] [code]@[/code] [code]/[/code] [code]"[/code] [code]%[/code]). + Returns a copy of the string with all characters that are not allowed in [member Node.name] removed ([code].[/code] [code]:[/code] [code]@[/code] [code]/[/code] [code]"[/code] [code]%[/code]). </description> </method> <method name="xml_escape" qualifiers="const"> diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index b46e39b8d7..c103fb2287 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -909,10 +909,16 @@ [/codeblocks] </description> </method> + <method name="validate_filename" qualifiers="const"> + <return type="String" /> + <description> + Returns a copy of the string with all characters that are not allowed in [method is_valid_filename] replaced with underscores. + </description> + </method> <method name="validate_node_name" qualifiers="const"> <return type="String" /> <description> - Removes all characters that are not allowed in [member Node.name] from the string ([code].[/code] [code]:[/code] [code]@[/code] [code]/[/code] [code]"[/code] [code]%[/code]). + Returns a copy of the string with all characters that are not allowed in [member Node.name] removed ([code].[/code] [code]:[/code] [code]@[/code] [code]/[/code] [code]"[/code] [code]%[/code]). </description> </method> <method name="xml_escape" qualifiers="const"> diff --git a/doc/classes/StyleBoxTexture.xml b/doc/classes/StyleBoxTexture.xml index 745187ed63..f2f6e59a9e 100644 --- a/doc/classes/StyleBoxTexture.xml +++ b/doc/classes/StyleBoxTexture.xml @@ -82,6 +82,7 @@ <member name="region_rect" type="Rect2" setter="set_region_rect" getter="get_region_rect" default="Rect2(0, 0, 0, 0)"> Species a sub-region of the texture to use. This is equivalent to first wrapping the texture in an [AtlasTexture] with the same region. + If empty ([code]Rect2(0, 0, 0, 0)[/code]), the whole texture will be used. </member> <member name="texture" type="Texture2D" setter="set_texture" getter="get_texture"> The texture to use when drawing this style box. diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 9d73e9fb39..5b567dbc28 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -133,7 +133,7 @@ <description> Generates normals from vertices so you do not have to do it manually. If [param flip] is [code]true[/code], the resulting normals will be inverted. [method generate_normals] should be called [i]after[/i] generating geometry and [i]before[/i] committing the mesh using [method commit] or [method commit_to_arrays]. For correct display of normal-mapped surfaces, you will also have to generate tangents using [method generate_tangents]. [b]Note:[/b] [method generate_normals] only works if the primitive type to be set to [constant Mesh.PRIMITIVE_TRIANGLES]. - [b]Note:[/b] [method generate_normals] takes smooth groups into account. If you don't specify any smooth group for each vertex, [method generate_normals] will smooth normals for you. + [b]Note:[/b] [method generate_normals] takes smooth groups into account. To generate smooth normals, set the smooth group to a value greater than or equal to [code]0[/code] using [method set_smooth_group] or leave the smooth group at the default of [code]0[/code]. To generate flat normals, set the smooth group to [code]-1[/code] using [method set_smooth_group] prior to adding vertices. </description> </method> <method name="generate_tangents"> @@ -241,7 +241,7 @@ <return type="void" /> <param index="0" name="index" type="int" /> <description> - Specifies whether the current vertex (if using only vertex arrays) or current index (if also using index arrays) should use smooth normals for normal calculation. + Specifies the smooth group to use for the [i]next[/i] vertex. If this is never called, all vertices will have the default smooth group of [code]0[/code] and will be smoothed with adjacent vertices of the same group. To produce a mesh with flat normals, set the smooth group to [code]-1[/code]. </description> </method> <method name="set_tangent"> diff --git a/doc/classes/SystemFont.xml b/doc/classes/SystemFont.xml index 20bfd0d8ae..5e7b79ae97 100644 --- a/doc/classes/SystemFont.xml +++ b/doc/classes/SystemFont.xml @@ -43,6 +43,12 @@ <member name="hinting" type="int" setter="set_hinting" getter="get_hinting" enum="TextServer.Hinting" default="1"> Font hinting mode. </member> + <member name="msdf_pixel_range" type="int" setter="set_msdf_pixel_range" getter="get_msdf_pixel_range" default="16"> + The width of the range around the shape between the minimum and maximum representable signed distance. If using font outlines, [member msdf_pixel_range] must be set to at least [i]twice[/i] the size of the largest font outline. The default [member msdf_pixel_range] value of [code]16[/code] allows outline sizes up to [code]8[/code] to look correct. + </member> + <member name="msdf_size" type="int" setter="set_msdf_size" getter="get_msdf_size" default="48"> + Source font size used to generate MSDF textures. Higher values allow for more precision, but are slower to render and require more memory. Only increase this value if you notice a visible lack of precision in glyph rendering. + </member> <member name="multichannel_signed_distance_field" type="bool" setter="set_multichannel_signed_distance_field" getter="is_multichannel_signed_distance_field" default="false"> If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data. </member> diff --git a/doc/classes/TLSOptions.xml b/doc/classes/TLSOptions.xml new file mode 100644 index 0000000000..0917bd9bce --- /dev/null +++ b/doc/classes/TLSOptions.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="TLSOptions" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + TLS configuration for clients and servers. + </brief_description> + <description> + TLSOptions abstracts the configuration options for the [StreamPeerTLS] and [PacketPeerDTLS] classes. + Objects of this class cannot be instantiated directly, and one of the static methods [method client], [method client_unsafe], or [method server] should be used instead. + [codeblocks] + [gdscript] + # Create a TLS client configuration which uses our custom trusted CA chain. + var client_trusted_cas = load("res://my_trusted_cas.crt") + var client_tls_options = TLSOptions.client(client_trusted_cas) + + # Create a TLS server configuration. + var server_certs = load("res://my_server_cas.crt") + var server_key = load("res://my_server_key.key") + var server_tls_options = TLSOptions.server(server_certs, server_key) + [/gdscript] + [/codeblocks] + </description> + <tutorials> + </tutorials> + <methods> + <method name="client" qualifiers="static"> + <return type="TLSOptions" /> + <param index="0" name="trusted_chain" type="X509Certificate" default="null" /> + <param index="1" name="common_name_override" type="String" default="""" /> + <description> + Creates a TLS client configuration which validates certificates and their common names (fully qualified domain names). + You can specify a custom [param trusted_chain] of certification authorities (the default CA list will be used if [code]null[/code]), and optionally provide a [param common_name_override] if you expect the certificate to have a common name other then the server FQDN. + Note: On the Web plafrom, TLS verification is always enforced against the CA list of the web browser. This is considered a security feature. + </description> + </method> + <method name="client_unsafe" qualifiers="static"> + <return type="TLSOptions" /> + <param index="0" name="trusted_chain" type="X509Certificate" default="null" /> + <description> + Creates an [b]unsafe[/b] TLS client configuration where certificate validation is optional. You can optionally provide a valid [param trusted_chain], but the common name of the certififcates will never be checked. Using this configuration for purposes other than testing [b]is not recommended[/b]. + Note: On the Web plafrom, TLS verification is always enforced against the CA list of the web browser. This is considered a security feature. + </description> + </method> + <method name="server" qualifiers="static"> + <return type="TLSOptions" /> + <param index="0" name="key" type="CryptoKey" /> + <param index="1" name="certificate" type="X509Certificate" /> + <description> + Creates a TLS server configuration using the provided [param key] and [param certificate]. + Note: The [param certificate] should include the full certificate chain up to the signing CA (certificates file can be concatenated using a general purpose text editor). + </description> + </method> + </methods> +</class> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index ccd79cd170..c309026aaa 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -390,6 +390,45 @@ <return type="PopupMenu" /> <description> Returns the [PopupMenu] of this [TextEdit]. By default, this menu is displayed when right-clicking on the [TextEdit]. + You can add custom menu items or remove standard ones. Make sure your IDs don't conflict with the standard ones (see [enum MenuItems]). For example: + [codeblocks] + [gdscript] + func _ready(): + var menu = get_menu() + # Remove all items after "Redo". + menu.item_count = menu.get_item_index(MENU_REDO) + 1 + # Add custom items. + menu.add_separator() + menu.add_item("Insert Date", MENU_MAX + 1) + # Connect callback. + menu.id_pressed.connect(_on_item_pressed) + + func _on_item_pressed(id): + if id == MENU_MAX + 1: + insert_text_at_caret(Time.get_date_string_from_system()) + [/gdscript] + [csharp] + public override void _Ready() + { + var menu = GetMenu(); + // Remove all items after "Redo". + menu.ItemCount = menu.GetItemIndex(TextEdit.MenuItems.Redo) + 1; + // Add custom items. + menu.AddSeparator(); + menu.AddItem("Insert Date", TextEdit.MenuItems.Max + 1); + // Add event handler. + menu.IdPressed += OnItemPressed; + } + + public void OnItemPressed(int id) + { + if (id == TextEdit.MenuItems.Max + 1) + { + InsertTextAtCaret(Time.GetDateStringFromSystem()); + } + } + [/csharp] + [/codeblocks] [b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member Window.visible] property. </description> </method> @@ -682,7 +721,7 @@ <return type="void" /> <param index="0" name="option" type="int" /> <description> - Triggers a right-click menu action by the specified index. See [enum MenuItems] for a list of available indexes. + Executes a given action as defined in the [enum MenuItems] enum. </description> </method> <method name="merge_gutters"> @@ -764,18 +803,18 @@ [codeblocks] [gdscript] var result = search("print", SEARCH_WHOLE_WORDS, 0, 0) - if result.x != -1: + if result.x != -1: # Result found. var line_number = result.y var column_number = result.x [/gdscript] [csharp] - Vector2i result = Search("print", (uint)TextEdit.SearchFlags.WholeWords, 0, 0); - if (result.Length > 0) + Vector2I result = Search("print", (uint)TextEdit.SearchFlags.WholeWords, 0, 0); + if (result.X != -1) { // Result found. - int lineNumber = result.y; - int columnNumber = result.x; + int lineNumber = result.Y; + int columnNumber = result.X; } [/csharp] [/codeblocks] @@ -1224,70 +1263,76 @@ <constant name="MENU_REDO" value="6" enum="MenuItems"> Redoes the previous action. </constant> - <constant name="MENU_DIR_INHERITED" value="7" enum="MenuItems"> + <constant name="MENU_SUBMENU_TEXT_DIR" value="7" enum="MenuItems"> + ID of "Text Writing Direction" submenu. + </constant> + <constant name="MENU_DIR_INHERITED" value="8" enum="MenuItems"> Sets text direction to inherited. </constant> - <constant name="MENU_DIR_AUTO" value="8" enum="MenuItems"> + <constant name="MENU_DIR_AUTO" value="9" enum="MenuItems"> Sets text direction to automatic. </constant> - <constant name="MENU_DIR_LTR" value="9" enum="MenuItems"> + <constant name="MENU_DIR_LTR" value="10" enum="MenuItems"> Sets text direction to left-to-right. </constant> - <constant name="MENU_DIR_RTL" value="10" enum="MenuItems"> + <constant name="MENU_DIR_RTL" value="11" enum="MenuItems"> Sets text direction to right-to-left. </constant> - <constant name="MENU_DISPLAY_UCC" value="11" enum="MenuItems"> + <constant name="MENU_DISPLAY_UCC" value="12" enum="MenuItems"> Toggles control character display. </constant> - <constant name="MENU_INSERT_LRM" value="12" enum="MenuItems"> + <constant name="MENU_SUBMENU_INSERT_UCC" value="13" enum="MenuItems"> + ID of "Insert Control Character" submenu. + </constant> + <constant name="MENU_INSERT_LRM" value="14" enum="MenuItems"> Inserts left-to-right mark (LRM) character. </constant> - <constant name="MENU_INSERT_RLM" value="13" enum="MenuItems"> + <constant name="MENU_INSERT_RLM" value="15" enum="MenuItems"> Inserts right-to-left mark (RLM) character. </constant> - <constant name="MENU_INSERT_LRE" value="14" enum="MenuItems"> + <constant name="MENU_INSERT_LRE" value="16" enum="MenuItems"> Inserts start of left-to-right embedding (LRE) character. </constant> - <constant name="MENU_INSERT_RLE" value="15" enum="MenuItems"> + <constant name="MENU_INSERT_RLE" value="17" enum="MenuItems"> Inserts start of right-to-left embedding (RLE) character. </constant> - <constant name="MENU_INSERT_LRO" value="16" enum="MenuItems"> + <constant name="MENU_INSERT_LRO" value="18" enum="MenuItems"> Inserts start of left-to-right override (LRO) character. </constant> - <constant name="MENU_INSERT_RLO" value="17" enum="MenuItems"> + <constant name="MENU_INSERT_RLO" value="19" enum="MenuItems"> Inserts start of right-to-left override (RLO) character. </constant> - <constant name="MENU_INSERT_PDF" value="18" enum="MenuItems"> + <constant name="MENU_INSERT_PDF" value="20" enum="MenuItems"> Inserts pop direction formatting (PDF) character. </constant> - <constant name="MENU_INSERT_ALM" value="19" enum="MenuItems"> + <constant name="MENU_INSERT_ALM" value="21" enum="MenuItems"> Inserts Arabic letter mark (ALM) character. </constant> - <constant name="MENU_INSERT_LRI" value="20" enum="MenuItems"> + <constant name="MENU_INSERT_LRI" value="22" enum="MenuItems"> Inserts left-to-right isolate (LRI) character. </constant> - <constant name="MENU_INSERT_RLI" value="21" enum="MenuItems"> + <constant name="MENU_INSERT_RLI" value="23" enum="MenuItems"> Inserts right-to-left isolate (RLI) character. </constant> - <constant name="MENU_INSERT_FSI" value="22" enum="MenuItems"> + <constant name="MENU_INSERT_FSI" value="24" enum="MenuItems"> Inserts first strong isolate (FSI) character. </constant> - <constant name="MENU_INSERT_PDI" value="23" enum="MenuItems"> + <constant name="MENU_INSERT_PDI" value="25" enum="MenuItems"> Inserts pop direction isolate (PDI) character. </constant> - <constant name="MENU_INSERT_ZWJ" value="24" enum="MenuItems"> + <constant name="MENU_INSERT_ZWJ" value="26" enum="MenuItems"> Inserts zero width joiner (ZWJ) character. </constant> - <constant name="MENU_INSERT_ZWNJ" value="25" enum="MenuItems"> + <constant name="MENU_INSERT_ZWNJ" value="27" enum="MenuItems"> Inserts zero width non-joiner (ZWNJ) character. </constant> - <constant name="MENU_INSERT_WJ" value="26" enum="MenuItems"> + <constant name="MENU_INSERT_WJ" value="28" enum="MenuItems"> Inserts word joiner (WJ) character. </constant> - <constant name="MENU_INSERT_SHY" value="27" enum="MenuItems"> + <constant name="MENU_INSERT_SHY" value="29" enum="MenuItems"> Inserts soft hyphen (SHY) character. </constant> - <constant name="MENU_MAX" value="28" enum="MenuItems"> + <constant name="MENU_MAX" value="30" enum="MenuItems"> Represents the size of the [enum MenuItems] enum. </constant> <constant name="ACTION_NONE" value="0" enum="EditAction"> diff --git a/doc/classes/TileData.xml b/doc/classes/TileData.xml index f815b8d0c3..bedc52abd1 100644 --- a/doc/classes/TileData.xml +++ b/doc/classes/TileData.xml @@ -218,7 +218,7 @@ <member name="terrain_set" type="int" setter="set_terrain_set" getter="get_terrain_set" default="-1"> ID of the terrain set that the tile uses. </member> - <member name="texture_offset" type="Vector2i" setter="set_texture_offset" getter="get_texture_offset" default="Vector2i(0, 0)"> + <member name="texture_origin" type="Vector2i" setter="set_texture_origin" getter="get_texture_origin" default="Vector2i(0, 0)"> Offsets the position of where the tile is drawn. </member> <member name="transpose" type="bool" setter="set_transpose" getter="get_transpose" default="false"> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 2fd42666b4..c387bd435b 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -251,7 +251,7 @@ <param index="0" name="map_position" type="Vector2i" /> <description> Returns the centered position of a cell in the TileMap's local coordinate space. To convert the returned value into global coordinates, use [method Node2D.to_global]. See also [method local_to_map]. - [b]Note:[/b] This may not correspond to the visual position of the tile, i.e. it ignores the [member TileData.texture_offset] property of individual tiles. + [b]Note:[/b] This may not correspond to the visual position of the tile, i.e. it ignores the [member TileData.texture_origin] property of individual tiles. </description> </method> <method name="move_layer"> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index a812b52307..a39a43be4c 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -143,6 +143,14 @@ Returns the custom data layers count. </description> </method> + <method name="get_navigation_layer_layer_value" qualifiers="const"> + <return type="bool" /> + <param index="0" name="layer_index" type="int" /> + <param index="1" name="layer_number" type="int" /> + <description> + Returns whether or not the specified navigation layer of the TileSet navigation data layer identified by the given [param layer_index] is enabled, given a navigation_layers [param layer_number] between 1 and 32. + </description> + </method> <method name="get_navigation_layer_layers" qualifiers="const"> <return type="int" /> <param index="0" name="layer_index" type="int" /> @@ -500,6 +508,15 @@ Sets the type of the custom data layer identified by the given index. </description> </method> + <method name="set_navigation_layer_layer_value"> + <return type="void" /> + <param index="0" name="layer_index" type="int" /> + <param index="1" name="layer_number" type="int" /> + <param index="2" name="value" type="bool" /> + <description> + Based on [param value], enables or disables the specified navigation layer of the TileSet navigation data layer identified by the given [param layer_index], given a navigation_layers [param layer_number] between 1 and 32. + </description> + </method> <method name="set_navigation_layer_layers"> <return type="void" /> <param index="0" name="layer_index" type="int" /> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 236d34383f..ab2de14638 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -55,7 +55,7 @@ <method name="get_final_transform" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the total transform of the viewport. + Returns the transform from the viewport's coordinate system to the embedder's coordinate system. </description> </method> <method name="get_mouse_position" qualifiers="const"> @@ -210,6 +210,7 @@ <param index="0" name="position" type="Vector2" /> <description> Moves the mouse pointer to the specified position in this [Viewport] using the coordinate system of this [Viewport]. + [b]Note:[/b] [method warp_mouse] is only supported on Windows, macOS and Linux. It has no effect on Android, iOS and Web. </description> </method> </methods> diff --git a/doc/classes/VisualShaderNodeDerivativeFunc.xml b/doc/classes/VisualShaderNodeDerivativeFunc.xml index 9a1ad53394..4a31969171 100644 --- a/doc/classes/VisualShaderNodeDerivativeFunc.xml +++ b/doc/classes/VisualShaderNodeDerivativeFunc.xml @@ -15,6 +15,9 @@ <member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeDerivativeFunc.OpType" default="0"> A type of operands and returned value. See [enum OpType] for options. </member> + <member name="precision" type="int" setter="set_precision" getter="get_precision" enum="VisualShaderNodeDerivativeFunc.Precision" default="0"> + Sets the level of precision to use for the derivative function. See [enum Precision] for options. When using the GL_Compatibility renderer, this setting has no effect. + </member> </members> <constants> <constant name="OP_TYPE_SCALAR" value="0" enum="OpType"> @@ -44,5 +47,17 @@ <constant name="FUNC_MAX" value="3" enum="Function"> Represents the size of the [enum Function] enum. </constant> + <constant name="PRECISION_NONE" value="0" enum="Precision"> + No precision is specified, the GPU driver is allowed to use whatever level of precision it chooses. This is the default option and is equivalent to using [code]dFdx()[/code] or [code]dFdy()[/code] in text shaders. + </constant> + <constant name="PRECISION_COARSE" value="1" enum="Precision"> + The derivative will be calculated using the current fragment's neighbors (which may not include the current fragment). This tends to be faster than using [constant PRECISION_FINE], but may not be suitable when more precision is needed. This is equivalent to using [code]dFdxCoarse()[/code] or [code]dFdyCoarse()[/code] in text shaders. + </constant> + <constant name="PRECISION_FINE" value="2" enum="Precision"> + The derivative will be calculated using the current fragment and its immediate neighbors. This tends to be slower than using [constant PRECISION_COARSE], but may be necessary when more precision is needed. This is equivalent to using [code]dFdxFine()[/code] or [code]dFdyFine()[/code] in text shaders. + </constant> + <constant name="PRECISION_MAX" value="3" enum="Precision"> + Represents the size of the [enum Precision] enum. + </constant> </constants> </class> diff --git a/drivers/alsa/asound-so_wrap.c b/drivers/alsa/asound-so_wrap.c index 6f9c5408d5..ffe24d4313 100644 --- a/drivers/alsa/asound-so_wrap.c +++ b/drivers/alsa/asound-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:26:13 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/alsa/asoundlib.h --sys-include "thirdparty/linuxbsd_headers/alsa/asoundlib.h" --soname libasound.so.2 --init-name asound --omit-prefix snd_pcm_sw_params_set_tstamp_type --omit-prefix snd_pcm_status_get_audio_htstamp_report --omit-prefix snd_pcm_sw_params_get_tstamp_type --omit-prefix snd_pcm_status_set_audio_htstamp_config --output-header ./drivers/alsa/asound-so_wrap.h --output-implementation ./drivers/alsa/asound-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:26:13 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/alsa/asoundlib.h --sys-include "thirdparty/linuxbsd_headers/alsa/asoundlib.h" --soname libasound.so.2 --init-name asound --omit-prefix snd_pcm_sw_params_set_tstamp_type --omit-prefix snd_pcm_status_get_audio_htstamp_report --omit-prefix snd_pcm_sw_params_get_tstamp_type --omit-prefix snd_pcm_status_set_audio_htstamp_config --output-header ./drivers/alsa/asound-so_wrap.h --output-implementation ./drivers/alsa/asound-so_wrap.c // #include <stdint.h> diff --git a/drivers/alsa/asound-so_wrap.h b/drivers/alsa/asound-so_wrap.h index ab54f4e8cf..8d12d15ba8 100644 --- a/drivers/alsa/asound-so_wrap.h +++ b/drivers/alsa/asound-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_ASOUND // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:26:13 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/alsa/asoundlib.h --sys-include "thirdparty/linuxbsd_headers/alsa/asoundlib.h" --soname libasound.so.2 --init-name asound --omit-prefix snd_pcm_sw_params_set_tstamp_type --omit-prefix snd_pcm_status_get_audio_htstamp_report --omit-prefix snd_pcm_sw_params_get_tstamp_type --omit-prefix snd_pcm_status_set_audio_htstamp_config --output-header ./drivers/alsa/asound-so_wrap.h --output-implementation ./drivers/alsa/asound-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:26:13 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/alsa/asoundlib.h --sys-include "thirdparty/linuxbsd_headers/alsa/asoundlib.h" --soname libasound.so.2 --init-name asound --omit-prefix snd_pcm_sw_params_set_tstamp_type --omit-prefix snd_pcm_status_get_audio_htstamp_report --omit-prefix snd_pcm_sw_params_get_tstamp_type --omit-prefix snd_pcm_status_set_audio_htstamp_config --output-header ./drivers/alsa/asound-so_wrap.h --output-implementation ./drivers/alsa/asound-so_wrap.c // #include <stdint.h> diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index 219529d2f6..e9eb11e2e3 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -283,7 +283,7 @@ void AudioDriverCoreAudio::unlock() { } bool AudioDriverCoreAudio::try_lock() { - return mutex.try_lock() == OK; + return mutex.try_lock(); } void AudioDriverCoreAudio::finish() { diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 89b1c1889e..7f381b3f3e 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -113,16 +113,19 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ // Clear out any state that may have been left from the 3D pass. reset_canvas(); - if (state.canvas_instance_data_buffers[state.current_buffer].fence != GLsync()) { + if (state.canvas_instance_data_buffers[state.current_data_buffer_index].fence != GLsync()) { GLint syncStatus; - glGetSynciv(state.canvas_instance_data_buffers[state.current_buffer].fence, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + glGetSynciv(state.canvas_instance_data_buffers[state.current_data_buffer_index].fence, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); if (syncStatus == GL_UNSIGNALED) { // If older than 2 frames, wait for sync OpenGL can have up to 3 frames in flight, any more and we need to sync anyway. - if (state.canvas_instance_data_buffers[state.current_buffer].last_frame_used < RSG::rasterizer->get_frame_number() - 2) { + if (state.canvas_instance_data_buffers[state.current_data_buffer_index].last_frame_used < RSG::rasterizer->get_frame_number() - 2) { #ifndef WEB_ENABLED // On web, we do nothing as the glSubBufferData will force a sync anyway and WebGL does not like waiting. - glClientWaitSync(state.canvas_instance_data_buffers[state.current_buffer].fence, 0, 100000000); // wait for up to 100ms + glClientWaitSync(state.canvas_instance_data_buffers[state.current_data_buffer_index].fence, 0, 100000000); // wait for up to 100ms #endif + state.canvas_instance_data_buffers[state.current_data_buffer_index].last_frame_used = RSG::rasterizer->get_frame_number(); + glDeleteSync(state.canvas_instance_data_buffers[state.current_data_buffer_index].fence); + state.canvas_instance_data_buffers[state.current_data_buffer_index].fence = GLsync(); } else { // Used in last frame or frame before that. OpenGL can get up to two frames behind, so these buffers may still be in use // Allocate a new buffer and use that. @@ -130,9 +133,9 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } } else { // Already finished all rendering commands, we can use it. - state.canvas_instance_data_buffers[state.current_buffer].last_frame_used = RSG::rasterizer->get_frame_number(); - glDeleteSync(state.canvas_instance_data_buffers[state.current_buffer].fence); - state.canvas_instance_data_buffers[state.current_buffer].fence = GLsync(); + state.canvas_instance_data_buffers[state.current_data_buffer_index].last_frame_used = RSG::rasterizer->get_frame_number(); + glDeleteSync(state.canvas_instance_data_buffers[state.current_data_buffer_index].fence); + state.canvas_instance_data_buffers[state.current_data_buffer_index].fence = GLsync(); } } @@ -279,7 +282,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } if (light_count > 0) { - glBindBufferBase(GL_UNIFORM_BUFFER, LIGHT_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].light_ubo); + glBindBufferBase(GL_UNIFORM_BUFFER, LIGHT_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_data_buffer_index].light_ubo); #ifdef WEB_ENABLED glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightUniform) * light_count, state.light_uniforms); @@ -361,7 +364,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); - glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].state_ubo); + glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_data_buffer_index].state_ubo); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW); GLuint global_buffer = material_storage->global_shader_parameters_get_uniform_buffer(); @@ -395,7 +398,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ Item *ci = p_item_list; Item *canvas_group_owner = nullptr; - uint32_t starting_index = 0; + state.last_item_index = 0; while (ci) { if (ci->copy_back_buffer && canvas_group_owner == nullptr) { @@ -440,6 +443,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ const Item::CommandMesh *cm = static_cast<const Item::CommandMesh *>(c); if (cm->mesh_instance.is_valid()) { mesh_storage->mesh_instance_check_for_update(cm->mesh_instance); + mesh_storage->mesh_instance_set_canvas_item_transform(cm->mesh_instance, canvas_transform_inverse * ci->final_transform); update_skeletons = true; } } @@ -454,7 +458,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ update_skeletons = false; } // Canvas group begins here, render until before this item - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); item_count = 0; if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { @@ -485,7 +489,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ mesh_storage->update_mesh_instances(); update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, r_sdf_used, true); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true); item_count = 0; if (ci->canvas_group->blur_mipmaps) { @@ -504,7 +508,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } //render anything pending, including clearing if no items - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); item_count = 0; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); @@ -530,7 +534,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ mesh_storage->update_mesh_instances(); update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); //then reset item_count = 0; } @@ -542,14 +546,15 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ RenderingServerDefault::redraw_request(); } - state.canvas_instance_data_buffers[state.current_buffer].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + state.canvas_instance_data_buffers[state.current_data_buffer_index].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); // Clear out state used in 2D pass reset_canvas(); - state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); + state.current_data_buffer_index = (state.current_data_buffer_index + 1) % state.canvas_instance_data_buffers.size(); + state.current_instance_buffer_index = 0; } -void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool &r_sdf_used, bool p_to_backbuffer) { +void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); canvas_begin(p_to_render_target, p_to_backbuffer); @@ -565,17 +570,17 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou // Record Batches. // First item always forms its own batch. bool batch_broken = false; - _new_batch(batch_broken, index); + _new_batch(batch_broken); // Override the start position and index as we want to start from where we finished off last time. - state.canvas_instance_batches[state.current_batch_index].start = r_last_index; + state.canvas_instance_batches[state.current_batch_index].start = state.last_item_index; index = 0; for (int i = 0; i < p_item_count; i++) { Item *ci = items[i]; if (ci->final_clip_owner != state.canvas_instance_batches[state.current_batch_index].clip) { - _new_batch(batch_broken, index); + _new_batch(batch_broken); state.canvas_instance_batches[state.current_batch_index].clip = ci->final_clip_owner; current_clip = ci->final_clip_owner; } @@ -599,7 +604,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou GLES3::CanvasShaderData *shader_data_cache = nullptr; if (material != state.canvas_instance_batches[state.current_batch_index].material) { - _new_batch(batch_broken, index); + _new_batch(batch_broken); GLES3::CanvasMaterialData *material_data = nullptr; if (material.is_valid()) { @@ -629,12 +634,12 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou } // Copy over all data needed for rendering. - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index]); #ifdef WEB_ENABLED - glBufferSubData(GL_ARRAY_BUFFER, r_last_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array); + glBufferSubData(GL_ARRAY_BUFFER, state.last_item_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array); #else // On Desktop and mobile we map the memory without synchronizing for maximum speed. - void *buffer = glMapBufferRange(GL_ARRAY_BUFFER, r_last_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + void *buffer = glMapBufferRange(GL_ARRAY_BUFFER, state.last_item_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); memcpy(buffer, state.instance_data_array, index * sizeof(InstanceData)); glUnmapBuffer(GL_ARRAY_BUFFER); #endif @@ -757,14 +762,14 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou state.current_batch_index = 0; state.canvas_instance_batches.clear(); - r_last_index += index; + state.last_item_index += index; } void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used) { RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter; if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].filter = texture_filter; } @@ -772,7 +777,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? state.default_repeat : p_item->texture_repeat; if (texture_repeat != state.canvas_instance_batches[state.current_batch_index].repeat) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat; } @@ -816,7 +821,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend bool lights_disabled = light_count == 0 && !state.using_directional_lights; if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled; } @@ -853,18 +858,19 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config - Color blend_color; + Color blend_color = base_color; + GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode; if (c->type == Item::Command::TYPE_RECT) { const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); if (rect->flags & CANVAS_RECT_LCD) { - p_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD; + blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD; blend_color = rect->modulate * base_color; } } - if (p_blend_mode != state.canvas_instance_batches[state.current_batch_index].blend_mode || blend_color != state.canvas_instance_batches[state.current_batch_index].blend_color) { - _new_batch(r_batch_broken, r_index); - state.canvas_instance_batches[state.current_batch_index].blend_mode = p_blend_mode; + if (blend_mode != state.canvas_instance_batches[state.current_batch_index].blend_mode || blend_color != state.canvas_instance_batches[state.current_batch_index].blend_color) { + _new_batch(r_batch_broken); + state.canvas_instance_batches[state.current_batch_index].blend_mode = blend_mode; state.canvas_instance_batches[state.current_batch_index].blend_color = blend_color; } @@ -873,12 +879,12 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); if (rect->flags & CANVAS_RECT_TILE && state.canvas_instance_batches[state.current_batch_index].repeat != RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; } if (rect->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_RECT) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].tex = rect->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT; state.canvas_instance_batches[state.current_batch_index].command = c; @@ -968,7 +974,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); if (np->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_NINEPATCH) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].tex = np->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH; state.canvas_instance_batches[state.current_batch_index].command = c; @@ -1033,7 +1039,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); // Polygon's can't be batched, so always create a new batch - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON; @@ -1060,7 +1066,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); if (primitive->point_count != state.canvas_instance_batches[state.current_batch_index].primitive_points || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_PRIMITIVE) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].tex = primitive->texture; state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE; @@ -1085,10 +1091,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend if (primitive->point_count == 4) { // Reset base data. _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); - state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; - state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; - - state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config + _prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); for (uint32_t j = 0; j < 3; j++) { int offset = j == 0 ? 0 : 1; @@ -1110,7 +1113,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend case Item::Command::TYPE_MULTIMESH: case Item::Command::TYPE_PARTICLES: { // Mesh's can't be batched, so always create a new batch - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); Color modulate(1, 1, 1, 1); state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES; @@ -1182,7 +1185,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); if (current_clip) { if (ci->ignore != reclip) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); if (ci->ignore) { state.canvas_instance_batches[state.current_batch_index].clip = nullptr; reclip = true; @@ -1226,7 +1229,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { case Item::Command::TYPE_RECT: case Item::Command::TYPE_NINEPATCH: { glBindVertexArray(data.indexed_quad_array); - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.canvas_instance_batches[p_index].instance_buffer_index]); uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); _enable_attributes(range_start, false); @@ -1242,7 +1245,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { ERR_FAIL_COND(!pb); glBindVertexArray(pb->vertex_array); - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.canvas_instance_batches[p_index].instance_buffer_index]); uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); _enable_attributes(range_start, false); @@ -1266,7 +1269,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { case Item::Command::TYPE_PRIMITIVE: { glBindVertexArray(data.canvas_quad_array); - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.canvas_instance_batches[p_index].instance_buffer_index]); uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); _enable_attributes(range_start, true); @@ -1369,7 +1372,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0); bool use_index_buffer = false; glBindVertexArray(vertex_array_gl); - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.canvas_instance_batches[p_index].instance_buffer_index]); uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); _enable_attributes(range_start, false, instance_count); @@ -1426,20 +1429,30 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { } void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broken) { - if (r_index >= data.max_instances_per_buffer - 1) { - ERR_PRINT_ONCE("Trying to draw too many items. Please increase maximum number of items in the project settings 'rendering/gl_compatibility/item_buffer_size'"); - return; - } - - if (state.canvas_instance_batches[state.current_batch_index].instance_count >= data.max_instances_per_batch) { - _new_batch(r_batch_broken, r_index); - } - state.canvas_instance_batches[state.current_batch_index].instance_count++; r_index++; + if (r_index >= data.max_instances_per_buffer) { + // Copy over all data needed for rendering right away + // then go back to recording item commands. + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index]); +#ifdef WEB_ENABLED + glBufferSubData(GL_ARRAY_BUFFER, state.last_item_index * sizeof(InstanceData), sizeof(InstanceData) * r_index, state.instance_data_array); +#else + // On Desktop and mobile we map the memory without synchronizing for maximum speed. + void *buffer = glMapBufferRange(GL_ARRAY_BUFFER, state.last_item_index * sizeof(InstanceData), r_index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(buffer, state.instance_data_array, r_index * sizeof(InstanceData)); + glUnmapBuffer(GL_ARRAY_BUFFER); +#endif + _allocate_instance_buffer(); + r_index = 0; + state.last_item_index = 0; + r_batch_broken = false; // Force a new batch to be created + _new_batch(r_batch_broken); + state.canvas_instance_batches[state.current_batch_index].start = 0; + } } -void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) { +void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken) { if (state.canvas_instance_batches.size() == 0) { state.canvas_instance_batches.push_back(Batch()); return; @@ -1455,7 +1468,7 @@ void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) Batch new_batch = state.canvas_instance_batches[state.current_batch_index]; new_batch.instance_count = 0; new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count; - + new_batch.instance_buffer_index = state.current_instance_buffer_index; state.current_batch_index++; state.canvas_instance_batches.push_back(new_batch); } @@ -1486,6 +1499,9 @@ void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) { if (cl->texture == p_texture) { return; } + + ERR_FAIL_COND(p_texture.is_valid() && !texture_storage->owns_texture(p_texture)); + if (cl->texture.is_valid()) { texture_storage->texture_remove_from_texture_atlas(cl->texture); } @@ -2432,17 +2448,35 @@ void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); - state.current_buffer = (state.current_buffer + 1); + state.current_data_buffer_index = (state.current_data_buffer_index + 1); DataBuffer db; - db.buffer = new_buffers[0]; + db.instance_buffers.push_back(new_buffers[0]); db.light_ubo = new_buffers[1]; db.state_ubo = new_buffers[2]; db.last_frame_used = RSG::rasterizer->get_frame_number(); - state.canvas_instance_data_buffers.insert(state.current_buffer, db); - state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size(); + state.canvas_instance_data_buffers.insert(state.current_data_buffer_index, db); + state.current_data_buffer_index = state.current_data_buffer_index % state.canvas_instance_data_buffers.size(); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_UNIFORM_BUFFER, 0); } +void RasterizerCanvasGLES3::_allocate_instance_buffer() { + state.current_instance_buffer_index++; + + if (int(state.current_instance_buffer_index) < state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.size()) { + // We already allocated another buffer in a previous frame, so we can just use it. + return; + } + + GLuint new_buffer; + glGenBuffers(1, &new_buffer); + + glBindBuffer(GL_ARRAY_BUFFER, new_buffer); + glBufferData(GL_ARRAY_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + + state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.push_back(new_buffer); + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} void RasterizerCanvasGLES3::set_time(double p_time) { state.time = p_time; @@ -2585,14 +2619,12 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { int uniform_max_size = config->max_uniform_buffer_size; if (uniform_max_size < 65536) { data.max_lights_per_render = 64; - data.max_instances_per_batch = 128; } else { data.max_lights_per_render = 256; - data.max_instances_per_batch = 2048; } // Reserve 3 Uniform Buffers for instance data Frame N, N+1 and N+2 - data.max_instances_per_buffer = MAX(data.max_instances_per_batch, uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size"))); + data.max_instances_per_buffer = uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size")); data.max_instance_buffer_size = data.max_instances_per_buffer * sizeof(InstanceData); // 16,384 instances * 128 bytes = 2,097,152 bytes = 2,048 kb state.canvas_instance_data_buffers.resize(3); state.canvas_instance_batches.reserve(200); @@ -2610,7 +2642,7 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); DataBuffer db; - db.buffer = new_buffers[0]; + db.instance_buffers.push_back(new_buffers[0]); db.light_ubo = new_buffers[1]; db.state_ubo = new_buffers[2]; db.last_frame_used = 0; @@ -2637,7 +2669,6 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { String global_defines; global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now global_defines += "#define MAX_LIGHTS " + itos(data.max_lights_per_render) + "\n"; - global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(data.max_instances_per_batch) + "\n"; GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines, 1); data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 916e12057c..1c14d0b466 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -247,7 +247,6 @@ public: uint32_t max_lights_per_render = 256; uint32_t max_lights_per_item = 16; - uint32_t max_instances_per_batch = 512; uint32_t max_instances_per_buffer = 16384; uint32_t max_instance_buffer_size = 16384 * 128; } data; @@ -256,6 +255,7 @@ public: // Position in the UBO measured in bytes uint32_t start = 0; uint32_t instance_count = 0; + uint32_t instance_buffer_index = 0; RID tex; RS::CanvasItemTextureFilter filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; @@ -281,7 +281,7 @@ public: // We track them and ensure that they don't get reused until at least 2 frames have passed // to avoid the GPU stalling to wait for a resource to become available. struct DataBuffer { - GLuint buffer = 0; + Vector<GLuint> instance_buffers; GLuint light_ubo = 0; GLuint state_ubo = 0; uint64_t last_frame_used = -3; @@ -291,9 +291,10 @@ public: struct State { LocalVector<DataBuffer> canvas_instance_data_buffers; LocalVector<Batch> canvas_instance_batches; - uint32_t current_buffer = 0; - uint32_t current_buffer_index = 0; + uint32_t current_data_buffer_index = 0; + uint32_t current_instance_buffer_index = 0; uint32_t current_batch_index = 0; + uint32_t last_item_index = 0; InstanceData *instance_data_array = nullptr; @@ -354,14 +355,14 @@ public: void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size); void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override; - void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool &r_sdf_used, bool p_to_backbuffer = false); + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false); void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used); void _render_batch(Light *p_lights, uint32_t p_index); bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); - void _new_batch(bool &r_batch_broken, uint32_t &r_index); + void _new_batch(bool &r_batch_broken); void _add_to_batch(uint32_t &r_index, bool &r_batch_broken); void _allocate_instance_data_buffer(); - void _align_instance_data_buffer(uint32_t &r_index); + void _allocate_instance_buffer(); void _enable_attributes(uint32_t p_start, bool p_primitive, uint32_t p_rate = 1); void set_time(double p_time); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 9547435607..949f2953d3 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -145,6 +145,7 @@ void RasterizerSceneGLES3::_geometry_instance_dependency_changed(Dependency::Dep case Dependency::DEPENDENCY_CHANGED_MULTIMESH: case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata)->data->dirty_dependencies = true; } break; case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata); @@ -160,6 +161,7 @@ void RasterizerSceneGLES3::_geometry_instance_dependency_changed(Dependency::Dep void RasterizerSceneGLES3::_geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata)->data->dirty_dependencies = true; } void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) { diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 71caf3b8e3..10f42bf22b 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -192,7 +192,7 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant builder.append("#define ViewIndex gl_ViewID_OVR\n"); builder.append("#define MAX_VIEWS 2\n"); builder.append("#else\n"); - builder.append("#define ViewIndex 0\n"); + builder.append("#define ViewIndex uint(0)\n"); builder.append("#define MAX_VIEWS 1\n"); builder.append("#endif\n"); diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 52ff70f746..f977c8ceaf 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -565,9 +565,15 @@ uniform highp samplerCubeShadow positional_shadow; // texunit:-4 #ifdef USE_MULTIVIEW uniform highp sampler2DArray depth_buffer; // texunit:-6 uniform highp sampler2DArray color_buffer; // texunit:-5 +vec3 multiview_uv(vec2 uv) { + return vec3(uv, ViewIndex); +} #else uniform highp sampler2D depth_buffer; // texunit:-6 uniform highp sampler2D color_buffer; // texunit:-5 +vec2 multiview_uv(vec2 uv) { + return uv; +} #endif uniform highp mat4 world_transform; @@ -925,8 +931,12 @@ void main() { vec3 vertex = vertex_interp; #ifdef USE_MULTIVIEW vec3 view = -normalize(vertex_interp - multiview_data.eye_offset[ViewIndex].xyz); + mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex]; + mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex]; #else vec3 view = -normalize(vertex_interp); + mat4 projection_matrix = scene_data.projection_matrix; + mat4 inv_projection_matrix = scene_data.inv_projection_matrix; #endif highp mat4 model_matrix = world_transform; vec3 albedo = vec3(1.0); diff --git a/drivers/gles3/shaders/skeleton.glsl b/drivers/gles3/shaders/skeleton.glsl index a1e3c098f4..aad856a5a2 100644 --- a/drivers/gles3/shaders/skeleton.glsl +++ b/drivers/gles3/shaders/skeleton.glsl @@ -87,6 +87,16 @@ uniform highp float blend_weight; uniform lowp float blend_shape_count; #endif +#ifdef USE_SKELETON +uniform mediump vec2 skeleton_transform_x; +uniform mediump vec2 skeleton_transform_y; +uniform mediump vec2 skeleton_transform_offset; + +uniform mediump vec2 inverse_transform_x; +uniform mediump vec2 inverse_transform_y; +uniform mediump vec2 inverse_transform_offset; +#endif + vec2 signNotZero(vec2 v) { return mix(vec2(-1.0), vec2(1.0), greaterThanEqual(v.xy, vec2(0.0))); } @@ -164,10 +174,13 @@ void main() { m += GET_BONE_MATRIX(bones.z, bones_a.z, in_weight_attrib.z); m += GET_BONE_MATRIX(bones.w, bones_a.w, in_weight_attrib.w); + mat4 skeleton_matrix = mat4(vec4(skeleton_transform_x, 0.0, 0.0), vec4(skeleton_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(skeleton_transform_offset, 0.0, 1.0)); + mat4 inverse_matrix = mat4(vec4(inverse_transform_x, 0.0, 0.0), vec4(inverse_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(inverse_transform_offset, 0.0, 1.0)); mat4 bone_matrix = mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); - //reverse order because its transposed - out_vertex = (vec4(out_vertex, 0.0, 1.0) * bone_matrix).xy; + bone_matrix = skeleton_matrix * transpose(bone_matrix) * inverse_matrix; + + out_vertex = (bone_matrix * vec4(out_vertex, 0.0, 1.0)).xy; #endif // USE_SKELETON #else // MODE_2D diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index d1a83e6851..77a4553c76 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -65,6 +65,10 @@ Config::Config() { } bptc_supported = extensions.has("GL_ARB_texture_compression_bptc") || extensions.has("EXT_texture_compression_bptc"); + astc_supported = extensions.has("GL_KHR_texture_compression_astc") || extensions.has("GL_OES_texture_compression_astc") || extensions.has("GL_KHR_texture_compression_astc_ldr") || extensions.has("GL_KHR_texture_compression_astc_hdr"); + astc_hdr_supported = extensions.has("GL_KHR_texture_compression_astc_ldr"); + astc_layered_supported = extensions.has("GL_KHR_texture_compression_astc_sliced_3d"); + #ifdef GLES_OVER_GL float_texture_supported = true; etc2_supported = false; diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index 8399502675..623f0442bd 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -77,6 +77,9 @@ public: bool rgtc_supported = false; bool bptc_supported = false; bool etc2_supported = false; + bool astc_supported = false; + bool astc_hdr_supported = false; + bool astc_layered_supported = false; bool force_vertex_shading = false; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 50e5c868d3..4fb780b665 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1546,6 +1546,8 @@ MaterialStorage::MaterialStorage() { actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n"; + actions.global_buffer_array_variable = "global_shader_uniforms"; + shaders.compiler_canvas.initialize(actions); } @@ -1635,8 +1637,8 @@ MaterialStorage::MaterialStorage() { actions.renames["NODE_POSITION_VIEW"] = "(model_matrix * scene_data.view_matrix)[3].xyz"; actions.renames["VIEW_INDEX"] = "ViewIndex"; - actions.renames["VIEW_MONO_LEFT"] = "0"; - actions.renames["VIEW_RIGHT"] = "1"; + actions.renames["VIEW_MONO_LEFT"] = "uint(0)"; + actions.renames["VIEW_RIGHT"] = "uint(1)"; //for light actions.renames["VIEW"] = "view"; @@ -1718,6 +1720,9 @@ MaterialStorage::MaterialStorage() { actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.check_multiview_samplers = true; + actions.global_buffer_array_variable = "global_shader_uniforms"; + shaders.compiler_scene.initialize(actions); } @@ -1773,6 +1778,8 @@ MaterialStorage::MaterialStorage() { actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.global_buffer_array_variable = "global_shader_uniforms"; + shaders.compiler_particles.initialize(actions); } @@ -1826,6 +1833,8 @@ MaterialStorage::MaterialStorage() { actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.global_buffer_array_variable = "global_shader_uniforms"; + shaders.compiler_sky.initialize(actions); } } diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 71f262af20..5ba0c5a09c 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -997,6 +997,11 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { } } +void MeshStorage::mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) { + MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); + mi->canvas_item_transform_2d = p_transform; +} + void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface) { glBindBuffer(GL_ARRAY_BUFFER, p_mi->surfaces[p_surface].vertex_buffers[0]); @@ -1163,6 +1168,16 @@ void MeshStorage::update_mesh_instances() { skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization); if (can_use_skeleton) { + Transform2D transform = mi->canvas_item_transform_2d.affine_inverse() * sk->base_transform_2d; + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_X, transform[0], skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_Y, transform[1], skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_OFFSET, transform[2], skeleton_shader.shader_version, variant, specialization); + + Transform2D inverse_transform = transform.affine_inverse(); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_X, inverse_transform[0], skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_Y, inverse_transform[1], skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_OFFSET, inverse_transform[2], skeleton_shader.shader_version, variant, specialization); + // Do last blendshape in the same pass as the Skeleton. _compute_skeleton(mi, sk, i); can_use_skeleton = false; @@ -1201,6 +1216,16 @@ void MeshStorage::update_mesh_instances() { continue; } + Transform2D transform = mi->canvas_item_transform_2d.affine_inverse() * sk->base_transform_2d; + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_X, transform[0], skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_Y, transform[1], skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_OFFSET, transform[2], skeleton_shader.shader_version, variant, specialization); + + Transform2D inverse_transform = transform.affine_inverse(); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_X, inverse_transform[0], skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_Y, inverse_transform[1], skeleton_shader.shader_version, variant, specialization); + skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_OFFSET, inverse_transform[2], skeleton_shader.shader_version, variant, specialization); + glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array); _compute_skeleton(mi, sk, i); } diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 2efc57462b..e1c2bc3f63 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -163,6 +163,7 @@ struct MeshInstance { bool weights_dirty = false; SelfList<MeshInstance> weight_update_list; SelfList<MeshInstance> array_update_list; + Transform2D canvas_item_transform_2d; MeshInstance() : weight_update_list(this), array_update_list(this) {} }; @@ -423,6 +424,7 @@ public: virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override; virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override; virtual void mesh_instance_check_for_update(RID p_mesh_instance) override; + virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) override; virtual void update_mesh_instances() override; // TODO: considering hashing versions with multimesh buffer RID. diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 67526bc003..97698af0a0 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -594,6 +594,50 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I } decompress_ra_to_rg = true; } break; + case Image::FORMAT_ASTC_4x4: { + if (config->astc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_ASTC_4x4_KHR; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ASTC_4x4_HDR: { + if (config->astc_hdr_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_ASTC_4x4_KHR; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ASTC_8x8: { + if (config->astc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_ASTC_8x8_KHR; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ASTC_8x8_HDR: { + if (config->astc_hdr_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_ASTC_8x8_KHR; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; default: { ERR_FAIL_V_MSG(Ref<Image>(), "Image Format: " + itos(p_format) + " is not supported by the OpenGL3 Renderer"); } diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 9d3f407226..522be64d35 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -84,6 +84,36 @@ namespace GLES3 { #define _EXT_COMPRESSED_RGBA8_ETC2_EAC 0x9278 #define _EXT_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define _EXT_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define _EXT_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define _EXT_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define _EXT_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define _EXT_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define _EXT_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define _EXT_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define _EXT_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define _EXT_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define _EXT_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define _EXT_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define _EXT_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define _EXT_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define _EXT_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD + +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD + #define _GL_TEXTURE_EXTERNAL_OES 0x8D65 #define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index e72240c69b..30c3e61ee7 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -309,6 +309,9 @@ bool Utilities::has_os_feature(const String &p_feature) const { if (p_feature == "bptc") { return config->bptc_supported; } + if (p_feature == "astc") { + return config->astc_supported; + } if (p_feature == "etc" || p_feature == "etc2") { return config->etc2_supported; diff --git a/drivers/pulseaudio/pulse-so_wrap.c b/drivers/pulseaudio/pulse-so_wrap.c index 1765d7e7e6..8f477740ce 100644 --- a/drivers/pulseaudio/pulse-so_wrap.c +++ b/drivers/pulseaudio/pulse-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:26:29 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/pulse/pulseaudio.h --sys-include "thirdparty/linuxbsd_headers/pulse/pulseaudio.h" --soname libpulse.so.0 --omit-prefix _pa_ --init-name pulse --output-header ./drivers/pulseaudio/pulse-so_wrap.h --output-implementation ./drivers/pulseaudio/pulse-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:26:29 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/pulse/pulseaudio.h --sys-include "thirdparty/linuxbsd_headers/pulse/pulseaudio.h" --soname libpulse.so.0 --omit-prefix _pa_ --init-name pulse --output-header ./drivers/pulseaudio/pulse-so_wrap.h --output-implementation ./drivers/pulseaudio/pulse-so_wrap.c // #include <stdint.h> diff --git a/drivers/pulseaudio/pulse-so_wrap.h b/drivers/pulseaudio/pulse-so_wrap.h index b3baf4c81a..bba8fd36a6 100644 --- a/drivers/pulseaudio/pulse-so_wrap.h +++ b/drivers/pulseaudio/pulse-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_PULSE // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:26:29 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/pulse/pulseaudio.h --sys-include "thirdparty/linuxbsd_headers/pulse/pulseaudio.h" --soname libpulse.so.0 --omit-prefix _pa_ --init-name pulse --output-header ./drivers/pulseaudio/pulse-so_wrap.h --output-implementation ./drivers/pulseaudio/pulse-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:26:29 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/pulse/pulseaudio.h --sys-include "thirdparty/linuxbsd_headers/pulse/pulseaudio.h" --soname libpulse.so.0 --omit-prefix _pa_ --init-name pulse --output-header ./drivers/pulseaudio/pulse-so_wrap.h --output-implementation ./drivers/pulseaudio/pulse-so_wrap.c // #include <stdint.h> diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 639f5e6de5..8defa04ada 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -1489,32 +1489,21 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } } -void AnimationBezierTrackEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec) { +void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { v_scroll += p_scroll_vec.y * v_zoom; v_scroll = CLAMP(v_scroll, -100000, 100000); timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); queue_redraw(); } -void AnimationBezierTrackEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { +void AnimationBezierTrackEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { const float v_zoom_orig = v_zoom; - if (p_alt) { + Ref<InputEventWithModifiers> iewm = p_event; + if (iewm.is_valid() && iewm->is_alt_pressed()) { // Alternate zoom (doesn't affect timeline). - if (p_scroll_vec.y > 0) { - v_zoom = MIN(v_zoom * 1.2, 100000); - } else { - v_zoom = MAX(v_zoom / 1.2, 0.000001); - } + v_zoom = CLAMP(v_zoom * p_zoom_factor, 0.000001, 100000); } else { - if (p_scroll_vec.y > 0) { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); - } else { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); - } + timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / p_zoom_factor); } v_scroll = v_scroll + (p_origin.y - get_size().y / 2.0) * (v_zoom - v_zoom_orig); queue_redraw(); @@ -1681,7 +1670,7 @@ void AnimationBezierTrackEdit::_bind_methods() { AnimationBezierTrackEdit::AnimationBezierTrackEdit() { panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_scroll_callback), callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback)); play_position = memnew(Control); play_position->set_mouse_filter(MOUSE_FILTER_PASS); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index e6d6424ef2..dbc231ccac 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -174,9 +174,8 @@ class AnimationBezierTrackEdit : public Control { SelectionSet selection; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _draw_line_clipped(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, int p_clip_left, int p_clip_right); void _draw_track(int p_track, const Color &p_color); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 857a9a664a..8dd087451c 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1704,25 +1704,13 @@ Control::CursorShape AnimationTimelineEdit::get_cursor_shape(const Point2 &p_pos } } -void AnimationTimelineEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - // Timeline has no vertical scroll, so we change it to horizontal. - p_scroll_vec.x += p_scroll_vec.y; - _pan_callback(-p_scroll_vec * 32); -} - -void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec) { +void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { set_value(get_value() - p_scroll_vec.x / get_zoom_scale()); } -void AnimationTimelineEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - double new_zoom_value; +void AnimationTimelineEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { double current_zoom_value = get_zoom()->get_value(); - if (current_zoom_value <= 0.1) { - new_zoom_value = MAX(0.01, current_zoom_value - 0.01 * SIGN(p_scroll_vec.y)); - } else { - new_zoom_value = p_scroll_vec.y > 0 ? MAX(0.01, current_zoom_value / 1.05) : current_zoom_value * 1.05; - } - get_zoom()->set_value(new_zoom_value); + get_zoom()->set_value(MAX(0.01, current_zoom_value * p_zoom_factor)); } void AnimationTimelineEdit::set_use_fps(bool p_use_fps) { @@ -1798,7 +1786,8 @@ AnimationTimelineEdit::AnimationTimelineEdit() { len_hb->hide(); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_scroll_callback), callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback)); + panner->set_pan_axis(ViewPanner::PAN_AXIS_HORIZONTAL); set_layout_direction(Control::LAYOUT_DIRECTION_LTR); } @@ -1965,6 +1954,10 @@ void AnimationTrackEdit::_notification(int p_what) { get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")) }; + Ref<Texture2D> blend_icon[2] = { + get_theme_icon(SNAME("UseBlendEnable"), SNAME("EditorIcons")), + get_theme_icon(SNAME("UseBlendDisable"), SNAME("EditorIcons")), + }; int ofs = get_size().width - timeline->get_buttons_width(); @@ -1993,6 +1986,11 @@ void AnimationTrackEdit::_notification(int p_what) { if (!animation->track_is_compressed(track) && animation->track_get_type(track) == Animation::TYPE_VALUE) { draw_texture(update_icon, update_mode_rect.position); } + if (animation->track_get_type(track) == Animation::TYPE_AUDIO) { + Ref<Texture2D> use_blend_icon = blend_icon[animation->audio_track_is_use_blend(track) ? 0 : 1]; + Vector2 use_blend_icon_pos = update_mode_rect.position + (update_mode_rect.size - use_blend_icon->get_size()) / 2; + draw_texture(use_blend_icon, use_blend_icon_pos); + } // Make it easier to click. update_mode_rect.position.y = 0; update_mode_rect.size.y = get_size().height; @@ -2001,13 +1999,12 @@ void AnimationTrackEdit::_notification(int p_what) { update_mode_rect.size.x += hsep / 2; if (!read_only) { - if (animation->track_get_type(track) == Animation::TYPE_VALUE) { + if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_AUDIO) { draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2)); update_mode_rect.size.x += down_icon->get_width(); } else if (animation->track_get_type(track) == Animation::TYPE_BEZIER) { Ref<Texture2D> bezier_icon = get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons")); update_mode_rect.size.x += down_icon->get_width(); - update_mode_rect = Rect2(); } else { update_mode_rect = Rect2(); @@ -2450,7 +2447,11 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { } if (update_mode_rect.has_point(p_pos)) { - return TTR("Update Mode (How this property is set)"); + if (animation->track_get_type(track) == Animation::TYPE_AUDIO) { + return TTR("Use Blend"); + } else { + return TTR("Update Mode (How this property is set)"); + } } if (interp_mode_rect.has_point(p_pos)) { @@ -2652,9 +2653,14 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected)); } menu->clear(); - menu->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), TTR("Continuous"), MENU_CALL_MODE_CONTINUOUS); - menu->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), TTR("Discrete"), MENU_CALL_MODE_DISCRETE); - menu->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), TTR("Capture"), MENU_CALL_MODE_CAPTURE); + if (animation->track_get_type(track) == Animation::TYPE_AUDIO) { + menu->add_icon_item(get_theme_icon(SNAME("UseBlendEnable"), SNAME("EditorIcons")), TTR("Use Blend"), MENU_USE_BLEND_ENABLED); + menu->add_icon_item(get_theme_icon(SNAME("UseBlendDisable"), SNAME("EditorIcons")), TTR("Don't Use Blend"), MENU_USE_BLEND_DISABLED); + } else { + menu->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), TTR("Continuous"), MENU_CALL_MODE_CONTINUOUS); + menu->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), TTR("Discrete"), MENU_CALL_MODE_DISCRETE); + menu->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), TTR("Capture"), MENU_CALL_MODE_CAPTURE); + } menu->reset_size(); Vector2 popup_pos = get_screen_position() + update_mode_rect.position + Vector2(0, update_mode_rect.size.height); @@ -2673,7 +2679,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->add_icon_item(get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")), TTR("Nearest"), MENU_INTERPOLATION_NEAREST); menu->add_icon_item(get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")), TTR("Linear"), MENU_INTERPOLATION_LINEAR); menu->add_icon_item(get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")), TTR("Cubic"), MENU_INTERPOLATION_CUBIC); - // Check is angle property. + // Check whether it is angle property. AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); if (ape) { AnimationPlayer *ap = ape->get_player(); @@ -3066,6 +3072,16 @@ void AnimationTrackEdit::_menu_selected(int p_index) { emit_signal(SNAME("delete_request")); } break; + case MENU_USE_BLEND_ENABLED: + case MENU_USE_BLEND_DISABLED: { + bool use_blend = p_index == MENU_USE_BLEND_ENABLED; + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Change Animation Use Blend")); + undo_redo->add_do_method(animation.ptr(), "audio_track_set_use_blend", track, use_blend); + undo_redo->add_undo_method(animation.ptr(), "audio_track_set_use_blend", track, animation->audio_track_is_use_blend(track)); + undo_redo->commit_action(); + queue_redraw(); + } break; } } @@ -3499,6 +3515,9 @@ void AnimationTrackEditor::_animation_track_remove_request(int p_track, Ref<Anim if (p_from_animation->track_get_type(idx) == Animation::TYPE_VALUE) { undo_redo->add_undo_method(p_from_animation.ptr(), "value_track_set_update_mode", idx, p_from_animation->value_track_get_update_mode(idx)); } + if (animation->track_get_type(idx) == Animation::TYPE_AUDIO) { + undo_redo->add_undo_method(animation.ptr(), "audio_track_set_use_blend", idx, animation->audio_track_is_use_blend(idx)); + } undo_redo->commit_action(); } @@ -5358,32 +5377,23 @@ void AnimationTrackEditor::_toggle_bezier_edit() { } } -void AnimationTrackEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - if (p_alt) { +void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { + Ref<InputEventWithModifiers> iewm = p_event; + if (iewm.is_valid() && iewm->is_alt_pressed()) { if (p_scroll_vec.x < 0 || p_scroll_vec.y < 0) { goto_prev_step(true); } else { goto_next_step(true); } } else { - _pan_callback(-p_scroll_vec * 32); + timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); + scroll->set_v_scroll(scroll->get_v_scroll() - p_scroll_vec.y); } } -void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec) { - timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); - scroll->set_v_scroll(scroll->get_v_scroll() - p_scroll_vec.y); -} - -void AnimationTrackEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - double new_zoom_value; +void AnimationTrackEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { double current_zoom_value = timeline->get_zoom()->get_value(); - if (current_zoom_value <= 0.1) { - new_zoom_value = MAX(0.01, current_zoom_value - 0.01 * SIGN(p_scroll_vec.y)); - } else { - new_zoom_value = p_scroll_vec.y > 0 ? MAX(0.01, current_zoom_value / 1.05) : current_zoom_value * 1.05; - } - timeline->get_zoom()->set_value(new_zoom_value); + timeline->get_zoom()->set_value(MAX(0.01, current_zoom_value * p_zoom_factor)); } void AnimationTrackEditor::_cancel_bezier_edit() { @@ -5638,6 +5648,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { if (tc.track_type == Animation::TYPE_VALUE) { tc.update_mode = animation->value_track_get_update_mode(idx); } + if (tc.track_type == Animation::TYPE_AUDIO) { + tc.use_blend = animation->audio_track_is_use_blend(idx); + } tc.loop_wrap = animation->track_get_interpolation_loop_wrap(idx); tc.enabled = animation->track_is_enabled(idx); for (int i = 0; i < animation->track_get_key_count(idx); i++) { @@ -5682,6 +5695,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { if (track_clipboard[i].track_type == Animation::TYPE_VALUE) { undo_redo->add_do_method(animation.ptr(), "value_track_set_update_mode", base_track, track_clipboard[i].update_mode); } + if (track_clipboard[i].track_type == Animation::TYPE_AUDIO) { + undo_redo->add_do_method(animation.ptr(), "audio_track_set_use_blend", base_track, track_clipboard[i].use_blend); + } for (int j = 0; j < track_clipboard[i].keys.size(); j++) { undo_redo->add_do_method(animation.ptr(), "track_insert_key", base_track, track_clipboard[i].keys[j].time, track_clipboard[i].keys[j].value, track_clipboard[i].keys[j].transition); @@ -6398,7 +6414,7 @@ AnimationTrackEditor::AnimationTrackEditor() { timeline->connect("length_changed", callable_mp(this, &AnimationTrackEditor::_update_length)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_scroll_callback), callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback)); scroll = memnew(ScrollContainer); timeline_vbox->add_child(scroll); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index ef06445011..2a59bda2a4 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -159,9 +159,8 @@ class AnimationTimelineEdit : public Range { bool use_fps = false; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); bool dragging_timeline = false; bool dragging_hsize = false; @@ -221,7 +220,9 @@ class AnimationTrackEdit : public Control { MENU_KEY_INSERT, MENU_KEY_DUPLICATE, MENU_KEY_ADD_RESET, - MENU_KEY_DELETE + MENU_KEY_DELETE, + MENU_USE_BLEND_ENABLED, + MENU_USE_BLEND_DISABLED, }; AnimationTimelineEdit *timeline = nullptr; @@ -460,9 +461,8 @@ class AnimationTrackEditor : public VBoxContainer { PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val = nullptr); Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _timeline_value_changed(double); @@ -568,6 +568,7 @@ class AnimationTrackEditor : public VBoxContainer { Animation::LoopMode loop_mode = Animation::LOOP_PINGPONG; bool loop_wrap = false; bool enabled = false; + bool use_blend = false; struct Key { float time = 0; diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 644735a4d8..28d687488c 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -143,7 +143,9 @@ void FindReplaceBar::unhandled_input(const Ref<InputEvent> &p_event) { } bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col) { - text_editor->remove_secondary_carets(); + if (!preserve_cursor) { + text_editor->remove_secondary_carets(); + } String text = get_search_text(); Point2i pos = text_editor->search(text, p_flags, p_from_line, p_from_col); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index db12dbc72b..aaa07e98c8 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -186,7 +186,7 @@ void ConnectDialog::_unbind_count_changed(double p_count) { void ConnectDialog::_method_selected() { TreeItem *selected_item = method_tree->get_selected(); - dst_method->set_text(selected_item->get_text(0)); + dst_method->set_text(selected_item->get_metadata(0)); } /* @@ -260,12 +260,8 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, String p void ConnectDialog::_create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item) { for (const MethodInfo &mi : p_methods) { TreeItem *method_item = method_tree->create_item(p_parent_item); - method_item->set_text(0, mi.name); - if (mi.return_val.type == Variant::NIL) { - method_item->set_icon(0, get_theme_icon(SNAME("Variant"), "EditorIcons")); - } else { - method_item->set_icon(0, get_theme_icon(Variant::get_type_name(mi.return_val.type), "EditorIcons")); - } + method_item->set_text(0, get_signature(mi)); + method_item->set_metadata(0, mi.name); } } @@ -293,6 +289,11 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me type_mismatch = true; break; } + + if (stype == Variant::OBJECT && mtype == Variant::OBJECT && E->get().class_name != F->get().class_name) { + type_mismatch = true; + break; + } } if (type_mismatch) { @@ -350,10 +351,11 @@ void ConnectDialog::_update_method_tree() { } if (script_methods_only->is_pressed()) { + empty_tree_label->set_visible(root_item->get_first_child() == nullptr); return; } - // Get methods from each class in the heirarchy. + // Get methods from each class in the hierarchy. StringName current_class = target->get_class_name(); do { TreeItem *class_item = method_tree->create_item(root_item); @@ -376,6 +378,8 @@ void ConnectDialog::_update_method_tree() { } current_class = ClassDB::get_parent_class_nocheck(current_class); } while (current_class != StringName()); + + empty_tree_label->set_visible(root_item->get_first_child() == nullptr); } void ConnectDialog::_method_check_button_pressed(const CheckButton *p_button) { @@ -432,6 +436,7 @@ void ConnectDialog::_notification(int p_what) { from_signal->add_theme_style_override("normal", style); } method_search->set_right_icon(get_theme_icon("Search", "EditorIcons")); + open_method_tree->set_icon(get_theme_icon("Edit", "EditorIcons")); } break; } } @@ -444,10 +449,18 @@ Node *ConnectDialog::get_source() const { return source; } +ConnectDialog::ConnectionData ConnectDialog::get_source_connection_data() const { + return source_connection_data; +} + StringName ConnectDialog::get_signal_name() const { return signal; } +PackedStringArray ConnectDialog::get_signal_args() const { + return signal_args; +} + NodePath ConnectDialog::get_dst_path() const { return dst_path; } @@ -476,6 +489,34 @@ Vector<Variant> ConnectDialog::get_binds() const { return cdbinds->params; } +String ConnectDialog::get_signature(const MethodInfo &p_method, PackedStringArray *r_arg_names) { + PackedStringArray signature; + signature.append(p_method.name); + signature.append("("); + + for (int i = 0; i < p_method.arguments.size(); i++) { + if (i > 0) { + signature.append(", "); + } + + const PropertyInfo &pi = p_method.arguments[i]; + String tname = "var"; + if (pi.type == Variant::OBJECT && pi.class_name != StringName()) { + tname = pi.class_name.operator String(); + } else if (pi.type != Variant::NIL) { + tname = Variant::get_type_name(pi.type); + } + + signature.append((pi.name.is_empty() ? String("arg " + itos(i)) : pi.name) + ": " + tname); + if (r_arg_names) { + r_arg_names->push_back(pi.name + ":" + tname); + } + } + + signature.append(")"); + return String().join(signature); +} + bool ConnectDialog::get_deferred() const { return deferred->is_pressed(); } @@ -496,11 +537,12 @@ bool ConnectDialog::is_editing() const { * If creating a connection from scratch, sensible defaults are used. * If editing an existing connection, previous data is retained. */ -void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { +void ConnectDialog::init(const ConnectionData &p_cd, const PackedStringArray &p_signal_args, bool p_edit) { set_hide_on_ok(false); source = static_cast<Node *>(p_cd.source); signal = p_cd.signal; + signal_args = p_signal_args; tree->set_selected(nullptr); tree->set_marked(source, true); @@ -518,22 +560,7 @@ void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { deferred->set_pressed(b_deferred); one_shot->set_pressed(b_oneshot); - MethodInfo r_signal; - Ref<Script> source_script = source->get_script(); - if (source_script.is_valid() && source_script->has_script_signal(signal)) { - List<MethodInfo> signals; - source_script->get_script_signal_list(&signals); - for (MethodInfo &mi : signals) { - if (mi.name == signal) { - r_signal = mi; - break; - } - } - } else { - ClassDB::get_signal(source->get_class(), signal, &r_signal); - } - - unbind_count->set_max(r_signal.arguments.size()); + unbind_count->set_max(p_signal_args.size()); unbind_count->set_value(p_cd.unbinds); _unbind_count_changed(p_cd.unbinds); @@ -543,9 +570,11 @@ void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { cdbinds->notify_changed(); edit_mode = p_edit; + + source_connection_data = p_cd; } -void ConnectDialog::popup_dialog(const String &p_for_signal) { +void ConnectDialog::popup_dialog(const String p_for_signal) { from_signal->set_text(p_for_signal); error_label->add_theme_color_override("font_color", error_label->get_theme_color(SNAME("error_color"), SNAME("Editor"))); if (!advanced->is_pressed()) { @@ -597,18 +626,9 @@ ConnectDialog::ConnectDialog() { main_hb->add_child(vbc_left); vbc_left->set_h_size_flags(Control::SIZE_EXPAND_FILL); - HBoxContainer *from_signal_hb = memnew(HBoxContainer); - from_signal = memnew(LineEdit); + vbc_left->add_margin_child(TTR("From Signal:"), from_signal); from_signal->set_editable(false); - from_signal->set_h_size_flags(Control::SIZE_EXPAND_FILL); - from_signal_hb->add_child(from_signal); - - advanced = memnew(CheckButton(TTR("Advanced"))); - from_signal_hb->add_child(advanced); - advanced->connect("pressed", callable_mp(this, &ConnectDialog::_advanced_pressed)); - - vbc_left->add_margin_child(TTR("From Signal:"), from_signal_hb); tree = memnew(SceneTreeEditor(false)); tree->set_connecting_signal(true); @@ -646,6 +666,10 @@ ConnectDialog::ConnectDialog() { method_tree->connect("item_selected", callable_mp(this, &ConnectDialog::_method_selected)); method_tree->connect("item_activated", callable_mp((Window *)method_popup, &Window::hide)); + empty_tree_label = memnew(Label(TTR("No method found matching given filters."))); + method_tree->add_child(empty_tree_label); + empty_tree_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER); + script_methods_only = memnew(CheckButton(TTR("Script Methods Only"))); method_vbc->add_child(script_methods_only); script_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END); @@ -712,15 +736,13 @@ ConnectDialog::ConnectDialog() { dst_method->connect("text_submitted", callable_mp(this, &ConnectDialog::_text_submitted)); hbc_method->add_child(dst_method); - Button *open_tree_button = memnew(Button); - open_tree_button->set_flat(false); - open_tree_button->set_text("..."); - open_tree_button->connect("pressed", callable_mp(this, &ConnectDialog::_open_method_popup)); - hbc_method->add_child(open_tree_button); + open_method_tree = memnew(Button); + hbc_method->add_child(open_method_tree); + open_method_tree->set_text("Pick"); + open_method_tree->connect("pressed", callable_mp(this, &ConnectDialog::_open_method_popup)); - advanced = memnew(CheckButton); + advanced = memnew(CheckButton(TTR("Advanced"))); vbc_left->add_child(advanced); - advanced->set_text(TTR("Advanced")); advanced->set_h_size_flags(Control::SIZE_SHRINK_BEGIN | Control::SIZE_EXPAND); advanced->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "use_advanced_connections", false)); advanced->connect("pressed", callable_mp(this, &ConnectDialog::_advanced_pressed)); @@ -797,9 +819,6 @@ void ConnectionsDock::_filter_changed(const String &p_text) { * Creates or edits connections based on state of the ConnectDialog when "Connect" is pressed. */ void ConnectionsDock::_make_or_edit_connection() { - TreeItem *it = tree->get_selected(); - ERR_FAIL_COND(!it); - NodePath dst_path = connect_dialog->get_dst_path(); Node *target = selected_node->get_node(dst_path); ERR_FAIL_COND(!target); @@ -837,27 +856,21 @@ void ConnectionsDock::_make_or_edit_connection() { add_script_function = !found_inherited_function; } - PackedStringArray script_function_args; - if (add_script_function) { - // Pick up args here before "it" is deleted by update_tree. - script_function_args = it->get_metadata(0).operator Dictionary()["args"]; - script_function_args.resize(script_function_args.size() - cd.unbinds); - for (int i = 0; i < cd.binds.size(); i++) { - script_function_args.push_back("extra_arg_" + itos(i) + ":" + Variant::get_type_name(cd.binds[i].get_type())); - } - } if (connect_dialog->is_editing()) { - _disconnect(*it); + _disconnect(connect_dialog->get_source_connection_data()); _connect(cd); } else { _connect(cd); } - // IMPORTANT NOTE: _disconnect and _connect cause an update_tree, which will delete the object "it" is pointing to. - it = nullptr; - if (add_script_function) { + PackedStringArray script_function_args = connect_dialog->get_signal_args(); + script_function_args.resize(script_function_args.size() - cd.unbinds); + for (int i = 0; i < cd.binds.size(); i++) { + script_function_args.push_back("extra_arg_" + itos(i) + ":" + Variant::get_type_name(cd.binds[i].get_type())); + } + EditorNode::get_singleton()->emit_signal(SNAME("script_add_function_request"), target, cd.method, script_function_args); hide(); } @@ -868,7 +881,7 @@ void ConnectionsDock::_make_or_edit_connection() { /* * Creates single connection w/ undo-redo functionality. */ -void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) { +void ConnectionsDock::_connect(const ConnectDialog::ConnectionData &p_cd) { Node *source = Object::cast_to<Node>(p_cd.source); Node *target = Object::cast_to<Node>(p_cd.target); @@ -892,18 +905,15 @@ void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) { /* * Break single connection w/ undo-redo functionality. */ -void ConnectionsDock::_disconnect(TreeItem &p_item) { - Connection connection = p_item.get_metadata(0); - ConnectDialog::ConnectionData cd = connection; - - ERR_FAIL_COND(cd.source != selected_node); // Shouldn't happen but... Bugcheck. +void ConnectionsDock::_disconnect(const ConnectDialog::ConnectionData &p_cd) { + ERR_FAIL_COND(p_cd.source != selected_node); // Shouldn't happen but... Bugcheck. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), cd.signal, cd.method)); + undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), p_cd.signal, p_cd.method)); - Callable callable = cd.get_callable(); - undo_redo->add_do_method(selected_node, "disconnect", cd.signal, callable); - undo_redo->add_undo_method(selected_node, "connect", cd.signal, callable, cd.binds, cd.flags); + Callable callable = p_cd.get_callable(); + undo_redo->add_do_method(selected_node, "disconnect", p_cd.signal, callable); + undo_redo->add_undo_method(selected_node, "connect", p_cd.signal, callable, p_cd.flags); undo_redo->add_do_method(this, "update_tree"); undo_redo->add_undo_method(this, "update_tree"); undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); // To force redraw of scene tree. @@ -933,7 +943,7 @@ void ConnectionsDock::_disconnect_all() { if (!_is_connection_inherited(connection)) { ConnectDialog::ConnectionData cd = connection; undo_redo->add_do_method(selected_node, "disconnect", cd.signal, cd.get_callable()); - undo_redo->add_undo_method(selected_node, "connect", cd.signal, cd.get_callable(), cd.binds, cd.flags); + undo_redo->add_undo_method(selected_node, "connect", cd.signal, cd.get_callable(), cd.flags); } child = child->get_next(); } @@ -987,8 +997,9 @@ bool ConnectionsDock::_is_connection_inherited(Connection &p_connection) { * Open connection dialog with TreeItem data to CREATE a brand-new connection. */ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) { - String signal_name = p_item.get_metadata(0).operator Dictionary()["name"]; - const String &signal_name_ref = signal_name; + Dictionary sinfo = p_item.get_metadata(0); + String signal_name = sinfo["name"]; + PackedStringArray signal_args = sinfo["args"]; Node *dst_node = selected_node->get_owner() ? selected_node->get_owner() : selected_node; if (!dst_node || dst_node->get_script().is_null()) { @@ -997,26 +1008,34 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) { ConnectDialog::ConnectionData cd; cd.source = selected_node; - cd.signal = StringName(signal_name_ref); + cd.signal = StringName(signal_name); cd.target = dst_node; cd.method = ConnectDialog::generate_method_callback_name(cd.source, signal_name, cd.target); - connect_dialog->popup_dialog(signal_name_ref); - connect_dialog->init(cd); + connect_dialog->popup_dialog(signal_name + "(" + String(", ").join(signal_args) + ")"); + connect_dialog->init(cd, signal_args); connect_dialog->set_title(TTR("Connect a Signal to a Method")); } /* * Open connection dialog with Connection data to EDIT an existing connection. */ -void ConnectionsDock::_open_connection_dialog(ConnectDialog::ConnectionData p_cd) { - Node *src = Object::cast_to<Node>(p_cd.source); - Node *dst = Object::cast_to<Node>(p_cd.target); +void ConnectionsDock::_open_edit_connection_dialog(TreeItem &p_item) { + TreeItem *signal_item = p_item.get_parent(); + ERR_FAIL_COND(!signal_item); + + Connection connection = p_item.get_metadata(0); + ConnectDialog::ConnectionData cd = connection; + + Node *src = Object::cast_to<Node>(cd.source); + Node *dst = Object::cast_to<Node>(cd.target); if (src && dst) { - const String &signal_name_ref = p_cd.signal; - connect_dialog->set_title(TTR("Edit Connection:") + p_cd.signal); + const String &signal_name_ref = cd.signal; + PackedStringArray signal_args = signal_item->get_metadata(0).operator Dictionary()["args"]; + + connect_dialog->set_title(vformat(TTR("Edit Connection: '%s'"), cd.signal)); connect_dialog->popup_dialog(signal_name_ref); - connect_dialog->init(p_cd, true); + connect_dialog->init(cd, signal_args, true); } } @@ -1091,14 +1110,14 @@ void ConnectionsDock::_handle_slot_menu_option(int p_option) { switch (p_option) { case EDIT: { - Connection connection = item->get_metadata(0); - _open_connection_dialog(connection); + _open_edit_connection_dialog(*item); } break; case GO_TO_SCRIPT: { _go_to_script(*item); } break; case DISCONNECT: { - _disconnect(*item); + Connection connection = item->get_metadata(0); + _disconnect(connection); update_tree(); } break; } @@ -1149,7 +1168,8 @@ void ConnectionsDock::_connect_pressed() { if (_is_item_signal(*item)) { _open_connection_dialog(*item); } else { - _disconnect(*item); + Connection connection = item->get_metadata(0); + _disconnect(connection); update_tree(); } } @@ -1177,6 +1197,10 @@ void ConnectionsDock::set_node(Node *p_node) { } void ConnectionsDock::update_tree() { + String prev_selected; + if (tree->is_anything_selected()) { + prev_selected = tree->get_selected()->get_text(0); + } tree->clear(); if (!selected_node) { @@ -1238,37 +1262,22 @@ void ConnectionsDock::update_tree() { } for (MethodInfo &mi : node_signals2) { - StringName signal_name = mi.name; - String signaldesc = "("; - PackedStringArray argnames; - - String filter_text = search_box->get_text(); - if (!filter_text.is_subsequence_ofn(signal_name)) { + const StringName signal_name = mi.name; + if (!search_box->get_text().is_subsequence_ofn(signal_name)) { continue; } + PackedStringArray argnames; - if (mi.arguments.size()) { - for (int i = 0; i < mi.arguments.size(); i++) { - PropertyInfo &pi = mi.arguments[i]; + // Create the children of the subsection - the actual list of signals. + TreeItem *signal_item = tree->create_item(section_item); + String signame = connect_dialog->get_signature(mi, &argnames); + signal_item->set_text(0, signame); - if (i > 0) { - signaldesc += ", "; - } - String tname = "var"; - if (pi.type == Variant::OBJECT && pi.class_name != StringName()) { - tname = pi.class_name.operator String(); - } else if (pi.type != Variant::NIL) { - tname = Variant::get_type_name(pi.type); - } - signaldesc += (pi.name.is_empty() ? String("arg " + itos(i)) : pi.name) + ": " + tname; - argnames.push_back(pi.name + ":" + tname); - } + if (signame == prev_selected) { + signal_item->select(0); + prev_selected = ""; } - signaldesc += ")"; - // Create the children of the subsection - the actual list of signals. - TreeItem *signal_item = tree->create_item(section_item); - signal_item->set_text(0, String(signal_name) + signaldesc); Dictionary sinfo; sinfo["name"] = signal_name; sinfo["args"] = argnames; @@ -1309,7 +1318,7 @@ void ConnectionsDock::update_tree() { } // "::" separators used in make_custom_tooltip for formatting. - signal_item->set_tooltip_text(0, String(signal_name) + "::" + signaldesc + "::" + descr); + signal_item->set_tooltip_text(0, String(signal_name) + "::" + signame.trim_prefix(mi.name) + "::" + descr); } // List existing connections. diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 0bea897976..277ea03cf7 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -88,7 +88,7 @@ public: method = base_callable.get_method(); } - Callable get_callable() { + Callable get_callable() const { if (unbinds > 0) { return Callable(target, method).unbind(unbinds); } else if (!binds.is_empty()) { @@ -107,7 +107,9 @@ private: Label *connect_to_label = nullptr; LineEdit *from_signal = nullptr; Node *source = nullptr; + ConnectionData source_connection_data; StringName signal; + PackedStringArray signal_args; LineEdit *dst_method = nullptr; ConnectDialogBinds *cdbinds = nullptr; bool edit_mode = false; @@ -117,8 +119,10 @@ private: SceneTreeEditor *tree = nullptr; AcceptDialog *error = nullptr; + Button *open_method_tree = nullptr; AcceptDialog *method_popup = nullptr; Tree *method_tree = nullptr; + Label *empty_tree_label = nullptr; LineEdit *method_search = nullptr; CheckButton *script_methods_only = nullptr; CheckButton *compatible_methods_only = nullptr; @@ -159,21 +163,24 @@ protected: public: static StringName generate_method_callback_name(Node *p_source, String p_signal_name, Node *p_target); Node *get_source() const; + ConnectionData get_source_connection_data() const; StringName get_signal_name() const; + PackedStringArray get_signal_args() const; NodePath get_dst_path() const; void set_dst_node(Node *p_node); StringName get_dst_method_name() const; void set_dst_method(const StringName &p_method); int get_unbinds() const; Vector<Variant> get_binds() const; + String get_signature(const MethodInfo &p_method, PackedStringArray *r_arg_names = nullptr); bool get_deferred() const; bool get_one_shot() const; bool is_editing() const; - void init(ConnectionData p_cd, bool p_edit = false); + void init(const ConnectionData &p_cd, const PackedStringArray &p_signal_args, bool p_edit = false); - void popup_dialog(const String &p_for_signal); + void popup_dialog(const String p_for_signal); ConnectDialog(); ~ConnectDialog(); }; @@ -217,8 +224,8 @@ class ConnectionsDock : public VBoxContainer { void _filter_changed(const String &p_text); void _make_or_edit_connection(); - void _connect(ConnectDialog::ConnectionData p_cd); - void _disconnect(TreeItem &p_item); + void _connect(const ConnectDialog::ConnectionData &p_cd); + void _disconnect(const ConnectDialog::ConnectionData &p_cd); void _disconnect_all(); void _tree_item_selected(); @@ -227,7 +234,7 @@ class ConnectionsDock : public VBoxContainer { bool _is_connection_inherited(Connection &p_connection); void _open_connection_dialog(TreeItem &p_item); - void _open_connection_dialog(ConnectDialog::ConnectionData p_cd); + void _open_edit_connection_dialog(TreeItem &p_item); void _go_to_script(TreeItem &p_item); void _handle_signal_menu_option(int p_option); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 0814d5b5ca..aee907854c 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -122,7 +122,7 @@ bool CreateDialog::_should_hide_type(const String &p_type) const { return true; } - if (base_type == "Node" && p_type.begins_with("Editor")) { + if (is_base_type_node && p_type.begins_with("Editor")) { return true; // Do not show editor nodes. } @@ -508,6 +508,11 @@ String CreateDialog::get_selected_type() { return selected->get_text(0); } +void CreateDialog::set_base_type(const String &p_base) { + base_type = p_base; + is_base_type_node = ClassDB::is_parent_class(p_base, "Node"); +} + Variant CreateDialog::instantiate_selected() { TreeItem *selected = search_options->get_selected(); diff --git a/editor/create_dialog.h b/editor/create_dialog.h index ad63346a02..37579812cf 100644 --- a/editor/create_dialog.h +++ b/editor/create_dialog.h @@ -51,6 +51,7 @@ class CreateDialog : public ConfirmationDialog { Tree *search_options = nullptr; String base_type; + bool is_base_type_node = false; String icon_fallback; String preferred_search_result_type; @@ -113,7 +114,7 @@ public: Variant instantiate_selected(); String get_selected_type(); - void set_base_type(const String &p_base) { base_type = p_base; } + void set_base_type(const String &p_base); String get_base_type() const { return base_type; } void select_base(); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 2c6b9990ed..32952a367d 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -519,7 +519,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da String error_title; if (oe.callstack.size() > 0) { // If available, use the script's stack in the error title. - error_title = oe.callstack[oe.callstack.size() - 1].func + ": "; + error_title = _format_frame_text(&oe.callstack[0]) + ": "; } else if (!oe.source_func.is_empty()) { // Otherwise try to use the C++ source function. error_title += oe.source_func + ": "; @@ -530,13 +530,25 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da error->set_text(1, error_title); tooltip += " " + error_title + "\n"; + // Find the language of the error's source file. + String source_language_name = "C++"; // Default value is the old hard-coded one. + const String source_file_extension = oe.source_file.get_extension(); + for (int i = 0; i < ScriptServer::get_language_count(); ++i) { + ScriptLanguage *script_language = ScriptServer::get_language(i); + if (source_file_extension == script_language->get_extension()) { + source_language_name = script_language->get_name(); + break; + } + } + if (!oe.error_descr.is_empty()) { // Add item for C++ error condition. TreeItem *cpp_cond = error_tree->create_item(error); - cpp_cond->set_text(0, "<" + TTR("C++ Error") + ">"); + // TRANSLATORS: %s is the name of a language, e.g. C++. + cpp_cond->set_text(0, "<" + vformat(TTR("%s Error"), source_language_name) + ">"); cpp_cond->set_text(1, oe.error); cpp_cond->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT); - tooltip += TTR("C++ Error:") + " " + oe.error + "\n"; + tooltip += vformat(TTR("%s Error:"), source_language_name) + " " + oe.error + "\n"; if (source_is_project_file) { cpp_cond->set_metadata(0, source_meta); } @@ -547,14 +559,18 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da // Source of the error. String source_txt = (source_is_project_file ? oe.source_file.get_file() : oe.source_file) + ":" + itos(oe.source_line); if (!oe.source_func.is_empty()) { - source_txt += " @ " + oe.source_func + "()"; + source_txt += " @ " + oe.source_func; + if (!oe.source_func.ends_with(")")) { + source_txt += "()"; + } } TreeItem *cpp_source = error_tree->create_item(error); - cpp_source->set_text(0, "<" + (source_is_project_file ? TTR("Source") : TTR("C++ Source")) + ">"); + // TRANSLATORS: %s is the name of a language, e.g. C++. + cpp_source->set_text(0, "<" + vformat(TTR("%s Source"), source_language_name) + ">"); cpp_source->set_text(1, source_txt); cpp_source->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT); - tooltip += (source_is_project_file ? TTR("Source:") : TTR("C++ Source:")) + " " + source_txt + "\n"; + tooltip += vformat(TTR("%s Source:"), source_language_name) + " " + source_txt + "\n"; // Set metadata to highlight error line in scripts. if (source_is_project_file) { @@ -581,7 +597,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da tooltip += TTR("Stack Trace:") + "\n"; } - String frame_txt = infos[i].file.get_file() + ":" + itos(infos[i].line) + " @ " + infos[i].func + "()"; + String frame_txt = _format_frame_text(&infos[i]); tooltip += frame_txt + "\n"; stack_trace->set_text(1, frame_txt); } @@ -901,6 +917,14 @@ void ScriptEditorDebugger::_breakpoint_tree_clicked() { } } +String ScriptEditorDebugger::_format_frame_text(const ScriptLanguage::StackInfo *info) { + String text = info->file.get_file() + ":" + itos(info->line) + " @ " + info->func; + if (!text.ends_with(")")) { + text += "()"; + } + return text; +} + void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { _clear_errors_list(); stop(); diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index e31b3af3d9..a0c420522a 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -210,6 +210,8 @@ private: void _breakpoint_tree_clicked(); + String _format_frame_text(const ScriptLanguage::StackInfo *info); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 1690302e6e..6a1c77d376 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -47,11 +47,7 @@ class TextureRect; class EditorPropertyRevert { public: - static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default = true); - static bool is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig); - static bool is_property_value_different(const Variant &p_a, const Variant &p_b); static Variant get_property_revert_value(Object *p_object, const StringName &p_property, bool *r_is_valid); - static bool can_property_revert(Object *p_object, const StringName &p_property, const Variant *p_custom_current_value = nullptr); }; @@ -331,7 +327,7 @@ class EditorInspectorArray : public EditorInspectorSection { AcceptDialog *resize_dialog = nullptr; SpinBox *new_size_spin_box = nullptr; - // Pagination + // Pagination. int page_length = 5; int page = 0; int max_page = 0; @@ -495,7 +491,7 @@ class EditorInspector : public ScrollContainer { HashMap<ObjectID, int> scroll_cache; - String property_prefix; //used for sectioned inspector + String property_prefix; // Used for sectioned inspector. String object_class; Variant property_clipboard; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 173cbc6893..f3f2f771af 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -643,7 +643,7 @@ void EditorNode::_notification(int p_what) { } RenderingServer::get_singleton()->viewport_set_disable_2d(get_scene_root()->get_viewport_rid(), true); - RenderingServer::get_singleton()->viewport_set_disable_environment(get_viewport()->get_viewport_rid(), true); + RenderingServer::get_singleton()->viewport_set_environment_mode(get_viewport()->get_viewport_rid(), RenderingServer::VIEWPORT_ENVIRONMENT_DISABLED); feature_profile_manager->notify_changed(); @@ -2096,14 +2096,25 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) { if (!item_plugins.is_empty()) { ObjectID owner_id = p_editing_owner->get_instance_id(); + List<EditorPlugin *> to_remove; for (EditorPlugin *plugin : active_plugins[owner_id]) { if (!item_plugins.has(plugin)) { + // Remove plugins no longer used by this editing owner. + to_remove.push_back(plugin); plugin->make_visible(false); plugin->edit(nullptr); } } + for (EditorPlugin *plugin : to_remove) { + active_plugins[owner_id].erase(plugin); + } + for (EditorPlugin *plugin : item_plugins) { + if (active_plugins[owner_id].has(plugin)) { + continue; + } + for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) { if (kv.key != owner_id) { EditorPropertyResource *epres = Object::cast_to<EditorPropertyResource>(ObjectDB::get_instance(kv.key)); @@ -6195,7 +6206,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins List<PropertyInfo> pinfo; modifiable_node->get_property_list(&pinfo); - // Get names of all valid property names (TODO: make this more efficent). + // Get names of all valid property names (TODO: make this more efficient). List<String> property_names; for (const PropertyInfo &E2 : pinfo) { if (E2.usage & PROPERTY_USAGE_STORAGE) { @@ -6213,7 +6224,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins for (const ConnectionWithNodePath &E2 : E.value.connections_to) { Connection conn = E2.connection; - // Get the node the callable is targetting. + // Get the node the callable is targeting. Node *target_node = cast_to<Node>(conn.callable.get_object()); // If the callable object no longer exists or is marked for deletion, @@ -7262,6 +7273,7 @@ EditorNode::EditorNode() { project_title->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); project_title->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); project_title->set_h_size_flags(Control::SIZE_EXPAND_FILL); + project_title->set_mouse_filter(Control::MOUSE_FILTER_PASS); left_spacer->add_child(project_title); } diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 152e77acb7..f022027e65 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1323,10 +1323,12 @@ void EditorPropertyObjectID::update_property() { ObjectID id = get_edited_object()->get(get_edited_property()); if (id.is_valid()) { edit->set_text(type + " ID: " + uitos(id)); + edit->set_tooltip_text(type + " ID: " + uitos(id)); edit->set_disabled(false); edit->set_icon(EditorNode::get_singleton()->get_class_icon(type)); } else { edit->set_text(TTR("<empty>")); + edit->set_tooltip_text(""); edit->set_disabled(true); edit->set_icon(Ref<Texture2D>()); } @@ -1343,6 +1345,7 @@ EditorPropertyObjectID::EditorPropertyObjectID() { edit = memnew(Button); add_child(edit); add_focusable(edit); + edit->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); edit->connect("pressed", callable_mp(this, &EditorPropertyObjectID::_edit_pressed)); } diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index 18ba19f5f6..5380fddde2 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -115,6 +115,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["arm64-v8a"] = "arm64-v8a"; capitalize_string_remaps["armeabi-v7a"] = "armeabi-v7a"; capitalize_string_remaps["arvr"] = "ARVR"; + capitalize_string_remaps["astc"] = "ASTC"; capitalize_string_remaps["bg"] = "BG"; capitalize_string_remaps["bidi"] = "BiDi"; capitalize_string_remaps["bp"] = "BP"; diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 4bcd91376a..d3cceee1a3 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -272,7 +272,9 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { OS::ProcessID pid = 0; Error err = OS::get_singleton()->create_instance(args, &pid); ERR_FAIL_COND_V(err, err); - pids.push_back(pid); + if (pid != 0) { + pids.push_back(pid); + } } status = STATUS_PLAY; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4879790b74..1c988840ac 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -453,6 +453,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Theme EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_ENUM, "interface/theme/preset", "Default", "Default,Breeze Dark,Godot 2,Gray,Light,Solarized (Dark),Solarized (Light),Black (OLED),Custom") + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/theme/enable_touchscreen_touch_area", DisplayServer::get_singleton()->is_touchscreen_available(), "") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/theme/icon_and_font_color", 0, "Auto,Dark,Light") EDITOR_SETTING(Variant::COLOR, PROPERTY_HINT_NONE, "interface/theme/base_color", Color(0.2, 0.23, 0.31), "") EDITOR_SETTING(Variant::COLOR, PROPERTY_HINT_NONE, "interface/theme/accent_color", Color(0.41, 0.61, 0.91), "") @@ -474,10 +475,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { /* Filesystem */ // External Programs - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/raster_image_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/vector_image_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/audio_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/3d_model_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/raster_image_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/vector_image_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/audio_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/3d_model_editor", "", "") // Directories EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/directories/autoscan_project_path", "", "") diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 745acca04b..d2c82ad013 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -198,7 +198,7 @@ static Ref<StyleBoxTexture> make_stylebox(Ref<Texture2D> p_texture, float p_left Ref<StyleBoxTexture> style(memnew(StyleBoxTexture)); style->set_texture(p_texture); style->set_texture_margin_individual(p_left * EDSCALE, p_top * EDSCALE, p_right * EDSCALE, p_bottom * EDSCALE); - style->set_content_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE); + style->set_content_margin_individual((p_left + p_margin_left) * EDSCALE, (p_top + p_margin_top) * EDSCALE, (p_right + p_margin_right) * EDSCALE, (p_bottom + p_margin_bottom) * EDSCALE); style->set_draw_center(p_draw_center); return style; } @@ -394,6 +394,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Color accent_color = EDITOR_GET("interface/theme/accent_color"); Color base_color = EDITOR_GET("interface/theme/base_color"); float contrast = EDITOR_GET("interface/theme/contrast"); + bool enable_touchscreen_touch_area = EDITOR_GET("interface/theme/enable_touchscreen_touch_area"); bool draw_extra_borders = EDITOR_GET("interface/theme/draw_extra_borders"); float icon_saturation = EDITOR_GET("interface/theme/icon_saturation"); float relationship_line_opacity = EDITOR_GET("interface/theme/relationship_line_opacity"); @@ -1492,11 +1493,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // HScrollBar Ref<Texture2D> empty_icon = memnew(ImageTexture); - theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); - theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 2, 2, 2, 2)); - theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); + if (enable_touchscreen_touch_area) { + theme->set_stylebox("scroll", "HScrollBar", make_line_stylebox(separator_color, 50)); + } else { + theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + } + theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); + theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); theme->set_icon("increment", "HScrollBar", empty_icon); theme->set_icon("increment_highlight", "HScrollBar", empty_icon); @@ -1506,11 +1511,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("decrement_pressed", "HScrollBar", empty_icon); // VScrollBar - theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); - theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 2, 2, 2, 2)); - theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); + if (enable_touchscreen_touch_area) { + theme->set_stylebox("scroll", "VScrollBar", make_line_stylebox(separator_color, 50, 1, 1, true)); + } else { + theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + } + theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); + theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); theme->set_icon("increment", "VScrollBar", empty_icon); theme->set_icon("increment_highlight", "VScrollBar", empty_icon); diff --git a/editor/editor_title_bar.cpp b/editor/editor_title_bar.cpp index 0271bbd64a..ae5cdfd72b 100644 --- a/editor/editor_title_bar.cpp +++ b/editor/editor_title_bar.cpp @@ -30,7 +30,7 @@ #include "editor/editor_title_bar.h" -void EditorTitleBar::input(const Ref<InputEvent> &p_event) { +void EditorTitleBar::gui_input(const Ref<InputEvent> &p_event) { if (!can_move) { return; } diff --git a/editor/editor_title_bar.h b/editor/editor_title_bar.h index 6cac163830..4055476b82 100644 --- a/editor/editor_title_bar.h +++ b/editor/editor_title_bar.h @@ -42,7 +42,7 @@ class EditorTitleBar : public HBoxContainer { bool can_move = false; protected: - virtual void input(const Ref<InputEvent> &p_event) override; + virtual void gui_input(const Ref<InputEvent> &p_event) override; static void _bind_methods(){}; public: diff --git a/editor/editor_zoom_widget.cpp b/editor/editor_zoom_widget.cpp index 5a334bdaa6..3998b33a53 100644 --- a/editor/editor_zoom_widget.cpp +++ b/editor/editor_zoom_widget.cpp @@ -41,12 +41,12 @@ void EditorZoomWidget::_update_zoom_label() { // lower the editor scale to increase the available real estate, // even if their display doesn't have a particularly low DPI. if (zoom >= 10) { - // Don't show a decimal when the zoom level is higher than 1000 %. - zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))) + " " + TS->percent_sign(); + zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))); } else { - zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, 0.1))) + " " + TS->percent_sign(); + // 2 decimal places if the zoom is below 10%, 1 decimal place if it's below 1000%. + zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, (zoom >= 0.1) ? 0.1 : 0.01))); } - + zoom_text += " " + TS->percent_sign(); zoom_reset->set_text(zoom_text); } @@ -134,7 +134,7 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte float new_zoom_index = closest_zoom_index + p_increment_count; float new_zoom = Math::pow(2.f, new_zoom_index / 12.f); - // Restore Editor scale transformation + // Restore Editor scale transformation. new_zoom *= MAX(1, EDSCALE); set_zoom(new_zoom); @@ -179,8 +179,12 @@ EditorZoomWidget::EditorZoomWidget() { zoom_reset = memnew(Button); zoom_reset->set_flat(true); + zoom_reset->add_theme_style_override("normal", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("hover", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("focus", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("pressed", memnew(StyleBoxEmpty)); add_child(zoom_reset); - zoom_reset->add_theme_constant_override("outline_size", 1); + zoom_reset->add_theme_constant_override("outline_size", Math::ceil(2 * EDSCALE)); zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0)); zoom_reset->add_theme_color_override("font_color", Color(1, 1, 1)); zoom_reset->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_reset)); @@ -189,7 +193,7 @@ EditorZoomWidget::EditorZoomWidget() { zoom_reset->set_focus_mode(FOCUS_NONE); zoom_reset->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER); // Prevent the button's size from changing when the text size changes - zoom_reset->set_custom_minimum_size(Size2(75 * EDSCALE, 0)); + zoom_reset->set_custom_minimum_size(Size2(56 * EDSCALE, 0)); zoom_plus = memnew(Button); zoom_plus->set_flat(true); @@ -201,5 +205,5 @@ EditorZoomWidget::EditorZoomWidget() { _update_zoom_label(); - add_theme_constant_override("separation", Math::round(-8 * EDSCALE)); + add_theme_constant_override("separation", 0); } diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 4900ced2e4..bc429e1111 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -129,10 +129,20 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in } String EditorExportPlatform::test_etc2() const { - const bool etc2_supported = GLOBAL_GET("rendering/textures/vram_compression/import_etc2"); + const bool etc2_supported = GLOBAL_GET("rendering/textures/vram_compression/import_etc2_astc"); if (!etc2_supported) { - return TTR("Target platform requires 'ETC2' texture compression. Enable 'Import Etc 2' in Project Settings."); + return TTR("Target platform requires 'ETC2/ASTC' texture compression. Enable 'Import ETC2 ASTC' in Project Settings."); + } + + return String(); +} + +String EditorExportPlatform::test_bc() const { + const bool bc_supported = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc_bptc"); + + if (!bc_supported) { + return TTR("Target platform requires 'S3TC/BPTC' texture compression. Enable 'Import S3TC BPTC' in Project Settings."); } return String(); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 9f79eecfb7..fe67813d65 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -785,10 +785,10 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector break; } } - } - if (_export_customize_object(res.ptr(), customize_resources_plugins)) { - modified = true; + if (_export_customize_object(res.ptr(), customize_resources_plugins)) { + modified = true; + } } if (modified || p_force_save) { @@ -1802,6 +1802,13 @@ Error EditorExportPlatform::ssh_run_on_remote(const String &p_host, const String List<String> args; args.push_back("-p"); args.push_back(p_port); + args.push_back("-q"); + args.push_back("-o"); + args.push_back("LogLevel=error"); + args.push_back("-o"); + args.push_back("BatchMode=yes"); + args.push_back("-o"); + args.push_back("StrictHostKeyChecking=no"); for (const String &E : p_ssh_args) { args.push_back(E); } @@ -1852,6 +1859,13 @@ Error EditorExportPlatform::ssh_run_on_remote_no_wait(const String &p_host, cons List<String> args; args.push_back("-p"); args.push_back(p_port); + args.push_back("-q"); + args.push_back("-o"); + args.push_back("LogLevel=error"); + args.push_back("-o"); + args.push_back("BatchMode=yes"); + args.push_back("-o"); + args.push_back("StrictHostKeyChecking=no"); for (const String &E : p_ssh_args) { args.push_back(E); } @@ -1882,6 +1896,13 @@ Error EditorExportPlatform::ssh_push_to_remote(const String &p_host, const Strin List<String> args; args.push_back("-P"); args.push_back(p_port); + args.push_back("-q"); + args.push_back("-o"); + args.push_back("LogLevel=error"); + args.push_back("-o"); + args.push_back("BatchMode=yes"); + args.push_back("-o"); + args.push_back("StrictHostKeyChecking=no"); for (const String &E : p_scp_args) { args.push_back(E); } diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 3b4e92c9bd..05d985eb6a 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -229,6 +229,7 @@ public: virtual Ref<Texture2D> get_run_icon() const { return get_logo(); } String test_etc2() const; + String test_bc() const; bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0; diff --git a/editor/icons/UseBlendDisable.svg b/editor/icons/UseBlendDisable.svg new file mode 100644 index 0000000000..449dc2d0c8 --- /dev/null +++ b/editor/icons/UseBlendDisable.svg @@ -0,0 +1 @@ +<svg enable-background="new -595.5 420.5 16 16" height="16" viewBox="-595.5 420.5 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m-591 421.5h7v14h-7z" fill="#a3e595" opacity=".5"/><g fill="none" stroke="#a3e595" stroke-linecap="square" stroke-width="2"><path d="m-585 434.5v-12"/><path d="m-590 422.5v12"/><path d="m-581.5 422.5h-12"/></g></svg> diff --git a/editor/icons/UseBlendEnable.svg b/editor/icons/UseBlendEnable.svg new file mode 100644 index 0000000000..5567b2d8c6 --- /dev/null +++ b/editor/icons/UseBlendEnable.svg @@ -0,0 +1 @@ +<svg enable-background="new -595.5 420.5 16 16" height="16" viewBox="-595.5 420.5 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m-587.5 423.244c-3.995 2.354-7 6.775-7 11.256v1h14v-1c0-4.48-3.005-8.901-7-11.256z" fill="#95c6e8" opacity=".5"/><g fill="none" stroke="#95c6e8" stroke-linecap="square" stroke-width="2"><path d="m-581.5 422.5c-6 0-12 6-12 12"/><path d="m-581.5 434.5c0-6-6-12-12-12"/></g></svg> diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index bc4ced7ea2..10a0c2662f 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -123,6 +123,9 @@ bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_path, if (p_option == "compress/lossy_quality" && p_options.has("compress/mode")) { return int(p_options["compress/mode"]) == COMPRESS_LOSSY; } + if ((p_option == "compress/high_quality" || p_option == "compress/hdr_compression") && p_options.has("compress/mode")) { + return int(p_options["compress/mode"]) == COMPRESS_VRAM_COMPRESSED; + } return true; } @@ -136,9 +139,9 @@ String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { void ResourceImporterLayeredTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/high_quality"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized,Normal Map (RG Channels)"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1)); @@ -283,8 +286,8 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { int compress_mode = p_options["compress/mode"]; float lossy = p_options["compress/lossy_quality"]; + float high_quality = p_options["compress/high_quality"]; int hdr_compression = p_options["compress/hdr_compression"]; - int bptc_ldr = p_options["compress/bptc_ldr"]; bool mipmaps = p_options["mipmaps/generate"]; int channel_pack = p_options["compress/channel_pack"]; @@ -389,9 +392,10 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const texture_import->compress_mode = compress_mode; texture_import->lossy = lossy; texture_import->hdr_compression = hdr_compression; - texture_import->bptc_ldr = bptc_ldr; texture_import->mipmaps = mipmaps; texture_import->used_channels = used_channels; + texture_import->high_quality = high_quality; + _check_compress_ctex(p_source_file, texture_import); if (r_metadata) { Dictionary meta; @@ -406,12 +410,11 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const } const char *ResourceImporterLayeredTexture::compression_formats[] = { - "bptc", - "s3tc", - "etc", - "etc2", + "s3tc_bptc", + "etc2_astc", nullptr }; + String ResourceImporterLayeredTexture::get_import_settings_string() const { String s; @@ -450,12 +453,16 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p bool valid = true; while (compression_formats[index]) { String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); - bool test = GLOBAL_GET(setting_path); - if (test) { - if (!formats_imported.has(compression_formats[index])) { - valid = false; - break; + if (ProjectSettings::get_singleton()->has_setting(setting_path)) { + bool test = GLOBAL_GET(setting_path); + if (test) { + if (!formats_imported.has(compression_formats[index])) { + valid = false; + break; + } } + } else { + WARN_PRINT("Setting for imported format not found: " + setting_path); } index++; } @@ -484,64 +491,83 @@ void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source // Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). // Android, GLES 2.x - bool can_bptc = GLOBAL_GET("rendering/textures/vram_compression/import_bptc"); - if (can_bptc) { - r_texture_import->formats_imported.push_back("bptc"); // BPTC needs to be added anyway. + const bool can_s3tc_bptc = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc_bptc") || OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; + const bool can_etc2_astc = GLOBAL_GET("rendering/textures/vram_compression/import_etc2_astc") || OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC; + + // Add list of formats imported + if (can_s3tc_bptc) { + r_texture_import->formats_imported.push_back("s3tc_bptc"); } + if (can_etc2_astc) { + r_texture_import->formats_imported.push_back("etc2_astc"); + } + bool can_compress_hdr = r_texture_import->hdr_compression > 0; ERR_FAIL_NULL(r_texture_import->image); bool is_hdr = (r_texture_import->image->get_format() >= Image::FORMAT_RF && r_texture_import->image->get_format() <= Image::FORMAT_RGBE9995); - bool is_ldr = (r_texture_import->image->get_format() >= Image::FORMAT_L8 && r_texture_import->image->get_format() <= Image::FORMAT_RGB565); - bool can_s3tc = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc"); ERR_FAIL_NULL(r_texture_import->slices); // Can compress hdr, but hdr with alpha is not compressible. - if (r_texture_import->hdr_compression == 2) { - // The user selected to compress hdr anyway, so force an alpha-less format. - if (r_texture_import->image->get_format() == Image::FORMAT_RGBAF) { - for (int i = 0; i < r_texture_import->slices->size(); i++) { - r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBF); - } + bool use_uncompressed = false; + + if (is_hdr) { + if (r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA) { + if (r_texture_import->hdr_compression == 2) { + // The user selected to compress hdr anyway, so force an alpha-less format. + if (r_texture_import->image->get_format() == Image::FORMAT_RGBAF) { + for (int i = 0; i < r_texture_import->slices->size(); i++) { + r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBF); + } - } else if (r_texture_import->image->get_format() == Image::FORMAT_RGBAH) { - for (int i = 0; i < r_texture_import->slices->size(); i++) { - r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBH); + } else if (r_texture_import->image->get_format() == Image::FORMAT_RGBAH) { + for (int i = 0; i < r_texture_import->slices->size(); i++) { + r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBH); + } + } + } else { + can_compress_hdr = false; } } - } else { - can_compress_hdr = false; - } - if (is_hdr && can_compress_hdr) { - if (!can_bptc) { + if (!can_compress_hdr) { //default to rgbe if (r_texture_import->image->get_format() != Image::FORMAT_RGBE9995) { for (int i = 0; i < r_texture_import->slices->size(); i++) { r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBE9995); } } + use_uncompressed = true; } - } else { - can_bptc = false; } - if (is_ldr && can_bptc) { - if (r_texture_import->bptc_ldr == 0 || (r_texture_import->bptc_ldr == 1 && !(r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA))) { - can_bptc = false; - } - } - if (!(r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA)) { - if (GLOBAL_GET("rendering/textures/vram_compression/import_etc2")) { - _save_tex(*r_texture_import->slices, r_texture_import->save_path + ".etc2." + extension, r_texture_import->compress_mode, r_texture_import->lossy, Image::COMPRESS_ETC2, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true); - r_texture_import->platform_variants->push_back("etc2"); - r_texture_import->formats_imported.push_back("etc2"); + if (use_uncompressed) { + _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + extension, COMPRESS_VRAM_UNCOMPRESSED, r_texture_import->lossy, Image::COMPRESS_S3TC /* IGNORED */, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false); + } else { + if (can_s3tc_bptc) { + Image::CompressMode image_compress_mode; + String image_compress_format; + if (r_texture_import->high_quality || is_hdr) { + image_compress_mode = Image::COMPRESS_BPTC; + image_compress_format = "bptc"; + } else { + image_compress_mode = Image::COMPRESS_S3TC; + image_compress_format = "s3tc"; + } + _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + image_compress_format + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, image_compress_mode, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true); + r_texture_import->platform_variants->push_back(image_compress_format); } - if (can_bptc || can_s3tc) { - _save_tex(*r_texture_import->slices, r_texture_import->save_path + ".s3tc." + extension, r_texture_import->compress_mode, r_texture_import->lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false); - r_texture_import->platform_variants->push_back("s3tc"); - r_texture_import->formats_imported.push_back("s3tc"); + if (can_etc2_astc) { + Image::CompressMode image_compress_mode; + String image_compress_format; + if (r_texture_import->high_quality || is_hdr) { + image_compress_mode = Image::COMPRESS_ASTC; + image_compress_format = "astc"; + } else { + image_compress_mode = Image::COMPRESS_ETC2; + image_compress_format = "etc2"; + } + _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + image_compress_format + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, image_compress_mode, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true); + r_texture_import->platform_variants->push_back(image_compress_format); } - return; } - EditorNode::add_io_error(vformat(TTR("%s: No suitable PC VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms."), p_source_file)); } diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h index 5118ad7ba4..52fd37639d 100644 --- a/editor/import/resource_importer_layered_texture.h +++ b/editor/import/resource_importer_layered_texture.h @@ -51,8 +51,8 @@ public: int compress_mode = 0; float lossy = 1.0; int hdr_compression = 0; - int bptc_ldr = 0; bool mipmaps = true; + bool high_quality = false; Image::UsedChannels used_channels = Image::USED_CHANNELS_RGBA; virtual ~LayeredTextureImport() {} }; diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 102fa903b8..aa5f9ff29a 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1865,6 +1865,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary())); diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 2f543ea02d..c05e7582eb 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -169,9 +169,14 @@ String ResourceImporterTexture::get_resource_type() const { } bool ResourceImporterTexture::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const { - if (p_option == "compress/lossy_quality") { + if (p_option == "compress/high_quality" || p_option == "compress/hdr_compression") { int compress_mode = int(p_options["compress/mode"]); - if (compress_mode != COMPRESS_LOSSY && compress_mode != COMPRESS_VRAM_COMPRESSED) { + if (compress_mode != COMPRESS_VRAM_COMPRESSED) { + return false; + } + } else if (p_option == "compress/lossy_quality") { + int compress_mode = int(p_options["compress/mode"]); + if (compress_mode != COMPRESS_LOSSY) { return false; } } else if (p_option == "compress/hdr_mode") { @@ -186,15 +191,6 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_path, const } } else if (p_option == "mipmaps/limit") { return p_options["mipmaps/generate"]; - - } else if (p_option == "compress/bptc_ldr") { - int compress_mode = int(p_options["compress/mode"]); - if (compress_mode < COMPRESS_VRAM_COMPRESSED) { - return false; - } - if (!GLOBAL_GET("rendering/textures/vram_compression/import_bptc")) { - return false; - } } return true; @@ -216,9 +212,9 @@ String ResourceImporterTexture::get_preset_name(int p_idx) const { void ResourceImporterTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/high_quality"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), (p_preset == PRESET_3D ? true : false))); @@ -289,7 +285,7 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I case COMPRESS_VRAM_COMPRESSED: { Ref<Image> image = p_image->duplicate(); - image->compress_from_channels(p_compress_format, p_channels, p_lossy_quality); + image->compress_from_channels(p_compress_format, p_channels); f->store_32(CompressedTexture2D::DATA_FORMAT_IMAGE); f->store_16(image->get_width()); @@ -322,15 +318,11 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I f->store_16(p_image->get_height()); f->store_32(p_image->get_mipmap_count()); f->store_32(p_image->get_format()); - - for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { - Vector<uint8_t> data = Image::basis_universal_packer(p_image->get_image_from_mipmap(i), p_channels); - int data_len = data.size(); - f->store_32(data_len); - - const uint8_t *r = data.ptr(); - f->store_buffer(r, data_len); - } + Vector<uint8_t> data = Image::basis_universal_packer(p_image, p_channels); + int data_len = data.size(); + f->store_32(data_len); + const uint8_t *r = data.ptr(); + f->store_buffer(r, data_len); } break; } } @@ -387,7 +379,7 @@ void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String Ref<Image> image = p_image->duplicate(); - if (((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED && p_force_po2_for_compressed)) && p_mipmaps) { + if (p_force_po2_for_compressed && p_mipmaps && ((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED))) { image->resize_to_po2(); } @@ -425,7 +417,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String const int pack_channels = p_options["compress/channel_pack"]; const int normal = p_options["compress/normal_map"]; const int hdr_compression = p_options["compress/hdr_compression"]; - const int bptc_ldr = p_options["compress/bptc_ldr"]; + const int high_quality = p_options["compress/high_quality"]; // Mipmaps. const bool mipmaps = p_options["mipmaps/generate"]; @@ -598,19 +590,22 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String // Android, GLES 2.x const bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); - bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565); - const bool can_bptc = GLOBAL_GET("rendering/textures/vram_compression/import_bptc"); - const bool can_s3tc = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc"); + const bool can_s3tc_bptc = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc_bptc") || OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; + const bool can_etc2_astc = GLOBAL_GET("rendering/textures/vram_compression/import_etc2_astc") || OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC; - if (can_bptc) { - // Add to the list anyway. - formats_imported.push_back("bptc"); + // Add list of formats imported + if (can_s3tc_bptc) { + formats_imported.push_back("s3tc_bptc"); + } + if (can_etc2_astc) { + formats_imported.push_back("etc2_astc"); } bool can_compress_hdr = hdr_compression > 0; bool has_alpha = image->detect_alpha() != Image::ALPHA_NONE; + bool use_uncompressed = false; - if (is_hdr && can_compress_hdr) { + if (is_hdr) { if (has_alpha) { // Can compress HDR, but HDR with alpha is not compressible. if (hdr_compression == 2) { @@ -629,36 +624,41 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String // Fallback to RGBE99995. if (image->get_format() != Image::FORMAT_RGBE9995) { image->convert(Image::FORMAT_RGBE9995); + use_uncompressed = true; } } } - bool ok_on_pc = false; - if (can_bptc || can_s3tc) { - ok_on_pc = true; - Image::CompressMode image_compress_mode = Image::COMPRESS_BPTC; - if (!bptc_ldr && can_s3tc && is_ldr) { - image_compress_mode = Image::COMPRESS_S3TC; + if (use_uncompressed) { + _save_ctex(image, p_save_path + ".ctex", COMPRESS_VRAM_UNCOMPRESSED, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); + } else { + if (can_s3tc_bptc) { + Image::CompressMode image_compress_mode; + String image_compress_format; + if (high_quality || is_hdr) { + image_compress_mode = Image::COMPRESS_BPTC; + image_compress_format = "bptc"; + } else { + image_compress_mode = Image::COMPRESS_S3TC; + image_compress_format = "s3tc"; + } + _save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); + r_platform_variants->push_back(image_compress_format); } - _save_ctex(image, p_save_path + ".s3tc.ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); - r_platform_variants->push_back("s3tc"); - formats_imported.push_back("s3tc"); - } - - if (GLOBAL_GET("rendering/textures/vram_compression/import_etc2")) { - _save_ctex(image, p_save_path + ".etc2.ctex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); - r_platform_variants->push_back("etc2"); - formats_imported.push_back("etc2"); - } - if (GLOBAL_GET("rendering/textures/vram_compression/import_etc")) { - _save_ctex(image, p_save_path + ".etc.ctex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); - r_platform_variants->push_back("etc"); - formats_imported.push_back("etc"); - } - - if (!ok_on_pc) { - EditorNode::add_io_error(vformat(TTR("%s: No suitable desktop VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms."), p_source_file)); + if (can_etc2_astc) { + Image::CompressMode image_compress_mode; + String image_compress_format; + if (high_quality || is_hdr) { + image_compress_mode = Image::COMPRESS_ASTC; + image_compress_format = "astc"; + } else { + image_compress_mode = Image::COMPRESS_ETC2; + image_compress_format = "etc2"; + } + _save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); + r_platform_variants->push_back(image_compress_format); + } } } else { // Import normally. @@ -692,10 +692,8 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String } const char *ResourceImporterTexture::compression_formats[] = { - "bptc", - "s3tc", - "etc", - "etc2", + "s3tc_bptc", + "etc2_astc", nullptr }; String ResourceImporterTexture::get_import_settings_string() const { @@ -745,12 +743,16 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co bool valid = true; while (compression_formats[index]) { String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); - bool test = GLOBAL_GET(setting_path); - if (test) { - if (!formats_imported.has(compression_formats[index])) { - valid = false; - break; + if (ProjectSettings::get_singleton()->has_setting(setting_path)) { + bool test = GLOBAL_GET(setting_path); + if (test) { + if (!formats_imported.has(compression_formats[index])) { + valid = false; + break; + } } + } else { + WARN_PRINT("Setting for imported format not found: " + setting_path); } index++; } diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index 48e3759e57..fb450a41d3 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -201,7 +201,7 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven } if (k.is_valid()) { - k->set_pressed(false); // To avoid serialisation of 'pressed' property - doesn't matter for actions anyway. + k->set_pressed(false); // To avoid serialization of 'pressed' property - doesn't matter for actions anyway. if (key_mode->get_selected_id() == KEYMODE_KEYCODE) { k->set_physical_keycode(Key::NONE); k->set_key_label(Key::NONE); @@ -685,9 +685,9 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { // Key Mode Selection key_mode = memnew(OptionButton); - key_mode->add_item("Keycode (Latin equvialent)", KEYMODE_KEYCODE); - key_mode->add_item("Physical Keycode (poistion of US QWERTY keyboard)", KEYMODE_PHY_KEYCODE); - key_mode->add_item("Unicode (case-insencetive)", KEYMODE_UNICODE); + key_mode->add_item(TTR("Keycode (Latin Equivalent)"), KEYMODE_KEYCODE); + key_mode->add_item(TTR("Physical Keycode (Position on US QWERTY Keyboard)"), KEYMODE_PHY_KEYCODE); + key_mode->add_item(TTR("Key Label (Unicode, Case-Insensitive)"), KEYMODE_UNICODE); key_mode->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected)); key_mode->hide(); additional_options_container->add_child(key_mode); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 06241d07cf..b33ad67f23 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -245,7 +245,7 @@ void AnimationPlayerEditor::_play_bw_pressed() { player->stop(); //so it won't blend with itself } ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing."); - player->play(current, -1, -1, true); + player->play_backwards(current); } //unstop @@ -262,7 +262,7 @@ void AnimationPlayerEditor::_play_bw_from_pressed() { } ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing."); player->seek(time); - player->play(current, -1, -1, true); + player->play_backwards(current); } //unstop @@ -452,7 +452,9 @@ float AnimationPlayerEditor::_get_editor_step() const { } void AnimationPlayerEditor::_animation_name_edited() { - player->stop(); + if (player->is_playing()) { + player->stop(); + } String new_name = name->get_text(); if (!AnimationLibrary::is_valid_animation_name(new_name)) { @@ -1103,9 +1105,24 @@ void AnimationPlayerEditor::_animation_duplicate() { return; } + int count = 2; String new_name = current; - while (player->has_animation(new_name)) { - new_name = new_name + " (copy)"; + PackedStringArray split = new_name.split("_"); + int last_index = split.size() - 1; + if (last_index > 0 && split[last_index].is_valid_int() && split[last_index].to_int() >= 0) { + count = split[last_index].to_int(); + split.remove_at(last_index); + new_name = String("_").join(split); + } + while (true) { + String attempt = new_name; + attempt += vformat("_%d", count); + if (player->has_animation(attempt)) { + count++; + continue; + } + new_name = attempt; + break; } if (new_name.contains("/")) { @@ -1675,7 +1692,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug stop = memnew(Button); stop->set_flat(true); hb->add_child(stop); - stop->set_tooltip_text(TTR("Stop animation playback. (S)")); + stop->set_tooltip_text(TTR("Pause/stop animation playback. (S)")); play = memnew(Button); play->set_flat(true); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index c0972d201e..9632670658 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1127,7 +1127,7 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action connecting = false; } -void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance, bool p_multi_transitions) { +void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_multi_transitions) { Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label")); Color icon_color(1, 1, 1); Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); @@ -1138,7 +1138,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co accent.a *= 0.6; } - const Ref<Texture2D> icons[6] = { + const Ref<Texture2D> icons[] = { get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionSyncBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionEndBig"), SNAME("EditorIcons")), @@ -1146,6 +1146,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co get_theme_icon(SNAME("TransitionSyncAutoBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionEndAutoBig"), SNAME("EditorIcons")) }; + const int ICON_COUNT = sizeof(icons) / sizeof(*icons); if (p_selected) { state_machine_draw->draw_line(p_from, p_to, accent, 6); @@ -1153,12 +1154,18 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co if (p_travel) { linecolor = accent; - linecolor.set_hsv(1.0, linecolor.get_s(), linecolor.get_v()); } state_machine_draw->draw_line(p_from, p_to, linecolor, 2); - Ref<Texture2D> icon = icons[p_mode + (p_auto_advance ? 3 : 0)]; + if (p_fade_ratio > 0.0) { + Color fade_linecolor = accent; + fade_linecolor.set_hsv(1.0, fade_linecolor.get_s(), fade_linecolor.get_v()); + state_machine_draw->draw_line(p_from, p_from.lerp(p_to, p_fade_ratio), fade_linecolor, 2); + } + int icon_index = p_mode + (p_auto_advance ? ICON_COUNT / 2 : 0); + ERR_FAIL_COND(icon_index >= ICON_COUNT); + Ref<Texture2D> icon = icons[icon_index]; Transform2D xf; xf.columns[0] = (p_to - p_from).normalized(); @@ -1327,7 +1334,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } - _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, false, false); + _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, 0.0, false, false); } Ref<Texture2D> tr_reference_icon = get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")); @@ -1357,6 +1364,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { tl.mode = tr->get_switch_mode(); tl.width = tr_bidi_offset; tl.travel = false; + tl.fade_ratio = 0.0; tl.hidden = false; if (state_machine->has_local_transition(local_to, local_from)) { //offset if same exists @@ -1378,6 +1386,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { if (blend_from == local_from && current == local_to) { tl.travel = true; + tl.fade_ratio = MIN(1.0, fading_pos / fading_time); } if (travel_path.size()) { @@ -1418,7 +1427,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { for (int i = 0; i < transition_lines.size(); i++) { TransitionLine tl = transition_lines[i]; if (!tl.hidden) { - _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.auto_advance, !tl.multi_transitions.is_empty()); + _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, !tl.multi_transitions.is_empty()); } } @@ -1508,25 +1517,24 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { state_machine_play_pos->queue_redraw(); } -void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { +void AnimationNodeStateMachineEditor::_state_machine_pos_draw_individual(String p_name, float p_ratio) { AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); if (!tree) { return; } Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); - if (!playback.is_valid() || !playback->is_playing()) { return; } - if (playback->get_current_node() == state_machine->start_node || playback->get_current_node() == state_machine->end_node) { + if (p_name == state_machine->start_node || p_name == state_machine->end_node || p_name.is_empty()) { return; } int idx = -1; for (int i = 0; i < node_rects.size(); i++) { - if (node_rects[i].node_name == playback->get_current_node()) { + if (node_rects[i].node_name == p_name) { idx = i; break; } @@ -1550,10 +1558,7 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, current_length); - - float pos = CLAMP(play_pos, 0, len); - float c = pos / len; + float c = p_ratio; Color fg = get_theme_color(SNAME("font_color"), SNAME("Label")); Color bg = fg; bg.a *= 0.3; @@ -1565,6 +1570,32 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { state_machine_play_pos->draw_line(from, to, fg, 2); } +void AnimationNodeStateMachineEditor::_state_machine_pos_draw_all() { + AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); + if (!tree) { + return; + } + + Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (!playback.is_valid() || !playback->is_playing()) { + return; + } + + { + float len = MAX(0.0001, current_length); + float pos = CLAMP(current_play_pos, 0, len); + float c = pos / len; + _state_machine_pos_draw_individual(playback->get_current_node(), c); + } + + { + float len = MAX(0.0001, fade_from_length); + float pos = CLAMP(fade_from_current_play_pos, 0, len); + float c = pos / len; + _state_machine_pos_draw_individual(playback->get_fading_from_node(), c); + } +} + void AnimationNodeStateMachineEditor::_update_graph() { if (updating) { return; @@ -1687,17 +1718,28 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { Vector<StringName> tp; bool is_playing = false; StringName current_node; - StringName blend_from_node; - play_pos = 0; + StringName fading_from_node; + + current_play_pos = 0; current_length = 0; + fade_from_current_play_pos = 0; + fade_from_length = 0; + + fading_time = 0; + fading_pos = 0; + if (playback.is_valid()) { tp = playback->get_travel_path(); is_playing = playback->is_playing(); current_node = playback->get_current_node(); - blend_from_node = playback->get_fading_from_node(); - play_pos = playback->get_current_play_pos(); + fading_from_node = playback->get_fading_from_node(); + current_play_pos = playback->get_current_play_pos(); current_length = playback->get_current_length(); + fade_from_current_play_pos = playback->get_fade_from_play_pos(); + fade_from_length = playback->get_fade_from_length(); + fading_time = playback->get_fading_time(); + fading_pos = playback->get_fading_pos(); } { @@ -1714,12 +1756,19 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { } //redraw if travel state changed - if (!same_travel_path || last_active != is_playing || last_current_node != current_node || last_blend_from_node != blend_from_node) { + if (!same_travel_path || + last_active != is_playing || + last_current_node != current_node || + last_fading_from_node != fading_from_node || + last_fading_time != fading_time || + last_fading_pos != fading_pos) { state_machine_draw->queue_redraw(); last_travel_path = tp; last_current_node = current_node; last_active = is_playing; - last_blend_from_node = blend_from_node; + last_fading_from_node = fading_from_node; + last_fading_time = fading_time; + last_fading_pos = fading_pos; state_machine_play_pos->queue_redraw(); } @@ -1737,14 +1786,15 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { // when current_node is a state machine, use playback of current_node to set play_pos if (current_node_playback.is_valid()) { - play_pos = current_node_playback->get_current_play_pos(); + current_play_pos = current_node_playback->get_current_play_pos(); current_length = current_node_playback->get_current_length(); } } } - if (last_play_pos != play_pos) { - last_play_pos = play_pos; + if (last_play_pos != current_play_pos || fade_from_last_play_pos != fade_from_current_play_pos) { + last_play_pos = current_play_pos; + fade_from_last_play_pos = fade_from_current_play_pos; state_machine_play_pos->queue_redraw(); } } break; @@ -2048,7 +2098,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { state_machine_draw->add_child(state_machine_play_pos); state_machine_play_pos->set_mouse_filter(MOUSE_FILTER_PASS); //pass all to parent state_machine_play_pos->set_anchors_and_offsets_preset(PRESET_FULL_RECT); - state_machine_play_pos->connect("draw", callable_mp(this, &AnimationNodeStateMachineEditor::_state_machine_pos_draw)); + state_machine_play_pos->connect("draw", callable_mp(this, &AnimationNodeStateMachineEditor::_state_machine_pos_draw_all)); v_scroll = memnew(VScrollBar); state_machine_draw->add_child(v_scroll); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 46fe13ccc1..66338c820e 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -85,9 +85,12 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { static AnimationNodeStateMachineEditor *singleton; void _state_machine_gui_input(const Ref<InputEvent> &p_event); - void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance, bool p_multi_transitions); + void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_multi_transitions); + void _state_machine_draw(); - void _state_machine_pos_draw(); + + void _state_machine_pos_draw_individual(String p_name, float p_ratio); + void _state_machine_pos_draw_all(); void _update_graph(); @@ -150,6 +153,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { float width = 0; bool selected; bool travel; + float fade_ratio; bool hidden; int transition_index; Vector<TransitionLine> multi_transitions; @@ -204,13 +208,23 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { void _delete_tree_draw(); bool last_active = false; - StringName last_blend_from_node; + StringName last_fading_from_node; StringName last_current_node; Vector<StringName> last_travel_path; + + float fade_from_last_play_pos = 0.0f; + float fade_from_current_play_pos = 0.0f; + float fade_from_length = 0.0f; + float last_play_pos = 0.0f; - float play_pos = 0.0f; + float current_play_pos = 0.0f; float current_length = 0.0f; + float last_fading_time = 0.0f; + float last_fading_pos = 0.0f; + float fading_time = 0.0f; + float fading_pos = 0.0f; + float error_time = 0.0f; String error_text; diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 720deb0b92..ab46e8f04a 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -65,13 +65,14 @@ void AnimationTreeEditor::edit(AnimationTree *p_tree) { tree = p_tree; Vector<String> path; - if (tree && tree->has_meta("_tree_edit_path")) { - path = tree->get_meta("_tree_edit_path"); - } else { - current_root = ObjectID(); + if (tree) { + if (tree->has_meta("_tree_edit_path")) { + path = tree->get_meta("_tree_edit_path"); + } else { + current_root = ObjectID(); + } + edit_path(path); } - - edit_path(path); } void AnimationTreeEditor::_node_removed(Node *p_node) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 5d0555a10e..0f9ce89f02 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1259,57 +1259,25 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo } } - Ref<InputEventMagnifyGesture> magnify_gesture = p_event; - if (magnify_gesture.is_valid() && !p_already_accepted) { - // Zoom gesture - _zoom_on_position(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); - return true; - } - - Ref<InputEventPanGesture> pan_gesture = p_event; - if (pan_gesture.is_valid() && !p_already_accepted) { - // If ctrl key pressed, then zoom instead of pan. - if (pan_gesture->is_ctrl_pressed()) { - const real_t factor = pan_gesture->get_delta().y; - - zoom_widget->set_zoom_by_increments(1); - if (factor != 1.f) { - zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * factor + 1.f)); - } - _zoom_on_position(zoom_widget->get_zoom(), pan_gesture->get_position()); - - return true; - } - - // Pan gesture - const Vector2 delta = (pan_speed / zoom) * pan_gesture->get_delta(); - view_offset.x += delta.x; - view_offset.y += delta.y; - update_viewport(); - return true; - } - return false; } -void CanvasItemEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * pan_speed); -} - -void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec) { +void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { view_offset.x -= p_scroll_vec.x / zoom; view_offset.y -= p_scroll_vec.y / zoom; update_viewport(); } -void CanvasItemEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - int scroll_sign = (int)SIGN(p_scroll_vec.y); - // Inverted so that scrolling up (-1) zooms in, scrolling down (+1) zooms out. - zoom_widget->set_zoom_by_increments(-scroll_sign, p_alt); - if (!Math::is_equal_approx(ABS(p_scroll_vec.y), (real_t)1.0)) { - // Handle high-precision (analog) scrolling. - zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * ABS(p_scroll_vec.y) + 1.f)); +void CanvasItemEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + // Special behvior for scroll events, as the zoom_by_increment method can smartly end up on powers of two. + int increment = p_zoom_factor > 1.0 ? 1 : -1; + zoom_widget->set_zoom_by_increments(increment, mb->is_alt_pressed()); + } else { + zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor); } + _zoom_on_position(zoom_widget->get_zoom(), p_origin); } @@ -3868,7 +3836,7 @@ void CanvasItemEditor::_update_editor_settings() { context_menu_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles"))); panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning"))); - pan_speed = int(EDITOR_GET("editors/panning/2d_editor_pan_speed")); + panner->set_scroll_speed(EDITOR_GET("editors/panning/2d_editor_pan_speed")); warped_panning = bool(EDITOR_GET("editors/panning/warped_mouse_panning")); } @@ -5059,7 +5027,7 @@ CanvasItemEditor::CanvasItemEditor() { zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &CanvasItemEditor::_scroll_callback), callable_mp(this, &CanvasItemEditor::_pan_callback), callable_mp(this, &CanvasItemEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &CanvasItemEditor::_pan_callback), callable_mp(this, &CanvasItemEditor::_zoom_callback)); viewport = memnew(CanvasItemEditorViewport(this)); viewport_scrollable->add_child(viewport); @@ -5433,11 +5401,13 @@ void CanvasItemEditorPlugin::make_visible(bool p_visible) { canvas_item_editor->show(); canvas_item_editor->set_physics_process(true); RenderingServer::get_singleton()->viewport_set_disable_2d(EditorNode::get_singleton()->get_scene_root()->get_viewport_rid(), false); + RenderingServer::get_singleton()->viewport_set_environment_mode(EditorNode::get_singleton()->get_scene_root()->get_viewport_rid(), RS::VIEWPORT_ENVIRONMENT_ENABLED); } else { canvas_item_editor->hide(); canvas_item_editor->set_physics_process(false); RenderingServer::get_singleton()->viewport_set_disable_2d(EditorNode::get_singleton()->get_scene_root()->get_viewport_rid(), true); + RenderingServer::get_singleton()->viewport_set_environment_mode(EditorNode::get_singleton()->get_scene_root()->get_viewport_rid(), RS::VIEWPORT_ENVIRONMENT_DISABLED); } } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 1e01eac82d..ebe87a56f7 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -363,10 +363,8 @@ private: Ref<ViewPanner> panner; bool warped_panning = true; - int pan_speed = 20; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); bool _is_node_locked(const Node *p_node) const; bool _is_node_movable(const Node *p_node, bool p_popup_warning = false); diff --git a/editor/plugins/navigation_link_2d_editor_plugin.cpp b/editor/plugins/navigation_link_2d_editor_plugin.cpp index 21a1d839f0..dff92ced27 100644 --- a/editor/plugins/navigation_link_2d_editor_plugin.cpp +++ b/editor/plugins/navigation_link_2d_editor_plugin.cpp @@ -65,20 +65,20 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { - // Start location - if (xform.xform(node->get_start_location()).distance_to(mb->get_position()) < grab_threshold) { + // Start position + if (xform.xform(node->get_start_position()).distance_to(mb->get_position()) < grab_threshold) { start_grabbed = true; - original_start_location = node->get_start_location(); + original_start_position = node->get_start_position(); return true; } else { start_grabbed = false; } - // End location - if (xform.xform(node->get_end_location()).distance_to(mb->get_position()) < grab_threshold) { + // End position + if (xform.xform(node->get_end_position()).distance_to(mb->get_position()) < grab_threshold) { end_grabbed = true; - original_end_location = node->get_end_location(); + original_end_position = node->get_end_position(); return true; } else { @@ -87,10 +87,10 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e } else { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (start_grabbed) { - undo_redo->create_action(TTR("Set start_location")); - undo_redo->add_do_method(node, "set_start_location", node->get_start_location()); + undo_redo->create_action(TTR("Set start_position")); + undo_redo->add_do_method(node, "set_start_position", node->get_start_position()); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(node, "set_start_location", original_start_location); + undo_redo->add_undo_method(node, "set_start_position", original_start_position); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); @@ -100,10 +100,10 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e } if (end_grabbed) { - undo_redo->create_action(TTR("Set end_location")); - undo_redo->add_do_method(node, "set_end_location", node->get_end_location()); + undo_redo->create_action(TTR("Set end_position")); + undo_redo->add_do_method(node, "set_end_position", node->get_end_position()); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(node, "set_end_location", original_end_location); + undo_redo->add_undo_method(node, "set_end_position", original_end_position); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); @@ -120,14 +120,14 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e point = node->get_global_transform().affine_inverse().xform(point); if (start_grabbed) { - node->set_start_location(point); + node->set_start_position(point); canvas_item_editor->update_viewport(); return true; } if (end_grabbed) { - node->set_end_location(point); + node->set_end_position(point); canvas_item_editor->update_viewport(); return true; @@ -143,13 +143,13 @@ void NavigationLink2DEditor::forward_canvas_draw_over_viewport(Control *p_overla } Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); - Vector2 global_start_location = gt.xform(node->get_start_location()); - Vector2 global_end_location = gt.xform(node->get_end_location()); + Vector2 global_start_position = gt.xform(node->get_start_position()); + Vector2 global_end_position = gt.xform(node->get_end_position()); // Only drawing the handles here, since the debug rendering will fill in the rest. const Ref<Texture2D> handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); - p_overlay->draw_texture(handle, global_start_location - handle->get_size() / 2); - p_overlay->draw_texture(handle, global_end_location - handle->get_size() / 2); + p_overlay->draw_texture(handle, global_start_position - handle->get_size() / 2); + p_overlay->draw_texture(handle, global_end_position - handle->get_size() / 2); } void NavigationLink2DEditor::edit(NavigationLink2D *p_node) { diff --git a/editor/plugins/navigation_link_2d_editor_plugin.h b/editor/plugins/navigation_link_2d_editor_plugin.h index 76444403d0..ea731ca2cd 100644 --- a/editor/plugins/navigation_link_2d_editor_plugin.h +++ b/editor/plugins/navigation_link_2d_editor_plugin.h @@ -43,10 +43,10 @@ class NavigationLink2DEditor : public Control { NavigationLink2D *node = nullptr; bool start_grabbed = false; - Vector2 original_start_location; + Vector2 original_start_position; bool end_grabbed = false; - Vector2 original_end_location; + Vector2 original_end_position; protected: void _notification(int p_what); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index bb71c27bff..e11dc1c81d 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -5057,8 +5057,8 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3 up_vector = NavigationServer3D::get_singleton()->map_get_up(nav_map); Vector3::Axis up_axis = up_vector.max_axis_index(); - Vector3 start_location = link->get_start_location(); - Vector3 end_location = link->get_end_location(); + Vector3 start_position = link->get_start_position(); + Vector3 end_position = link->get_end_position(); Ref<Material> link_material = get_material("navigation_link_material", p_gizmo); Ref<Material> link_material_disabled = get_material("navigation_link_material_disabled", p_gizmo); @@ -5068,10 +5068,10 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw line between the points. Vector<Vector3> lines; - lines.append(start_location); - lines.append(end_location); + lines.append(start_position); + lines.append(end_position); - // Draw start location search radius + // Draw start position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -5082,21 +5082,21 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(start_location + Vector3(0, a.x, a.y)); - lines.append(start_location + Vector3(0, b.x, b.y)); + lines.append(start_position + Vector3(0, a.x, a.y)); + lines.append(start_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(start_location + Vector3(a.x, 0, a.y)); - lines.append(start_location + Vector3(b.x, 0, b.y)); + lines.append(start_position + Vector3(a.x, 0, a.y)); + lines.append(start_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(start_location + Vector3(a.x, a.y, 0)); - lines.append(start_location + Vector3(b.x, b.y, 0)); + lines.append(start_position + Vector3(a.x, a.y, 0)); + lines.append(start_position + Vector3(b.x, b.y, 0)); break; } } - // Draw end location search radius + // Draw end position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -5107,16 +5107,16 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(end_location + Vector3(0, a.x, a.y)); - lines.append(end_location + Vector3(0, b.x, b.y)); + lines.append(end_position + Vector3(0, a.x, a.y)); + lines.append(end_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(end_location + Vector3(a.x, 0, a.y)); - lines.append(end_location + Vector3(b.x, 0, b.y)); + lines.append(end_position + Vector3(a.x, 0, a.y)); + lines.append(end_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(end_location + Vector3(a.x, a.y, 0)); - lines.append(end_location + Vector3(b.x, b.y, 0)); + lines.append(end_position + Vector3(a.x, a.y, 0)); + lines.append(end_position + Vector3(b.x, b.y, 0)); break; } } @@ -5125,8 +5125,8 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->add_collision_segments(lines); Vector<Vector3> handles; - handles.append(start_location); - handles.append(end_location); + handles.append(start_position); + handles.append(end_position); p_gizmo->add_handles(handles, handles_material); } @@ -5136,7 +5136,7 @@ String NavigationLink3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g Variant NavigationLink3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { NavigationLink3D *link = Object::cast_to<NavigationLink3D>(p_gizmo->get_node_3d()); - return p_id == 0 ? link->get_start_location() : link->get_end_location(); + return p_id == 0 ? link->get_start_position() : link->get_end_position(); } void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { @@ -5151,8 +5151,8 @@ void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i Vector3 ray_from = p_camera->project_ray_origin(p_point); Vector3 ray_dir = p_camera->project_ray_normal(p_point); - Vector3 location = p_id == 0 ? link->get_start_location() : link->get_end_location(); - Plane move_plane = Plane(cam_dir, gt.xform(location)); + Vector3 position = p_id == 0 ? link->get_start_position() : link->get_end_position(); + Plane move_plane = Plane(cam_dir, gt.xform(position)); Vector3 intersection; if (!move_plane.intersects_ray(ray_from, ray_dir, &intersection)) { @@ -5164,11 +5164,11 @@ void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i intersection.snap(Vector3(snap, snap, snap)); } - location = gi.xform(intersection); + position = gi.xform(intersection); if (p_id == 0) { - link->set_start_location(location); + link->set_start_position(position); } else if (p_id == 1) { - link->set_end_location(location); + link->set_end_position(position); } } @@ -5177,22 +5177,22 @@ void NavigationLink3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo if (p_cancel) { if (p_id == 0) { - link->set_start_location(p_restore); + link->set_start_position(p_restore); } else { - link->set_end_location(p_restore); + link->set_end_position(p_restore); } return; } EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); if (p_id == 0) { - ur->create_action(TTR("Change Start Location")); - ur->add_do_method(link, "set_start_location", link->get_start_location()); - ur->add_undo_method(link, "set_start_location", p_restore); + ur->create_action(TTR("Change Start Position")); + ur->add_do_method(link, "set_start_position", link->get_start_position()); + ur->add_undo_method(link, "set_start_position", p_restore); } else { - ur->create_action(TTR("Change End Location")); - ur->add_do_method(link, "set_end_location", link->get_end_location()); - ur->add_undo_method(link, "set_end_location", p_restore); + ur->create_action(TTR("Change End Position")); + ur->add_do_method(link, "set_end_position", link->get_end_position()); + ur->add_undo_method(link, "set_end_position", p_restore); } ur->commit_action(); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index f1b7ed73b8..36d1e54246 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1414,7 +1414,7 @@ Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const // Recalculate orthogonalized scale without moving origin. if (p_orthogonal) { - s.basis = p_original_local.basis.scaled_orthogonal(p_motion + Vector3(1, 1, 1)); + s.basis = p_original.basis.scaled_orthogonal(p_motion + Vector3(1, 1, 1)); // The scaled_orthogonal() does not require orthogonal Basis, // but it may make a bit skew by precision problems. s.basis.orthogonalize(); @@ -4611,7 +4611,9 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { // TRANSLATORS: Refers to changing the scale of a node in the 3D editor. set_message(TTR("Scaling:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " + String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); - motion = _edit.original.basis.inverse().xform(motion); + if (local_coords) { + motion = _edit.original.basis.inverse().xform(motion); + } List<Node *> &selection = editor_selection->get_selected_node_list(); for (Node *E : selection) { @@ -4639,7 +4641,7 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { se->gizmo->set_subgizmo_transform(GE.key, new_xform); } } else { - Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS); + Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS && _edit.plane != TRANSFORM_VIEW); _transform_gizmo_apply(se->sp, new_xform, local_coords); } } @@ -4712,7 +4714,9 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { // TRANSLATORS: Refers to changing the position of a node in the 3D editor. set_message(TTR("Translating:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " + String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); - motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion); + if (local_coords) { + motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion); + } List<Node *> &selection = editor_selection->get_selected_node_list(); for (Node *E : selection) { diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index c30f0ec62d..fb35668310 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -939,21 +939,13 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } -void Polygon2DEditor::_uv_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _uv_pan_callback(-p_scroll_vec * 32); -} - -void Polygon2DEditor::_uv_pan_callback(Vector2 p_scroll_vec) { +void Polygon2DEditor::_uv_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { uv_hscroll->set_value(uv_hscroll->get_value() - p_scroll_vec.x); uv_vscroll->set_value(uv_vscroll->get_value() - p_scroll_vec.y); } -void Polygon2DEditor::_uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - if (p_scroll_vec.y < 0) { - uv_zoom->set_value(uv_zoom->get_value() / (1 - (0.1 * Math::abs(p_scroll_vec.y)))); - } else { - uv_zoom->set_value(uv_zoom->get_value() * (1 - (0.1 * Math::abs(p_scroll_vec.y)))); - } +void Polygon2DEditor::_uv_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + uv_zoom->set_value(uv_zoom->get_value() * p_zoom_factor); } void Polygon2DEditor::_uv_scroll_changed(real_t) { @@ -1478,7 +1470,7 @@ Polygon2DEditor::Polygon2DEditor() { bone_scroll->add_child(bone_scroll_vb); uv_panner.instantiate(); - uv_panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_uv_scroll_callback), callable_mp(this, &Polygon2DEditor::_uv_pan_callback), callable_mp(this, &Polygon2DEditor::_uv_zoom_callback)); + uv_panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_uv_pan_callback), callable_mp(this, &Polygon2DEditor::_uv_zoom_callback)); uv_edit_draw->connect("draw", callable_mp(this, &Polygon2DEditor::_uv_draw)); uv_edit_draw->connect("gui_input", callable_mp(this, &Polygon2DEditor::_uv_input)); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 7246c08bea..2c55a5f631 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -90,9 +90,8 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { TextureRect *uv_icon_zoom = nullptr; Ref<ViewPanner> uv_panner; - void _uv_scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _uv_pan_callback(Vector2 p_scroll_vec); - void _uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _uv_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _uv_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); VBoxContainer *bone_scroll_main_vb = nullptr; ScrollContainer *bone_scroll = nullptr; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index b719a2ce30..6b4e7184d9 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -252,12 +252,14 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { } else if (p_line.get_type() == Variant::DICTIONARY) { Dictionary meta = p_line.operator Dictionary(); const int line = meta["line"].operator int64_t() - 1; + const String code = meta["code"].operator String(); + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; CodeEdit *text_editor = code_editor->get_text_editor(); String prev_line = line > 0 ? text_editor->get_line(line - 1) : ""; if (prev_line.contains("@warning_ignore")) { const int closing_bracket_idx = prev_line.find(")"); - const String text_to_insert = ", " + meta["code"].operator String(); + const String text_to_insert = ", " + code.quote(quote_style); prev_line = prev_line.insert(closing_bracket_idx, text_to_insert); text_editor->set_line(line - 1, prev_line); } else { @@ -268,7 +270,7 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { } else { annotation_indent = String(" ").repeat(text_editor->get_indent_size() * indent); } - text_editor->insert_line_at(line, annotation_indent + "@warning_ignore(" + meta["code"].operator String() + ")"); + text_editor->insert_line_at(line, annotation_indent + "@warning_ignore(" + code.quote(quote_style) + ")"); } _validate_script(); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 74c9286325..9df2f0a9d1 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -40,7 +40,6 @@ #include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/scene_tree_dock.h" -#include "scene/3d/sprite_3d.h" #include "scene/gui/center_container.h" #include "scene/gui/margin_container.h" #include "scene/gui/panel_container.h" @@ -252,8 +251,7 @@ void SpriteFramesEditor::_sheet_add_frames() { const Size2i separation = _get_separation(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Add Frame")); - + undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); int fc = frames->get_frame_count(edited_anim); for (const int &E : frames_selected) { @@ -265,8 +263,8 @@ void SpriteFramesEditor::_sheet_add_frames() { at->set_atlas(split_sheet_preview->get_texture()); at->set_region(Rect2(offset + frame_coords * (frame_size + separation), frame_size)); - undo_redo->add_do_method(frames, "add_frame", edited_anim, at, 1.0, -1); - undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc); + undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, at, 1.0, -1); + undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, fc); } undo_redo->add_do_method(this, "_update_library"); @@ -415,8 +413,24 @@ void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { void SpriteFramesEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_ENTER_TREE: { + get_tree()->connect("node_removed", callable_mp(this, &SpriteFramesEditor::_node_removed)); + + [[fallthrough]]; + } case NOTIFICATION_THEME_CHANGED: { + autoplay_icon = get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons")); + stop_icon = get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")); + pause_icon = get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")); + _update_stop_icon(); + + autoplay->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons"))); + anim_loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons"))); + play->set_icon(get_theme_icon(SNAME("PlayStart"), SNAME("EditorIcons"))); + play_from->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); + play_bw->set_icon(get_theme_icon(SNAME("PlayStartBackwards"), SNAME("EditorIcons"))); + play_bw_from->set_icon(get_theme_icon(SNAME("PlayBackwards"), SNAME("EditorIcons"))); + load->set_icon(get_theme_icon(SNAME("Load"), SNAME("EditorIcons"))); load_sheet->set_icon(get_theme_icon(SNAME("SpriteSheet"), SNAME("EditorIcons"))); copy->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); @@ -441,6 +455,10 @@ void SpriteFramesEditor::_notification(int p_what) { case NOTIFICATION_READY: { add_theme_constant_override("autohide", 1); // Fixes the dragger always showing up. } break; + + case NOTIFICATION_EXIT_TREE: { + get_tree()->disconnect("node_removed", callable_mp(this, &SpriteFramesEditor::_node_removed)); + } break; } } @@ -471,14 +489,14 @@ void SpriteFramesEditor::_file_load_request(const Vector<String> &p_path, int p_ } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Add Frame")); + undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); int fc = frames->get_frame_count(edited_anim); int count = 0; for (const Ref<Texture2D> &E : resources) { - undo_redo->add_do_method(frames, "add_frame", edited_anim, E, 1.0, p_at_pos == -1 ? -1 : p_at_pos + count); - undo_redo->add_undo_method(frames, "remove_frame", edited_anim, p_at_pos == -1 ? fc : p_at_pos); + undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, E, 1.0, p_at_pos == -1 ? -1 : p_at_pos + count); + undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, p_at_pos == -1 ? fc : p_at_pos); count++; } undo_redo->add_do_method(this, "_update_library"); @@ -542,9 +560,9 @@ void SpriteFramesEditor::_paste_pressed() { } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Paste Frame")); - undo_redo->add_do_method(frames, "add_frame", edited_anim, texture, duration); - undo_redo->add_undo_method(frames, "remove_frame", edited_anim, frames->get_frame_count(edited_anim)); + undo_redo->create_action(TTR("Paste Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, duration); + undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, frames->get_frame_count(edited_anim)); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); @@ -585,9 +603,9 @@ void SpriteFramesEditor::_empty_pressed() { Ref<Texture2D> texture; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Add Empty")); - undo_redo->add_do_method(frames, "add_frame", edited_anim, texture, 1.0, from); - undo_redo->add_undo_method(frames, "remove_frame", edited_anim, from); + undo_redo->create_action(TTR("Add Empty"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, 1.0, from); + undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, from); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); @@ -609,9 +627,9 @@ void SpriteFramesEditor::_empty2_pressed() { Ref<Texture2D> texture; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Add Empty")); - undo_redo->add_do_method(frames, "add_frame", edited_anim, texture, 1.0, from + 1); - undo_redo->add_undo_method(frames, "remove_frame", edited_anim, from + 1); + undo_redo->create_action(TTR("Add Empty"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, 1.0, from + 1); + undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, from + 1); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); @@ -633,11 +651,11 @@ void SpriteFramesEditor::_up_pressed() { sel -= 1; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Move Frame")); - undo_redo->add_do_method(frames, "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move - 1), frames->get_frame_duration(edited_anim, to_move - 1)); - undo_redo->add_do_method(frames, "set_frame", edited_anim, to_move - 1, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move)); - undo_redo->add_undo_method(frames, "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move)); - undo_redo->add_undo_method(frames, "set_frame", edited_anim, to_move - 1, frames->get_frame_texture(edited_anim, to_move - 1), frames->get_frame_duration(edited_anim, to_move - 1)); + undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move - 1), frames->get_frame_duration(edited_anim, to_move - 1)); + undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move - 1, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move)); + undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move)); + undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move - 1, frames->get_frame_texture(edited_anim, to_move - 1), frames->get_frame_duration(edited_anim, to_move - 1)); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); @@ -659,11 +677,11 @@ void SpriteFramesEditor::_down_pressed() { sel += 1; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Move Frame")); - undo_redo->add_do_method(frames, "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move + 1), frames->get_frame_duration(edited_anim, to_move + 1)); - undo_redo->add_do_method(frames, "set_frame", edited_anim, to_move + 1, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move)); - undo_redo->add_undo_method(frames, "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move)); - undo_redo->add_undo_method(frames, "set_frame", edited_anim, to_move + 1, frames->get_frame_texture(edited_anim, to_move + 1), frames->get_frame_duration(edited_anim, to_move + 1)); + undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move + 1), frames->get_frame_duration(edited_anim, to_move + 1)); + undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, to_move + 1, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move)); + undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move, frames->get_frame_texture(edited_anim, to_move), frames->get_frame_duration(edited_anim, to_move)); + undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, to_move + 1, frames->get_frame_texture(edited_anim, to_move + 1), frames->get_frame_duration(edited_anim, to_move + 1)); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); @@ -682,15 +700,15 @@ void SpriteFramesEditor::_delete_pressed() { } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Delete Resource")); - undo_redo->add_do_method(frames, "remove_frame", edited_anim, to_delete); - undo_redo->add_undo_method(frames, "add_frame", edited_anim, frames->get_frame_texture(edited_anim, to_delete), frames->get_frame_duration(edited_anim, to_delete), to_delete); + undo_redo->create_action(TTR("Delete Resource"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "remove_frame", edited_anim, to_delete); + undo_redo->add_undo_method(frames.ptr(), "add_frame", edited_anim, frames->get_frame_texture(edited_anim, to_delete), frames->get_frame_duration(edited_anim, to_delete), to_delete); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); } -void SpriteFramesEditor::_animation_select() { +void SpriteFramesEditor::_animation_selected() { if (updating) { return; } @@ -705,9 +723,42 @@ void SpriteFramesEditor::_animation_select() { TreeItem *selected = animations->get_selected(); ERR_FAIL_COND(!selected); edited_anim = selected->get_text(0); + + if (animated_sprite) { + sprite_node_updating = true; + animated_sprite->call("set_animation", edited_anim); + sprite_node_updating = false; + } + _update_library(true); } +void SpriteFramesEditor::_sync_animation() { + if (!animated_sprite || sprite_node_updating) { + return; + } + _select_animation(animated_sprite->call("get_animation"), false); + _update_stop_icon(); +} + +void SpriteFramesEditor::_select_animation(const String &p_name, bool p_update_node) { + TreeItem *selected = nullptr; + selected = animations->get_item_with_text(p_name); + if (!selected) { + return; + }; + + edited_anim = selected->get_text(0); + + if (animated_sprite) { + if (p_update_node) { + animated_sprite->call("set_animation", edited_anim); + } + } + + _update_library(); +} + static void _find_anim_sprites(Node *p_node, List<Node *> *r_nodes, Ref<SpriteFrames> p_sfames) { Node *edited = EditorNode::get_singleton()->get_edited_scene(); if (!edited) { @@ -765,26 +816,48 @@ void SpriteFramesEditor::_animation_name_edited() { name = new_name + " " + itos(counter); } - List<Node *> nodes; - _find_anim_sprites(EditorNode::get_singleton()->get_edited_scene(), &nodes, Ref<SpriteFrames>(frames)); - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Rename Animation")); - undo_redo->add_do_method(frames, "rename_animation", edited_anim, name); - undo_redo->add_undo_method(frames, "rename_animation", name, edited_anim); - - for (Node *E : nodes) { - String current = E->call("get_animation"); - undo_redo->add_do_method(E, "set_animation", name); - undo_redo->add_undo_method(E, "set_animation", edited_anim); - } - + undo_redo->create_action(TTR("Rename Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + _rename_node_animation(undo_redo, false, edited_anim, "", ""); + undo_redo->add_do_method(frames.ptr(), "rename_animation", edited_anim, name); + undo_redo->add_undo_method(frames.ptr(), "rename_animation", name, edited_anim); + _rename_node_animation(undo_redo, false, edited_anim, name, name); + _rename_node_animation(undo_redo, true, edited_anim, edited_anim, edited_anim); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); + undo_redo->commit_action(); + + _select_animation(name); + animations->grab_focus(); +} - edited_anim = name; +void SpriteFramesEditor::_rename_node_animation(EditorUndoRedoManager *undo_redo, bool is_undo, const String &p_filter, const String &p_new_animation, const String &p_new_autoplay) { + List<Node *> nodes; + _find_anim_sprites(EditorNode::get_singleton()->get_edited_scene(), &nodes, Ref<SpriteFrames>(frames)); - undo_redo->commit_action(); + if (is_undo) { + for (Node *E : nodes) { + String current_name = E->call("get_animation"); + if (current_name == p_filter) { + undo_redo->add_undo_method(E, "set_animation", p_new_animation); + } + String autoplay_name = E->call("get_autoplay"); + if (autoplay_name == p_filter) { + undo_redo->add_undo_method(E, "set_autoplay", p_new_autoplay); + } + } + } else { + for (Node *E : nodes) { + String current_name = E->call("get_animation"); + if (current_name == p_filter) { + undo_redo->add_do_method(E, "set_animation", p_new_animation); + } + String autoplay_name = E->call("get_autoplay"); + if (autoplay_name == p_filter) { + undo_redo->add_do_method(E, "set_autoplay", p_new_autoplay); + } + } + } } void SpriteFramesEditor::_animation_add() { @@ -795,25 +868,15 @@ void SpriteFramesEditor::_animation_add() { name = vformat("new_animation_%d", counter); } - List<Node *> nodes; - _find_anim_sprites(EditorNode::get_singleton()->get_edited_scene(), &nodes, Ref<SpriteFrames>(frames)); - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Add Animation")); - undo_redo->add_do_method(frames, "add_animation", name); - undo_redo->add_undo_method(frames, "remove_animation", name); + undo_redo->create_action(TTR("Add Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "add_animation", name); + undo_redo->add_undo_method(frames.ptr(), "remove_animation", name); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); - - for (Node *E : nodes) { - String current = E->call("get_animation"); - undo_redo->add_do_method(E, "set_animation", name); - undo_redo->add_undo_method(E, "set_animation", current); - } - - edited_anim = name; - undo_redo->commit_action(); + + _select_animation(name); animations->grab_focus(); } @@ -831,24 +894,39 @@ void SpriteFramesEditor::_animation_remove() { } void SpriteFramesEditor::_animation_remove_confirmed() { + StringName new_edited; + List<StringName> anim_names; + frames->get_animation_list(&anim_names); + anim_names.sort_custom<StringName::AlphCompare>(); + if (anim_names.size() >= 2) { + if (edited_anim == anim_names[0]) { + new_edited = anim_names[1]; + } else { + new_edited = anim_names[0]; + } + } else { + new_edited = StringName(); + } + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Remove Animation")); - undo_redo->add_do_method(frames, "remove_animation", edited_anim); - undo_redo->add_undo_method(frames, "add_animation", edited_anim); - undo_redo->add_undo_method(frames, "set_animation_speed", edited_anim, frames->get_animation_speed(edited_anim)); - undo_redo->add_undo_method(frames, "set_animation_loop", edited_anim, frames->get_animation_loop(edited_anim)); + undo_redo->create_action(TTR("Remove Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + _rename_node_animation(undo_redo, false, edited_anim, new_edited, ""); + undo_redo->add_do_method(frames.ptr(), "remove_animation", edited_anim); + undo_redo->add_undo_method(frames.ptr(), "add_animation", edited_anim); + _rename_node_animation(undo_redo, true, edited_anim, edited_anim, edited_anim); + undo_redo->add_undo_method(frames.ptr(), "set_animation_speed", edited_anim, frames->get_animation_speed(edited_anim)); + undo_redo->add_undo_method(frames.ptr(), "set_animation_loop", edited_anim, frames->get_animation_loop(edited_anim)); int fc = frames->get_frame_count(edited_anim); for (int i = 0; i < fc; i++) { Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, i); float duration = frames->get_frame_duration(edited_anim, i); - undo_redo->add_undo_method(frames, "add_frame", edited_anim, texture, duration); + undo_redo->add_undo_method(frames.ptr(), "add_frame", edited_anim, texture, duration); } undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); - - edited_anim = StringName(); - undo_redo->commit_action(); + + _select_animation(new_edited); } void SpriteFramesEditor::_animation_search_text_changed(const String &p_text) { @@ -861,9 +939,9 @@ void SpriteFramesEditor::_animation_loop_changed() { } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Change Animation Loop")); - undo_redo->add_do_method(frames, "set_animation_loop", edited_anim, anim_loop->is_pressed()); - undo_redo->add_undo_method(frames, "set_animation_loop", edited_anim, frames->get_animation_loop(edited_anim)); + undo_redo->create_action(TTR("Change Animation Loop"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "set_animation_loop", edited_anim, anim_loop->is_pressed()); + undo_redo->add_undo_method(frames.ptr(), "set_animation_loop", edited_anim, frames->get_animation_loop(edited_anim)); undo_redo->add_do_method(this, "_update_library", true); undo_redo->add_undo_method(this, "_update_library", true); undo_redo->commit_action(); @@ -875,9 +953,9 @@ void SpriteFramesEditor::_animation_speed_changed(double p_value) { } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Change Animation FPS"), UndoRedo::MERGE_ENDS); - undo_redo->add_do_method(frames, "set_animation_speed", edited_anim, p_value); - undo_redo->add_undo_method(frames, "set_animation_speed", edited_anim, frames->get_animation_speed(edited_anim)); + undo_redo->create_action(TTR("Change Animation FPS"), UndoRedo::MERGE_ENDS, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "set_animation_speed", edited_anim, p_value); + undo_redo->add_undo_method(frames.ptr(), "set_animation_speed", edited_anim, frames->get_animation_speed(edited_anim)); undo_redo->add_do_method(this, "_update_library", true); undo_redo->add_undo_method(this, "_update_library", true); undo_redo->commit_action(); @@ -927,9 +1005,9 @@ void SpriteFramesEditor::_frame_duration_changed(double p_value) { float old_duration = frames->get_frame_duration(edited_anim, index); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Set Frame Duration")); - undo_redo->add_do_method(frames, "set_frame", edited_anim, index, texture, p_value); - undo_redo->add_undo_method(frames, "set_frame", edited_anim, index, texture, old_duration); + undo_redo->create_action(TTR("Set Frame Duration"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "set_frame", edited_anim, index, texture, p_value); + undo_redo->add_undo_method(frames.ptr(), "set_frame", edited_anim, index, texture, old_duration); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); @@ -968,6 +1046,10 @@ void SpriteFramesEditor::_zoom_reset() { } void SpriteFramesEditor::_update_library(bool p_skip_selector) { + if (frames.is_null()) { + return; + } + updating = true; frame_duration->set_value(1.0); // Default. @@ -998,12 +1080,27 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { it->set_text(0, name); it->set_editable(0, true); + if (animated_sprite) { + if (name == String(animated_sprite->call("get_autoplay"))) { + it->set_icon(0, autoplay_icon); + } + } + if (E == edited_anim) { it->select(0); } } } + if (animated_sprite) { + String autoplay_name = animated_sprite->call("get_autoplay"); + if (autoplay_name.is_empty()) { + autoplay->set_pressed(false); + } else { + autoplay->set_pressed(String(edited_anim) == autoplay_name); + } + } + frame_list->clear(); if (!frames->has_animation(edited_anim)) { @@ -1018,18 +1115,19 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { } for (int i = 0; i < frames->get_frame_count(edited_anim); i++) { - String name; + String name = itos(i); Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, i); float duration = frames->get_frame_duration(edited_anim, i); - String duration_string; - if (duration != 1.0f) { - duration_string = String::utf8(" [ ×") + String::num_real(frames->get_frame_duration(edited_anim, i)) + " ]"; - } if (texture.is_null()) { - name = itos(i) + ": " + TTR("(empty)") + duration_string; - } else { - name = itos(i) + ": " + texture->get_name() + duration_string; + texture = empty_icon; + name += ": " + TTR("(empty)"); + } else if (!texture->get_name().is_empty()) { + name += ": " + texture->get_name(); + } + + if (duration != 1.0f) { + name += String::utf8(" [× ") + String::num(duration, 2) + "]"; } frame_list->add_item(name, texture); @@ -1061,20 +1159,25 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { updating = false; } -void SpriteFramesEditor::edit(SpriteFrames *p_frames) { - bool new_read_only_state = false; - if (p_frames) { - new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_frames); +void SpriteFramesEditor::_edit() { + if (!animated_sprite) { + return; } + edit(animated_sprite->call("get_sprite_frames")); +} + +void SpriteFramesEditor::edit(Ref<SpriteFrames> p_frames) { + _update_stop_icon(); - if (frames == p_frames && new_read_only_state == read_only) { + if (!p_frames.is_valid()) { + frames.unref(); return; } frames = p_frames; - read_only = new_read_only_state; + read_only = EditorNode::get_singleton()->is_resource_read_only(p_frames); - if (p_frames) { + if (p_frames.is_valid()) { if (!p_frames->has_animation(edited_anim)) { List<StringName> anim_names; frames->get_animation_list(&anim_names); @@ -1107,6 +1210,8 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) { move_up->set_disabled(read_only); move_down->set_disabled(read_only); delete_frame->set_disabled(read_only); + + _fetch_sprite_node(); // Fetch node after set frames. } Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { @@ -1215,18 +1320,18 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da duration = frames->get_frame_duration(edited_anim, from_frame); } - undo_redo->create_action(TTR("Move Frame")); - undo_redo->add_do_method(frames, "remove_frame", edited_anim, from_frame == -1 ? frames->get_frame_count(edited_anim) : from_frame); - undo_redo->add_do_method(frames, "add_frame", edited_anim, texture, duration, at_pos == -1 ? -1 : at_pos); - undo_redo->add_undo_method(frames, "remove_frame", edited_anim, at_pos == -1 ? frames->get_frame_count(edited_anim) - 1 : at_pos); - undo_redo->add_undo_method(frames, "add_frame", edited_anim, texture, duration, from_frame); + undo_redo->create_action(TTR("Move Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "remove_frame", edited_anim, from_frame == -1 ? frames->get_frame_count(edited_anim) : from_frame); + undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, duration, at_pos == -1 ? -1 : at_pos); + undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, at_pos == -1 ? frames->get_frame_count(edited_anim) - 1 : at_pos); + undo_redo->add_undo_method(frames.ptr(), "add_frame", edited_anim, texture, duration, from_frame); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); } else { - undo_redo->create_action(TTR("Add Frame")); - undo_redo->add_do_method(frames, "add_frame", edited_anim, texture, 1.0, at_pos == -1 ? -1 : at_pos); - undo_redo->add_undo_method(frames, "remove_frame", edited_anim, at_pos == -1 ? frames->get_frame_count(edited_anim) : at_pos); + undo_redo->create_action(TTR("Add Frame"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "add_frame", edited_anim, texture, 1.0, at_pos == -1 ? -1 : at_pos); + undo_redo->add_undo_method(frames.ptr(), "remove_frame", edited_anim, at_pos == -1 ? frames->get_frame_count(edited_anim) : at_pos); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); @@ -1245,10 +1350,156 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } } +void SpriteFramesEditor::_update_stop_icon() { + bool is_playing = false; + if (animated_sprite) { + is_playing = animated_sprite->call("is_playing"); + } + if (is_playing) { + stop->set_icon(pause_icon); + } else { + stop->set_icon(stop_icon); + } +} + +void SpriteFramesEditor::_remove_sprite_node() { + if (!animated_sprite) { + return; + } + if (animated_sprite->is_connected("sprite_frames_changed", callable_mp(this, &SpriteFramesEditor::_edit))) { + animated_sprite->disconnect("sprite_frames_changed", callable_mp(this, &SpriteFramesEditor::_edit)); + } + if (animated_sprite->is_connected("animation_changed", callable_mp(this, &SpriteFramesEditor::_sync_animation))) { + animated_sprite->disconnect("animation_changed", callable_mp(this, &SpriteFramesEditor::_sync_animation)); + } + if (animated_sprite->is_connected("animation_finished", callable_mp(this, &SpriteFramesEditor::_update_stop_icon))) { + animated_sprite->disconnect("animation_finished", callable_mp(this, &SpriteFramesEditor::_update_stop_icon)); + } + animated_sprite = nullptr; +} + +void SpriteFramesEditor::_fetch_sprite_node() { + Node *selected = nullptr; + EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); + if (editor_selection->get_selected_node_list().size() == 1) { + selected = editor_selection->get_selected_node_list()[0]; + } + + bool show_node_edit = false; + AnimatedSprite2D *as2d = Object::cast_to<AnimatedSprite2D>(selected); + AnimatedSprite3D *as3d = Object::cast_to<AnimatedSprite3D>(selected); + if (as2d || as3d) { + if (frames != selected->call("get_sprite_frames")) { + _remove_sprite_node(); + } else { + animated_sprite = selected; + if (!animated_sprite->is_connected("sprite_frames_changed", callable_mp(this, &SpriteFramesEditor::_edit))) { + animated_sprite->connect("sprite_frames_changed", callable_mp(this, &SpriteFramesEditor::_edit)); + } + if (!animated_sprite->is_connected("animation_changed", callable_mp(this, &SpriteFramesEditor::_sync_animation))) { + animated_sprite->connect("animation_changed", callable_mp(this, &SpriteFramesEditor::_sync_animation), CONNECT_DEFERRED); + } + if (!animated_sprite->is_connected("animation_finished", callable_mp(this, &SpriteFramesEditor::_update_stop_icon))) { + animated_sprite->connect("animation_finished", callable_mp(this, &SpriteFramesEditor::_update_stop_icon)); + } + show_node_edit = true; + } + } else { + _remove_sprite_node(); + } + + if (show_node_edit) { + _sync_animation(); + autoplay_container->show(); + playback_container->show(); + } else { + _update_library(); // To init autoplay icon. + autoplay_container->hide(); + playback_container->hide(); + } +} + +void SpriteFramesEditor::_play_pressed() { + if (animated_sprite) { + animated_sprite->call("stop"); + animated_sprite->call("play", animated_sprite->call("get_animation")); + } + _update_stop_icon(); +} + +void SpriteFramesEditor::_play_from_pressed() { + if (animated_sprite) { + animated_sprite->call("play", animated_sprite->call("get_animation")); + } + _update_stop_icon(); +} + +void SpriteFramesEditor::_play_bw_pressed() { + if (animated_sprite) { + animated_sprite->call("stop"); + animated_sprite->call("play_backwards", animated_sprite->call("get_animation")); + } + _update_stop_icon(); +} + +void SpriteFramesEditor::_play_bw_from_pressed() { + if (animated_sprite) { + animated_sprite->call("play_backwards", animated_sprite->call("get_animation")); + } + _update_stop_icon(); +} + +void SpriteFramesEditor::_stop_pressed() { + if (animated_sprite) { + if (animated_sprite->call("is_playing")) { + animated_sprite->call("pause"); + } else { + animated_sprite->call("stop"); + } + } + _update_stop_icon(); +} + +void SpriteFramesEditor::_autoplay_pressed() { + if (updating) { + return; + } + + if (animated_sprite) { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Toggle Autoplay"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + String current = animated_sprite->call("get_animation"); + String current_auto = animated_sprite->call("get_autoplay"); + if (current == current_auto) { + //unset + undo_redo->add_do_method(animated_sprite, "set_autoplay", ""); + undo_redo->add_undo_method(animated_sprite, "set_autoplay", current_auto); + } else { + //set + undo_redo->add_do_method(animated_sprite, "set_autoplay", current); + undo_redo->add_undo_method(animated_sprite, "set_autoplay", current_auto); + } + undo_redo->add_do_method(this, "_update_library"); + undo_redo->add_undo_method(this, "_update_library"); + undo_redo->commit_action(); + } + + _update_library(); +} + void SpriteFramesEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_library", "skipsel"), &SpriteFramesEditor::_update_library, DEFVAL(false)); } +void SpriteFramesEditor::_node_removed(Node *p_node) { + if (animated_sprite) { + if (animated_sprite != p_node) { + return; + } + _remove_sprite_node(); + } +} + SpriteFramesEditor::SpriteFramesEditor() { VBoxContainer *vbc_animlist = memnew(VBoxContainer); add_child(vbc_animlist); @@ -1272,8 +1523,37 @@ SpriteFramesEditor::SpriteFramesEditor() { delete_anim->set_disabled(true); delete_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_remove)); + hbc_animlist->add_child(memnew(VSeparator)); + + anim_speed = memnew(SpinBox); + anim_speed->set_suffix(TTR("FPS")); + anim_speed->set_min(0); + anim_speed->set_max(120); + anim_speed->set_step(0.01); + anim_speed->set_custom_arrow_step(1); + anim_speed->set_tooltip_text(TTR("Animation Speed")); + anim_speed->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_animation_speed_changed)); + hbc_animlist->add_child(anim_speed); + + anim_loop = memnew(Button); + anim_loop->set_toggle_mode(true); + anim_loop->set_flat(true); + anim_loop->set_tooltip_text(TTR("Animation Looping")); + anim_loop->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_loop_changed)); + hbc_animlist->add_child(anim_loop); + + autoplay_container = memnew(HBoxContainer); + hbc_animlist->add_child(autoplay_container); + + autoplay_container->add_child(memnew(VSeparator)); + + autoplay = memnew(Button); + autoplay->set_flat(true); + autoplay->set_tooltip_text(TTR("Autoplay on Load")); + autoplay_container->add_child(autoplay); + anim_search_box = memnew(LineEdit); - hbc_animlist->add_child(anim_search_box); + sub_vb->add_child(anim_search_box); anim_search_box->set_h_size_flags(SIZE_EXPAND_FILL); anim_search_box->set_placeholder(TTR("Filter Animations")); anim_search_box->set_clear_button_enabled(true); @@ -1283,7 +1563,7 @@ SpriteFramesEditor::SpriteFramesEditor() { sub_vb->add_child(animations); animations->set_v_size_flags(SIZE_EXPAND_FILL); animations->set_hide_root(true); - animations->connect("cell_selected", callable_mp(this, &SpriteFramesEditor::_animation_select)); + animations->connect("cell_selected", callable_mp(this, &SpriteFramesEditor::_animation_selected)); animations->connect("item_edited", callable_mp(this, &SpriteFramesEditor::_animation_name_edited)); animations->set_allow_reselect(true); @@ -1292,23 +1572,6 @@ SpriteFramesEditor::SpriteFramesEditor() { delete_anim->set_shortcut_context(animations); delete_anim->set_shortcut(ED_SHORTCUT("sprite_frames/delete_animation", TTR("Delete Animation"), Key::KEY_DELETE)); - HBoxContainer *hbc_anim_speed = memnew(HBoxContainer); - hbc_anim_speed->add_child(memnew(Label(TTR("Speed:")))); - vbc_animlist->add_child(hbc_anim_speed); - anim_speed = memnew(SpinBox); - anim_speed->set_suffix(TTR("FPS")); - anim_speed->set_min(0); - anim_speed->set_max(120); - anim_speed->set_step(0.01); - anim_speed->set_h_size_flags(SIZE_EXPAND_FILL); - hbc_anim_speed->add_child(anim_speed); - anim_speed->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_animation_speed_changed)); - - anim_loop = memnew(CheckButton); - anim_loop->set_text(TTR("Loop")); - vbc_animlist->add_child(anim_loop); - anim_loop->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_loop_changed)); - VBoxContainer *vbc = memnew(VBoxContainer); add_child(vbc); vbc->set_h_size_flags(SIZE_EXPAND_FILL); @@ -1319,6 +1582,44 @@ SpriteFramesEditor::SpriteFramesEditor() { HBoxContainer *hbc = memnew(HBoxContainer); sub_vb->add_child(hbc); + playback_container = memnew(HBoxContainer); + hbc->add_child(playback_container); + + play_bw_from = memnew(Button); + play_bw_from->set_flat(true); + play_bw_from->set_tooltip_text(TTR("Play selected animation backwards from current pos. (A)")); + playback_container->add_child(play_bw_from); + + play_bw = memnew(Button); + play_bw->set_flat(true); + play_bw->set_tooltip_text(TTR("Play selected animation backwards from end. (Shift+A)")); + playback_container->add_child(play_bw); + + stop = memnew(Button); + stop->set_flat(true); + stop->set_tooltip_text(TTR("Pause/stop animation playback. (S)")); + playback_container->add_child(stop); + + play = memnew(Button); + play->set_flat(true); + play->set_tooltip_text(TTR("Play selected animation from start. (Shift+D)")); + playback_container->add_child(play); + + play_from = memnew(Button); + play_from->set_flat(true); + play_from->set_tooltip_text(TTR("Play selected animation from current pos. (D)")); + playback_container->add_child(play_from); + + playback_container->add_child(memnew(VSeparator)); + + autoplay->connect("pressed", callable_mp(this, &SpriteFramesEditor::_autoplay_pressed)); + autoplay->set_toggle_mode(true); + play->connect("pressed", callable_mp(this, &SpriteFramesEditor::_play_pressed)); + play_from->connect("pressed", callable_mp(this, &SpriteFramesEditor::_play_from_pressed)); + play_bw->connect("pressed", callable_mp(this, &SpriteFramesEditor::_play_bw_pressed)); + play_bw_from->connect("pressed", callable_mp(this, &SpriteFramesEditor::_play_bw_from_pressed)); + stop->connect("pressed", callable_mp(this, &SpriteFramesEditor::_stop_pressed)); + load = memnew(Button); load->set_flat(true); hbc->add_child(load); @@ -1369,9 +1670,11 @@ SpriteFramesEditor::SpriteFramesEditor() { frame_duration = memnew(SpinBox); frame_duration->set_prefix(String::utf8("×")); - frame_duration->set_min(0); + frame_duration->set_min(SPRITE_FRAME_MINIMUM_DURATION); // Avoid zero div. frame_duration->set_max(10); frame_duration->set_step(0.01); + frame_duration->set_custom_arrow_step(0.1); + frame_duration->set_allow_lesser(false); frame_duration->set_allow_greater(true); hbc->add_child(frame_duration); @@ -1616,16 +1919,16 @@ SpriteFramesEditor::SpriteFramesEditor() { } void SpriteFramesEditorPlugin::edit(Object *p_object) { - SpriteFrames *s; + Ref<SpriteFrames> s; AnimatedSprite2D *animated_sprite = Object::cast_to<AnimatedSprite2D>(p_object); if (animated_sprite) { - s = *animated_sprite->get_sprite_frames(); + s = animated_sprite->get_sprite_frames(); } else { AnimatedSprite3D *animated_sprite_3d = Object::cast_to<AnimatedSprite3D>(p_object); if (animated_sprite_3d) { - s = *animated_sprite_3d->get_sprite_frames(); + s = animated_sprite_3d->get_sprite_frames(); } else { - s = Object::cast_to<SpriteFrames>(p_object); + s = p_object; } } diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index a5e0e54fb8..1dfb909388 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -33,6 +33,7 @@ #include "editor/editor_plugin.h" #include "scene/2d/animated_sprite_2d.h" +#include "scene/3d/sprite_3d.h" #include "scene/gui/button.h" #include "scene/gui/check_button.h" #include "scene/gui/dialogs.h" @@ -57,6 +58,9 @@ public: class SpriteFramesEditor : public HSplitContainer { GDCLASS(SpriteFramesEditor, HSplitContainer); + Ref<SpriteFrames> frames; + Node *animated_sprite = nullptr; + enum { PARAM_USE_CURRENT, // Used in callbacks to indicate `dominant_param` should be not updated. PARAM_FRAME_COUNT, // Keep "Horizontal" & "Vertical" values. @@ -66,6 +70,18 @@ class SpriteFramesEditor : public HSplitContainer { bool read_only = false; + Ref<Texture2D> autoplay_icon; + Ref<Texture2D> stop_icon; + Ref<Texture2D> pause_icon; + Ref<Texture2D> empty_icon = memnew(ImageTexture); + + HBoxContainer *playback_container = nullptr; + Button *stop = nullptr; + Button *play = nullptr; + Button *play_from = nullptr; + Button *play_bw = nullptr; + Button *play_bw_from = nullptr; + Button *load = nullptr; Button *load_sheet = nullptr; Button *delete_frame = nullptr; @@ -85,18 +101,19 @@ class SpriteFramesEditor : public HSplitContainer { Button *add_anim = nullptr; Button *delete_anim = nullptr; - LineEdit *anim_search_box = nullptr; + SpinBox *anim_speed = nullptr; + Button *anim_loop = nullptr; + + HBoxContainer *autoplay_container = nullptr; + Button *autoplay = nullptr; + LineEdit *anim_search_box = nullptr; Tree *animations = nullptr; - SpinBox *anim_speed = nullptr; - CheckButton *anim_loop = nullptr; EditorFileDialog *file = nullptr; AcceptDialog *dialog = nullptr; - SpriteFrames *frames = nullptr; - StringName edited_anim; ConfirmationDialog *delete_dialog = nullptr; @@ -146,7 +163,15 @@ class SpriteFramesEditor : public HSplitContainer { void _frame_duration_changed(double p_value); void _update_library(bool p_skip_selector = false); - void _animation_select(); + void _update_stop_icon(); + void _play_pressed(); + void _play_from_pressed(); + void _play_bw_pressed(); + void _play_bw_from_pressed(); + void _autoplay_pressed(); + void _stop_pressed(); + + void _animation_selected(); void _animation_name_edited(); void _animation_add(); void _animation_remove(); @@ -183,12 +208,24 @@ class SpriteFramesEditor : public HSplitContainer { void _sheet_zoom_reset(); void _sheet_select_clear_all_frames(); + void _edit(); + void _regist_scene_undo(EditorUndoRedoManager *undo_redo); + void _fetch_sprite_node(); + void _remove_sprite_node(); + + bool sprite_node_updating = false; + void _sync_animation(); + + void _select_animation(const String &p_name, bool p_update_node = true); + void _rename_node_animation(EditorUndoRedoManager *undo_redo, bool is_undo, const String &p_filter, const String &p_new_animation, const String &p_new_autoplay); + protected: void _notification(int p_what); + void _node_removed(Node *p_node); static void _bind_methods(); public: - void edit(SpriteFrames *p_frames); + void edit(Ref<SpriteFrames> p_frames); SpriteFramesEditor(); }; diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index 1b29999796..ffd9564816 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -383,11 +383,12 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLa List<ScriptLanguage::CodeCompletionOption> pp_defines; ShaderPreprocessor preprocessor; String code; - complete_from_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path()).get_base_dir(); + String resource_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path()); + complete_from_path = resource_path.get_base_dir(); if (!complete_from_path.ends_with("/")) { complete_from_path += "/"; } - preprocessor.preprocess(p_code, "", code, nullptr, nullptr, nullptr, nullptr, &pp_options, &pp_defines, _complete_include_paths); + preprocessor.preprocess(p_code, resource_path, code, nullptr, nullptr, nullptr, nullptr, &pp_options, &pp_defines, _complete_include_paths); complete_from_path = String(); if (pp_options.size()) { for (const ScriptLanguage::CodeCompletionOption &E : pp_options) { diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index d7559bc18e..c5aa60c816 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -620,22 +620,14 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } } -void TextureRegionEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void TextureRegionEditor::_pan_callback(Vector2 p_scroll_vec) { +void TextureRegionEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { p_scroll_vec /= draw_zoom; hscroll->set_value(hscroll->get_value() - p_scroll_vec.x); vscroll->set_value(vscroll->get_value() - p_scroll_vec.y); } -void TextureRegionEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - if (p_scroll_vec.y < 0) { - _zoom_on_position(draw_zoom * ((0.95 + (0.05 * Math::abs(p_scroll_vec.y))) / 0.95), p_origin); - } else { - _zoom_on_position(draw_zoom * (1 - (0.05 * Math::abs(p_scroll_vec.y))), p_origin); - } +void TextureRegionEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + _zoom_on_position(draw_zoom * p_zoom_factor, p_origin); } void TextureRegionEditor::_scroll_changed(float) { @@ -748,6 +740,9 @@ void TextureRegionEditor::_update_rect() { } } else if (obj_styleBox.is_valid()) { rect = obj_styleBox->get_region_rect(); + if (rect == Rect2()) { + rect = Rect2(Vector2(), obj_styleBox->get_texture()->get_size()); + } } } @@ -1169,7 +1164,7 @@ TextureRegionEditor::TextureRegionEditor() { hb_grid->hide(); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_scroll_callback), callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); edit_draw = memnew(Panel); vb->add_child(edit_draw); diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 0325700d25..ba64a04084 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -103,9 +103,8 @@ class TextureRegionEditor : public AcceptDialog { bool request_center = false; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _set_snap_mode(int p_mode); void _set_snap_off_x(float p_val); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 0ac375407c..fd651dd507 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -47,18 +47,14 @@ void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) { } } -void TileAtlasView::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void TileAtlasView::_pan_callback(Vector2 p_scroll_vec) { +void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { panning += p_scroll_vec; _update_zoom_and_panning(true); emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning); } -void TileAtlasView::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - zoom_widget->set_zoom_by_increments(-p_scroll_vec.y * 2); +void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor); _update_zoom_and_panning(true); emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning); } @@ -251,7 +247,7 @@ void TileAtlasView::_draw_base_tiles() { for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) { // Update the y to max value. Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame); - Vector2i offset_pos = base_frame_rect.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0); + Vector2i offset_pos = base_frame_rect.get_center() + tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin(); // Draw the tile. TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame); @@ -326,18 +322,19 @@ void TileAtlasView::_draw_base_tiles_shape_grid() { Vector2i tile_shape_size = tile_set->get_tile_size(); for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i tile_id = tile_set_atlas_source->get_tile_id(i); - Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_effective_texture_offset(tile_id, 0); - - for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) { - Color color = grid_color; - if (frame > 0) { - color.a *= 0.3; + Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin(); + if (tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, -tile_shape_size / 2) && tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, tile_shape_size / 2 - Vector2(1, 1))) { + for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) { + Color color = grid_color; + if (frame > 0) { + color.a *= 0.3; + } + Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id, frame); + Transform2D tile_xform; + tile_xform.set_origin(texture_region.get_center() + in_tile_base_offset); + tile_xform.set_scale(tile_shape_size); + tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color); } - Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id); - Transform2D tile_xform; - tile_xform.set_origin(texture_region.get_center() + in_tile_base_offset); - tile_xform.set_scale(tile_shape_size); - tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color); } } } @@ -376,10 +373,10 @@ void TileAtlasView::_draw_alternatives() { // Update the y to max value. Vector2i offset_pos; if (transposed) { - offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id)); + offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_data->get_texture_origin()); y_increment = MAX(y_increment, texture_region_size.x); } else { - offset_pos = (current_pos + texture_region_size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id)); + offset_pos = (current_pos + texture_region_size / 2 + tile_data->get_texture_origin()); y_increment = MAX(y_increment, texture_region_size.y); } @@ -583,7 +580,7 @@ TileAtlasView::TileAtlasView() { add_child(button_center_view); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &TileAtlasView::_scroll_callback), callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback)); panner->set_enable_rmb(true); center_container = memnew(CenterContainer); diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index f719bee704..4a7547f34b 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -65,9 +65,8 @@ private: virtual void gui_input(const Ref<InputEvent> &p_event) override; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); HashMap<Vector2, HashMap<int, Rect2i>> alternative_tiles_rect_cache; void _update_alternative_tiles_rect_cache(); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 1a4223e9e6..3dd0c84ee7 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -1122,14 +1122,15 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2 return; } + Vector2 texture_origin = tile_data->get_texture_origin(); if (value.get_type() == Variant::BOOL) { Ref<Texture2D> texture = (bool)value ? tile_bool_checked : tile_bool_unchecked; int size = MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 3; - Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2), Vector2(size, size))); + Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2) - texture_origin, Vector2(size, size))); p_canvas_item->draw_texture_rect(texture, rect); } else if (value.get_type() == Variant::COLOR) { int size = MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 3; - Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2), Vector2(size, size))); + Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2) - texture_origin, Vector2(size, size))); p_canvas_item->draw_rect(rect, value); } else { Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts")); @@ -1166,8 +1167,8 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2 } Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); - p_canvas_item->draw_string_outline(font, p_transform.get_origin() + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1)); - p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); + p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1)); + p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); } } @@ -1241,20 +1242,38 @@ TileDataDefaultEditor::~TileDataDefaultEditor() { memdelete(dummy_object); } -void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { +void TileDataTextureOriginEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { TileData *tile_data = _get_tile_data(p_cell); ERR_FAIL_COND(!tile_data); Vector2i tile_set_tile_size = tile_set->get_tile_size(); - Color color = Color(1.0, 0.0, 0.0); + Color color = Color(1.0, 1.0, 1.0); if (p_selected) { Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color"); Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); color = selection_color; } - Transform2D tile_xform; - tile_xform.set_scale(tile_set_tile_size); - tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color); + + TileSetSource *source = *(tile_set->get_source(p_cell.source_id)); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, -tile_set_tile_size / 2) && atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, tile_set_tile_size / 2 - Vector2(1, 1))) { + Transform2D tile_xform; + tile_xform.set_scale(tile_set_tile_size); + tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color); + } + + if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2())) { + Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon(SNAME("EditorPosition"), SNAME("EditorIcons")); + p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2()) - (position_icon->get_size() / 2), color); + } else { + Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); + Vector2 texture_origin = tile_data->get_texture_origin(); + String text = vformat("%s", texture_origin); + Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); + p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1)); + p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); + } } void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { @@ -1288,8 +1307,21 @@ void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); color = selection_color; } - Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon(SNAME("EditorPosition"), SNAME("EditorIcons")); - p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(0, tile_data->get_y_sort_origin())) - position_icon->get_size() / 2, color); + Vector2 texture_origin = tile_data->get_texture_origin(); + TileSetSource *source = *(tile_set->get_source(p_cell.source_id)); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2(0, tile_data->get_y_sort_origin()))) { + Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon(SNAME("EditorPosition"), SNAME("EditorIcons")); + p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(0, tile_data->get_y_sort_origin())) - position_icon->get_size() / 2, color); + } else { + Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); + String text = vformat("%s", tile_data->get_y_sort_origin()); + + Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); + p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1)); + p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); + } } void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { @@ -1333,7 +1365,7 @@ void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile if (occluder_polygon.is_valid()) { polygon_editor->add_polygon(occluder_polygon->get_polygon()); } - polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { @@ -1342,7 +1374,7 @@ void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atl Ref<OccluderPolygon2D> occluder_polygon = p_value; tile_data->set_occluder(occlusion_layer, occluder_polygon); - polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { @@ -1489,7 +1521,7 @@ void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_ E.value->update_property(); } - polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { @@ -1508,7 +1540,7 @@ void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_so tile_data->set_collision_polygon_one_way_margin(physics_layer, i, polygon_dict["one_way_margin"]); } - polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { @@ -1741,7 +1773,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas TileData *tile_data = p_tile_set_atlas_source->get_tile_data(hovered_coords, 0); int terrain_set = tile_data->get_terrain_set(); Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(hovered_coords); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, 0); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); if (terrain_set >= 0 && terrain_set == int(dummy_object->get("terrain_set"))) { // Draw hovered bit. @@ -1792,7 +1824,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas // Text p_canvas_item->draw_set_transform_matrix(Transform2D()); Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); Color color = Color(1, 1, 1); String text; @@ -1885,7 +1917,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas Vector2i coords = E.get_atlas_coords(); Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_data(coords, 0)->get_texture_origin(); Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); for (int j = 0; j < polygon.size(); j++) { @@ -1930,7 +1962,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til TileData *tile_data = p_tile_set_atlas_source->get_tile_data(hovered_coords, hovered_alternative); int terrain_set = tile_data->get_terrain_set(); Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(hovered_coords, hovered_alternative); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, hovered_alternative); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); if (terrain_set == int(dummy_object->get("terrain_set"))) { // Draw hovered bit. @@ -1984,7 +2016,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til // Text p_canvas_item->draw_set_transform_matrix(Transform2D()); Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); Color color = Color(1, 1, 1); String text; @@ -2069,7 +2101,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t // Set the terrains bits. Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set()); if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { @@ -2103,7 +2135,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); int terrain_set = tile_data->get_terrain_set(); Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); dummy_object->set("terrain_set", terrain_set); dummy_object->set("terrain", -1); @@ -2218,7 +2250,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t // Set the terrain bit. Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { @@ -2365,7 +2397,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); for (int j = 0; j < polygon.size(); j++) { @@ -2465,7 +2497,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi // Set the terrains bits. Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set()); if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { @@ -2501,7 +2533,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile); int terrain_set = tile_data->get_terrain_set(); Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); dummy_object->set("terrain_set", terrain_set); dummy_object->set("terrain", -1); @@ -2591,7 +2623,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi // Set the terrain bit. Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); - Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { @@ -2741,7 +2773,7 @@ void TileDataNavigationEditor::_set_painted_value(TileSetAtlasSource *p_tile_set polygon_editor->add_polygon(nav_polygon->get_outline(i)); } } - polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { @@ -2750,7 +2782,7 @@ void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_s Ref<NavigationPolygon> nav_polygon = p_value; tile_data->set_navigation_polygon(navigation_layer, nav_polygon); - polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index 02d4584428..1ebf30aecd 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -242,8 +242,8 @@ public: ~TileDataDefaultEditor(); }; -class TileDataTextureOffsetEditor : public TileDataDefaultEditor { - GDCLASS(TileDataTextureOffsetEditor, TileDataDefaultEditor); +class TileDataTextureOriginEditor : public TileDataDefaultEditor { + GDCLASS(TileDataTextureOriginEditor, TileDataDefaultEditor); public: virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 2394130ad6..f0a02a3768 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -266,7 +266,7 @@ void TileMapEditorTilesPlugin::_patterns_item_list_gui_input(const Ref<InputEven } Ref<TileSet> tile_set = tile_map->get_tileset(); - if (!tile_set.is_valid()) { + if (!tile_set.is_valid() || EditorNode::get_singleton()->is_resource_read_only(tile_set)) { return; } @@ -897,7 +897,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over // Compute the offset Rect2i source_rect = atlas_source->get_tile_texture_region(E.value.get_atlas_coords()); - Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(E.value.get_atlas_coords(), E.value.alternative_tile); + Vector2i tile_offset = tile_data->get_texture_origin(); // Compute the destination rectangle in the CanvasItem. Rect2 dest_rect; @@ -1277,13 +1277,15 @@ void TileMapEditorTilesPlugin::_stop_dragging() { tile_map->set_cell(tile_map_layer, kv.key, kv.value.source_id, kv.value.get_atlas_coords(), kv.value.alternative_tile); } - // Creating a pattern in the pattern list. - select_last_pattern = true; - int new_pattern_index = tile_set->get_patterns_count(); - undo_redo->create_action(TTR("Add TileSet pattern")); - undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index); - undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index); - undo_redo->commit_action(); + if (EditorNode::get_singleton()->is_resource_read_only(tile_set)) { + // Creating a pattern in the pattern list. + select_last_pattern = true; + int new_pattern_index = tile_set->get_patterns_count(); + undo_redo->create_action(TTR("Add TileSet pattern")); + undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index); + undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index); + undo_redo->commit_action(); + } } else { // Get the top-left cell. Vector2i top_left; @@ -1989,6 +1991,15 @@ TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const { void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) { _stop_dragging(); // Avoids staying in a wrong drag state. + // Disable sort button if the tileset is read-only + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (tile_map) { + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (tile_set.is_valid()) { + source_sort_button->set_disabled(EditorNode::get_singleton()->is_resource_read_only(tile_set)); + } + } + if (tile_map_id != p_tile_map_id) { tile_map_id = p_tile_map_id; @@ -3986,7 +3997,7 @@ TileMapEditor::TileMapEditor() { tabs_bar->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed)); // --- TileMap toolbar --- - tile_map_toolbar = memnew(HBoxContainer); + tile_map_toolbar = memnew(HFlowContainer); tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL); add_child(tile_map_toolbar); @@ -4001,8 +4012,11 @@ TileMapEditor::TileMapEditor() { } } - // Wide empty separation control. - tile_map_toolbar->add_spacer(); + // Wide empty separation control. (like BoxContainer::add_spacer()) + Control *c = memnew(Control); + c->set_mouse_filter(MOUSE_FILTER_PASS); + c->set_h_size_flags(SIZE_EXPAND_FILL); + tile_map_toolbar->add_child(c); // Layer selector. layers_selection_button = memnew(OptionButton); diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index fb9c2f3689..1cab1d1500 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -38,6 +38,7 @@ #include "scene/2d/tile_map.h" #include "scene/gui/box_container.h" #include "scene/gui/check_box.h" +#include "scene/gui/flow_container.h" #include "scene/gui/item_list.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" @@ -323,7 +324,7 @@ private: Vector<TileMapEditorPlugin *> tile_map_editor_plugins; // Toolbar. - HBoxContainer *tile_map_toolbar = nullptr; + HFlowContainer *tile_map_toolbar = nullptr; OptionButton *layers_selection_button = nullptr; Button *toggle_highlight_selected_layer_button = nullptr; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 32421daa92..840a3911af 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -585,6 +585,7 @@ void TileSetAtlasSourceEditor::_update_atlas_source_inspector() { // Update visibility. bool inspector_visible = tools_button_group->get_pressed_button() == tool_setup_atlas_source_button; atlas_source_inspector->set_visible(inspector_visible); + atlas_source_inspector->set_read_only(read_only); } void TileSetAtlasSourceEditor::_update_tile_inspector() { @@ -599,6 +600,7 @@ void TileSetAtlasSourceEditor::_update_tile_inspector() { tile_inspector->hide(); tile_inspector_no_tile_selected_label->hide(); } + tile_inspector->set_read_only(read_only); } void TileSetAtlasSourceEditor::_update_tile_data_editors() { @@ -638,14 +640,14 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() { // --- Rendering --- ADD_TILE_DATA_EDITOR_GROUP("Rendering"); - ADD_TILE_DATA_EDITOR(group, "Texture Offset", "texture_offset"); - if (!tile_data_editors.has("texture_offset")) { - TileDataTextureOffsetEditor *tile_data_texture_offset_editor = memnew(TileDataTextureOffsetEditor); - tile_data_texture_offset_editor->hide(); - tile_data_texture_offset_editor->setup_property_editor(Variant::VECTOR2, "texture_offset"); - tile_data_texture_offset_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::queue_redraw)); - tile_data_texture_offset_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::queue_redraw)); - tile_data_editors["texture_offset"] = tile_data_texture_offset_editor; + ADD_TILE_DATA_EDITOR(group, "Texture Origin", "texture_origin"); + if (!tile_data_editors.has("texture_origin")) { + TileDataTextureOriginEditor *tile_data_texture_origin_editor = memnew(TileDataTextureOriginEditor); + tile_data_texture_origin_editor->hide(); + tile_data_texture_origin_editor->setup_property_editor(Variant::VECTOR2, "texture_origin"); + tile_data_texture_origin_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::queue_redraw)); + tile_data_texture_origin_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::queue_redraw)); + tile_data_editors["texture_origin"] = tile_data_texture_origin_editor; } ADD_TILE_DATA_EDITOR(group, "Modulate", "modulate"); @@ -970,19 +972,19 @@ void TileSetAtlasSourceEditor::_update_toolbar() { current_tile_data_editor_toolbar->hide(); } tools_settings_erase_button->show(); - tool_advanced_menu_buttom->show(); + tool_advanced_menu_button->show(); } else if (tools_button_group->get_pressed_button() == tool_select_button) { if (current_tile_data_editor_toolbar) { current_tile_data_editor_toolbar->hide(); } tools_settings_erase_button->hide(); - tool_advanced_menu_buttom->hide(); + tool_advanced_menu_button->hide(); } else if (tools_button_group->get_pressed_button() == tool_paint_button) { if (current_tile_data_editor_toolbar) { current_tile_data_editor_toolbar->show(); } tools_settings_erase_button->hide(); - tool_advanced_menu_buttom->hide(); + tool_advanced_menu_button->hide(); } } @@ -1862,7 +1864,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() { for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i coords = tile_set_atlas_source->get_tile_id(i); Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(coords); - Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_data(coords, 0)->get_texture_origin(); Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); xform.translate_local(position); @@ -1885,7 +1887,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() { continue; } Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(E.tile); - Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(E.tile, 0); + Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_data(E.tile, 0)->get_texture_origin(); Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); xform.translate_local(position); @@ -2037,7 +2039,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() { continue; } Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); - Vector2 position = rect.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + Vector2 position = rect.get_center() + tile_set_atlas_source->get_tile_data(coords, alternative_tile)->get_texture_origin(); Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform(); xform.translate_local(position); @@ -2061,7 +2063,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() { continue; } Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E.tile, E.alternative); - Vector2 position = rect.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(E.tile, E.alternative); + Vector2 position = rect.get_center() + tile_set_atlas_source->get_tile_data(E.tile, E.alternative)->get_texture_origin(); Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform(); xform.translate_local(position); @@ -2188,7 +2190,12 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource ERR_FAIL_COND(p_source_id < 0); ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source); - if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == tile_set_atlas_source_id) { + bool new_read_only_state = false; + if (p_tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == tile_set_atlas_source_id && new_read_only_state == read_only) { return; } @@ -2205,11 +2212,23 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource tile_set_atlas_source = p_tile_set_atlas_source; tile_set_atlas_source_id = p_source_id; - // Add the listener again. + // Read-only is off by default. + read_only = new_read_only_state; + if (tile_set.is_valid()) { tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); } + if (read_only && tools_button_group->get_pressed_button() == tool_paint_button) { + tool_paint_button->set_pressed(false); + tool_setup_atlas_source_button->set_pressed(true); + } + + // Disable buttons in read-only mode. + tool_paint_button->set_disabled(read_only); + tools_settings_erase_button->set_disabled(read_only); + tool_advanced_menu_button->set_disabled(read_only); + // Update everything. _update_source_inspector(); @@ -2344,7 +2363,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { tools_settings_erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons"))); - tool_advanced_menu_buttom->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + tool_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); resize_handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); resize_handle_disabled = get_theme_icon(SNAME("EditorHandleDisabled"), SNAME("EditorIcons")); @@ -2352,6 +2371,18 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { if (tile_set_changed_needs_update) { + // Read-only is off by default + read_only = false; + // Add the listener again and check for read-only status. + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + + // Disable buttons in read-only mode. + tool_paint_button->set_disabled(read_only); + tools_settings_erase_button->set_disabled(read_only); + tool_advanced_menu_button->set_disabled(read_only); + // Update everything. _update_source_inspector(); @@ -2516,12 +2547,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tools_settings_erase_button->set_shortcut_context(this); tool_settings->add_child(tools_settings_erase_button); - tool_advanced_menu_buttom = memnew(MenuButton); - tool_advanced_menu_buttom->set_flat(true); - tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES); - tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES); - tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); - tool_settings->add_child(tool_advanced_menu_buttom); + tool_advanced_menu_button = memnew(MenuButton); + tool_advanced_menu_button->set_flat(true); + tool_advanced_menu_button->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES); + tool_advanced_menu_button->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES); + tool_advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); + tool_settings->add_child(tool_advanced_menu_button); _update_toolbar(); @@ -2673,7 +2704,7 @@ void EditorPropertyTilePolygon::update_property() { Vector2i coords = atlas_tile_proxy_object->get_edited_tiles().front()->get().tile; int alternative = atlas_tile_proxy_object->get_edited_tiles().front()->get().alternative; TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative); - generic_tile_polygon_editor->set_background(tile_set_atlas_source->get_texture(), tile_set_atlas_source->get_tile_texture_region(coords), tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); + generic_tile_polygon_editor->set_background(tile_set_atlas_source->get_texture(), tile_set_atlas_source->get_tile_texture_region(coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); // Reset the polygons. generic_tile_polygon_editor->clear_polygons(); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index bcab1296ad..a4826bc56f 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -113,6 +113,8 @@ public: }; private: + bool read_only = false; + Ref<TileSet> tile_set; TileSetAtlasSource *tile_set_atlas_source = nullptr; int tile_set_atlas_source_id = TileSet::INVALID_SOURCE; @@ -209,7 +211,7 @@ private: HBoxContainer *tool_settings = nullptr; HBoxContainer *tool_settings_tile_data_toolbar_container = nullptr; Button *tools_settings_erase_button = nullptr; - MenuButton *tool_advanced_menu_buttom = nullptr; + MenuButton *tool_advanced_menu_button = nullptr; // Selection. RBSet<TileSelection> selection; diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 53c2d4de51..39d17c718e 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -89,6 +89,10 @@ void TileSetEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, bool TileSetEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { ERR_FAIL_COND_V(!tile_set.is_valid(), false); + if (read_only) { + return false; + } + if (p_from == sources_list) { Dictionary d = p_data; @@ -223,7 +227,7 @@ void TileSetEditor::_source_selected(int p_source_index) { ERR_FAIL_COND(!tile_set.is_valid()); // Update the selected source. - sources_delete_button->set_disabled(p_source_index < 0); + sources_delete_button->set_disabled(p_source_index < 0 || read_only); if (p_source_index >= 0) { int source_id = sources_list->get_item_metadata(p_source_index); @@ -356,8 +360,19 @@ void TileSetEditor::_notification(int p_what) { if (tile_set.is_valid()) { tile_set->set_edited(true); } + + read_only = false; + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + _update_sources_list(); _update_patterns_list(); + + sources_add_button->set_disabled(read_only); + sources_advanced_menu_button->set_disabled(read_only); + source_sort_button->set_disabled(read_only); + tile_set_changed_needs_update = false; } } break; @@ -367,6 +382,10 @@ void TileSetEditor::_notification(int p_what) { void TileSetEditor::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(!tile_set.is_valid()); + if (EditorNode::get_singleton()->is_resource_read_only(tile_set)) { + return; + } + if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) { Vector<int> selected = patterns_item_list->get_selected_items(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); @@ -667,7 +686,12 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p } void TileSetEditor::edit(Ref<TileSet> p_tile_set) { - if (p_tile_set == tile_set) { + bool new_read_only_state = false; + if (tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && new_read_only_state == read_only) { return; } @@ -679,8 +703,15 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { // Change the edited object. tile_set = p_tile_set; - // Add the listener again. + // Read-only status is false by default + read_only = new_read_only_state; + + // Add the listener again and check for read-only status. if (tile_set.is_valid()) { + sources_add_button->set_disabled(read_only); + sources_advanced_menu_button->set_disabled(read_only); + source_sort_button->set_disabled(read_only); + tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed)); if (first_edit) { first_edit = false; @@ -690,10 +721,6 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { } _update_patterns_list(); } - - tile_set_atlas_source_editor->hide(); - tile_set_scenes_collection_source_editor->hide(); - no_source_selected_label->show(); } TileSetEditor::TileSetEditor() { diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index e3dff11277..d36d3bde41 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -45,6 +45,8 @@ class TileSetEditor : public VBoxContainer { static TileSetEditor *singleton; private: + bool read_only = false; + Ref<TileSet> tile_set; bool tile_set_changed_needs_update = false; HSplitContainer *split_container = nullptr; diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 6251cd18f7..cc276597fa 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -284,7 +284,7 @@ void TileSetScenesCollectionSourceEditor::_update_tile_inspector() { void TileSetScenesCollectionSourceEditor::_update_action_buttons() { Vector<int> selected_indices = scene_tiles_list->get_selected_items(); - scene_tile_delete_button->set_disabled(selected_indices.size() <= 0); + scene_tile_delete_button->set_disabled(selected_indices.size() <= 0 || read_only); } void TileSetScenesCollectionSourceEditor::_update_scenes_list() { @@ -342,6 +342,12 @@ void TileSetScenesCollectionSourceEditor::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { if (tile_set_scenes_collection_source_changed_needs_update) { + read_only = false; + // Add the listener again and check for read-only status. + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + // Update everything. _update_source_inspector(); _update_scenes_list(); @@ -365,7 +371,12 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS ERR_FAIL_COND(p_source_id < 0); ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source); - if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id) { + bool new_read_only_state = false; + if (p_tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id && new_read_only_state == read_only) { return; } @@ -379,6 +390,16 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS tile_set_scenes_collection_source = p_tile_set_scenes_collection_source; tile_set_source_id = p_source_id; + // Read-only status is false by default + read_only = new_read_only_state; + + if (tile_set.is_valid()) { + scenes_collection_source_inspector->set_read_only(read_only); + tile_inspector->set_read_only(read_only); + + scene_tile_add_button->set_disabled(read_only); + } + // Add the listener again. if (tile_set_scenes_collection_source) { tile_set_scenes_collection_source->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h index 0901205a29..2a0e8595c4 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h @@ -91,6 +91,8 @@ private: }; private: + bool read_only = false; + Ref<TileSet> tile_set; TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; int tile_set_source_id = -1; diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 19ee0ae98d..fad36660d9 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -101,12 +101,12 @@ void TilesEditorPlugin::_thread() { encompassing_rect.expand_to(world_pos); // Texture. - Ref<TileSetAtlasSource> atlas_source = tile_set->get_source(tile_map->get_cell_source_id(0, cell)); + Ref<TileSetAtlasSource> atlas_source = item.tile_set->get_source(tile_map->get_cell_source_id(0, cell)); if (atlas_source.is_valid()) { Vector2i coords = tile_map->get_cell_atlas_coords(0, cell); int alternative = tile_map->get_cell_alternative_tile(0, cell); - Vector2 center = world_pos - atlas_source->get_tile_effective_texture_offset(coords, alternative); + Vector2 center = world_pos - atlas_source->get_tile_data(coords, alternative)->get_texture_origin(); encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2); encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2); } diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index 37f941c7b2..9695a7042d 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -257,6 +257,7 @@ ProgressDialog::ProgressDialog() { add_child(main); main->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); set_exclusive(true); + set_flag(Window::FLAG_POPUP, false); last_progress_tick = 0; singleton = this; cancel_hb = memnew(HBoxContainer); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 88fb88de98..319da02d7a 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -215,7 +215,6 @@ static const char *gdscript_function_renames[][2] = { { "_get_configuration_warning", "_get_configuration_warnings" }, // Node { "_set_current", "set_current" }, // Camera2D { "_set_editor_description", "set_editor_description" }, // Node - { "_set_playing", "set_playing" }, // AnimatedSprite3D { "_toplevel_raise_self", "_top_level_raise_self" }, // CanvasItem { "_update_wrap_at", "_update_wrap_at_column" }, // TextEdit { "add_animation", "add_animation_library" }, // AnimationPlayer @@ -312,8 +311,8 @@ static const char *gdscript_function_renames[][2] = { { "get_font_types", "get_font_type_list" }, // Theme { "get_frame_color", "get_color" }, // ColorRect { "get_global_rate_scale", "get_playback_speed_scale" }, // AudioServer - { "get_gravity_distance_scale", "get_gravity_point_distance_scale" }, //Area2D - { "get_gravity_vector", "get_gravity_direction" }, //Area2D + { "get_gravity_distance_scale", "get_gravity_point_unit_distance" }, // Area(2D/3D) + { "get_gravity_vector", "get_gravity_direction" }, // Area(2D/3D) { "get_h_scrollbar", "get_h_scroll_bar" }, //ScrollContainer { "get_hand", "get_tracker_hand" }, // XRPositionalTracker { "get_handle_name", "_get_handle_name" }, // EditorNode3DGizmo @@ -510,8 +509,8 @@ static const char *gdscript_function_renames[][2] = { { "set_follow_smoothing", "set_position_smoothing_speed" }, // Camera2D { "set_frame_color", "set_color" }, // ColorRect { "set_global_rate_scale", "set_playback_speed_scale" }, // AudioServer - { "set_gravity_distance_scale", "set_gravity_point_distance_scale" }, // Area2D - { "set_gravity_vector", "set_gravity_direction" }, // Area2D + { "set_gravity_distance_scale", "set_gravity_point_unit_distance" }, // Area(2D/3D) + { "set_gravity_vector", "set_gravity_direction" }, // Area(2D/3D) { "set_h_drag_enabled", "set_drag_horizontal_enabled" }, // Camera2D { "set_icon_align", "set_icon_alignment" }, // Button { "set_interior_ambient", "set_ambient_color" }, // ReflectionProbe @@ -1112,8 +1111,8 @@ static const char *gdscript_properties_renames[][2] = { { "files_disabled", "file_disabled_color" }, // Theme { "folder_icon_modulate", "folder_icon_color" }, // Theme { "global_rate_scale", "playback_speed_scale" }, // AudioServer - { "gravity_distance_scale", "gravity_point_distance_scale" }, // Area2D - { "gravity_vec", "gravity_direction" }, // Area2D + { "gravity_distance_scale", "gravity_point_unit_distance" }, // Area(2D/3D) + { "gravity_vec", "gravity_direction" }, // Area(2D/3D) { "hint_tooltip", "tooltip_text" }, // Control { "hseparation", "h_separation" }, // Theme { "icon_align", "icon_alignment" }, // Button @@ -1168,6 +1167,7 @@ static const char *gdscript_properties_renames[][2] = { { "unit_db", "volume_db" }, // AudioStreamPlayer3D { "unit_offset", "progress_ratio" }, // PathFollow2D, PathFollow3D { "vseparation", "v_separation" }, // Theme + { "frames", "sprite_frames" }, // AnimatedSprite2D, AnimatedSprite3D { nullptr, nullptr }, }; @@ -1362,11 +1362,12 @@ static const char *project_settings_renames[][2] = { { "rendering/quality/shadow_atlas/quadrant_3_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_3_subdiv" }, { "rendering/quality/shadow_atlas/size", "rendering/lights_and_shadows/shadow_atlas/size" }, { "rendering/quality/shadow_atlas/size.mobile", "rendering/lights_and_shadows/shadow_atlas/size.mobile" }, - { "rendering/vram_compression/import_bptc", "rendering/textures/vram_compression/import_bptc" }, - { "rendering/vram_compression/import_etc", "rendering/textures/vram_compression/import_etc" }, - { "rendering/vram_compression/import_etc2", "rendering/textures/vram_compression/import_etc2" }, - { "rendering/vram_compression/import_pvrtc", "rendering/textures/vram_compression/import_pvrtc" }, - { "rendering/vram_compression/import_s3tc", "rendering/textures/vram_compression/import_s3tc" }, + { "rendering/vram_compression/import_etc2", "rendering/textures/vram_compression/import_etc2_astc" }, + { "rendering/vram_compression/import_s3tc", "rendering/textures/vram_compression/import_s3tc_bptc" }, + { "window/size/width", "window/size/viewport_width" }, + { "window/size/height", "window/size/viewport_height" }, + { "window/size/test_width", "window/size/window_width_override" }, + { "window/size/test_height", "window/size/window_height_override" }, { nullptr, nullptr }, }; @@ -2549,14 +2550,14 @@ bool ProjectConverter3To4::test_conversion(RegExContainer ®_container) { valid = valid && test_conversion_with_regex("tool", "@tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n tool", "\n tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\ntool", "\n\n@tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nremote func", "\n\n@rpc(any_peer) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nremotesync func", "\n\n@rpc(any_peer, call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nsync func", "\n\n@rpc(any_peer, call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nremote func", "\n\n@rpc(\"any_peer\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nremotesync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nsync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\nslave func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\npuppet func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\npuppetsync func", "\n\n@rpc(call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\npuppetsync func", "\n\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\nmaster func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function , get_function", "var size : Vector2 = Vector2() : get = get_function, set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function , ", "var size : Vector2 = Vector2() : set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); @@ -4087,13 +4088,13 @@ void ProjectConverter3To4::rename_gdscript_keywords(Vector<String> &lines, const line = reg_container.keyword_gdscript_onready.sub(line, "@onready", true); } if (line.contains("remote")) { - line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(any_peer) func", true); + line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(\"any_peer\") func", true); } if (line.contains("remote")) { - line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(any_peer, call_local) func", true); + line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(\"any_peer\", \"call_local\") func", true); } if (line.contains("sync")) { - line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(any_peer, call_local) func", true); + line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(\"any_peer\", \"call_local\") func", true); } if (line.contains("slave")) { line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true); @@ -4102,13 +4103,13 @@ void ProjectConverter3To4::rename_gdscript_keywords(Vector<String> &lines, const line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true); } if (line.contains("puppet")) { - line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(\"call_local\") func", true); } if (line.contains("master")) { line = reg_container.keyword_gdscript_master.sub(line, error_message + "@rpc func", true); } if (line.contains("master")) { - line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + "@rpc(\"call_local\") func", true); } } } @@ -4156,25 +4157,25 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("remote")) { old = line; - line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(any_peer) func", true); + line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(\"any_peer\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "remote func", "@rpc(any_peer) func", line)); + found_renames.append(line_formatter(current_line, "remote func", "@rpc(\"any_peer\") func", line)); } } if (line.contains("remote")) { old = line; - line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(any_peer, call_local)) func", true); + line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(\"any_peer\", \"call_local\")) func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "remotesync func", "@rpc(any_peer, call_local)) func", line)); + found_renames.append(line_formatter(current_line, "remotesync func", "@rpc(\"any_peer\", \"call_local\")) func", line)); } } if (line.contains("sync")) { old = line; - line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(any_peer, call_local)) func", true); + line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(\"any_peer\", \"call_local\")) func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "sync func", "@rpc(any_peer, call_local)) func", line)); + found_renames.append(line_formatter(current_line, "sync func", "@rpc(\"any_peer\", \"call_local\")) func", line)); } } @@ -4196,9 +4197,9 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("puppet")) { old = line; - line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(\"call_local\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "puppetsync func", "@rpc(call_local) func", line)); + found_renames.append(line_formatter(current_line, "puppetsync func", "@rpc(\"call_local\") func", line)); } } @@ -4212,9 +4213,9 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("master")) { old = line; - line = reg_container.keyword_gdscript_master.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_master.sub(line, "@rpc(\"call_local\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "mastersync func", "@rpc(call_local) func", line)); + found_renames.append(line_formatter(current_line, "mastersync func", "@rpc(\"call_local\") func", line)); } } } diff --git a/misc/scripts/codespell.sh b/misc/scripts/codespell.sh index 34885016b6..44084b3348 100755 --- a/misc/scripts/codespell.sh +++ b/misc/scripts/codespell.sh @@ -1,5 +1,5 @@ #!/bin/sh -SKIP_LIST="./.*,./bin,.platform/web/node_modules,./platform/android/java/lib/src/com,./thirdparty,*.gen.*,*.po,*.pot,*.rc,package-lock.json,./core/string/locales.h,./AUTHORS.md,./COPYRIGHT.txt,./DONORS.md,./misc/dist/linux/org.godotengine.Godot.desktop,./misc/scripts/codespell.sh" +SKIP_LIST="./.*,./bin,./editor/project_converter_3_to_4.cpp,./platform/web/node_modules,./platform/android/java/lib/src/com,./thirdparty,*.gen.*,*.po,*.pot,*.rc,package-lock.json,./core/string/locales.h,./AUTHORS.md,./COPYRIGHT.txt,./DONORS.md,./misc/dist/linux/org.godotengine.Godot.desktop,./misc/scripts/codespell.sh" IGNORE_LIST="alo,ba,childs,complies,curvelinear,doubleclick,expct,fave,findn,gird,gud,inout,lod,nd,numer,ois,readded,ro,sav,statics,te,varius,varn,wan" codespell -w -q 3 -S "${SKIP_LIST}" -L "${IGNORE_LIST}" --builtin "clear,rare,en-GB_to_en-US" diff --git a/modules/astcenc/image_compress_astcenc.cpp b/modules/astcenc/image_compress_astcenc.cpp index ce10201343..1c643d780d 100644 --- a/modules/astcenc/image_compress_astcenc.cpp +++ b/modules/astcenc/image_compress_astcenc.cpp @@ -35,7 +35,7 @@ #include <astcenc.h> -void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format) { +void _compress_astc(Image *r_img, Image::ASTCFormat p_format) { uint64_t start_time = OS::get_singleton()->get_ticks_msec(); // TODO: See how to handle lossy quality. @@ -83,65 +83,91 @@ void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_for const bool mipmaps = r_img->has_mipmaps(); int width = r_img->get_width(); int height = r_img->get_height(); + int required_width = (width % block_x) != 0 ? width + (block_x - (width % block_x)) : width; + int required_height = (height % block_y) != 0 ? height + (block_y - (height % block_y)) : height; + + if (width != required_width || height != required_height) { + // Resize texture to fit block size. + r_img->resize(required_width, required_height); + width = required_width; + height = required_height; + } print_verbose(vformat("astcenc: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : "")); // Initialize astcenc. + int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps); + Vector<uint8_t> dest_data; + dest_data.resize(dest_size); + uint8_t *dest_write = dest_data.ptrw(); + astcenc_config config; config.block_x = block_x; config.block_y = block_y; config.profile = profile; - const float quality = ASTCENC_PRE_MEDIUM; - astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config); + const float quality = ASTCENC_PRE_MEDIUM; + astcenc_error status = astcenc_config_init(profile, block_x, block_y, 1, quality, 0, &config); ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status))); // Context allocation. astcenc_context *context; - const unsigned int thread_count = OS::get_singleton()->get_processor_count(); - + const unsigned int thread_count = 1; // Godot compresses multiple images each on a thread, which is more efficient for large amount of images imported. status = astcenc_context_alloc(&config, thread_count, &context); ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status))); - // Compress image. - Vector<uint8_t> image_data = r_img->get_data(); - uint8_t *slices = image_data.ptrw(); - astcenc_image image; - image.dim_x = width; - image.dim_y = height; - image.dim_z = 1; - image.data_type = ASTCENC_TYPE_U8; - if (is_hdr) { - image.data_type = ASTCENC_TYPE_F32; - } - image.data = reinterpret_cast<void **>(&slices); + int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0; + for (int i = 0; i < mip_count + 1; i++) { + int src_mip_w, src_mip_h; + int src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h); - // Compute the number of ASTC blocks in each dimension. - unsigned int block_count_x = (width + block_x - 1) / block_x; - unsigned int block_count_y = (height + block_y - 1) / block_y; - size_t comp_len = block_count_x * block_count_y * 16; + const uint8_t *slices = &image_data.ptr()[src_ofs]; - Vector<uint8_t> compressed_data; - compressed_data.resize(comp_len); - compressed_data.fill(0); + int dst_mip_w, dst_mip_h; + int dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h); + // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer). + ERR_FAIL_COND(dst_ofs % 8 != 0); + uint8_t *dest_mip_write = (uint8_t *)&dest_write[dst_ofs]; - const astcenc_swizzle swizzle = { - ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A - }; + // Compress image. - status = astcenc_compress_image(context, &image, &swizzle, compressed_data.ptrw(), comp_len, 0); - ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, - vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status))); + astcenc_image image; + image.dim_x = src_mip_w; + image.dim_y = src_mip_h; + image.dim_z = 1; + image.data_type = ASTCENC_TYPE_U8; + if (is_hdr) { + image.data_type = ASTCENC_TYPE_F32; + } + image.data = (void **)(&slices); + + // Compute the number of ASTC blocks in each dimension. + unsigned int block_count_x = (src_mip_w + block_x - 1) / block_x; + unsigned int block_count_y = (src_mip_h + block_y - 1) / block_y; + size_t comp_len = block_count_x * block_count_y * 16; + + const astcenc_swizzle swizzle = { + ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A + }; + + status = astcenc_compress_image(context, &image, &swizzle, dest_mip_write, comp_len, 0); + + ERR_BREAK_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status))); + astcenc_compress_reset(context); + } + + astcenc_context_free(context); // Replace original image with compressed one. - r_img->set_data(width, height, mipmaps, target_format, compressed_data); + r_img->set_data(width, height, mipmaps, target_format, dest_data); print_verbose(vformat("astcenc: Encoding took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); } @@ -184,68 +210,81 @@ void _decompress_astc(Image *r_img) { astcenc_config config; const float quality = ASTCENC_PRE_MEDIUM; - astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config); + astcenc_error status = astcenc_config_init(profile, block_x, block_y, 1, quality, 0, &config); ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status))); // Context allocation. astcenc_context *context = nullptr; - const unsigned int thread_count = OS::get_singleton()->get_processor_count(); + const unsigned int thread_count = 1; status = astcenc_context_alloc(&config, thread_count, &context); ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status))); - // Decompress image. + Image::Format target_format = is_hdr ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8; const bool mipmaps = r_img->has_mipmaps(); int width = r_img->get_width(); int height = r_img->get_height(); + int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps); + Vector<uint8_t> dest_data; + dest_data.resize(dest_size); + uint8_t *dest_write = dest_data.ptrw(); - astcenc_image image; - image.dim_x = width; - image.dim_y = height; - image.dim_z = 1; - image.data_type = ASTCENC_TYPE_U8; - Image::Format target_format = Image::FORMAT_RGBA8; - if (is_hdr) { - target_format = Image::FORMAT_RGBAF; - image.data_type = ASTCENC_TYPE_F32; - } + // Decompress image. Vector<uint8_t> image_data = r_img->get_data(); + int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0; - Vector<uint8_t> new_image_data; - new_image_data.resize(Image::get_image_data_size(width, height, target_format, false)); - new_image_data.fill(0); - uint8_t *slices = new_image_data.ptrw(); - image.data = reinterpret_cast<void **>(&slices); + for (int i = 0; i < mip_count + 1; i++) { + int src_mip_w, src_mip_h; + + int src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h); + const uint8_t *src_data = &image_data.ptr()[src_ofs]; + int src_size; + if (i == mip_count) { + src_size = image_data.size() - src_ofs; + } else { + int auxw, auxh; + src_size = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i + 1, auxw, auxh) - src_ofs; + } - const astcenc_swizzle swizzle = { - ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A - }; + int dst_mip_w, dst_mip_h; + int dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h); + // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer). + ERR_FAIL_COND(dst_ofs % 8 != 0); + uint8_t *dest_mip_write = (uint8_t *)&dest_write[dst_ofs]; + + astcenc_image image; + image.dim_x = dst_mip_w; + image.dim_y = dst_mip_h; + image.dim_z = 1; + image.data_type = ASTCENC_TYPE_U8; + if (is_hdr) { + target_format = Image::FORMAT_RGBAF; + image.data_type = ASTCENC_TYPE_F32; + } - status = astcenc_decompress_image(context, image_data.ptr(), image_data.size(), &image, &swizzle, 0); - ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, - vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status))); - ERR_FAIL_COND_MSG(image.dim_z > 1, - "astcenc: ASTC decompression failed because this is a 3D texture, which is not supported."); + image.data = (void **)(&dest_mip_write); - // Replace original image with compressed one. + const astcenc_swizzle swizzle = { + ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A + }; - Image::Format image_format = Image::FORMAT_RGBA8; - if (image.data_type == ASTCENC_TYPE_F32) { - image_format = Image::FORMAT_RGBAF; - } else if (image.data_type == ASTCENC_TYPE_U8) { - image_format = Image::FORMAT_RGBA8; - } else if (image.data_type == ASTCENC_TYPE_F16) { - image_format = Image::FORMAT_RGBAH; - } else { - ERR_FAIL_MSG("astcenc: ASTC decompression failed with an unknown format."); + status = astcenc_decompress_image(context, src_data, src_size, &image, &swizzle, 0); + ERR_BREAK_MSG(status != ASTCENC_SUCCESS, + vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status))); + ERR_BREAK_MSG(image.dim_z > 1, + "astcenc: ASTC decompression failed because this is a 3D texture, which is not supported."); + astcenc_compress_reset(context); } + astcenc_context_free(context); + + // Replace original image with compressed one. - r_img->set_data(image.dim_x, image.dim_y, mipmaps, image_format, new_image_data); + r_img->set_data(width, height, mipmaps, target_format, dest_data); print_verbose(vformat("astcenc: Decompression took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); } diff --git a/modules/astcenc/image_compress_astcenc.h b/modules/astcenc/image_compress_astcenc.h index a197a91e0d..ad157d7c0a 100644 --- a/modules/astcenc/image_compress_astcenc.h +++ b/modules/astcenc/image_compress_astcenc.h @@ -33,7 +33,7 @@ #include "core/io/image.h" -void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format); +void _compress_astc(Image *r_img, Image::ASTCFormat p_format); void _decompress_astc(Image *r_img); #endif // IMAGE_COMPRESS_ASTCENC_H diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index 7251511460..ff7c01f9fc 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -52,44 +52,50 @@ enum BasisDecompressFormat { #ifdef TOOLS_ENABLED static Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) { Vector<uint8_t> budata; - { + basisu::basis_compressor_params params; Ref<Image> image = p_image->duplicate(); - - // unfortunately, basis universal does not support compressing supplied mipmaps, - // so for the time being, only compressing individual images will have to do. - - if (image->has_mipmaps()) { - image->clear_mipmaps(); - } if (image->get_format() != Image::FORMAT_RGBA8) { image->convert(Image::FORMAT_RGBA8); } - - basisu::image buimg(image->get_width(), image->get_height()); - + Ref<Image> image_single = image->duplicate(); { - Vector<uint8_t> vec = image->get_data(); + if (image_single->has_mipmaps()) { + image_single->clear_mipmaps(); + } + basisu::image buimg(image_single->get_width(), image_single->get_height()); + Vector<uint8_t> vec = image_single->get_data(); const uint8_t *r = vec.ptr(); - memcpy(buimg.get_ptr(), r, vec.size()); + params.m_source_images.push_back(buimg); + } + basisu::vector<basisu::image> source_images; + for (int32_t mipmap_i = 1; mipmap_i < image->get_mipmap_count(); mipmap_i++) { + Ref<Image> mip = image->get_image_from_mipmap(mipmap_i); + basisu::image buimg(mip->get_width(), mip->get_height()); + Vector<uint8_t> vec = mip->get_data(); + const uint8_t *r = vec.ptr(); + memcpy(buimg.get_ptr(), r, vec.size()); + source_images.push_back(buimg); } - basisu::basis_compressor_params params; params.m_uastc = true; - params.m_max_endpoint_clusters = 512; - params.m_max_selector_clusters = 512; + params.m_quality_level = basisu::BASISU_QUALITY_MIN; + + params.m_pack_uastc_flags &= ~basisu::cPackUASTCLevelMask; + + static const uint32_t s_level_flags[basisu::TOTAL_PACK_UASTC_LEVELS] = { basisu::cPackUASTCLevelFastest, basisu::cPackUASTCLevelFaster, basisu::cPackUASTCLevelDefault, basisu::cPackUASTCLevelSlower, basisu::cPackUASTCLevelVerySlow }; + params.m_pack_uastc_flags |= s_level_flags[0]; + params.m_rdo_uastc = 0.0f; + params.m_rdo_uastc_quality_scalar = 0.0f; + params.m_rdo_uastc_dict_size = 1024; + + params.m_mip_fast = true; params.m_multithreading = true; - //params.m_quality_level = 0; - //params.m_disable_hierarchical_endpoint_codebooks = true; - //params.m_no_selector_rdo = true; basisu::job_pool jpool(OS::get_singleton()->get_processor_count()); params.m_pJob_pool = &jpool; - params.m_mip_gen = false; //sorry, please some day support provided mipmaps. - params.m_source_images.push_back(buimg); - BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG; params.m_check_for_alpha = false; @@ -252,8 +258,7 @@ static Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size }; }; - image.instantiate(); - image->set_data(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); + image = Image::create_from_data(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); return image; } diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 9cc3d0413d..13c7a8202c 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -558,7 +558,7 @@ void CSGShape3D::_notification(int p_what) { set_collision_layer(collision_layer); set_collision_mask(collision_mask); set_collision_priority(collision_priority); - _update_collision_faces(); + _make_dirty(); } } break; @@ -1763,7 +1763,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { } } - if (!path) { + if (!path || !path->is_inside_tree()) { return new_brush; } diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 4982b6b995..f19228cb18 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -129,7 +129,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const } } -void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels) { +void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) { if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) { return; //do not compress, already compressed } diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h index 5dc8b6f52c..ca88a9d4c9 100644 --- a/modules/cvtt/image_compress_cvtt.h +++ b/modules/cvtt/image_compress_cvtt.h @@ -33,7 +33,7 @@ #include "core/io/image.h" -void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels); +void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels); void image_decompress_cvtt(Image *p_image); #endif // IMAGE_COMPRESS_CVTT_H diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml index 8c84fe87d7..dc832976d9 100644 --- a/modules/enet/doc_classes/ENetConnection.xml +++ b/modules/enet/doc_classes/ENetConnection.xml @@ -84,19 +84,17 @@ </method> <method name="dtls_client_setup"> <return type="int" enum="Error" /> - <param index="0" name="certificate" type="X509Certificate" /> - <param index="1" name="hostname" type="String" /> - <param index="2" name="verify" type="bool" default="true" /> + <param index="0" name="hostname" type="String" /> + <param index="1" name="client_options" type="TLSOptions" default="null" /> <description> - Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet clients. Call this before [method connect_to_host] to have ENet connect using DTLS with [code]certificate[/code] and [code]hostname[/code] verification. Verification can be optionally turned off via the [code]verify[/code] parameter. + Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet clients. Call this before [method connect_to_host] to have ENet connect using DTLS validating the server certificate against [code]hostname[/code]. You can pass the optional [param client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe]. </description> </method> <method name="dtls_server_setup"> <return type="int" enum="Error" /> - <param index="0" name="key" type="CryptoKey" /> - <param index="1" name="certificate" type="X509Certificate" /> + <param index="0" name="server_options" type="TLSOptions" /> <description> - Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet servers. Call this right after [method create_host_bound] to have ENet expect peers to connect using DTLS. + Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet servers. Call this right after [method create_host_bound] to have ENet expect peers to connect using DTLS. See [method TLSOptions.server]. </description> </method> <method name="flush"> diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp index d16e7d7c4a..804263186f 100644 --- a/modules/enet/enet_connection.cpp +++ b/modules/enet/enet_connection.cpp @@ -273,10 +273,11 @@ TypedArray<ENetPacketPeer> ENetConnection::_get_peers() { return out; } -Error ENetConnection::dtls_server_setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert) { +Error ENetConnection::dtls_server_setup(const Ref<TLSOptions> &p_options) { #ifdef GODOT_ENET ERR_FAIL_COND_V_MSG(!host, ERR_UNCONFIGURED, "The ENetConnection instance isn't currently active."); - return enet_host_dtls_server_setup(host, p_key.ptr(), p_cert.ptr()) ? FAILED : OK; + ERR_FAIL_COND_V(p_options.is_null() || !p_options->is_server(), ERR_INVALID_PARAMETER); + return enet_host_dtls_server_setup(host, const_cast<TLSOptions *>(p_options.ptr())) ? FAILED : OK; #else ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "ENet DTLS support not available in this build."); #endif @@ -291,10 +292,11 @@ void ENetConnection::refuse_new_connections(bool p_refuse) { #endif } -Error ENetConnection::dtls_client_setup(Ref<X509Certificate> p_cert, const String &p_hostname, bool p_verify) { +Error ENetConnection::dtls_client_setup(const String &p_hostname, const Ref<TLSOptions> &p_options) { #ifdef GODOT_ENET ERR_FAIL_COND_V_MSG(!host, ERR_UNCONFIGURED, "The ENetConnection instance isn't currently active."); - return enet_host_dtls_client_setup(host, p_cert.ptr(), p_verify, p_hostname.utf8().get_data()) ? FAILED : OK; + ERR_FAIL_COND_V(p_options.is_null() || p_options->is_server(), ERR_INVALID_PARAMETER); + return enet_host_dtls_client_setup(host, p_hostname.utf8().get_data(), const_cast<TLSOptions *>(p_options.ptr())) ? FAILED : OK; #else ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "ENet DTLS support not available in this build."); #endif @@ -351,8 +353,8 @@ void ENetConnection::_bind_methods() { ClassDB::bind_method(D_METHOD("channel_limit", "limit"), &ENetConnection::channel_limit); ClassDB::bind_method(D_METHOD("broadcast", "channel", "packet", "flags"), &ENetConnection::_broadcast); ClassDB::bind_method(D_METHOD("compress", "mode"), &ENetConnection::compress); - ClassDB::bind_method(D_METHOD("dtls_server_setup", "key", "certificate"), &ENetConnection::dtls_server_setup); - ClassDB::bind_method(D_METHOD("dtls_client_setup", "certificate", "hostname", "verify"), &ENetConnection::dtls_client_setup, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("dtls_server_setup", "server_options"), &ENetConnection::dtls_server_setup); + ClassDB::bind_method(D_METHOD("dtls_client_setup", "hostname", "client_options"), &ENetConnection::dtls_client_setup, DEFVAL(Ref<TLSOptions>())); ClassDB::bind_method(D_METHOD("refuse_new_connections", "refuse"), &ENetConnection::refuse_new_connections); ClassDB::bind_method(D_METHOD("pop_statistic", "statistic"), &ENetConnection::pop_statistic); ClassDB::bind_method(D_METHOD("get_max_channels"), &ENetConnection::get_max_channels); diff --git a/modules/enet/enet_connection.h b/modules/enet/enet_connection.h index 9e444911cc..481afc48bb 100644 --- a/modules/enet/enet_connection.h +++ b/modules/enet/enet_connection.h @@ -128,8 +128,8 @@ public: int get_local_port() const; // Godot additions - Error dtls_server_setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert); - Error dtls_client_setup(Ref<X509Certificate> p_cert, const String &p_hostname, bool p_verify = true); + Error dtls_server_setup(const Ref<TLSOptions> &p_options); + Error dtls_client_setup(const String &p_hostname, const Ref<TLSOptions> &p_options); void refuse_new_connections(bool p_refuse); ENetConnection() {} diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index 50ea0dd37a..93a20ab1f8 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -363,7 +363,7 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size #ifdef DEBUG_ENABLED if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size > ENET_HOST_DEFAULT_MTU) { - WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size, ENET_HOST_DEFAULT_MTU)); + WARN_PRINT_ONCE(vformat("Sending %d bytes unreliably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size, ENET_HOST_DEFAULT_MTU)); } #endif diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp index a6aeec54cc..16a59d3880 100644 --- a/modules/etcpak/image_compress_etcpak.cpp +++ b/modules/etcpak/image_compress_etcpak.cpp @@ -74,25 +74,23 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) { } } -void _compress_etc1(Image *r_img, float p_lossy_quality) { - _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img, p_lossy_quality); +void _compress_etc1(Image *r_img) { + _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img); } -void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) { +void _compress_etc2(Image *r_img, Image::UsedChannels p_channels) { EtcpakType type = _determine_etc_type(p_channels); - _compress_etcpak(type, r_img, p_lossy_quality); + _compress_etcpak(type, r_img); } -void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) { +void _compress_bc(Image *r_img, Image::UsedChannels p_channels) { EtcpakType type = _determine_dxt_type(p_channels); - _compress_etcpak(type, r_img, p_lossy_quality); + _compress_etcpak(type, r_img); } -void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality) { +void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) { uint64_t start_time = OS::get_singleton()->get_ticks_msec(); - // TODO: See how to handle lossy quality. - Image::Format img_format = r_img->get_format(); if (img_format >= Image::FORMAT_DXT1) { return; // Do not compress, already compressed. diff --git a/modules/etcpak/image_compress_etcpak.h b/modules/etcpak/image_compress_etcpak.h index 8cb17b1c8a..ff267631a6 100644 --- a/modules/etcpak/image_compress_etcpak.h +++ b/modules/etcpak/image_compress_etcpak.h @@ -43,10 +43,10 @@ enum class EtcpakType { ETCPAK_TYPE_DXT5_RA_AS_RG, }; -void _compress_etc1(Image *r_img, float p_lossy_quality); -void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels); -void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels); +void _compress_etc1(Image *r_img); +void _compress_etc2(Image *r_img, Image::UsedChannels p_channels); +void _compress_bc(Image *r_img, Image::UsedChannels p_channels); -void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality); +void _compress_etcpak(EtcpakType p_compresstype, Image *r_img); #endif // IMAGE_COMPRESS_ETCPAK_H diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 3fe741a582..7f9d4ae253 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -316,12 +316,21 @@ <return type="void" /> <param index="0" name="names" type="String" /> <description> - Export a [String] or integer property as an enumerated list of options. If the property is an integer field, then the index of the value is stored, in the same order the values are provided. You can add specific identifiers for allowed values using a colon. + Export an [int] or [String] property as an enumerated list of options. If the property is an [int], then the index of the value is stored, in the same order the values are provided. You can add specific identifiers for allowed values using a colon. If the property is a [String], then the value is stored. See also [constant PROPERTY_HINT_ENUM]. [codeblock] - @export_enum("Rebecca", "Mary", "Leah") var character_name: String @export_enum("Warrior", "Magician", "Thief") var character_class: int @export_enum("Slow:30", "Average:60", "Very Fast:200") var character_speed: int + @export_enum("Rebecca", "Mary", "Leah") var character_name: String + [/codeblock] + If you want to set an initial value, you must specify it explicitly: + [codeblock] + @export_enum("Rebecca", "Mary", "Leah") var character_name: String = "Rebecca" + [/codeblock] + If you want to use named GDScript enums, then use [annotation @export] instead: + [codeblock] + enum CharacterName {REBECCA, MARY, LEAH} + @export var character_name: CharacterName [/codeblock] </description> </annotation> @@ -485,7 +494,7 @@ Export a [NodePath] property with a filter for allowed node types. See also [constant PROPERTY_HINT_NODE_PATH_VALID_TYPES]. [codeblock] - @export_node_path(Button, TouchScreenButton) var some_button + @export_node_path("Button", "TouchScreenButton") var some_button [/codeblock] </description> </annotation> @@ -507,7 +516,7 @@ <param index="2" name="step" type="float" default="1.0" /> <param index="3" name="extra_hints" type="String" default="""" /> <description> - Export a numeric property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [code]EditorSettings.interface/inspector/default_float_step[/code] setting. + Export an [int] or [float] property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [code]EditorSettings.interface/inspector/default_float_step[/code] setting. If hints [code]"or_greater"[/code] and [code]"or_less"[/code] are provided, the editor widget will not cap the value at range boundaries. The [code]"exp"[/code] hint will make the edited values on range to change exponentially. The [code]"hide_slider"[/code] hint will hide the slider element of the editor widget. Hints also allow to indicate the units for the edited value. Using [code]"radians"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock. [code]"degrees"[/code] allows to add a degree sign as a unit suffix. Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string. See also [constant PROPERTY_HINT_RANGE]. diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 23be913a24..5883ec863d 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -307,7 +307,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_number = true; } - if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !in_number) { + if (!in_word && is_unicode_identifier_start(str[j]) && !in_number) { in_word = true; } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 6b325d6451..d9b8a540c0 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -98,7 +98,7 @@ Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p return Object::callp(p_method, p_args, p_argcount, r_error); } MethodBind *method = ClassDB::get_method(name, p_method); - if (method) { + if (method && method->is_static()) { // Native static method. return method->call(nullptr, p_args, p_argcount, r_error); } @@ -2448,7 +2448,6 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const { } String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { - Vector<uint8_t> sourcef; Error err; Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { @@ -2459,88 +2458,31 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b GDScriptParser parser; err = parser.parse(source, p_path, false); + if (err) { + return String(); + } - // TODO: Simplify this code by using the analyzer to get full inheritance. - if (err == OK) { - const GDScriptParser::ClassNode *c = parser.get_tree(); - if (r_icon_path) { - if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) { - *r_icon_path = c->icon_path; - } else if (c->icon_path.is_relative_path()) { - *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path(); - } - } - if (r_base_type) { - const GDScriptParser::ClassNode *subclass = c; - String path = p_path; - GDScriptParser subparser; - while (subclass) { - if (subclass->extends_used) { - if (!subclass->extends_path.is_empty()) { - if (subclass->extends.size() == 0) { - get_global_class_name(subclass->extends_path, r_base_type); - subclass = nullptr; - break; - } else { - Vector<StringName> extend_classes = subclass->extends; - - Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); - if (subfile.is_null()) { - break; - } - String subsource = subfile->get_as_utf8_string(); - - if (subsource.is_empty()) { - break; - } - String subpath = subclass->extends_path; - if (subpath.is_relative_path()) { - subpath = path.get_base_dir().path_join(subpath).simplify_path(); - } - - if (OK != subparser.parse(subsource, subpath, false)) { - break; - } - path = subpath; - subclass = subparser.get_tree(); - - while (extend_classes.size() > 0) { - bool found = false; - for (int i = 0; i < subclass->members.size(); i++) { - if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) { - continue; - } - - const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class; - if (inner_class->identifier->name == extend_classes[0]) { - extend_classes.remove_at(0); - found = true; - subclass = inner_class; - break; - } - } - if (!found) { - subclass = nullptr; - break; - } - } - } - } else if (subclass->extends.size() == 1) { - *r_base_type = subclass->extends[0]; - subclass = nullptr; - } else { - break; - } - } else { - *r_base_type = "RefCounted"; - subclass = nullptr; - } - } + GDScriptAnalyzer analyzer(&parser); + err = analyzer.resolve_inheritance(); + if (err) { + return String(); + } + + const GDScriptParser::ClassNode *c = parser.get_tree(); + + if (r_base_type) { + *r_base_type = c->get_datatype().native_type; + } + + if (r_icon_path) { + if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) { + *r_icon_path = c->icon_path.simplify_path(); + } else if (c->icon_path.is_relative_path()) { + *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path(); } - return c->identifier != nullptr ? String(c->identifier->name) : String(); } - return String(); + return c->identifier != nullptr ? String(c->identifier->name) : String(); } GDScriptLanguage::GDScriptLanguage() { diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index e04a962dcb..8dd65a700a 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -160,19 +160,6 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) { return type; } -static StringName enum_get_value_name(const GDScriptParser::DataType p_type, int64_t p_val) { - // Check that an enum has a given value, not key. - // Make sure that implicit conversion to int64_t is sensible before calling! - HashMap<StringName, int64_t>::ConstIterator i = p_type.enum_values.begin(); - while (i) { - if (i->value == p_val) { - return i->key; - } - ++i; - } - return StringName(); -} - bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_member) { if (p_class->members_indices.has(p_member_name)) { int index = p_class->members_indices[p_member_name]; @@ -802,6 +789,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) { + resolve_annotation(E); E->apply(parser, member.variable); } } break; @@ -812,6 +800,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) { + resolve_annotation(E); E->apply(parser, member.constant); } } break; @@ -835,6 +824,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) { + resolve_annotation(E); E->apply(parser, member.signal); } } break; @@ -876,12 +866,13 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, current_enum = prev_enum; - dictionary.set_read_only(true); + dictionary.make_read_only(); member.m_enum->set_datatype(enum_type); member.m_enum->dictionary = dictionary; // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) { + resolve_annotation(E); E->apply(parser, member.m_enum); } } break; @@ -1058,12 +1049,13 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co resolve_class_body(base_class, p_class); } - // Do functions and properties now. + // Do functions, properties, and groups now. for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) { // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.function->annotations) { + resolve_annotation(E); E->apply(parser, member.function); } @@ -1097,6 +1089,10 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co resolve_function_body(member.variable->setter); } } + } else if (member.type == GDScriptParser::ClassNode::Member::GROUP) { + // Apply annotation (`@export_{category,group,subgroup}`). + resolve_annotation(member.annotation); + member.annotation->apply(parser, nullptr); } } @@ -1290,7 +1286,55 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root } void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_annotation) { - // TODO: Add second validation function for annotations, so they can use checked types. + ERR_FAIL_COND_MSG(!parser->valid_annotations.has(p_annotation->name), vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name)); + + const MethodInfo &annotation_info = parser->valid_annotations[p_annotation->name].info; + + const List<PropertyInfo>::Element *E = annotation_info.arguments.front(); + for (int i = 0; i < p_annotation->arguments.size(); i++) { + GDScriptParser::ExpressionNode *argument = p_annotation->arguments[i]; + const PropertyInfo &argument_info = E->get(); + + if (E->next() != nullptr) { + E = E->next(); + } + + reduce_expression(argument); + + if (!argument->is_constant) { + push_error(vformat(R"(Argument %d of annotation "%s" isn't a constant expression.)", i + 1, p_annotation->name), argument); + return; + } + + Variant value = argument->reduced_value; + + if (value.get_type() != argument_info.type) { +#ifdef DEBUG_ENABLED + if (argument_info.type == Variant::INT && value.get_type() == Variant::FLOAT) { + parser->push_warning(argument, GDScriptWarning::NARROWING_CONVERSION); + } +#endif + + if (!Variant::can_convert_strict(value.get_type(), argument_info.type)) { + push_error(vformat(R"(Invalid argument for annotation "%s": argument %d should be "%s" but is "%s".)", p_annotation->name, i + 1, Variant::get_type_name(argument_info.type), argument->get_datatype().to_string()), argument); + return; + } + + Variant converted_to; + const Variant *converted_from = &value; + Callable::CallError call_error; + Variant::construct(argument_info.type, converted_to, &converted_from, 1, call_error); + + if (call_error.error != Callable::CallError::CALL_OK) { + push_error(vformat(R"(Cannot convert argument %d of annotation "%s" from "%s" to "%s".)", i + 1, p_annotation->name, Variant::get_type_name(value.get_type()), Variant::get_type_name(argument_info.type)), argument); + return; + } + + value = converted_to; + } + + p_annotation->resolved_arguments.push_back(value); + } } void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source, bool p_is_lambda) { @@ -1486,8 +1530,10 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) { for (int i = 0; i < p_suite->statements.size(); i++) { GDScriptParser::Node *stmt = p_suite->statements[i]; - for (GDScriptParser::AnnotationNode *&annotation : stmt->annotations) { - annotation->apply(parser, stmt); + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : stmt->annotations) { + resolve_annotation(E); + E->apply(parser, stmt); } #ifdef DEBUG_ENABLED @@ -1541,13 +1587,14 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi } } + if (has_specified_type && p_assignable->initializer->is_constant) { + update_const_expression_builtin_type(p_assignable->initializer, specified_type, "assign"); + } GDScriptParser::DataType initializer_type = p_assignable->initializer->get_datatype(); if (p_assignable->infer_datatype) { - if (!initializer_type.is_set() || initializer_type.has_no_type()) { + if (!initializer_type.is_set() || initializer_type.has_no_type() || !initializer_type.is_hard_type()) { push_error(vformat(R"(Cannot infer the type of "%s" %s because the value doesn't have a set type.)", p_assignable->identifier->name, p_kind), p_assignable->initializer); - } else if (initializer_type.is_variant() && !initializer_type.is_hard_type()) { - push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is Variant. Use explicit "Variant" type if this is intended.)", p_assignable->identifier->name, p_kind), p_assignable->initializer); } else if (initializer_type.kind == GDScriptParser::DataType::BUILTIN && initializer_type.builtin_type == Variant::NIL && !is_constant) { push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is "null".)", p_assignable->identifier->name, p_kind), p_assignable->initializer); } @@ -1577,7 +1624,7 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi downgrade_node_type_source(p_assignable->initializer); } } else if (!is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) { - if (!is_constant && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) { + if (!is_constant && is_type_compatible(initializer_type, specified_type)) { mark_node_unsafe(p_assignable->initializer); p_assignable->use_conversion_assign = true; } else { @@ -1848,11 +1895,22 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc break; case GDScriptParser::PatternNode::PT_EXPRESSION: if (p_match_pattern->expression) { - reduce_expression(p_match_pattern->expression); - if (!p_match_pattern->expression->is_constant) { - push_error(R"(Expression in match pattern must be a constant.)", p_match_pattern->expression); + GDScriptParser::ExpressionNode *expr = p_match_pattern->expression; + reduce_expression(expr); + result = expr->get_datatype(); + if (!expr->is_constant) { + while (expr && expr->type == GDScriptParser::Node::SUBSCRIPT) { + GDScriptParser::SubscriptNode *sub = static_cast<GDScriptParser::SubscriptNode *>(expr); + if (!sub->is_attribute) { + expr = nullptr; + } else { + expr = sub->base; + } + } + if (!expr || expr->type != GDScriptParser::Node::IDENTIFIER) { + push_error(R"(Expression in match pattern must be a constant expression, an identifier, or an attribute access ("A.B").)", expr); + } } - result = p_match_pattern->expression->get_datatype(); } break; case GDScriptParser::PatternNode::PT_BIND: @@ -1905,11 +1963,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { GDScriptParser::DataType result; GDScriptParser::DataType expected_type; - bool has_expected_type = false; - - if (parser->current_function != nullptr) { + bool has_expected_type = parser->current_function != nullptr; + if (has_expected_type) { expected_type = parser->current_function->get_datatype(); - has_expected_type = true; } if (p_return->return_value != nullptr) { @@ -1923,6 +1979,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) { push_error("A void function cannot return a value.", p_return); } + if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) { + update_const_expression_builtin_type(p_return->return_value, expected_type, "return"); + } result = p_return->return_value->get_datatype(); } else { // Return type is null by default. @@ -1932,24 +1991,21 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { result.is_constant = true; } - if (has_expected_type) { - expected_type.is_meta_type = false; - if (expected_type.is_hard_type()) { - if (!is_type_compatible(expected_type, result)) { - // Try other way. Okay but not safe. - if (!is_type_compatible(result, expected_type)) { - push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return); - } else { - // TODO: Add warning. - mark_node_unsafe(p_return); - } + if (has_expected_type && !expected_type.is_variant()) { + if (result.is_variant() || !result.is_hard_type()) { + mark_node_unsafe(p_return); + if (!is_type_compatible(expected_type, result, true, p_return)) { + downgrade_node_type_source(p_return); + } + } else if (!is_type_compatible(expected_type, result, true, p_return)) { + mark_node_unsafe(p_return); + if (!is_type_compatible(result, expected_type)) { + push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return); + } #ifdef DEBUG_ENABLED - } else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) { - parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION); - } else if (result.is_variant()) { - mark_node_unsafe(p_return); + } else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) { + parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION); #endif - } } } @@ -2014,7 +2070,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre reduce_subscript(static_cast<GDScriptParser::SubscriptNode *>(p_expression)); break; case GDScriptParser::Node::TERNARY_OPERATOR: - reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression)); + reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression), p_is_root); break; case GDScriptParser::Node::UNARY_OPERATOR: reduce_unary_op(static_cast<GDScriptParser::UnaryOpNode *>(p_expression)); @@ -2063,6 +2119,68 @@ void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) { p_array->set_datatype(arr_type); } +#ifdef DEBUG_ENABLED +static bool enum_has_value(const GDScriptParser::DataType p_type, int64_t p_value) { + for (const KeyValue<StringName, int64_t> &E : p_type.enum_values) { + if (E.value == p_value) { + return true; + } + } + return false; +} +#endif + +void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::ExpressionNode *p_expression, const GDScriptParser::DataType &p_type, const char *p_usage, bool p_is_cast) { + if (p_expression->get_datatype() == p_type) { + return; + } + if (p_type.kind != GDScriptParser::DataType::BUILTIN && p_type.kind != GDScriptParser::DataType::ENUM) { + return; + } + + GDScriptParser::DataType expression_type = p_expression->get_datatype(); + bool is_enum_cast = p_is_cast && p_type.kind == GDScriptParser::DataType::ENUM && p_type.is_meta_type == false && expression_type.builtin_type == Variant::INT; + if (!is_enum_cast && !is_type_compatible(p_type, expression_type, true, p_expression)) { + push_error(vformat(R"(Cannot %s a value of type "%s" as "%s".)", p_usage, expression_type.to_string(), p_type.to_string()), p_expression); + return; + } + + GDScriptParser::DataType value_type = type_from_variant(p_expression->reduced_value, p_expression); + if (expression_type.is_variant() && !is_enum_cast && !is_type_compatible(p_type, value_type, true, p_expression)) { + push_error(vformat(R"(Cannot %s a value of type "%s" as "%s".)", p_usage, value_type.to_string(), p_type.to_string()), p_expression); + return; + } + +#ifdef DEBUG_ENABLED + if (p_type.kind == GDScriptParser::DataType::ENUM && value_type.builtin_type == Variant::INT && !enum_has_value(p_type, p_expression->reduced_value)) { + parser->push_warning(p_expression, GDScriptWarning::INT_AS_ENUM_WITHOUT_MATCH, p_usage, p_expression->reduced_value.stringify(), p_type.to_string()); + } +#endif + + if (value_type.builtin_type == p_type.builtin_type) { + p_expression->set_datatype(p_type); + return; + } + + Variant converted_to; + const Variant *converted_from = &p_expression->reduced_value; + Callable::CallError call_error; + Variant::construct(p_type.builtin_type, converted_to, &converted_from, 1, call_error); + if (call_error.error) { + push_error(vformat(R"(Failed to convert a value of type "%s" to "%s".)", value_type.to_string(), p_type.to_string()), p_expression); + return; + } + +#ifdef DEBUG_ENABLED + if (p_type.builtin_type == Variant::INT && value_type.builtin_type == Variant::FLOAT) { + parser->push_warning(p_expression, GDScriptWarning::NARROWING_CONVERSION); + } +#endif + + p_expression->reduced_value = converted_to; + p_expression->set_datatype(p_type); +} + // When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed. // This function determines which type is that (if any). void GDScriptAnalyzer::update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal) { @@ -2129,6 +2247,10 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value)); } + if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) { + update_const_expression_builtin_type(p_assignment->assigned_value, assignee_type, "assign"); + } + GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype(); bool assignee_is_variant = assignee_type.is_variant(); @@ -2189,7 +2311,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig // non-variant assignee and incompatible result mark_node_unsafe(p_assignment); if (assignee_is_hard) { - if (is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) { + if (is_type_compatible(op_type, assignee_type)) { // hard non-variant assignee and maybe compatible result p_assignment->use_conversion_assign = true; } else { @@ -2305,7 +2427,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o GDScriptParser::DataType test_type = right_type; test_type.is_meta_type = false; - if (!is_type_compatible(test_type, left_type, false)) { + if (!is_type_compatible(test_type, left_type)) { push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)"), p_binary_op->left_operand); p_binary_op->reduced_value = false; } else { @@ -2322,43 +2444,41 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o GDScriptParser::DataType result; - if (left_type.is_variant() || right_type.is_variant()) { - // Cannot infer type because one operand can be anything. - result.kind = GDScriptParser::DataType::VARIANT; - mark_node_unsafe(p_binary_op); - } else { - if (p_binary_op->variant_op < Variant::OP_MAX) { - bool valid = false; - result = get_operation_type(p_binary_op->variant_op, left_type, right_type, valid, p_binary_op); - - if (!valid) { - push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", left_type.to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op); - } - } else { - if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) { - GDScriptParser::DataType test_type = right_type; - test_type.is_meta_type = false; - - if (!is_type_compatible(test_type, left_type, false)) { - // Test reverse as well to consider for subtypes. - if (!is_type_compatible(left_type, test_type, false)) { - if (left_type.is_hard_type()) { - push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", left_type.to_string(), test_type.to_string()), p_binary_op->left_operand); - } else { - // TODO: Warning. - mark_node_unsafe(p_binary_op); - } - } - } + if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) { + GDScriptParser::DataType test_type = right_type; + test_type.is_meta_type = false; - // "is" operator is always a boolean anyway. - result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - result.kind = GDScriptParser::DataType::BUILTIN; - result.builtin_type = Variant::BOOL; + if (!is_type_compatible(test_type, left_type) && !is_type_compatible(left_type, test_type)) { + if (left_type.is_hard_type()) { + push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", left_type.to_string(), test_type.to_string()), p_binary_op->left_operand); } else { - ERR_PRINT("Parser bug: unknown binary operation."); + // TODO: Warning. + mark_node_unsafe(p_binary_op); } } + + // "is" operator is always a boolean anyway. + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::BOOL; + } else if ((p_binary_op->variant_op == Variant::OP_EQUAL || p_binary_op->variant_op == Variant::OP_NOT_EQUAL) && + ((left_type.kind == GDScriptParser::DataType::BUILTIN && left_type.builtin_type == Variant::NIL) || (right_type.kind == GDScriptParser::DataType::BUILTIN && right_type.builtin_type == Variant::NIL))) { + // "==" and "!=" operators always return a boolean when comparing to null. + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::BOOL; + } else if (left_type.is_variant() || right_type.is_variant()) { + // Cannot infer type because one operand can be anything. + result.kind = GDScriptParser::DataType::VARIANT; + mark_node_unsafe(p_binary_op); + } else if (p_binary_op->variant_op < Variant::OP_MAX) { + bool valid = false; + result = get_operation_type(p_binary_op->variant_op, left_type, right_type, valid, p_binary_op); + if (!valid) { + push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", left_type.to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op); + } + } else { + ERR_PRINT("Parser bug: unknown binary operation."); } p_binary_op->set_datatype(result); @@ -2504,6 +2624,11 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } if (types_match) { + for (int i = 0; i < p_call->arguments.size(); i++) { + if (p_call->arguments[i]->is_constant) { + update_const_expression_builtin_type(p_call->arguments[i], type_from_property(info.arguments[i], true), "pass"); + } + } match = true; call_type = type_from_property(info.return_val); break; @@ -2800,67 +2925,39 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { } p_cast->set_datatype(cast_type); + if (p_cast->operand->is_constant) { + update_const_expression_builtin_type(p_cast->operand, cast_type, "cast", true); + if (cast_type.is_variant() || p_cast->operand->get_datatype() == cast_type) { + p_cast->is_constant = true; + p_cast->reduced_value = p_cast->operand->reduced_value; + } + } if (!cast_type.is_variant()) { GDScriptParser::DataType op_type = p_cast->operand->get_datatype(); - if (!op_type.is_variant()) { + if (op_type.is_variant() || !op_type.is_hard_type()) { + mark_node_unsafe(p_cast); +#ifdef DEBUG_ENABLED + if (op_type.is_variant() && !op_type.is_hard_type()) { + parser->push_warning(p_cast, GDScriptWarning::UNSAFE_CAST, cast_type.to_string()); + } +#endif + } else { bool valid = false; - bool more_informative_error = false; - if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) { - // Enum casts are compatible when value from operand exists in target enum - if (p_cast->operand->is_constant && p_cast->operand->reduced) { - if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) { - valid = true; - } else { - valid = false; - more_informative_error = true; - push_error(vformat(R"(Invalid cast. Enum "%s" does not have value corresponding to "%s.%s" (%d).)", - cast_type.to_string(), op_type.enum_type, - enum_get_value_name(op_type, p_cast->operand->reduced_value), // Can never be null - p_cast->operand->reduced_value.operator uint64_t()), - p_cast->cast_type); - } - } else { - // Can't statically tell whether int has a corresponding enum value. Valid but dangerous! - mark_node_unsafe(p_cast); - valid = true; - } - } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) { - // Int assignment to enum not valid when exact int assigned is known but is not an enum value - if (p_cast->operand->is_constant && p_cast->operand->reduced) { - if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) { - valid = true; - } else { - valid = false; - more_informative_error = true; - push_error(vformat(R"(Invalid cast. Enum "%s" does not have enum value %d.)", cast_type.to_string(), p_cast->operand->reduced_value.operator uint64_t()), p_cast->cast_type); - } - } else { - // Can't statically tell whether int has a corresponding enum value. Valid but dangerous! - mark_node_unsafe(p_cast); - valid = true; - } + if (op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) { + mark_node_unsafe(p_cast); + valid = true; } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) { valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type); } else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) { valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type); } - if (!valid && !more_informative_error) { + if (!valid) { push_error(vformat(R"(Invalid cast. Cannot convert from "%s" to "%s".)", op_type.to_string(), cast_type.to_string()), p_cast->cast_type); } } - } else { - mark_node_unsafe(p_cast); } -#ifdef DEBUG_ENABLED - if (p_cast->operand->get_datatype().is_variant()) { - parser->push_warning(p_cast, GDScriptWarning::UNSAFE_CAST, cast_type.to_string()); - mark_node_unsafe(p_cast); - } -#endif - - // TODO: Perform cast on constants. } void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) { @@ -3534,6 +3631,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } #endif result_type.kind = GDScriptParser::DataType::VARIANT; + mark_node_unsafe(p_subscript); } } if (!valid) { @@ -3735,10 +3833,10 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri p_subscript->set_datatype(result_type); } -void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op) { +void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root) { reduce_expression(p_ternary_op->condition); - reduce_expression(p_ternary_op->true_expr); - reduce_expression(p_ternary_op->false_expr); + reduce_expression(p_ternary_op->true_expr, p_is_root); + reduce_expression(p_ternary_op->false_expr, p_is_root); GDScriptParser::DataType result; @@ -3838,7 +3936,7 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool array[i] = p_array->elements[i]->reduced_value; } if (p_is_const) { - array.set_read_only(true); + array.make_read_only(); } p_array->is_constant = true; p_array->reduced_value = array; @@ -3865,7 +3963,7 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d dict[element.key->reduced_value] = element.value->reduced_value; } if (p_is_const) { - dict.set_read_only(true); + dict.make_read_only(); } p_dictionary->is_constant = true; p_dictionary->reduced_value = dict; @@ -4158,26 +4256,22 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD return true; } -bool GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call) { +void GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call) { List<GDScriptParser::DataType> arg_types; for (const PropertyInfo &E : p_method.arguments) { arg_types.push_back(type_from_property(E, true)); } - return validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call); + validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call); } -bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call) { - bool valid = true; - +void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call) { if (p_call->arguments.size() < p_par_types.size() - p_default_args_count) { push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", p_call->function_name, p_par_types.size() - p_default_args_count, p_call->arguments.size()), p_call); - valid = false; } if (!p_is_vararg && p_call->arguments.size() > p_par_types.size()) { push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", p_call->function_name, p_par_types.size(), p_call->arguments.size()), p_call->arguments[p_par_types.size()]); - valid = false; } for (int i = 0; i < p_call->arguments.size(); i++) { @@ -4186,9 +4280,13 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p break; } GDScriptParser::DataType par_type = p_par_types[i]; + + if (par_type.is_hard_type() && p_call->arguments[i]->is_constant) { + update_const_expression_builtin_type(p_call->arguments[i], par_type, "pass"); + } GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype(); - if (arg_type.is_variant()) { + if ((arg_type.is_variant() || !arg_type.is_hard_type()) && !(par_type.is_hard_type() && par_type.is_variant())) { // Argument can be anything, so this is unsafe. mark_node_unsafe(p_call->arguments[i]); } else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) { @@ -4198,17 +4296,13 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()), p_call->arguments[i]); - valid = false; } #ifdef DEBUG_ENABLED - } else { - if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) { - parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name); - } + } else if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) { + parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name); #endif } } - return valid; } #ifdef DEBUG_ENABLED @@ -4360,7 +4454,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) { #ifdef DEBUG_ENABLED if (p_source_node) { - parser->push_warning(p_source_node, GDScriptWarning::INT_ASSIGNED_TO_ENUM); + parser->push_warning(p_source_node, GDScriptWarning::INT_AS_ENUM_WITHOUT_CAST); } #endif return true; @@ -4552,6 +4646,11 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) { } Error GDScriptAnalyzer::resolve_inheritance() { + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) { + resolve_annotation(E); + E->apply(parser, parser->head); + } return resolve_class_inheritance(parser->head, true); } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index b22d47982f..cd2c4c6569 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -99,7 +99,7 @@ class GDScriptAnalyzer { void reduce_preload(GDScriptParser::PreloadNode *p_preload); void reduce_self(GDScriptParser::SelfNode *p_self); void reduce_subscript(GDScriptParser::SubscriptNode *p_subscript); - void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op); + void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const); @@ -112,10 +112,11 @@ class GDScriptAnalyzer { GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source); bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); - bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call); - bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call); + void validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call); + void validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call); GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source); GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source); + void update_const_expression_builtin_type(GDScriptParser::ExpressionNode *p_expression, const GDScriptParser::DataType &p_type, const char *p_usage, bool p_is_cast = false); void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal); bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr); void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index c78dd1528f..6acdc9f212 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -196,7 +196,11 @@ static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataTyp } } -static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { +static bool _can_use_ptrcall(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { + if (p_method->is_vararg()) { + // ptrcall won't work with vararg methods. + return false; + } if (p_method->get_argument_count() != p_arguments.size()) { // ptrcall won't work with default arguments. return false; @@ -563,7 +567,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code self.mode = GDScriptCodeGenerator::Address::SELF; MethodBind *method = ClassDB::get_method(codegen.script->native->get_name(), call->function_name); - if (_have_exact_arguments(method, arguments)) { + if (_can_use_ptrcall(method, arguments)) { // Exact arguments, use ptrcall. gen->write_call_ptrcall(result, self, method, arguments); } else { @@ -613,7 +617,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { MethodBind *method = ClassDB::get_method(class_name, call->function_name); - if (_have_exact_arguments(method, arguments)) { + if (_can_use_ptrcall(method, arguments)) { // Exact arguments, use ptrcall. gen->write_call_ptrcall(result, base, method, arguments); } else { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 3fc0924b4c..f88ac581ca 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -782,6 +782,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a } } else if (p_annotation->name == SNAME("@export_node_path")) { ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + node.insert_text = node.display.quote(p_quote_style); r_result.insert(node.display, node); List<StringName> node_types; ClassDB::get_inheriters_from_class("Node", &node_types); @@ -790,11 +791,13 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a continue; } ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS); + option.insert_text = option.display.quote(p_quote_style); r_result.insert(option.display, option); } } else if (p_annotation->name == SNAME("@warning_ignore")) { for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) { ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + warning.insert_text = warning.display.quote(p_quote_style); r_result.insert(warning.display, warning); } } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 99b78a8326..66374d0a6d 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -122,7 +122,7 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); // Export annotations. register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>); - register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, varray(), true); + register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true); register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true); register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>); register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true); @@ -529,7 +529,7 @@ void GDScriptParser::parse_program() { AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); if (annotation != nullptr) { if (annotation->applies_to(AnnotationInfo::SCRIPT)) { - annotation->apply(this, head); + head->annotations.push_back(annotation); } else { annotation_stack.push_back(annotation); // This annotation must appear after script-level annotations @@ -771,7 +771,6 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)() return; } - // Apply annotations. for (AnnotationNode *&annotation : annotations) { member->annotations.push_back(annotation); } @@ -841,14 +840,19 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) { case GDScriptTokenizer::Token::ANNOTATION: { advance(); - // Check for class-level annotations. + // Check for standalone and class-level annotations. AnnotationNode *annotation = parse_annotation(AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); if (annotation != nullptr) { if (annotation->applies_to(AnnotationInfo::STANDALONE)) { if (previous.type != GDScriptTokenizer::Token::NEWLINE) { push_error(R"(Expected newline after a standalone annotation.)"); } - annotation->apply(this, head); + if (annotation->name == "@export_category" || annotation->name == "@export_group" || annotation->name == "@export_subgroup") { + current_class->add_member_group(annotation); + } else { + // For potential non-group standalone annotations. + push_error(R"(Unexpected standalone annotation in class body.)"); + } } else { annotation_stack.push_back(annotation); } @@ -1470,7 +1474,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional. if (valid) { - valid = validate_annotation_arguments(annotation); + valid = validate_annotation_argument_count(annotation); } return valid ? annotation : nullptr; @@ -1701,6 +1705,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { case Node::CALL: case Node::ASSIGNMENT: case Node::AWAIT: + case Node::TERNARY_OPERATOR: // Fine. break; case Node::LAMBDA: @@ -1716,7 +1721,6 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { } } - // Apply annotations to statement. while (!is_annotation && result != nullptr && !annotation_stack.is_empty()) { AnnotationNode *last_annotation = annotation_stack.back()->get(); if (last_annotation->applies_to(AnnotationInfo::STATEMENT)) { @@ -1902,11 +1906,8 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { return match; } -#ifdef DEBUG_ENABLED bool all_have_return = true; bool have_wildcard = false; - bool have_wildcard_without_continue = false; -#endif while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) { MatchBranchNode *branch = parse_match_branch(); @@ -1916,31 +1917,22 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { } #ifdef DEBUG_ENABLED - if (have_wildcard_without_continue && !branch->patterns.is_empty()) { + if (have_wildcard && !branch->patterns.is_empty()) { push_warning(branch->patterns[0], GDScriptWarning::UNREACHABLE_PATTERN); } - - if (branch->has_wildcard) { - have_wildcard = true; - if (!branch->block->has_continue) { - have_wildcard_without_continue = true; - } - } - if (!branch->block->has_return) { - all_have_return = false; - } #endif + + have_wildcard = have_wildcard || branch->has_wildcard; + all_have_return = all_have_return && branch->block->has_return; match->branches.push_back(branch); } complete_extents(match); consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)"); -#ifdef DEBUG_ENABLED if (all_have_return && have_wildcard) { current_suite->has_return = true; } -#endif return match; } @@ -3597,7 +3589,7 @@ bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const { return (info->target_kind & p_target_kinds) > 0; } -bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) { +bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annotation) { ERR_FAIL_COND_V_MSG(!valid_annotations.has(p_annotation->name), false, vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name)); const MethodInfo &info = valid_annotations[p_annotation->name].info; @@ -3612,62 +3604,6 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) return false; } - const List<PropertyInfo>::Element *E = info.arguments.front(); - for (int i = 0; i < p_annotation->arguments.size(); i++) { - ExpressionNode *argument = p_annotation->arguments[i]; - const PropertyInfo ¶meter = E->get(); - - if (E->next() != nullptr) { - E = E->next(); - } - - switch (parameter.type) { - case Variant::STRING: - case Variant::STRING_NAME: - case Variant::NODE_PATH: - // Allow "quote-less strings", as long as they are recognized as identifiers. - if (argument->type == Node::IDENTIFIER) { - IdentifierNode *string = static_cast<IdentifierNode *>(argument); - Callable::CallError error; - Vector<Variant> args = varray(string->name); - const Variant *name = args.ptr(); - Variant r; - Variant::construct(parameter.type, r, &(name), 1, error); - p_annotation->resolved_arguments.push_back(r); - if (error.error != Callable::CallError::CALL_OK) { - push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name)); - p_annotation->resolved_arguments.remove_at(p_annotation->resolved_arguments.size() - 1); - return false; - } - break; - } - [[fallthrough]]; - default: { - if (argument->type != Node::LITERAL) { - push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name)); - return false; - } - - Variant value = static_cast<LiteralNode *>(argument)->value; - if (!Variant::can_convert_strict(value.get_type(), parameter.type)) { - push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name)); - return false; - } - Callable::CallError error; - const Variant *args = &value; - Variant r; - Variant::construct(parameter.type, r, &(args), 1, error); - p_annotation->resolved_arguments.push_back(r); - if (error.error != Callable::CallError::CALL_OK) { - push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name)); - p_annotation->resolved_arguments.remove_at(p_annotation->resolved_arguments.size() - 1); - return false; - } - break; - } - } - } - return true; } @@ -3722,6 +3658,10 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node String hint_string; for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) { + if (p_annotation->name != SNAME("@export_placeholder") && String(p_annotation->resolved_arguments[i]).contains(",")) { + push_error(vformat(R"(Argument %d of annotation "%s" contains a comma. Use separate arguments instead.)", i + 1, p_annotation->name), p_annotation->arguments[i]); + return false; + } if (i > 0) { hint_string += ","; } @@ -3866,6 +3806,24 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string; variable->export_info.type = Variant::ARRAY; } + } else if (p_annotation->name == SNAME("@export_enum")) { + Variant::Type enum_type = Variant::INT; + + if (export_type.kind == DataType::BUILTIN && export_type.builtin_type == Variant::STRING) { + enum_type = Variant::STRING; + } else if (export_type.is_variant() && variable->initializer != nullptr) { + DataType initializer_type = variable->initializer->get_datatype(); + if (initializer_type.kind == DataType::BUILTIN && initializer_type.builtin_type == Variant::STRING) { + enum_type = Variant::STRING; + } + } + + variable->export_info.type = enum_type; + + if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != enum_type)) { + push_error(vformat(R"("@export_enum" annotation requires a variable of type "int" or "String" but type "%s" was given instead.)", export_type.to_string()), variable); + return false; + } } else { // Validate variable type with export. if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != t_type)) { @@ -3906,7 +3864,6 @@ bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation } break; } - current_class->add_member_group(annotation); return true; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 5da709e8cd..74e12d0b5e 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1401,7 +1401,7 @@ private: // Annotations AnnotationNode *parse_annotation(uint32_t p_valid_targets); bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false); - bool validate_annotation_arguments(AnnotationNode *p_annotation); + bool validate_annotation_argument_count(AnnotationNode *p_annotation); void clear_unused_annotations(); bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target); bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target); diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index a6cbb7f6ae..024fed8517 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -148,9 +148,13 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(3); return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]); } - case INT_ASSIGNED_TO_ENUM: { + case INT_AS_ENUM_WITHOUT_CAST: { return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type."; } + case INT_AS_ENUM_WITHOUT_MATCH: { + CHECK_SYMBOLS(3); + return vformat(R"(Cannot %s %s as Enum "%s": no enum member has matching value.)", symbols[0], symbols[1], symbols[2]); + } break; case STATIC_CALLED_ON_INSTANCE: { CHECK_SYMBOLS(2); return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]); @@ -221,7 +225,8 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "REDUNDANT_AWAIT", "EMPTY_FILE", "SHADOWED_GLOBAL_IDENTIFIER", - "INT_ASSIGNED_TO_ENUM", + "INT_AS_ENUM_WITHOUT_CAST", + "INT_AS_ENUM_WITHOUT_MATCH", "STATIC_CALLED_ON_INSTANCE", "CONFUSABLE_IDENTIFIER", }; diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index b485f02b9c..7492972c1a 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -76,7 +76,8 @@ public: REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine). EMPTY_FILE, // A script file is empty. SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable. - INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting. + INT_AS_ENUM_WITHOUT_CAST, // An integer value was used as an enum value without casting. + INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member. STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e"). WARNING_MAX, diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index d2c8b5c317..5b8af0ff34 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -132,9 +132,10 @@ void finish_language() { StringName GDScriptTestRunner::test_function_name; -GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language) { +GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames) { test_function_name = StaticCString::create("test"); do_init_languages = p_init_language; + print_filenames = p_print_filenames; source_dir = p_source_dir; if (!source_dir.ends_with("/")) { @@ -194,6 +195,9 @@ int GDScriptTestRunner::run_tests() { int failed = 0; for (int i = 0; i < tests.size(); i++) { GDScriptTest test = tests[i]; + if (print_filenames) { + print_line(test.get_source_relative_filepath()); + } GDScriptTest::TestResult result = test.run_test(); String expected = FileAccess::get_file_as_string(test.get_output_file()); @@ -225,8 +229,13 @@ bool GDScriptTestRunner::generate_outputs() { } for (int i = 0; i < tests.size(); i++) { - OS::get_singleton()->print("."); GDScriptTest test = tests[i]; + if (print_filenames) { + print_line(test.get_source_relative_filepath()); + } else { + OS::get_singleton()->print("."); + } + bool result = test.generate_output(); if (!result) { @@ -337,15 +346,10 @@ GDScriptTest::GDScriptTest(const String &p_source_path, const String &p_output_p void GDScriptTestRunner::handle_cmdline() { List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); - // TODO: this could likely be ported to use test commands: - // https://github.com/godotengine/godot/pull/41355 - // Currently requires to startup the whole engine, which is slow. - String test_cmd = "--gdscript-test"; - String gen_cmd = "--gdscript-generate-tests"; for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) { String &cmd = E->get(); - if (cmd == test_cmd || cmd == gen_cmd) { + if (cmd == "--gdscript-generate-tests") { if (E->next() == nullptr) { ERR_PRINT("Needed a path for the test files."); exit(-1); @@ -353,14 +357,10 @@ void GDScriptTestRunner::handle_cmdline() { const String &path = E->next()->get(); - GDScriptTestRunner runner(path, false); - int failed = 0; - if (cmd == test_cmd) { - failed = runner.run_tests(); - } else { - bool completed = runner.generate_outputs(); - failed = completed ? 0 : -1; - } + GDScriptTestRunner runner(path, false, cmdline_args.find("--print-filenames") != nullptr); + + bool completed = runner.generate_outputs(); + int failed = completed ? 0 : -1; exit(failed); } } diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h index b097f1b485..60b48c6a57 100644 --- a/modules/gdscript/tests/gdscript_test_runner.h +++ b/modules/gdscript/tests/gdscript_test_runner.h @@ -92,6 +92,7 @@ public: bool generate_output(); const String &get_source_file() const { return source_file; } + const String get_source_relative_filepath() const { return source_file.trim_prefix(base_dir); } const String &get_output_file() const { return output_file; } GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir); @@ -105,6 +106,7 @@ class GDScriptTestRunner { bool is_generating = false; bool do_init_languages = false; + bool print_filenames; // Whether filenames should be printed when generated/running tests bool make_tests(); bool make_tests_for_dir(const String &p_dir); @@ -117,7 +119,7 @@ public: int run_tests(); bool generate_outputs(); - GDScriptTestRunner(const String &p_source_dir, bool p_init_language); + GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames = false); ~GDScriptTestRunner(); }; diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h index aed0ac2baf..e27b6218f1 100644 --- a/modules/gdscript/tests/gdscript_test_runner_suite.h +++ b/modules/gdscript/tests/gdscript_test_runner_suite.h @@ -41,7 +41,8 @@ TEST_SUITE("[Modules][GDScript]") { // Allow the tests to fail, but do not ignore errors during development. // Update the scripts and expected output as needed. TEST_CASE("Script compilation and runtime") { - GDScriptTestRunner runner("modules/gdscript/tests/scripts", true); + bool print_filenames = OS::get_singleton()->get_cmdline_args().find("--print-filenames") != nullptr; + GDScriptTestRunner runner("modules/gdscript/tests/scripts", true, print_filenames); int fail_count = runner.run_tests(); INFO("Make sure `*.out` files have expected results."); REQUIRE_MESSAGE(fail_count == 0, "All GDScript tests should pass."); diff --git a/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd new file mode 100644 index 0000000000..75524c32ae --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.gd @@ -0,0 +1,6 @@ +var num := 1 + +@export_range(num, 10) var a + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out new file mode 100644 index 0000000000..b4f0e79237 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/annotation_non_constant_parameter.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Argument 1 of annotation "@export_range" isn't a constant expression. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.gd b/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.gd new file mode 100644 index 0000000000..1b22d173c7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.gd @@ -0,0 +1,3 @@ +func test(): + var var_color: String = Color.RED + print('not ok') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.out b/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.out new file mode 100644 index 0000000000..cc4b1d86bf --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a value of type "Color" as "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out deleted file mode 100644 index 3a8d2a205a..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out +++ /dev/null @@ -1,2 +0,0 @@ -GDTEST_ANALYZER_ERROR -Invalid cast. Enum "cast_enum_bad_enum.gd::MyEnum" does not have value corresponding to "MyOtherEnum.OTHER_ENUM_VALUE_3" (2). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out deleted file mode 100644 index bc0d8b7834..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out +++ /dev/null @@ -1,2 +0,0 @@ -GDTEST_ANALYZER_ERROR -Invalid cast. Enum "cast_enum_bad_int.gd::MyEnum" does not have enum value 2. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out index 02c4633586..84958f1aa2 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Value of type "enum_class_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_class_var_assign_with_wrong_enum_type.gd::MyEnum". +Cannot assign a value of type "enum_class_var_assign_with_wrong_enum_type.gd::MyOtherEnum" as "enum_class_var_assign_with_wrong_enum_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out index 441cccbf7b..e294f3496a 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot assign a value of type enum_class_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "class_var" with specified type enum_class_var_init_with_wrong_enum_type.gd::MyEnum. +Cannot assign a value of type "enum_class_var_init_with_wrong_enum_type.gd::MyOtherEnum" as "enum_class_var_init_with_wrong_enum_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out index e85f7d6f9f..a91189e2dd 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Invalid argument for "enum_func()" function: argument 1 should be "enum_function_parameter_wrong_type.gd::MyEnum" but is "enum_function_parameter_wrong_type.gd::MyOtherEnum". +Cannot pass a value of type "enum_function_parameter_wrong_type.gd::MyOtherEnum" as "enum_function_parameter_wrong_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out index f7ea3267fa..6b4eba3740 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot return value of type "enum_function_return_wrong_type.gd::MyOtherEnum" because the function return type is "enum_function_return_wrong_type.gd::MyEnum". +Cannot return a value of type "enum_function_return_wrong_type.gd::MyOtherEnum" as "enum_function_return_wrong_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out index 38df5a0cd8..616358bb61 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass::MyEnum" cannot be assigned to a variable of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::MyEnum". +Cannot assign a value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass::MyEnum" as "enum_local_var_assign_outer_with_wrong_enum_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out index 2adcbd9edf..af3dde663f 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Value of type "enum_local_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_local_var_assign_with_wrong_enum_type.gd::MyEnum". +Cannot assign a value of type "enum_local_var_assign_with_wrong_enum_type.gd::MyOtherEnum" as "enum_local_var_assign_with_wrong_enum_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out index 331113dd30..781b0bc85f 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot assign a value of type enum_local_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "local_var" with specified type enum_local_var_init_with_wrong_enum_type.gd::MyEnum. +Cannot assign a value of type "enum_local_var_init_with_wrong_enum_type.gd::MyOtherEnum" as "enum_local_var_init_with_wrong_enum_type.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out index 6298c026b4..e8c7f86c4f 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Value of type "enum_value_from_parent.gd::<anonymous enum>" cannot be assigned to a variable of type "enum_preload_unnamed_assign_to_named.gd::MyEnum". +Cannot assign a value of type "enum_value_from_parent.gd::<anonymous enum>" as "enum_preload_unnamed_assign_to_named.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out index b70121ed81..fb18c94d0b 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot assign a value of type enum_unnamed_assign_to_named.gd::<anonymous enum> to variable "local_var" with specified type enum_unnamed_assign_to_named.gd::MyEnum. +Cannot assign a value of type "enum_unnamed_assign_to_named.gd::<anonymous enum>" as "enum_unnamed_assign_to_named.gd::MyEnum". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd new file mode 100644 index 0000000000..6014ee831c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.gd @@ -0,0 +1,3 @@ +func test(): + var untyped = 1 + var inferred := untyped diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out new file mode 100644 index 0000000000..b6dc6d0b01 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_local_variable.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot infer the type of "inferred" variable because the value doesn't have a set type. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd new file mode 100644 index 0000000000..040aa2e82a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.gd @@ -0,0 +1,5 @@ +var untyped = 1 +var inferred := untyped + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out new file mode 100644 index 0000000000..b6dc6d0b01 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_member_variable.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot infer the type of "inferred" variable because the value doesn't have a set type. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd new file mode 100644 index 0000000000..80c676488e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.gd @@ -0,0 +1,5 @@ +func check(untyped = 1, inferred := untyped): + pass + +func test(): + check() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out new file mode 100644 index 0000000000..8c9f0c13ae --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/inferring_with_weak_type_parameter.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot infer the type of "inferred" parameter because the value doesn't have a set type. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out b/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out index 53e2b012e6..69af0984f7 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot return value of type "String" because the function return type is "int". +Cannot return a value of type "String" as "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/match_with_subscript.gd b/modules/gdscript/tests/scripts/analyzer/errors/match_with_subscript.gd new file mode 100644 index 0000000000..6d92db34c1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/match_with_subscript.gd @@ -0,0 +1,5 @@ +func test(): + var dict = { a = 1 } + match 2: + dict["a"]: + print("not allowed") diff --git a/modules/gdscript/tests/scripts/analyzer/errors/match_with_subscript.out b/modules/gdscript/tests/scripts/analyzer/errors/match_with_subscript.out new file mode 100644 index 0000000000..b7385a50d5 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/match_with_subscript.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression in match pattern must be a constant expression, an identifier, or an attribute access ("A.B"). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/match_with_variable_expression.gd b/modules/gdscript/tests/scripts/analyzer/errors/match_with_variable_expression.gd new file mode 100644 index 0000000000..4df44d9ea2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/match_with_variable_expression.gd @@ -0,0 +1,5 @@ +func test(): + var a = 1 + match 2: + a + 2: + print("not allowed") diff --git a/modules/gdscript/tests/scripts/analyzer/errors/match_with_variable_expression.out b/modules/gdscript/tests/scripts/analyzer/errors/match_with_variable_expression.out new file mode 100644 index 0000000000..b7385a50d5 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/match_with_variable_expression.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression in match pattern must be a constant expression, an identifier, or an attribute access ("A.B"). diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out index 5e3c446bf6..08a973503f 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Value of type "enum_from_outer.gd::Named" cannot be assigned to a variable of type "preload_enum_error.gd::LocalNamed". +Cannot assign a value of type "enum_from_outer.gd::Named" as "preload_enum_error.gd::LocalNamed". diff --git a/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd new file mode 100644 index 0000000000..272dce8bbe --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.gd @@ -0,0 +1,10 @@ +const BEFORE = 1 + +@export_range(-10, 10) var a = 0 +@export_range(1 + 2, absi(-10) + 1) var b = 5 +@export_range(BEFORE + 1, BEFORE + AFTER + 1) var c = 5 + +const AFTER = 10 + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/annotation_constant_expression_parameters.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd index 2d2c2bef19..595563541f 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd @@ -1,9 +1,4 @@ func test(): - var one_0 = 0 - one_0 = 1 - var one_1 := one_0 - print(one_1) - var two: Variant = 0 two += 2 print(two) diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out index 7536c38490..0ddfa4b75f 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out +++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out @@ -1,5 +1,4 @@ GDTEST_OK -1 2 3 4 diff --git a/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.gd b/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.gd new file mode 100644 index 0000000000..efd8ad6edb --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.gd @@ -0,0 +1,16 @@ +const const_color: Color = 'red' + +func func_color(arg_color: Color = 'blue') -> bool: + return arg_color == Color.BLUE + +@warning_ignore("assert_always_true") +func test(): + assert(const_color == Color.RED) + + assert(func_color() == true) + assert(func_color('blue') == true) + + var var_color: Color = 'green' + assert(var_color == Color.GREEN) + + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.out b/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/const_conversions.gd b/modules/gdscript/tests/scripts/analyzer/features/const_conversions.gd new file mode 100644 index 0000000000..bed9dd0e96 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/const_conversions.gd @@ -0,0 +1,24 @@ +const const_float_int: float = 19 +const const_float_plus: float = 12 + 22 +const const_float_cast: float = 76 as float + +const const_packed_empty: PackedFloat64Array = [] +const const_packed_ints: PackedFloat64Array = [52] + +@warning_ignore("assert_always_true") +func test(): + assert(typeof(const_float_int) == TYPE_FLOAT) + assert(str(const_float_int) == '19') + assert(typeof(const_float_plus) == TYPE_FLOAT) + assert(str(const_float_plus) == '34') + assert(typeof(const_float_cast) == TYPE_FLOAT) + assert(str(const_float_cast) == '76') + + assert(typeof(const_packed_empty) == TYPE_PACKED_FLOAT64_ARRAY) + assert(str(const_packed_empty) == '[]') + assert(typeof(const_packed_ints) == TYPE_PACKED_FLOAT64_ARRAY) + assert(str(const_packed_ints) == '[52]') + assert(typeof(const_packed_ints[0]) == TYPE_FLOAT) + assert(str(const_packed_ints[0]) == '52') + + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/const_conversions.out b/modules/gdscript/tests/scripts/analyzer/features/const_conversions.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/const_conversions.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd new file mode 100644 index 0000000000..48a804ff54 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.gd @@ -0,0 +1,32 @@ +func variant() -> Variant: return null + +var member_weak = variant() +var member_typed: Variant = variant() +var member_inferred := variant() + +func param_weak(param = variant()) -> void: print(param) +func param_typed(param: Variant = variant()) -> void: print(param) +func param_inferred(param := variant()) -> void: print(param) + +func return_untyped(): return variant() +func return_typed() -> Variant: return variant() + +@warning_ignore("unused_variable") +func test() -> void: + var weak = variant() + var typed: Variant = variant() + var inferred := variant() + + weak = variant() + typed = variant() + inferred = variant() + + param_weak(typed) + param_typed(typed) + param_inferred(typed) + + if typed == null: pass + if typed != null: pass + if typed is Node: pass + + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/hard_variants.out b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.out new file mode 100644 index 0000000000..08491efa07 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/hard_variants.out @@ -0,0 +1,5 @@ +GDTEST_OK +<null> +<null> +<null> +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_conversions.gd b/modules/gdscript/tests/scripts/analyzer/features/return_conversions.gd new file mode 100644 index 0000000000..0b1576e66e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/return_conversions.gd @@ -0,0 +1,34 @@ +func convert_literal_int_to_float() -> float: return 76 +func convert_arg_int_to_float(arg: int) -> float: return arg +func convert_var_int_to_float() -> float: var number := 59; return number + +func convert_literal_array_to_packed() -> PackedStringArray: return ['46'] +func convert_arg_array_to_packed(arg: Array) -> PackedStringArray: return arg +func convert_var_array_to_packed() -> PackedStringArray: var array := ['79']; return array + +func test(): + var converted_literal_int := convert_literal_int_to_float() + assert(typeof(converted_literal_int) == TYPE_FLOAT) + assert(converted_literal_int == 76.0) + + var converted_arg_int := convert_arg_int_to_float(36) + assert(typeof(converted_arg_int) == TYPE_FLOAT) + assert(converted_arg_int == 36.0) + + var converted_var_int := convert_var_int_to_float() + assert(typeof(converted_var_int) == TYPE_FLOAT) + assert(converted_var_int == 59.0) + + var converted_literal_array := convert_literal_array_to_packed() + assert(typeof(converted_literal_array) == TYPE_PACKED_STRING_ARRAY) + assert(str(converted_literal_array) == '["46"]') + + var converted_arg_array := convert_arg_array_to_packed(['91']) + assert(typeof(converted_arg_array) == TYPE_PACKED_STRING_ARRAY) + assert(str(converted_arg_array) == '["91"]') + + var converted_var_array := convert_var_array_to_packed() + assert(typeof(converted_var_array) == TYPE_PACKED_STRING_ARRAY) + assert(str(converted_var_array) == '["79"]') + + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_conversions.out b/modules/gdscript/tests/scripts/analyzer/features/return_conversions.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/return_conversions.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd new file mode 100644 index 0000000000..d444250f1e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.gd @@ -0,0 +1,6 @@ +signal ok() + +@warning_ignore("return_value_discarded") +func test(): + ok.connect(func(): print('ok')) + emit_signal(&'ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/vararg_call.out b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/vararg_call.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd index 877a4ea221..4c02fd4b0d 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd @@ -1,12 +1,12 @@ -@warning_ignore(unused_private_class_variable) +@warning_ignore("unused_private_class_variable") var _unused = 2 -@warning_ignore(unused_variable) +@warning_ignore("unused_variable") func test(): print("test") var unused = 3 - @warning_ignore(redundant_await) + @warning_ignore("redundant_await") print(await regular_func()) print("done") diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.gd index 71616ea3af..71616ea3af 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd +++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.gd diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out new file mode 100644 index 0000000000..6e086a0918 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out @@ -0,0 +1,6 @@ +GDTEST_OK +>> WARNING +>> Line: 5 +>> INT_AS_ENUM_WITHOUT_MATCH +>> Cannot cast 2 as Enum "cast_enum_bad_enum.gd::MyEnum": no enum member has matching value. +2 diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.gd index 60a31fb318..60a31fb318 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd +++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.gd diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out new file mode 100644 index 0000000000..c19d57f98e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out @@ -0,0 +1,6 @@ +GDTEST_OK +>> WARNING +>> Line: 4 +>> INT_AS_ENUM_WITHOUT_MATCH +>> Cannot cast 2 as Enum "cast_enum_bad_int.gd::MyEnum": no enum member has matching value. +2 diff --git a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd index cc78309ae4..a34cc26e67 100644 --- a/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd +++ b/modules/gdscript/tests/scripts/parser/features/arrays_dictionaries_nested_const.gd @@ -1,6 +1,6 @@ # https://github.com/godotengine/godot/issues/50285 -@warning_ignore(unused_local_constant) +@warning_ignore("unused_local_constant") func test(): const CONST_INNER_DICTIONARY = { "key": true } const CONST_NESTED_DICTIONARY_OLD_WORKAROUND = { diff --git a/modules/gdscript/tests/scripts/parser/features/export_enum.gd b/modules/gdscript/tests/scripts/parser/features/export_enum.gd new file mode 100644 index 0000000000..9b2c22dea1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/export_enum.gd @@ -0,0 +1,15 @@ +@export_enum("Red", "Green", "Blue") var untyped + +@export_enum("Red", "Green", "Blue") var weak_int = 0 +@export_enum("Red", "Green", "Blue") var weak_string = "" + +@export_enum("Red", "Green", "Blue") var hard_int: int +@export_enum("Red", "Green", "Blue") var hard_string: String + +@export_enum("Red:10", "Green:20", "Blue:30") var with_values + +func test(): + for property in get_property_list(): + if property.name in ["untyped", "weak_int", "weak_string", "hard_int", + "hard_string", "with_values"]: + prints(property.name, property.type, property.hint_string) diff --git a/modules/gdscript/tests/scripts/parser/features/export_enum.out b/modules/gdscript/tests/scripts/parser/features/export_enum.out new file mode 100644 index 0000000000..330b7eaf01 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/export_enum.out @@ -0,0 +1,7 @@ +GDTEST_OK +untyped 2 Red,Green,Blue +weak_int 2 Red,Green,Blue +weak_string 4 Red,Green,Blue +hard_int 2 Red,Green,Blue +hard_string 4 Red,Green,Blue +with_values 2 Red:10,Green:20,Blue:30 diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.gd b/modules/gdscript/tests/scripts/parser/features/export_variable.gd index 1e072728fc..acf9ff2e21 100644 --- a/modules/gdscript/tests/scripts/parser/features/export_variable.gd +++ b/modules/gdscript/tests/scripts/parser/features/export_variable.gd @@ -5,7 +5,7 @@ @export var color: Color @export_color_no_alpha var color_no_alpha: Color -@export_node_path(Sprite2D, Sprite3D, Control, Node) var nodepath := ^"hello" +@export_node_path("Sprite2D", "Sprite3D", "Control", "Node") var nodepath := ^"hello" func test(): diff --git a/modules/gdscript/tests/scripts/parser/features/match_with_variables.gd b/modules/gdscript/tests/scripts/parser/features/match_with_variables.gd new file mode 100644 index 0000000000..aa38c3bf41 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_with_variables.gd @@ -0,0 +1,22 @@ +func test(): + var a = 1 + match 1: + a: + print("reach 1") + + var dict = { b = 2 } + match 2: + dict.b: + print("reach 2") + + var nested_dict = { + sub = { c = 3 } + } + match 3: + nested_dict.sub.c: + print("reach 3") + + var sub_pattern = { d = 4 } + match [4]: + [sub_pattern.d]: + print("reach 4") diff --git a/modules/gdscript/tests/scripts/parser/features/match_with_variables.out b/modules/gdscript/tests/scripts/parser/features/match_with_variables.out new file mode 100644 index 0000000000..de1dcb0d40 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/match_with_variables.out @@ -0,0 +1,5 @@ +GDTEST_OK +reach 1 +reach 2 +reach 3 +reach 4 diff --git a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out index eef13bbff8..b8e243769f 100644 --- a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out +++ b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out @@ -1,19 +1,19 @@ GDTEST_OK >> WARNING >> Line: 5 ->> INT_ASSIGNED_TO_ENUM +>> INT_AS_ENUM_WITHOUT_CAST >> Integer used when an enum value is expected. If this is intended cast the integer to the enum type. >> WARNING >> Line: 9 ->> INT_ASSIGNED_TO_ENUM +>> INT_AS_ENUM_WITHOUT_CAST >> Integer used when an enum value is expected. If this is intended cast the integer to the enum type. >> WARNING >> Line: 12 ->> INT_ASSIGNED_TO_ENUM +>> INT_AS_ENUM_WITHOUT_CAST >> Integer used when an enum value is expected. If this is intended cast the integer to the enum type. >> WARNING >> Line: 14 ->> INT_ASSIGNED_TO_ENUM +>> INT_AS_ENUM_WITHOUT_CAST >> Integer used when an enum value is expected. If this is intended cast the integer to the enum type. 0 1 diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd deleted file mode 100644 index 3e71cd0518..0000000000 --- a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd +++ /dev/null @@ -1,4 +0,0 @@ -const array: Array = [0] - -func test(): - array.push_back(0) diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out deleted file mode 100644 index ba3e1c46c6..0000000000 --- a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out +++ /dev/null @@ -1,7 +0,0 @@ -GDTEST_RUNTIME_ERROR ->> ERROR ->> on function: push_back() ->> core/variant/array.cpp ->> 253 ->> Condition "_p->read_only" is true. ->> Array is in read-only state. diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd deleted file mode 100644 index 7b350e81ad..0000000000 --- a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd +++ /dev/null @@ -1,4 +0,0 @@ -const dictionary := {} - -func test(): - dictionary.erase(0) diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out deleted file mode 100644 index 3e7ca11a4f..0000000000 --- a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out +++ /dev/null @@ -1,7 +0,0 @@ -GDTEST_RUNTIME_ERROR ->> ERROR ->> on function: erase() ->> core/variant/dictionary.cpp ->> 177 ->> Condition "_p->read_only" is true. Returning: false ->> Dictionary is in read-only state. diff --git a/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.gd b/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.gd new file mode 100644 index 0000000000..0c15701364 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.gd @@ -0,0 +1,6 @@ +# https://github.com/godotengine/godot/issues/66675 +func test(): + example(Node2D) + +func example(thing): + print(thing.has_method('asdf')) diff --git a/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.out b/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.out new file mode 100644 index 0000000000..3a90f98d99 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: example() +>> runtime/errors/non_static_method_call_on_native_class.gd +>> 6 +>> Invalid call. Nonexistent function 'has_method' in base 'Node2D'. diff --git a/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd index 46b9fbc951..1490a164c9 100644 --- a/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd +++ b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd @@ -2,6 +2,6 @@ func wait() -> void: pass func test(): - @warning_ignore(redundant_await) + @warning_ignore("redundant_await") await wait() print("end") diff --git a/modules/gdscript/tests/scripts/runtime/features/constants_are_read_only.gd b/modules/gdscript/tests/scripts/runtime/features/constants_are_read_only.gd new file mode 100644 index 0000000000..d1746979be --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/constants_are_read_only.gd @@ -0,0 +1,10 @@ +const array: Array = [0] +const dictionary := {1: 2} + +@warning_ignore("assert_always_true") +func test(): + assert(array.is_read_only() == true) + assert(str(array) == '[0]') + assert(dictionary.is_read_only() == true) + assert(str(dictionary) == '{ 1: 2 }') + print('ok') diff --git a/modules/gdscript/tests/scripts/runtime/features/constants_are_read_only.out b/modules/gdscript/tests/scripts/runtime/features/constants_are_read_only.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/constants_are_read_only.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd index 1d4b400d81..48af734317 100644 --- a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd +++ b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd @@ -7,11 +7,11 @@ func test(): func builtin_method(): var pba := PackedByteArray() - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") pba.resize(1) # Built-in validated. func builtin_method_static(): var _pba := PackedByteArray() - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") Vector2.from_angle(PI) # Static built-in validated. diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd index f2368643de..e686cffc48 100644 --- a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd +++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd @@ -11,10 +11,10 @@ class InnerClass: func _init() -> void: prints("Inner") ''' - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") gdscr.reload() var inst = gdscr.new() - @warning_ignore(unsafe_method_access) + @warning_ignore("unsafe_method_access") inst.test() diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd index cc34e71b01..2f55059334 100644 --- a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd +++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd @@ -20,26 +20,26 @@ func test_utility(v, f): assert(not f) # Test unary operator reading from `nil`. func test_builtin_call(v, f): - @warning_ignore(unsafe_method_access) + @warning_ignore("unsafe_method_access") v.angle() # Built-in method call. assert(not f) # Test unary operator reading from `nil`. func test_builtin_call_validated(v: Vector2, f): - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") v.abs() # Built-in method call validated. assert(not f) # Test unary operator reading from `nil`. func test_object_call(v, f): - @warning_ignore(unsafe_method_access) + @warning_ignore("unsafe_method_access") v.get_reference_count() # Native type method call. assert(not f) # Test unary operator reading from `nil`. func test_object_call_method_bind(v: Resource, f): - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") v.duplicate() # Native type method call with MethodBind. assert(not f) # Test unary operator reading from `nil`. func test_object_call_ptrcall(v: RefCounted, f): - @warning_ignore(return_value_discarded) + @warning_ignore("return_value_discarded") v.get_reference_count() # Native type method call with ptrcall. assert(not f) # Test unary operator reading from `nil`. diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd index af3f3cb941..efa8270526 100644 --- a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd +++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd @@ -1,7 +1,7 @@ # https://github.com/godotengine/godot/issues/71172 func test(): - @warning_ignore(narrowing_conversion) + @warning_ignore("narrowing_conversion") var foo: int = 0.0 print(typeof(foo) == TYPE_INT) var dict : Dictionary = {"a":0.0} diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index d7e8141eb1..d6657cad5d 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -51,6 +51,7 @@ <param index="0" name="state" type="GLTFState" /> <param index="1" name="bake_fps" type="float" default="30" /> <param index="2" name="trimming" type="bool" default="false" /> + <param index="3" name="remove_immutable_tracks" type="bool" default="true" /> <description> Takes a [GLTFState] object through the [param state] parameter and returns a Godot Engine scene node. </description> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 9a554a0d49..b8943795a0 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -55,6 +55,11 @@ <description> </description> </method> + <method name="get_handle_binary_image"> + <return type="int" /> + <description> + </description> + </method> <method name="get_images"> <return type="Texture2D[]" /> <description> @@ -155,6 +160,12 @@ <description> </description> </method> + <method name="set_handle_binary_image"> + <return type="void" /> + <param index="0" name="method" type="int" /> + <description> + </description> + </method> <method name="set_images"> <return type="void" /> <param index="0" name="images" type="Texture2D[]" /> @@ -251,4 +262,12 @@ <member name="use_named_skin_binds" type="bool" setter="set_use_named_skin_binds" getter="get_use_named_skin_binds" default="false"> </member> </members> + <constants> + <constant name="HANDLE_BINARY_DISCARD_TEXTURES" value="0"> + </constant> + <constant name="HANDLE_BINARY_EXTRACT_TEXTURES" value="1"> + </constant> + <constant name="HANDLE_BINARY_EMBED_AS_BASISU" value="2"> + </constant> + </constants> </class> diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 2b8c057ee5..5415c5818f 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -32,6 +32,7 @@ #ifdef TOOLS_ENABLED +#include "../gltf_defines.h" #include "../gltf_document.h" #include "core/config/project_settings.h" @@ -43,7 +44,6 @@ #include "scene/gui/line_edit.h" #ifdef WINDOWS_ENABLED -// Code by Pedro Estebanez (https://github.com/godotengine/godot/pull/59766) #include <shlwapi.h> #endif @@ -221,6 +221,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ gltf.instantiate(); Ref<GLTFState> state; state.instantiate(); + String base_dir; if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) { base_dir = sink.get_base_dir(); @@ -232,7 +233,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ } return nullptr; } - return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]); + return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]); } Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, @@ -274,9 +275,6 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li ADD_OPTION_BOOL("blender/animation/limit_playback", true); ADD_OPTION_BOOL("blender/animation/always_sample", true); ADD_OPTION_BOOL("blender/animation/group_tracks", true); - -#undef ADD_OPTION_BOOL -#undef ADD_OPTION_ENUM } /////////////////////////// diff --git a/modules/gltf/editor/editor_scene_importer_fbx.cpp b/modules/gltf/editor/editor_scene_importer_fbx.cpp index 6c6ab7cd03..d829630032 100644 --- a/modules/gltf/editor/editor_scene_importer_fbx.cpp +++ b/modules/gltf/editor/editor_scene_importer_fbx.cpp @@ -100,7 +100,7 @@ Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t } return nullptr; } - return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]); + return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]); } Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation, diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp index 39956a6ff6..67bbf8dd15 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -32,6 +32,7 @@ #include "editor_scene_importer_gltf.h" +#include "../gltf_defines.h" #include "../gltf_document.h" uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const { @@ -50,6 +51,10 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t doc.instantiate(); Ref<GLTFState> state; state.instantiate(); + if (p_options.has("meshes/handle_gltf_embedded_images")) { + int32_t enum_option = p_options["meshes/handle_gltf_embedded_images"]; + state->set_handle_binary_image(enum_option); + } Error err = doc->append_from_file(p_path, state, p_flags); if (err != OK) { if (r_err) { @@ -61,11 +66,28 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t state->set_create_animations(bool(p_options["animation/import"])); } +#ifndef DISABLE_DEPRECATED if (p_options.has("animation/trimming")) { - return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]); + if (p_options.has("animation/remove_immutable_tracks")) { + return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]); + } else { + return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], true); + } } else { - return doc->generate_scene(state, (float)p_options["animation/fps"], false); + if (p_options.has("animation/remove_immutable_tracks")) { + return doc->generate_scene(state, (float)p_options["animation/fps"], false, (bool)p_options["animation/remove_immutable_tracks"]); + } else { + return doc->generate_scene(state, (float)p_options["animation/fps"], false, true); + } } +#else + return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]); +#endif +} + +void EditorSceneFormatImporterGLTF::get_import_options(const String &p_path, + List<ResourceImporter::ImportOption> *r_options) { + r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "meshes/handle_gltf_embedded_images", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), GLTFState::HANDLE_BINARY_EXTRACT_TEXTURES)); } #endif // TOOLS_ENABLED diff --git a/modules/gltf/editor/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h index c2a4bf046d..ed57ec8cdb 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.h +++ b/modules/gltf/editor/editor_scene_importer_gltf.h @@ -47,6 +47,8 @@ public: virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr) override; + virtual void get_import_options(const String &p_path, + List<ResourceImporter::ImportOption> *r_options) override; }; #endif // TOOLS_ENABLED diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index b243ba933d..0b519bd6a3 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -33,6 +33,7 @@ #include "extensions/gltf_spec_gloss.h" #include "core/crypto/crypto_core.h" +#include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/file_access_memory.h" @@ -543,6 +544,7 @@ String GLTFDocument::_gen_unique_bone_name(Ref<GLTFState> p_state, const GLTFSke } Error GLTFDocument::_parse_scenes(Ref<GLTFState> p_state) { + p_state->unique_names.insert("Skeleton3D"); // Reserve skeleton name. ERR_FAIL_COND_V(!p_state->json.has("scenes"), ERR_FILE_CORRUPT); const Array &scenes = p_state->json["scenes"]; int loaded_scene = 0; @@ -3089,9 +3091,12 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p const uint8_t *data_ptr = nullptr; int data_size = 0; + String image_name; + if (d.has("uri")) { // Handles the first two bullet points from the spec (embedded data, or external file). String uri = d["uri"]; + image_name = uri; if (uri.begins_with("data:")) { // Embedded data using base64. // Validate data MIME types and throw a warning if it's one we don't know/support. @@ -3127,6 +3132,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p String extension = uri.get_extension().to_lower(); if (texture.is_valid()) { p_state->images.push_back(texture); + p_state->source_images.push_back(texture->get_image()); continue; } else if (mimetype == "image/png" || mimetype == "image/jpeg" || extension == "png" || extension == "jpg" || extension == "jpeg") { // Fallback to loading as byte array. @@ -3152,6 +3158,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p vformat("glTF: Image index '%d' specifies 'bufferView' but no 'mimeType', which is invalid.", i)); const GLTFBufferViewIndex bvi = d["bufferView"]; + image_name = itos(bvi); ERR_FAIL_INDEX_V(bvi, p_state->buffer_views.size(), ERR_PARAMETER_RANGE_ERROR); @@ -3196,9 +3203,70 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p if (img.is_null()) { ERR_PRINT(vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype)); p_state->images.push_back(Ref<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + continue; + } + if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { + p_state->images.push_back(Ref<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + continue; + } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) { + String extracted_image_name = image_name.get_file().get_basename().validate_filename(); + img->set_name(extracted_image_name); + if (p_state->base_path.is_empty()) { + p_state->images.push_back(Ref<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + } else if (img->get_name().is_empty()) { + WARN_PRINT(vformat("glTF: Image index '%d' couldn't be named. Skipping it.", i)); + p_state->images.push_back(Ref<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + } else { + String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + img->get_name() + ".png"; + Ref<ConfigFile> config; + config.instantiate(); + if (FileAccess::exists(file_path + ".import")) { + config->load(file_path + ".import"); + } + config->set_value("remap", "importer", "texture"); + config->set_value("remap", "type", "Texture2D"); + if (!config->has_section_key("params", "compress/mode")) { + config->set_value("remap", "compress/mode", 2); //user may want another compression, so leave it bes + } + if (!config->has_section_key("params", "mipmaps/generate")) { + config->set_value("params", "mipmaps/generate", true); + } + Error err = OK; + err = config->save(file_path + ".import"); + ERR_FAIL_COND_V(err != OK, err); + img->save_png(file_path); + ERR_FAIL_COND_V(err != OK, err); + ResourceLoader::import(file_path); + Ref<Texture2D> saved_image = ResourceLoader::load(file_path, "Texture2D"); + if (saved_image.is_valid()) { + p_state->images.push_back(saved_image); + p_state->source_images.push_back(img); + } else { + WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded with the name: %s. Skipping it.", i, img->get_name())); + // Placeholder to keep count. + p_state->images.push_back(Ref<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + } + } + continue; + } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { + Ref<PortableCompressedTexture2D> tex; + tex.instantiate(); + tex->set_name(img->get_name()); + tex->set_keep_compressed_buffer(true); + p_state->source_images.push_back(img); + tex->create_from_image(img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL); + p_state->images.push_back(tex); + p_state->source_images.push_back(img); continue; } - p_state->images.push_back(ImageTexture::create_from_image(img)); + + p_state->images.push_back(Ref<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); } print_verbose("glTF: Total images: " + itos(p_state->images.size())); @@ -3268,12 +3336,24 @@ GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> p_state, Ref<Texture2 return gltf_texture_i; } -Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> p_state, const GLTFTextureIndex p_texture) { +Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> p_state, const GLTFTextureIndex p_texture, int p_texture_types) { ERR_FAIL_INDEX_V(p_texture, p_state->textures.size(), Ref<Texture2D>()); const GLTFImageIndex image = p_state->textures[p_texture]->get_src_image(); - ERR_FAIL_INDEX_V(image, p_state->images.size(), Ref<Texture2D>()); - + if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { + Ref<PortableCompressedTexture2D> portable_texture; + portable_texture.instantiate(); + portable_texture->set_keep_compressed_buffer(true); + Ref<Image> new_img = p_state->source_images[p_texture]->duplicate(); + ERR_FAIL_COND_V(new_img.is_null(), Ref<Texture2D>()); + new_img->generate_mipmaps(); + if (p_texture_types) { + portable_texture->create_from_image(new_img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL, true); + } else { + portable_texture->create_from_image(new_img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL, false); + } + p_state->images.write[image] = portable_texture; + } return p_state->images[image]; } @@ -3684,7 +3764,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { material->set_texture_filter(diffuse_sampler->get_filter_mode()); material->set_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT, diffuse_sampler->get_wrap_mode()); } - Ref<Texture2D> diffuse_texture = _get_texture(p_state, diffuse_texture_dict["index"]); + Ref<Texture2D> diffuse_texture = _get_texture(p_state, diffuse_texture_dict["index"], TEXTURE_TYPE_GENERIC); if (diffuse_texture.is_valid()) { spec_gloss->diffuse_img = diffuse_texture->get_image(); material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_texture); @@ -3712,7 +3792,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { if (sgm.has("specularGlossinessTexture")) { const Dictionary &spec_gloss_texture = sgm["specularGlossinessTexture"]; if (spec_gloss_texture.has("index")) { - const Ref<Texture2D> orig_texture = _get_texture(p_state, spec_gloss_texture["index"]); + const Ref<Texture2D> orig_texture = _get_texture(p_state, spec_gloss_texture["index"], TEXTURE_TYPE_GENERIC); if (orig_texture.is_valid()) { spec_gloss->spec_gloss_img = orig_texture->get_image(); } @@ -3735,7 +3815,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { Ref<GLTFTextureSampler> bct_sampler = _get_sampler_for_texture(p_state, bct["index"]); material->set_texture_filter(bct_sampler->get_filter_mode()); material->set_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT, bct_sampler->get_wrap_mode()); - material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(p_state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC)); } if (!mr.has("baseColorFactor")) { material->set_albedo(Color(1, 1, 1)); @@ -3758,7 +3838,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { if (mr.has("metallicRoughnessTexture")) { const Dictionary &bct = mr["metallicRoughnessTexture"]; if (bct.has("index")) { - const Ref<Texture2D> t = _get_texture(p_state, bct["index"]); + const Ref<Texture2D> t = _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC); material->set_texture(BaseMaterial3D::TEXTURE_METALLIC, t); material->set_metallic_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_BLUE); material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, t); @@ -3776,7 +3856,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { if (d.has("normalTexture")) { const Dictionary &bct = d["normalTexture"]; if (bct.has("index")) { - material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(p_state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(p_state, bct["index"], TEXTURE_TYPE_NORMAL)); material->set_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING, true); } if (bct.has("scale")) { @@ -3786,7 +3866,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { if (d.has("occlusionTexture")) { const Dictionary &bct = d["occlusionTexture"]; if (bct.has("index")) { - material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(p_state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC)); material->set_ao_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_RED); material->set_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION, true); } @@ -3804,7 +3884,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { if (d.has("emissiveTexture")) { const Dictionary &bct = d["emissiveTexture"]; if (bct.has("index")) { - material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC)); material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true); material->set_emission(Color(0, 0, 0)); } @@ -4233,6 +4313,21 @@ Error GLTFDocument::_parse_skins(Ref<GLTFState> p_state) { return OK; } +void GLTFDocument::_recurse_children(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, + RBSet<GLTFNodeIndex> &p_all_skin_nodes, HashSet<GLTFNodeIndex> &p_child_visited_set) { + if (p_child_visited_set.has(p_node_index)) { + return; + } + p_child_visited_set.insert(p_node_index); + for (int i = 0; i < p_state->nodes[p_node_index]->children.size(); ++i) { + _recurse_children(p_state, p_state->nodes[p_node_index]->children[i], p_all_skin_nodes, p_child_visited_set); + } + + if (p_state->nodes[p_node_index]->skin < 0 || p_state->nodes[p_node_index]->mesh < 0 || !p_state->nodes[p_node_index]->children.is_empty()) { + p_all_skin_nodes.insert(p_node_index); + } +} + Error GLTFDocument::_determine_skeletons(Ref<GLTFState> p_state) { // Using a disjoint set, we are going to potentially combine all skins that are actually branches // of a main skeleton, or treat skins defining the same set of nodes as ONE skeleton. @@ -4243,16 +4338,21 @@ Error GLTFDocument::_determine_skeletons(Ref<GLTFState> p_state) { for (GLTFSkinIndex skin_i = 0; skin_i < p_state->skins.size(); ++skin_i) { const Ref<GLTFSkin> skin = p_state->skins[skin_i]; - Vector<GLTFNodeIndex> all_skin_nodes; - all_skin_nodes.append_array(skin->joints); - all_skin_nodes.append_array(skin->non_joints); - - for (int i = 0; i < all_skin_nodes.size(); ++i) { - const GLTFNodeIndex node_index = all_skin_nodes[i]; + HashSet<GLTFNodeIndex> child_visited_set; + RBSet<GLTFNodeIndex> all_skin_nodes; + for (int i = 0; i < skin->joints.size(); ++i) { + all_skin_nodes.insert(skin->joints[i]); + _recurse_children(p_state, skin->joints[i], all_skin_nodes, child_visited_set); + } + for (int i = 0; i < skin->non_joints.size(); ++i) { + all_skin_nodes.insert(skin->non_joints[i]); + _recurse_children(p_state, skin->non_joints[i], all_skin_nodes, child_visited_set); + } + for (GLTFNodeIndex node_index : all_skin_nodes) { const GLTFNodeIndex parent = p_state->nodes[node_index]->parent; skeleton_sets.insert(node_index); - if (all_skin_nodes.find(parent) >= 0) { + if (all_skin_nodes.has(parent)) { skeleton_sets.create_union(parent, node_index); } } @@ -4471,7 +4571,7 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> p_state) { p_state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skel_i; // Make a unique name, no gltf node represents this skeleton - skeleton->set_name(_gen_unique_name(p_state, "Skeleton3D")); + skeleton->set_name("Skeleton3D"); List<GLTFNodeIndex> bones; @@ -5163,6 +5263,7 @@ ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> p_s ImporterMeshInstance3D *mi = memnew(ImporterMeshInstance3D); print_verbose("glTF: Creating mesh for: " + gltf_node->get_name()); + p_state->scene_mesh_instances.insert(p_node_index, mi); Ref<GLTFMesh> mesh = p_state->meshes.write[gltf_node->mesh]; if (mesh.is_null()) { return mi; @@ -5603,7 +5704,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, Node *scene_pare bone_attachment->set_owner(scene_root); // There is no gltf_node that represent this, so just directly create a unique name - bone_attachment->set_name(_gen_unique_name(p_state, "BoneAttachment3D")); + bone_attachment->set_name(gltf_node->get_name()); // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node // and attach it to the bone_attachment @@ -5619,7 +5720,13 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, Node *scene_pare } // If none of our GLTFDocumentExtension classes generated us a node, we generate one. if (!current_node) { - if (gltf_node->mesh >= 0) { + if (gltf_node->skin >= 0 && gltf_node->mesh >= 0 && !gltf_node->children.is_empty()) { + current_node = _generate_spatial(p_state, node_index); + Node3D *mesh_inst = _generate_mesh_instance(p_state, node_index); + mesh_inst->set_name(gltf_node->get_name()); + + current_node->add_child(mesh_inst, true); + } else if (gltf_node->mesh >= 0) { current_node = _generate_mesh_instance(p_state, node_index); } else if (gltf_node->camera >= 0) { current_node = _generate_camera(p_state, node_index); @@ -5640,7 +5747,6 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, Node *scene_pare current_node->set_name(gltf_node->get_name()); p_state->scene_nodes.insert(node_index, current_node); - for (int i = 0; i < gltf_node->children.size(); ++i) { _generate_scene_node(p_state, current_node, scene_root, gltf_node->children[i]); } @@ -5659,22 +5765,17 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, Node *p_ Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(p_scene_parent); if (active_skeleton != skeleton) { if (active_skeleton) { - // Bone Attachment - Direct Parented Skeleton Case + // Should no longer be possible. + ERR_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", p_node_index)); BoneAttachment3D *bone_attachment = _generate_bone_attachment(p_state, active_skeleton, p_node_index, gltf_node->parent); - p_scene_parent->add_child(bone_attachment, true); bone_attachment->set_owner(p_scene_root); - // There is no gltf_node that represent this, so just directly create a unique name bone_attachment->set_name(_gen_unique_name(p_state, "BoneAttachment3D")); - // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node // and attach it to the bone_attachment p_scene_parent = bone_attachment; - WARN_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", p_node_index)); } - - // Add it to the scene if it has not already been added if (skeleton->get_parent() == nullptr) { p_scene_parent->add_child(skeleton, true); skeleton->set_owner(p_scene_root); @@ -5682,9 +5783,10 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, Node *p_ } active_skeleton = skeleton; - current_node = skeleton; + current_node = active_skeleton; if (requires_extra_node) { + current_node = nullptr; // skinned meshes must not be placed in a bone attachment. if (!is_skinned_mesh) { // Bone Attachment - Same Node Case @@ -5694,7 +5796,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, Node *p_ bone_attachment->set_owner(p_scene_root); // There is no gltf_node that represent this, so just directly create a unique name - bone_attachment->set_name(_gen_unique_name(p_state, "BoneAttachment3D")); + bone_attachment->set_name(gltf_node->get_name()); // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node // and attach it to the bone_attachment @@ -5859,7 +5961,7 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T ERR_FAIL_V(p_values[0]); } -void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming) { +void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks) { Ref<GLTFAnimation> anim = p_state->animations[p_index]; String anim_name = anim->get_name(); @@ -5885,6 +5987,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ NodePath node_path; //for skeletons, transform tracks always affect bones NodePath transform_node_path; + //for meshes, especially skinned meshes, there are cases where it will be added as a child + NodePath mesh_instance_node_path; GLTFNodeIndex node_index = track_i.key; @@ -5895,6 +5999,12 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ HashMap<GLTFNodeIndex, Node *>::Iterator node_element = p_state->scene_nodes.find(node_index); ERR_CONTINUE_MSG(!node_element, vformat("Unable to find node %d for animation", node_index)); node_path = root->get_path_to(node_element->value); + HashMap<GLTFNodeIndex, ImporterMeshInstance3D *>::Iterator mesh_instance_element = p_state->scene_mesh_instances.find(node_index); + if (mesh_instance_element) { + mesh_instance_node_path = root->get_path_to(mesh_instance_element->value); + } else { + mesh_instance_node_path = node_path; + } if (gltf_node->skeleton >= 0) { const Skeleton3D *sk = p_state->skeletons[gltf_node->skeleton]->godot_skeleton; @@ -5954,35 +6064,38 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ int scale_idx = -1; if (track.position_track.values.size()) { - Vector3 base_pos = p_state->nodes[track_i.key]->position; - bool not_default = false; //discard the track if all it contains is default values - for (int i = 0; i < track.position_track.times.size(); i++) { - Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; - if (!value.is_equal_approx(base_pos)) { - not_default = true; - break; + bool is_default = true; //discard the track if all it contains is default values + if (p_remove_immutable_tracks) { + Vector3 base_pos = p_state->nodes[track_i.key]->position; + for (int i = 0; i < track.position_track.times.size(); i++) { + Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; + if (!value.is_equal_approx(base_pos)) { + is_default = false; + break; + } } } - if (not_default) { + if (!p_remove_immutable_tracks || !is_default) { position_idx = base_idx; animation->add_track(Animation::TYPE_POSITION_3D); animation->track_set_path(position_idx, transform_node_path); animation->track_set_imported(position_idx, true); //helps merging later - base_idx++; } } if (track.rotation_track.values.size()) { - Quaternion base_rot = p_state->nodes[track_i.key]->rotation.normalized(); - bool not_default = false; //discard the track if all it contains is default values - for (int i = 0; i < track.rotation_track.times.size(); i++) { - Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized(); - if (!value.is_equal_approx(base_rot)) { - not_default = true; - break; + bool is_default = true; //discard the track if all it contains is default values + if (p_remove_immutable_tracks) { + Quaternion base_rot = p_state->nodes[track_i.key]->rotation.normalized(); + for (int i = 0; i < track.rotation_track.times.size(); i++) { + Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized(); + if (!value.is_equal_approx(base_rot)) { + is_default = false; + break; + } } } - if (not_default) { + if (!p_remove_immutable_tracks || !is_default) { rotation_idx = base_idx; animation->add_track(Animation::TYPE_ROTATION_3D); animation->track_set_path(rotation_idx, transform_node_path); @@ -5991,16 +6104,18 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ } } if (track.scale_track.values.size()) { - Vector3 base_scale = p_state->nodes[track_i.key]->scale; - bool not_default = false; //discard the track if all it contains is default values - for (int i = 0; i < track.scale_track.times.size(); i++) { - Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; - if (!value.is_equal_approx(base_scale)) { - not_default = true; - break; + bool is_default = true; //discard the track if all it contains is default values + if (p_remove_immutable_tracks) { + Vector3 base_scale = p_state->nodes[track_i.key]->scale; + for (int i = 0; i < track.scale_track.times.size(); i++) { + Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; + if (!value.is_equal_approx(base_scale)) { + is_default = false; + break; + } } } - if (not_default) { + if (!p_remove_immutable_tracks || !is_default) { scale_idx = base_idx; animation->add_track(Animation::TYPE_SCALE_3D); animation->track_set_path(scale_idx, transform_node_path); @@ -6067,7 +6182,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ ERR_CONTINUE(mesh->get_mesh().is_null()); ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null()); - const String blend_path = String(node_path) + ":" + String(mesh->get_mesh()->get_blend_shape_name(i)); + const String blend_path = String(mesh_instance_node_path) + ":" + String(mesh->get_mesh()->get_blend_shape_name(i)); const int track_idx = animation->get_track_count(); animation->add_track(Animation::TYPE_BLEND_SHAPE); @@ -6258,11 +6373,16 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene if (node->skin >= 0 && node->mesh >= 0) { const GLTFSkinIndex skin_i = node->skin; - HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = p_state->scene_nodes.find(node_i); - ERR_CONTINUE_MSG(!mi_element, vformat("Unable to find node %d", node_i)); - - ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(mi_element->value); - ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->value->get_class_name())); + ImporterMeshInstance3D *mi = nullptr; + HashMap<GLTFNodeIndex, ImporterMeshInstance3D *>::Iterator mi_element = p_state->scene_mesh_instances.find(node_i); + if (mi_element) { + mi = mi_element->value; + } else { + HashMap<GLTFNodeIndex, Node *>::Iterator si_element = p_state->scene_nodes.find(node_i); + ERR_CONTINUE_MSG(!si_element, vformat("Unable to find node %d", node_i)); + mi = Object::cast_to<ImporterMeshInstance3D>(si_element->value); + ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, si_element->value->get_class_name())); + } const GLTFSkeletonIndex skel_i = p_state->skins.write[node->skin]->skeleton; Ref<GLTFSkeleton> gltf_skeleton = p_state->skeletons.write[skel_i]; @@ -6780,8 +6900,8 @@ void GLTFDocument::_bind_methods() { &GLTFDocument::append_from_buffer, DEFVAL(0)); ClassDB::bind_method(D_METHOD("append_from_scene", "node", "state", "flags"), &GLTFDocument::append_from_scene, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("generate_scene", "state", "bake_fps", "trimming"), - &GLTFDocument::generate_scene, DEFVAL(30), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("generate_scene", "state", "bake_fps", "trimming", "remove_immutable_tracks"), + &GLTFDocument::generate_scene, DEFVAL(30), DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("generate_buffer", "state"), &GLTFDocument::generate_buffer); ClassDB::bind_method(D_METHOD("write_to_filesystem", "state", "path"), @@ -6890,7 +7010,7 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_ return OK; } -Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming) { +Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) { ERR_FAIL_NULL_V(p_state, nullptr); ERR_FAIL_INDEX_V(0, p_state->root_nodes.size(), nullptr); Error err = OK; @@ -6904,7 +7024,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo root->add_child(ap, true); ap->set_owner(root); for (int i = 0; i < p_state->animations.size(); i++) { - _import_animation(p_state, ap, i, p_bake_fps, p_trimming); + _import_animation(p_state, ap, i, p_bake_fps, p_trimming, p_remove_immutable_tracks); } } for (KeyValue<GLTFNodeIndex, Node *> E : p_state->scene_nodes) { diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 164c63c53c..b8b989bf89 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -64,6 +64,10 @@ public: COMPONENT_TYPE_INT = 5125, COMPONENT_TYPE_FLOAT = 5126, }; + enum { + TEXTURE_TYPE_GENERIC = 0, + TEXTURE_TYPE_NORMAL = 1, + }; protected: static void _bind_methods(); @@ -92,7 +96,7 @@ private: GLTFTextureIndex _set_texture(Ref<GLTFState> p_state, Ref<Texture2D> p_texture, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats); Ref<Texture2D> _get_texture(Ref<GLTFState> p_state, - const GLTFTextureIndex p_texture); + const GLTFTextureIndex p_texture, int p_texture_type); GLTFTextureSamplerIndex _set_sampler_for_mode(Ref<GLTFState> p_state, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats); Ref<GLTFTextureSampler> _get_sampler_for_texture(Ref<GLTFState> p_state, @@ -160,6 +164,8 @@ private: float &r_metallic); GLTFNodeIndex _find_highest_node(Ref<GLTFState> p_state, const Vector<GLTFNodeIndex> &p_subset); + void _recurse_children(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, + RBSet<GLTFNodeIndex> &p_all_skin_nodes, HashSet<GLTFNodeIndex> &p_child_visited_set); bool _capture_nodes_in_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin, const GLTFNodeIndex p_node_index); void _capture_nodes_for_multirooted_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin); @@ -290,7 +296,7 @@ public: Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0); public: - Node *generate_scene(Ref<GLTFState> p_state, float p_bake_fps = 30.0f, bool p_trimming = false); + Node *generate_scene(Ref<GLTFState> p_state, float p_bake_fps = 30.0f, bool p_trimming = false, bool p_remove_immutable_tracks = true); PackedByteArray generate_buffer(Ref<GLTFState> p_state); Error write_to_filesystem(Ref<GLTFState> p_state, const String &p_path); @@ -303,7 +309,7 @@ public: const GLTFNodeIndex p_node_index); void _generate_skeleton_bone_node(Ref<GLTFState> p_state, Node *p_scene_parent, Node3D *p_scene_root, const GLTFNodeIndex p_node_index); void _import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, - const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming); + const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks); void _convert_mesh_instances(Ref<GLTFState> p_state); GLTFCameraIndex _convert_camera(Ref<GLTFState> p_state, Camera3D *p_camera); void _convert_light_to_gltf(Light3D *p_light, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node); diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index 252453dfc2..b67484fc8e 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -91,6 +91,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node); ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFState::get_additional_data); ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data); + ClassDB::bind_method(D_METHOD("get_handle_binary_image"), &GLTFState::get_handle_binary_image); + ClassDB::bind_method(D_METHOD("set_handle_binary_image", "method"), &GLTFState::set_handle_binary_image); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int @@ -118,6 +120,11 @@ void GLTFState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // RBMap<GLTFSkeletonIndex, ADD_PROPERTY(PropertyInfo(Variant::BOOL, "create_animations"), "set_create_animations", "get_create_animations"); // bool ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>> + ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum + + BIND_CONSTANT(HANDLE_BINARY_DISCARD_TEXTURES); + BIND_CONSTANT(HANDLE_BINARY_EXTRACT_TEXTURES); + BIND_CONSTANT(HANDLE_BINARY_EMBED_AS_BASISU); } void GLTFState::add_used_extension(const String &p_extension_name, bool p_required) { diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index 488ad038aa..52d7949d03 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -59,6 +59,8 @@ class GLTFState : public Resource { bool discard_meshes_and_materials = false; bool create_animations = true; + int handle_binary_image = HANDLE_BINARY_EXTRACT_TEXTURES; + Vector<Ref<GLTFNode>> nodes; Vector<Vector<uint8_t>> buffers; Vector<Ref<GLTFBufferView>> buffer_views; @@ -78,6 +80,7 @@ class GLTFState : public Resource { Vector<Ref<Texture2D>> images; Vector<String> extensions_used; Vector<String> extensions_required; + Vector<Ref<Image>> source_images; Vector<Ref<GLTFSkin>> skins; Vector<Ref<GLTFCamera>> cameras; @@ -89,6 +92,7 @@ class GLTFState : public Resource { HashMap<GLTFSkeletonIndex, GLTFNodeIndex> skeleton_to_node; Vector<Ref<GLTFAnimation>> animations; HashMap<GLTFNodeIndex, Node *> scene_nodes; + HashMap<GLTFNodeIndex, ImporterMeshInstance3D *> scene_mesh_instances; HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton; HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin; @@ -100,6 +104,18 @@ protected: public: void add_used_extension(const String &p_extension, bool p_required = false); + enum GLTFHandleBinary { + HANDLE_BINARY_DISCARD_TEXTURES = 0, + HANDLE_BINARY_EXTRACT_TEXTURES, + HANDLE_BINARY_EMBED_AS_BASISU, + }; + int32_t get_handle_binary_image() { + return handle_binary_image; + } + void set_handle_binary_image(int32_t p_handle_binary_image) { + handle_binary_image = p_handle_binary_image; + } + Dictionary get_json(); void set_json(Dictionary p_json); @@ -115,6 +131,15 @@ public: bool get_use_named_skin_binds(); void set_use_named_skin_binds(bool p_use_named_skin_binds); + bool get_discard_textures(); + void set_discard_textures(bool p_discard_textures); + + bool get_embed_as_basisu(); + void set_embed_as_basisu(bool p_embed_as_basisu); + + bool get_extract_textures(); + void set_extract_textures(bool p_extract_textures); + bool get_discard_meshes_and_materials(); void set_discard_meshes_and_materials(bool p_discard_meshes_and_materials); @@ -182,6 +207,7 @@ public: void set_animations(TypedArray<GLTFAnimation> p_animations); Node *get_scene_node(GLTFNodeIndex idx); + ImporterMeshInstance3D *get_scene_mesh_instance(GLTFNodeIndex idx); int get_animation_players_count(int idx); diff --git a/modules/mbedtls/dtls_server_mbedtls.cpp b/modules/mbedtls/dtls_server_mbedtls.cpp index c54ab8ef6e..62513929ea 100644 --- a/modules/mbedtls/dtls_server_mbedtls.cpp +++ b/modules/mbedtls/dtls_server_mbedtls.cpp @@ -31,25 +31,25 @@ #include "dtls_server_mbedtls.h" #include "packet_peer_mbed_dtls.h" -Error DTLSServerMbedTLS::setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain) { - ERR_FAIL_COND_V(_cookies->setup() != OK, ERR_ALREADY_IN_USE); - _key = p_key; - _cert = p_cert; - _ca_chain = p_ca_chain; +Error DTLSServerMbedTLS::setup(Ref<TLSOptions> p_options) { + ERR_FAIL_COND_V(p_options.is_null() || !p_options->is_server(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(cookies->setup() != OK, ERR_ALREADY_IN_USE); + tls_options = p_options; return OK; } void DTLSServerMbedTLS::stop() { - _cookies->clear(); + cookies->clear(); } Ref<PacketPeerDTLS> DTLSServerMbedTLS::take_connection(Ref<PacketPeerUDP> p_udp_peer) { Ref<PacketPeerMbedDTLS> out; - out.instantiate(); - ERR_FAIL_COND_V(!out.is_valid(), out); + ERR_FAIL_COND_V(tls_options.is_null(), out); ERR_FAIL_COND_V(!p_udp_peer.is_valid(), out); - out->accept_peer(p_udp_peer, _key, _cert, _ca_chain, _cookies); + + out.instantiate(); + out->accept_peer(p_udp_peer, tls_options, cookies); return out; } @@ -68,7 +68,7 @@ void DTLSServerMbedTLS::finalize() { } DTLSServerMbedTLS::DTLSServerMbedTLS() { - _cookies.instantiate(); + cookies.instantiate(); } DTLSServerMbedTLS::~DTLSServerMbedTLS() { diff --git a/modules/mbedtls/dtls_server_mbedtls.h b/modules/mbedtls/dtls_server_mbedtls.h index e4612d01ef..d5841a45fa 100644 --- a/modules/mbedtls/dtls_server_mbedtls.h +++ b/modules/mbedtls/dtls_server_mbedtls.h @@ -37,16 +37,14 @@ class DTLSServerMbedTLS : public DTLSServer { private: static DTLSServer *_create_func(); - Ref<CryptoKey> _key; - Ref<X509Certificate> _cert; - Ref<X509Certificate> _ca_chain; - Ref<CookieContextMbedTLS> _cookies; + Ref<TLSOptions> tls_options; + Ref<CookieContextMbedTLS> cookies; public: static void initialize(); static void finalize(); - virtual Error setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()); + virtual Error setup(Ref<TLSOptions> p_options); virtual void stop(); virtual Ref<PacketPeerDTLS> take_connection(Ref<PacketPeerUDP> p_peer); diff --git a/modules/mbedtls/packet_peer_mbed_dtls.cpp b/modules/mbedtls/packet_peer_mbed_dtls.cpp index 16450e151e..e8eb32f88d 100644 --- a/modules/mbedtls/packet_peer_mbed_dtls.cpp +++ b/modules/mbedtls/packet_peer_mbed_dtls.cpp @@ -114,16 +114,14 @@ Error PacketPeerMbedDTLS::_do_handshake() { return OK; } -Error PacketPeerMbedDTLS::connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_validate_certs, const String &p_for_hostname, Ref<X509Certificate> p_ca_certs) { +Error PacketPeerMbedDTLS::connect_to_peer(Ref<PacketPeerUDP> p_base, const String &p_hostname, Ref<TLSOptions> p_options) { ERR_FAIL_COND_V(!p_base.is_valid() || !p_base->is_socket_connected(), ERR_INVALID_PARAMETER); - base = p_base; - int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE; - - Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_DATAGRAM, authmode, p_ca_certs); + Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_DATAGRAM, p_hostname, p_options.is_valid() ? p_options : TLSOptions::client()); ERR_FAIL_COND_V(err != OK, err); - mbedtls_ssl_set_hostname(tls_ctx->get_context(), p_for_hostname.utf8().get_data()); + base = p_base; + mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr); mbedtls_ssl_set_timer_cb(tls_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); @@ -137,8 +135,10 @@ Error PacketPeerMbedDTLS::connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_vali return OK; } -Error PacketPeerMbedDTLS::accept_peer(Ref<PacketPeerUDP> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain, Ref<CookieContextMbedTLS> p_cookies) { - Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert, p_cookies); +Error PacketPeerMbedDTLS::accept_peer(Ref<PacketPeerUDP> p_base, Ref<TLSOptions> p_options, Ref<CookieContextMbedTLS> p_cookies) { + ERR_FAIL_COND_V(!p_base.is_valid() || !p_base->is_socket_connected(), ERR_INVALID_PARAMETER); + + Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_DATAGRAM, p_options, p_cookies); ERR_FAIL_COND_V(err != OK, err); base = p_base; diff --git a/modules/mbedtls/packet_peer_mbed_dtls.h b/modules/mbedtls/packet_peer_mbed_dtls.h index 744ef81524..05decec783 100644 --- a/modules/mbedtls/packet_peer_mbed_dtls.h +++ b/modules/mbedtls/packet_peer_mbed_dtls.h @@ -64,8 +64,8 @@ protected: public: virtual void poll(); - virtual Error accept_peer(Ref<PacketPeerUDP> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert = Ref<X509Certificate>(), Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>(), Ref<CookieContextMbedTLS> p_cookies = Ref<CookieContextMbedTLS>()); - virtual Error connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_validate_certs = true, const String &p_for_hostname = String(), Ref<X509Certificate> p_ca_certs = Ref<X509Certificate>()); + virtual Error accept_peer(Ref<PacketPeerUDP> p_base, Ref<TLSOptions> p_options, Ref<CookieContextMbedTLS> p_cookies = Ref<CookieContextMbedTLS>()); + virtual Error connect_to_peer(Ref<PacketPeerUDP> p_base, const String &p_hostname, Ref<TLSOptions> p_options = Ref<TLSOptions>()); virtual Status get_status() const; virtual void disconnect_from_peer(); diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp index 1d17fb9441..a9d187bd64 100644 --- a/modules/mbedtls/stream_peer_mbedtls.cpp +++ b/modules/mbedtls/stream_peer_mbedtls.cpp @@ -80,38 +80,30 @@ void StreamPeerMbedTLS::_cleanup() { } Error StreamPeerMbedTLS::_do_handshake() { - int ret = 0; - while ((ret = mbedtls_ssl_handshake(tls_ctx->get_context())) != 0) { - if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - // An error occurred. - ERR_PRINT("TLS handshake error: " + itos(ret)); - TLSContextMbedTLS::print_mbedtls_error(ret); - disconnect_from_stream(); - status = STATUS_ERROR; - return FAILED; - } - - // Handshake is still in progress. - if (!blocking_handshake) { - // Will retry via poll later - return OK; - } + int ret = mbedtls_ssl_handshake(tls_ctx->get_context()); + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + // Handshake is still in progress, will retry via poll later. + return OK; + } else if (ret != 0) { + // An error occurred. + ERR_PRINT("TLS handshake error: " + itos(ret)); + TLSContextMbedTLS::print_mbedtls_error(ret); + disconnect_from_stream(); + status = STATUS_ERROR; + return FAILED; } status = STATUS_CONNECTED; return OK; } -Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname, Ref<X509Certificate> p_ca_certs) { +Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, const String &p_common_name, Ref<TLSOptions> p_options) { ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER); - base = p_base; - int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE; - - Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, authmode, p_ca_certs); + Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, p_common_name, p_options.is_valid() ? p_options : TLSOptions::client()); ERR_FAIL_COND_V(err != OK, err); - mbedtls_ssl_set_hostname(tls_ctx->get_context(), p_for_hostname.utf8().get_data()); + base = p_base; mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr); status = STATUS_HANDSHAKING; @@ -124,10 +116,11 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida return OK; } -Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain) { +Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base, Ref<TLSOptions> p_options) { ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_options.is_null() || !p_options->is_server(), ERR_INVALID_PARAMETER); - Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert); + Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, p_options); ERR_FAIL_COND_V(err != OK, err); base = p_base; @@ -308,10 +301,8 @@ StreamPeerTLS *StreamPeerMbedTLS::_create_func() { void StreamPeerMbedTLS::initialize_tls() { _create = _create_func; - available = true; } void StreamPeerMbedTLS::finalize_tls() { - available = false; _create = nullptr; } diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h index 8a36a7ea9a..ec0446c380 100644 --- a/modules/mbedtls/stream_peer_mbedtls.h +++ b/modules/mbedtls/stream_peer_mbedtls.h @@ -54,8 +54,8 @@ protected: public: virtual void poll(); - virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()); - virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()); + virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<TLSOptions> p_options); + virtual Error connect_to_stream(Ref<StreamPeer> p_base, const String &p_common_name, Ref<TLSOptions> p_options); virtual Status get_status() const; virtual Ref<StreamPeer> get_stream() const; diff --git a/modules/mbedtls/tls_context_mbedtls.cpp b/modules/mbedtls/tls_context_mbedtls.cpp index a01137f262..aab082f488 100644 --- a/modules/mbedtls/tls_context_mbedtls.cpp +++ b/modules/mbedtls/tls_context_mbedtls.cpp @@ -110,22 +110,20 @@ Error TLSContextMbedTLS::_setup(int p_endpoint, int p_transport, int p_authmode) return OK; } -Error TLSContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<CryptoKeyMbedTLS> p_pkey, Ref<X509CertificateMbedTLS> p_cert, Ref<CookieContextMbedTLS> p_cookies) { - ERR_FAIL_COND_V(!p_pkey.is_valid(), ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!p_cert.is_valid(), ERR_INVALID_PARAMETER); +Error TLSContextMbedTLS::init_server(int p_transport, Ref<TLSOptions> p_options, Ref<CookieContextMbedTLS> p_cookies) { + ERR_FAIL_COND_V(p_options.is_null() || !p_options->is_server(), ERR_INVALID_PARAMETER); - Error err = _setup(MBEDTLS_SSL_IS_SERVER, p_transport, p_authmode); + // Check key and certificate(s) + pkey = p_options->get_private_key(); + certs = p_options->get_own_certificate(); + ERR_FAIL_COND_V(pkey.is_null() || certs.is_null(), ERR_INVALID_PARAMETER); + + Error err = _setup(MBEDTLS_SSL_IS_SERVER, p_transport, MBEDTLS_SSL_VERIFY_NONE); // TODO client auth. ERR_FAIL_COND_V(err != OK, err); // Locking key and certificate(s) - pkey = p_pkey; - certs = p_cert; - if (pkey.is_valid()) { - pkey->lock(); - } - if (certs.is_valid()) { - certs->lock(); - } + pkey->lock(); + certs->lock(); // Adding key and certificate int ret = mbedtls_ssl_conf_own_cert(&conf, &(certs->cert), &(pkey->pkey)); @@ -150,15 +148,32 @@ Error TLSContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<Crypto return OK; } -Error TLSContextMbedTLS::init_client(int p_transport, int p_authmode, Ref<X509CertificateMbedTLS> p_valid_cas) { - Error err = _setup(MBEDTLS_SSL_IS_CLIENT, p_transport, p_authmode); +Error TLSContextMbedTLS::init_client(int p_transport, const String &p_hostname, Ref<TLSOptions> p_options) { + ERR_FAIL_COND_V(p_options.is_null() || p_options->is_server(), ERR_INVALID_PARAMETER); + + int authmode = MBEDTLS_SSL_VERIFY_REQUIRED; + if (p_options->get_verify_mode() == TLSOptions::TLS_VERIFY_NONE) { + authmode = MBEDTLS_SSL_VERIFY_NONE; + } + + Error err = _setup(MBEDTLS_SSL_IS_CLIENT, p_transport, authmode); ERR_FAIL_COND_V(err != OK, err); + if (p_options->get_verify_mode() == TLSOptions::TLS_VERIFY_FULL) { + String cn = p_options->get_common_name(); + if (cn.is_empty()) { + cn = p_hostname; + } + mbedtls_ssl_set_hostname(&tls, cn.utf8().get_data()); + } else { + mbedtls_ssl_set_hostname(&tls, nullptr); + } + X509CertificateMbedTLS *cas = nullptr; - if (p_valid_cas.is_valid()) { + if (p_options->get_trusted_ca_chain().is_valid()) { // Locking CA certificates - certs = p_valid_cas; + certs = p_options->get_trusted_ca_chain(); certs->lock(); cas = certs.ptr(); } else { diff --git a/modules/mbedtls/tls_context_mbedtls.h b/modules/mbedtls/tls_context_mbedtls.h index 574e80e199..f1bad6a40c 100644 --- a/modules/mbedtls/tls_context_mbedtls.h +++ b/modules/mbedtls/tls_context_mbedtls.h @@ -71,17 +71,17 @@ public: static void print_mbedtls_error(int p_ret); Ref<X509CertificateMbedTLS> certs; + Ref<CryptoKeyMbedTLS> pkey; + Ref<CookieContextMbedTLS> cookies; + mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_ssl_context tls; mbedtls_ssl_config conf; - Ref<CookieContextMbedTLS> cookies; - Ref<CryptoKeyMbedTLS> pkey; - Error _setup(int p_endpoint, int p_transport, int p_authmode); - Error init_server(int p_transport, int p_authmode, Ref<CryptoKeyMbedTLS> p_pkey, Ref<X509CertificateMbedTLS> p_cert, Ref<CookieContextMbedTLS> p_cookies = Ref<CookieContextMbedTLS>()); - Error init_client(int p_transport, int p_authmode, Ref<X509CertificateMbedTLS> p_valid_cas); + Error init_server(int p_transport, Ref<TLSOptions> p_options, Ref<CookieContextMbedTLS> p_cookies = Ref<CookieContextMbedTLS>()); + Error init_client(int p_transport, const String &p_hostname, Ref<TLSOptions> p_options); void clear(); mbedtls_ssl_context *get_context(); diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml index 8f03681c06..a88ff23b6b 100644 --- a/modules/minimp3/doc_classes/AudioStreamMP3.xml +++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml @@ -21,21 +21,17 @@ [codeblocks] [gdscript] func load_mp3(path): - var file = File.new() - file.open(path, File.READ) + var file = FileAccess.open(path, FileAccess.READ) var sound = AudioStreamMP3.new() sound.data = file.get_buffer(file.get_length()) - file.close() return sound [/gdscript] [csharp] public AudioStreamMP3 LoadMP3(string path) { - var file = new File(); - file.Open(path, File.READ); + using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read); var sound = new AudioStreamMP3(); sound.Data = file.GetBuffer(file.GetLength()); - file.Close(); return sound; } [/csharp] diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index ca94917938..fe0f4aae81 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -60,6 +60,7 @@ #include "mono_gd/gd_mono_cache.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" +#include "utils/naming_utils.h" #include "utils/string_utils.h" #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) @@ -333,7 +334,7 @@ void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const { } static String get_base_class_name(const String &p_base_class_name, const String p_class_name) { - String base_class = p_base_class_name; + String base_class = pascal_to_pascal_case(p_base_class_name); if (p_class_name == base_class) { base_class = "Godot." + base_class; } @@ -394,17 +395,22 @@ bool CSharpLanguage::supports_builtin_mode() const { } #ifdef TOOLS_ENABLED +struct VariantCsName { + Variant::Type variant_type; + const String cs_type; +}; + static String variant_type_to_managed_name(const String &p_var_type_name) { if (p_var_type_name.is_empty()) { return "Variant"; } if (ClassDB::class_exists(p_var_type_name)) { - return p_var_type_name; + return pascal_to_pascal_case(p_var_type_name); } if (p_var_type_name == Variant::get_type_name(Variant::OBJECT)) { - return "Godot.Object"; + return "GodotObject"; } if (p_var_type_name == Variant::get_type_name(Variant::INT)) { @@ -459,34 +465,34 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { return "Signal"; } - Variant::Type var_types[] = { - Variant::BOOL, - Variant::INT, - Variant::VECTOR2, - Variant::VECTOR2I, - Variant::RECT2, - Variant::RECT2I, - Variant::VECTOR3, - Variant::VECTOR3I, - Variant::TRANSFORM2D, - Variant::VECTOR4, - Variant::VECTOR4I, - Variant::PLANE, - Variant::QUATERNION, - Variant::AABB, - Variant::BASIS, - Variant::TRANSFORM3D, - Variant::PROJECTION, - Variant::COLOR, - Variant::STRING_NAME, - Variant::NODE_PATH, - Variant::RID, - Variant::CALLABLE + const VariantCsName var_types[] = { + { Variant::BOOL, "bool" }, + { Variant::INT, "long" }, + { Variant::VECTOR2, "Vector2" }, + { Variant::VECTOR2I, "Vector2I" }, + { Variant::RECT2, "Rect2" }, + { Variant::RECT2I, "Rect2I" }, + { Variant::VECTOR3, "Vector3" }, + { Variant::VECTOR3I, "Vector3I" }, + { Variant::TRANSFORM2D, "Transform2D" }, + { Variant::VECTOR4, "Vector4" }, + { Variant::VECTOR4I, "Vector4I" }, + { Variant::PLANE, "Plane" }, + { Variant::QUATERNION, "Quaternion" }, + { Variant::AABB, "Aabb" }, + { Variant::BASIS, "Basis" }, + { Variant::TRANSFORM3D, "Transform3D" }, + { Variant::PROJECTION, "Projection" }, + { Variant::COLOR, "Color" }, + { Variant::STRING_NAME, "StringName" }, + { Variant::NODE_PATH, "NodePath" }, + { Variant::RID, "Rid" }, + { Variant::CALLABLE, "Callable" }, }; - for (unsigned int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) { - if (p_var_type_name == Variant::get_type_name(var_types[i])) { - return p_var_type_name; + for (unsigned int i = 0; i < sizeof(var_types) / sizeof(VariantCsName); i++) { + if (p_var_type_name == Variant::get_type_name(var_types[i].variant_type)) { + return var_types[i].cs_type; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Bar.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Bar.cs index 5eaebc4474..2d797e2f46 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Bar.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Bar.cs @@ -1,6 +1,6 @@ namespace Godot.SourceGenerators.Sample { - partial class Bar : Godot.Object + partial class Bar : GodotObject { } @@ -9,7 +9,7 @@ namespace Godot.SourceGenerators.Sample { } - partial class NotSameNameAsFile : Godot.Object + partial class NotSameNameAsFile : GodotObject { } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs index 764ba8f121..ee6aa857fc 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs @@ -1,6 +1,6 @@ namespace Godot.SourceGenerators.Sample; -public partial class EventSignals : Godot.Object +public partial class EventSignals : GodotObject { [Signal] public delegate void MySignalEventHandler(string str, int num); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs index ccaba4d727..31e66ac306 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs @@ -11,7 +11,7 @@ namespace Godot.SourceGenerators.Sample [SuppressMessage("ReSharper", "RedundantNameQualifier")] [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")] [SuppressMessage("ReSharper", "InconsistentNaming")] - public partial class ExportedFields : Godot.Object + public partial class ExportedFields : GodotObject { [Export] private Boolean field_Boolean = true; [Export] private Char field_Char = 'f'; @@ -29,19 +29,19 @@ namespace Godot.SourceGenerators.Sample // Godot structs [Export] private Vector2 field_Vector2 = new(10f, 10f); - [Export] private Vector2i field_Vector2i = Vector2i.Up; + [Export] private Vector2I field_Vector2I = Vector2I.Up; [Export] private Rect2 field_Rect2 = new(new Vector2(10f, 10f), new Vector2(10f, 10f)); - [Export] private Rect2i field_Rect2i = new(new Vector2i(10, 10), new Vector2i(10, 10)); + [Export] private Rect2I field_Rect2I = new(new Vector2I(10, 10), new Vector2I(10, 10)); [Export] private Transform2D field_Transform2D = Transform2D.Identity; [Export] private Vector3 field_Vector3 = new(10f, 10f, 10f); - [Export] private Vector3i field_Vector3i = Vector3i.Back; + [Export] private Vector3I field_Vector3I = Vector3I.Back; [Export] private Basis field_Basis = new Basis(Quaternion.Identity); [Export] private Quaternion field_Quaternion = new Quaternion(Basis.Identity); [Export] private Transform3D field_Transform3D = Transform3D.Identity; [Export] private Vector4 field_Vector4 = new(10f, 10f, 10f, 10f); - [Export] private Vector4i field_Vector4i = Vector4i.One; + [Export] private Vector4I field_Vector4I = Vector4I.One; [Export] private Projection field_Projection = Projection.Identity; - [Export] private AABB field_AABB = new AABB(10f, 10f, 10f, new Vector3(1f, 1f, 1f)); + [Export] private Aabb field_Aabb = new Aabb(10f, 10f, 10f, new Vector3(1f, 1f, 1f)); [Export] private Color field_Color = Colors.Aquamarine; [Export] private Plane field_Plane = Plane.PlaneXZ; [Export] private Callable field_Callable = new Callable(Engine.GetMainLoop(), "_process"); @@ -80,10 +80,10 @@ namespace Godot.SourceGenerators.Sample [Export] private Vector2[] field_Vector2Array = { Vector2.Up, Vector2.Down, Vector2.Left, Vector2.Right }; [Export] private Vector3[] field_Vector3Array = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right }; [Export] private Color[] field_ColorArray = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige }; - [Export] private Godot.Object[] field_GodotObjectOrDerivedArray = { null }; + [Export] private GodotObject[] field_GodotObjectOrDerivedArray = { null }; [Export] private StringName[] field_StringNameArray = { "foo", "bar" }; [Export] private NodePath[] field_NodePathArray = { "foo", "bar" }; - [Export] private RID[] field_RIDArray = { default, default, default }; + [Export] private Rid[] field_RidArray = { default, default, default }; // Note we use Array and not System.Array. This tests the generated namespace qualification. [Export] private Int32[] field_empty_Int32Array = Array.Empty<Int32>(); // Note we use List and not System.Collections.Generic. @@ -93,11 +93,11 @@ namespace Godot.SourceGenerators.Sample [Export] private Variant field_Variant = "foo"; // Classes - [Export] private Godot.Object field_GodotObjectOrDerived; + [Export] private GodotObject field_GodotObjectOrDerived; [Export] private Godot.Texture field_GodotResourceTexture; [Export] private StringName field_StringName = new StringName("foo"); [Export] private NodePath field_NodePath = new NodePath("foo"); - [Export] private RID field_RID; + [Export] private Rid field_Rid; [Export] private Godot.Collections.Dictionary field_GodotDictionary = diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs index 5afaeb736f..aef2a8824e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs @@ -10,7 +10,7 @@ namespace Godot.SourceGenerators.Sample [SuppressMessage("ReSharper", "RedundantNameQualifier")] [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")] [SuppressMessage("ReSharper", "InconsistentNaming")] - public partial class ExportedProperties : Godot.Object + public partial class ExportedProperties : GodotObject { // Do not generate default value private String _notGenerate_Property_String = new string("not generate"); @@ -117,19 +117,19 @@ namespace Godot.SourceGenerators.Sample // Godot structs [Export] private Vector2 property_Vector2 { get; set; } = new(10f, 10f); - [Export] private Vector2i property_Vector2i { get; set; } = Vector2i.Up; + [Export] private Vector2I property_Vector2I { get; set; } = Vector2I.Up; [Export] private Rect2 property_Rect2 { get; set; } = new(new Vector2(10f, 10f), new Vector2(10f, 10f)); - [Export] private Rect2i property_Rect2i { get; set; } = new(new Vector2i(10, 10), new Vector2i(10, 10)); + [Export] private Rect2I property_Rect2I { get; set; } = new(new Vector2I(10, 10), new Vector2I(10, 10)); [Export] private Transform2D property_Transform2D { get; set; } = Transform2D.Identity; [Export] private Vector3 property_Vector3 { get; set; } = new(10f, 10f, 10f); - [Export] private Vector3i property_Vector3i { get; set; } = Vector3i.Back; + [Export] private Vector3I property_Vector3I { get; set; } = Vector3I.Back; [Export] private Basis property_Basis { get; set; } = new Basis(Quaternion.Identity); [Export] private Quaternion property_Quaternion { get; set; } = new Quaternion(Basis.Identity); [Export] private Transform3D property_Transform3D { get; set; } = Transform3D.Identity; [Export] private Vector4 property_Vector4 { get; set; } = new(10f, 10f, 10f, 10f); - [Export] private Vector4i property_Vector4i { get; set; } = Vector4i.One; + [Export] private Vector4I property_Vector4I { get; set; } = Vector4I.One; [Export] private Projection property_Projection { get; set; } = Projection.Identity; - [Export] private AABB property_AABB { get; set; } = new AABB(10f, 10f, 10f, new Vector3(1f, 1f, 1f)); + [Export] private Aabb property_Aabb { get; set; } = new Aabb(10f, 10f, 10f, new Vector3(1f, 1f, 1f)); [Export] private Color property_Color { get; set; } = Colors.Aquamarine; [Export] private Plane property_Plane { get; set; } = Plane.PlaneXZ; [Export] private Callable property_Callable { get; set; } = new Callable(Engine.GetMainLoop(), "_process"); @@ -168,20 +168,20 @@ namespace Godot.SourceGenerators.Sample [Export] private Vector2[] property_Vector2Array { get; set; } = { Vector2.Up, Vector2.Down, Vector2.Left, Vector2.Right }; [Export] private Vector3[] property_Vector3Array { get; set; } = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right }; [Export] private Color[] property_ColorArray { get; set; } = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige }; - [Export] private Godot.Object[] property_GodotObjectOrDerivedArray { get; set; } = { null }; + [Export] private GodotObject[] property_GodotObjectOrDerivedArray { get; set; } = { null }; [Export] private StringName[] field_StringNameArray { get; set; } = { "foo", "bar" }; [Export] private NodePath[] field_NodePathArray { get; set; } = { "foo", "bar" }; - [Export] private RID[] field_RIDArray { get; set; } = { default, default, default }; + [Export] private Rid[] field_RidArray { get; set; } = { default, default, default }; // Variant [Export] private Variant property_Variant { get; set; } = "foo"; // Classes - [Export] private Godot.Object property_GodotObjectOrDerived { get; set; } + [Export] private GodotObject property_GodotObjectOrDerived { get; set; } [Export] private Godot.Texture property_GodotResourceTexture { get; set; } [Export] private StringName property_StringName { get; set; } = new StringName("foo"); [Export] private NodePath property_NodePath { get; set; } = new NodePath("foo"); - [Export] private RID property_RID { get; set; } + [Export] private Rid property_Rid { get; set; } [Export] private Godot.Collections.Dictionary property_GodotDictionary { get; set; } = diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Foo.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Foo.cs index 21a5bfe560..9ef72d9e02 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Foo.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Foo.cs @@ -1,6 +1,6 @@ namespace Godot.SourceGenerators.Sample { - partial class Foo : Godot.Object + partial class Foo : GodotObject { } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs index b21b035b4d..2cd1a08c0e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs @@ -2,19 +2,19 @@ namespace Godot.SourceGenerators.Sample { - partial class Generic<T> : Godot.Object + partial class Generic<T> : GodotObject { private int _field; } // Generic again but different generic parameters - partial class Generic<T, R> : Godot.Object + partial class Generic<T, R> : GodotObject { private int _field; } // Generic again but without generic parameters - partial class Generic : Godot.Object + partial class Generic : GodotObject { private int _field; } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs index 618ba24abc..9aa6be3972 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; namespace Godot.SourceGenerators.Sample; [SuppressMessage("ReSharper", "RedundantNameQualifier")] -public partial class Methods : Godot.Object +public partial class Methods : GodotObject { private void MethodWithOverload() { diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MoreExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MoreExportedFields.cs index a6c8e52667..64088215e9 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MoreExportedFields.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MoreExportedFields.cs @@ -11,7 +11,7 @@ namespace Godot.SourceGenerators.Sample [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")] [SuppressMessage("ReSharper", "InconsistentNaming")] // We split the definition of ExportedFields to verify properties work across multiple files. - public partial class ExportedFields : Godot.Object + public partial class ExportedFields : GodotObject { // Note we use Array and not System.Array. This tests the generated namespace qualification. [Export] private Int64[] field_empty_Int64Array = Array.Empty<Int64>(); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs index 4eed2d7b7b..41bf89e6d8 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs @@ -14,9 +14,9 @@ namespace Godot.SourceGenerators { string message = "Missing partial modifier on declaration of type '" + - $"{symbol.FullQualifiedNameOmitGlobal()}' which is a subclass of '{GodotClasses.Object}'"; + $"{symbol.FullQualifiedNameOmitGlobal()}' which is a subclass of '{GodotClasses.GodotObject}'"; - string description = $"{message}. Subclasses of '{GodotClasses.Object}' " + + string description = $"{message}. Subclasses of '{GodotClasses.GodotObject}' " + "must be declared with the partial modifier."; context.ReportDiagnostic(Diagnostic.Create( @@ -46,9 +46,9 @@ namespace Godot.SourceGenerators string message = $"Missing partial modifier on declaration of type '{fullQualifiedName}', " + - $"which contains one or more subclasses of '{GodotClasses.Object}'"; + $"which contains one or more subclasses of '{GodotClasses.GodotObject}'"; - string description = $"{message}. Subclasses of '{GodotClasses.Object}' and their " + + string description = $"{message}. Subclasses of '{GodotClasses.GodotObject}' and their " + "containing types must be declared with the partial modifier."; context.ReportDiagnostic(Diagnostic.Create( diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs index 8852b7ebad..f0c9043fd5 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs @@ -85,7 +85,7 @@ namespace Godot.SourceGenerators var classTypeSymbol = sm.GetDeclaredSymbol(cds); if (classTypeSymbol?.BaseType == null - || !classTypeSymbol.BaseType.InheritsFrom("GodotSharp", GodotClasses.Object)) + || !classTypeSymbol.BaseType.InheritsFrom("GodotSharp", GodotClasses.GodotObject)) { symbol = null; return false; diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs index 1d8ddbabf2..b60148b34f 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs @@ -2,7 +2,7 @@ namespace Godot.SourceGenerators { public static class GodotClasses { - public const string Object = "Godot.Object"; + public const string GodotObject = "Godot.GodotObject"; public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute"; public const string ExportAttr = "Godot.ExportAttribute"; public const string ExportCategoryAttr = "Godot.ExportCategoryAttribute"; @@ -10,7 +10,7 @@ namespace Godot.SourceGenerators public const string ExportSubgroupAttr = "Godot.ExportSubgroupAttribute"; public const string SignalAttr = "Godot.SignalAttribute"; public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute"; - public const string GodotClassNameAttr = "Godot.GodotClassName"; + public const string GodotClassNameAttr = "Godot.GodotClassNameAttribute"; public const string SystemFlagsAttr = "System.FlagsAttribute"; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs index 22a21a1754..b30c8c240e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs @@ -12,19 +12,19 @@ namespace Godot.SourceGenerators Float = 3, String = 4, Vector2 = 5, - Vector2i = 6, + Vector2I = 6, Rect2 = 7, - Rect2i = 8, + Rect2I = 8, Vector3 = 9, - Vector3i = 10, - Transform2d = 11, + Vector3I = 10, + Transform2D = 11, Vector4 = 12, - Vector4i = 13, + Vector4I = 13, Plane = 14, Quaternion = 15, Aabb = 16, Basis = 17, - Transform3d = 18, + Transform3D = 18, Projection = 19, Color = 20, StringName = 21, @@ -56,12 +56,12 @@ namespace Godot.SourceGenerators ExpEasing = 4, Link = 5, Flags = 6, - Layers2dRender = 7, - Layers2dPhysics = 8, - Layers2dNavigation = 9, - Layers3dRender = 10, - Layers3dPhysics = 11, - Layers3dNavigation = 12, + Layers2DRender = 7, + Layers2DPhysics = 8, + Layers2DNavigation = 9, + Layers3DRender = 10, + Layers3DPhysics = 11, + Layers3DNavigation = 12, File = 13, Dir = 14, GlobalFile = 15, diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs index 19fdd51dab..813bdf1e9f 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs @@ -34,7 +34,7 @@ namespace GodotPlugins.Game { DllImportResolver dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport; - var coreApiAssembly = typeof(Godot.Object).Assembly; + var coreApiAssembly = typeof(global::Godot.GodotObject).Assembly; NativeLibrary.SetDllImportResolver(coreApiAssembly, dllImportResolver); @@ -42,7 +42,7 @@ namespace GodotPlugins.Game ManagedCallbacks.Create(outManagedCallbacks); - ScriptManagerBridge.LookupScriptsInAssembly(typeof(GodotPlugins.Game.Main).Assembly); + ScriptManagerBridge.LookupScriptsInAssembly(typeof(global::GodotPlugins.Game.Main).Assembly); return godot_bool.True; } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs index ee1374d0b9..be6af117eb 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs @@ -21,19 +21,19 @@ namespace Godot.SourceGenerators // Godot structs Vector2, - Vector2i, + Vector2I, Rect2, - Rect2i, + Rect2I, Transform2D, Vector3, - Vector3i, + Vector3I, Basis, Quaternion, Transform3D, Vector4, - Vector4i, + Vector4I, Projection, - AABB, + Aabb, Color, Plane, Callable, @@ -55,7 +55,7 @@ namespace Godot.SourceGenerators GodotObjectOrDerivedArray, SystemArrayOfStringName, SystemArrayOfNodePath, - SystemArrayOfRID, + SystemArrayOfRid, // Variant Variant, @@ -64,7 +64,7 @@ namespace Godot.SourceGenerators GodotObjectOrDerived, StringName, NodePath, - RID, + Rid, GodotDictionary, GodotArray, GodotGenericDictionary, diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs index f0a6a72281..0258f53062 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs @@ -19,7 +19,7 @@ namespace Godot.SourceGenerators throw new InvalidOperationException($"Type not found: '{fullyQualifiedMetadataName}'."); } - GodotObjectType = GetTypeByMetadataNameOrThrow("Godot.Object"); + GodotObjectType = GetTypeByMetadataNameOrThrow(GodotClasses.GodotObject); } } @@ -40,19 +40,19 @@ namespace Godot.SourceGenerators MarshalType.Double => VariantType.Float, MarshalType.String => VariantType.String, MarshalType.Vector2 => VariantType.Vector2, - MarshalType.Vector2i => VariantType.Vector2i, + MarshalType.Vector2I => VariantType.Vector2I, MarshalType.Rect2 => VariantType.Rect2, - MarshalType.Rect2i => VariantType.Rect2i, - MarshalType.Transform2D => VariantType.Transform2d, + MarshalType.Rect2I => VariantType.Rect2I, + MarshalType.Transform2D => VariantType.Transform2D, MarshalType.Vector3 => VariantType.Vector3, - MarshalType.Vector3i => VariantType.Vector3i, + MarshalType.Vector3I => VariantType.Vector3I, MarshalType.Basis => VariantType.Basis, MarshalType.Quaternion => VariantType.Quaternion, - MarshalType.Transform3D => VariantType.Transform3d, + MarshalType.Transform3D => VariantType.Transform3D, MarshalType.Vector4 => VariantType.Vector4, - MarshalType.Vector4i => VariantType.Vector4i, + MarshalType.Vector4I => VariantType.Vector4I, MarshalType.Projection => VariantType.Projection, - MarshalType.AABB => VariantType.Aabb, + MarshalType.Aabb => VariantType.Aabb, MarshalType.Color => VariantType.Color, MarshalType.Plane => VariantType.Plane, MarshalType.Callable => VariantType.Callable, @@ -70,12 +70,12 @@ namespace Godot.SourceGenerators MarshalType.GodotObjectOrDerivedArray => VariantType.Array, MarshalType.SystemArrayOfStringName => VariantType.Array, MarshalType.SystemArrayOfNodePath => VariantType.Array, - MarshalType.SystemArrayOfRID => VariantType.Array, + MarshalType.SystemArrayOfRid => VariantType.Array, MarshalType.Variant => VariantType.Nil, MarshalType.GodotObjectOrDerived => VariantType.Object, MarshalType.StringName => VariantType.StringName, MarshalType.NodePath => VariantType.NodePath, - MarshalType.RID => VariantType.Rid, + MarshalType.Rid => VariantType.Rid, MarshalType.GodotDictionary => VariantType.Dictionary, MarshalType.GodotArray => VariantType.Array, MarshalType.GodotGenericDictionary => VariantType.Dictionary, @@ -130,22 +130,22 @@ namespace Godot.SourceGenerators return type switch { { Name: "Vector2" } => MarshalType.Vector2, - { Name: "Vector2i" } => MarshalType.Vector2i, + { Name: "Vector2I" } => MarshalType.Vector2I, { Name: "Rect2" } => MarshalType.Rect2, - { Name: "Rect2i" } => MarshalType.Rect2i, + { Name: "Rect2I" } => MarshalType.Rect2I, { Name: "Transform2D" } => MarshalType.Transform2D, { Name: "Vector3" } => MarshalType.Vector3, - { Name: "Vector3i" } => MarshalType.Vector3i, + { Name: "Vector3I" } => MarshalType.Vector3I, { Name: "Basis" } => MarshalType.Basis, { Name: "Quaternion" } => MarshalType.Quaternion, { Name: "Transform3D" } => MarshalType.Transform3D, { Name: "Vector4" } => MarshalType.Vector4, - { Name: "Vector4i" } => MarshalType.Vector4i, + { Name: "Vector4I" } => MarshalType.Vector4I, { Name: "Projection" } => MarshalType.Projection, - { Name: "AABB" } => MarshalType.AABB, + { Name: "Aabb" } => MarshalType.Aabb, { Name: "Color" } => MarshalType.Color, { Name: "Plane" } => MarshalType.Plane, - { Name: "RID" } => MarshalType.RID, + { Name: "Rid" } => MarshalType.Rid, { Name: "Callable" } => MarshalType.Callable, { Name: "Signal" } => MarshalType.Signal, { Name: "Variant" } => MarshalType.Variant, @@ -196,8 +196,8 @@ namespace Godot.SourceGenerators return MarshalType.SystemArrayOfStringName; case { Name: "NodePath" }: return MarshalType.SystemArrayOfNodePath; - case { Name: "RID" }: - return MarshalType.SystemArrayOfRID; + case { Name: "Rid" }: + return MarshalType.SystemArrayOfRid; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs index 98ca534c66..2a9758516c 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs @@ -26,6 +26,10 @@ namespace Godot.SourceGenerators private void AnalyzeNode(SyntaxNodeAnalysisContext context) { + // Ignore syntax inside comments + if (IsInsideDocumentation(context.Node)) + return; + var typeArgListSyntax = (TypeArgumentListSyntax)context.Node; // Method invocation or variable declaration that contained the type arguments @@ -74,6 +78,26 @@ namespace Godot.SourceGenerators } /// <summary> + /// Check if the syntax node is inside a documentation syntax. + /// </summary> + /// <param name="syntax">Syntax node to check.</param> + /// <returns><see langword="true"/> if the syntax node is inside a documentation syntax.</returns> + private bool IsInsideDocumentation(SyntaxNode? syntax) + { + while (syntax != null) + { + if (syntax is DocumentationCommentTriviaSyntax) + { + return true; + } + + syntax = syntax.Parent; + } + + return false; + } + + /// <summary> /// Check if the given type argument is being used in a type parameter that contains /// the <c>MustBeVariantAttribute</c>; otherwise, we ignore the attribute. /// </summary> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index b64b843b7c..b720fb93a3 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -328,15 +328,15 @@ namespace Godot.SourceGenerators private static void AppendGroupingPropertyInfo(StringBuilder source, PropertyInfo propertyInfo) { - source.Append(" properties.Add(new(type: (Godot.Variant.Type)") + source.Append(" properties.Add(new(type: (global::Godot.Variant.Type)") .Append((int)VariantType.Nil) .Append(", name: \"") .Append(propertyInfo.Name) - .Append("\", hint: (Godot.PropertyHint)") + .Append("\", hint: (global::Godot.PropertyHint)") .Append((int)PropertyHint.None) .Append(", hintString: \"") .Append(propertyInfo.HintString) - .Append("\", usage: (Godot.PropertyUsageFlags)") + .Append("\", usage: (global::Godot.PropertyUsageFlags)") .Append((int)propertyInfo.Usage) .Append(", exported: true));\n"); } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs index ba6c10aa31..d67cb5349d 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs @@ -272,6 +272,25 @@ namespace Godot.SourceGenerators source.Append(" }\n"); } + // Generate HasGodotClassSignal + + if (godotSignalDelegates.Count > 0) + { + source.Append( + " protected override bool HasGodotClassSignal(in godot_string_name signal)\n {\n"); + + bool isFirstEntry = true; + foreach (var signal in godotSignalDelegates) + { + GenerateHasSignalEntry(signal.Name, source, isFirstEntry); + isFirstEntry = false; + } + + source.Append(" return base.HasGodotClassSignal(signal);\n"); + + source.Append(" }\n"); + } + source.Append("}\n"); // partial class if (isInnerClass) @@ -397,6 +416,20 @@ namespace Godot.SourceGenerators PropertyHint.None, string.Empty, propUsage, exported: false); } + private static void GenerateHasSignalEntry( + string signalName, + StringBuilder source, + bool isFirstEntry + ) + { + source.Append(" "); + if (!isFirstEntry) + source.Append("else "); + source.Append("if (signal == SignalName."); + source.Append(signalName); + source.Append(") {\n return true;\n }\n"); + } + private static void GenerateSignalEventInvoker( GodotSignalDelegateData signal, StringBuilder source diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs index edbf53a389..a4d5e1a569 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs @@ -23,7 +23,7 @@ namespace GodotTools.Build public Godot.Collections.Array CustomProperties { get; private set; } = new(); public string LogsDirPath => - Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}"); + Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.Md5Text()}_{Configuration}"); public override bool Equals(object? obj) { diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs index 993c6d9217..349f9d0cb8 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs @@ -55,7 +55,7 @@ namespace GodotTools.Build private static void PrintVerbose(string text) { - if (OS.IsStdoutVerbose()) + if (OS.IsStdOutVerbose()) GD.Print(text); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs index e439822666..c083b9cc60 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs @@ -336,7 +336,7 @@ namespace GodotTools.Build _ = index; // Unused _issuesListContextMenu.Clear(); - _issuesListContextMenu.Size = new Vector2i(1, 1); + _issuesListContextMenu.Size = new Vector2I(1, 1); if (_issuesList.IsAnythingSelected()) { @@ -347,7 +347,7 @@ namespace GodotTools.Build if (_issuesListContextMenu.ItemCount > 0) { - _issuesListContextMenu.Position = (Vector2i)(_issuesList.GlobalPosition + atPosition); + _issuesListContextMenu.Position = (Vector2I)(_issuesList.GlobalPosition + atPosition); _issuesListContextMenu.Popup(); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs index d0cd529d1f..ae0ffaf4cb 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs @@ -28,7 +28,7 @@ namespace GodotTools.Build string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString(); stdOutHandler?.Invoke(launchMessage); - if (Godot.OS.IsStdoutVerbose()) + if (Godot.OS.IsStdOutVerbose()) Console.WriteLine(launchMessage); startInfo.RedirectStandardOutput = true; @@ -89,7 +89,7 @@ namespace GodotTools.Build string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString(); stdOutHandler?.Invoke(launchMessage); - if (Godot.OS.IsStdoutVerbose()) + if (Godot.OS.IsStdOutVerbose()) Console.WriteLine(launchMessage); startInfo.RedirectStandardOutput = true; diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 2e438f3f8f..262de024ca 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -122,7 +122,7 @@ namespace GodotTools.Build { base._Ready(); - CustomMinimumSize = new Vector2i(0, (int)(228 * EditorScale)); + CustomMinimumSize = new Vector2(0, 228 * EditorScale); SizeFlagsVertical = SizeFlags.ExpandFill; var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill }; @@ -175,7 +175,7 @@ namespace GodotTools.Build AddChild(BuildOutputView); } - public override void _Notification(long what) + public override void _Notification(int what) { base._Notification(what); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index db96003baf..019504ad66 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -71,7 +71,7 @@ namespace GodotTools.Export } } - public override void _ExportBegin(string[] features, bool isDebug, string path, long flags) + public override void _ExportBegin(string[] features, bool isDebug, string path, uint flags) { base._ExportBegin(features, isDebug, path, flags); diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 08147d9f6a..5abbe8752c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -192,7 +192,7 @@ namespace GodotTools try { - if (Godot.OS.IsStdoutVerbose()) + if (Godot.OS.IsStdOutVerbose()) Console.WriteLine( $"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}"); @@ -385,7 +385,7 @@ namespace GodotTools // correct version first (`RegisterDefaults` always picks the latest). if (DotNetFinder.TryFindDotNetSdk(dotNetSdkSearchVersion, out var sdkVersion, out string sdkPath)) { - if (Godot.OS.IsStdoutVerbose()) + if (Godot.OS.IsStdOutVerbose()) Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}"); ProjectUtils.MSBuildLocatorRegisterMSBuildPath(sdkPath); @@ -395,12 +395,12 @@ namespace GodotTools try { ProjectUtils.MSBuildLocatorRegisterDefaults(out sdkVersion, out sdkPath); - if (Godot.OS.IsStdoutVerbose()) + if (Godot.OS.IsStdOutVerbose()) Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}"); } catch (InvalidOperationException e) { - if (Godot.OS.IsStdoutVerbose()) + if (Godot.OS.IsStdOutVerbose()) GD.PrintErr(e.ToString()); GD.PushError($".NET Sdk not found. The required version is '{dotNetSdkSearchVersion}'."); } diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs index 89ac8058b9..eb42f01b3a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs +++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs @@ -9,9 +9,9 @@ namespace GodotTools { private Timer _watchTimer; - public override void _Notification(long what) + public override void _Notification(int what) { - if (what == Node.NotificationWmWindowFocusIn) + if (what == Node.NotificationWMWindowFocusIn) { RestartTimer(); diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs index 9df90ac608..5de2c9833b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs @@ -200,13 +200,13 @@ namespace GodotTools.Ides { public void LogDebug(string message) { - if (OS.IsStdoutVerbose()) + if (OS.IsStdOutVerbose()) Console.WriteLine(message); } public void LogInfo(string message) { - if (OS.IsStdoutVerbose()) + if (OS.IsStdOutVerbose()) Console.WriteLine(message); } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 03cbfda1bd..ad6306bb41 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -38,10 +38,10 @@ #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/os/os.h" -#include "core/string/ucaps.h" #include "main/main.h" #include "../godotsharp_defs.h" +#include "../utils/naming_utils.h" #include "../utils/path_utils.h" #include "../utils/string_utils.h" @@ -84,10 +84,12 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { #define CS_PROPERTY_SINGLETON "Singleton" #define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod" #define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod" +#define CS_METHOD_HAS_GODOT_CLASS_SIGNAL "HasGodotClassSignal" #define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor" #define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind" #define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_" +#define CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX "SignalProxyName_" #define ICALL_PREFIX "godot_icall_" #define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method" @@ -144,74 +146,6 @@ static String fix_doc_description(const String &p_bbcode) { .strip_edges(); } -static String snake_to_pascal_case(const String &p_identifier, bool p_input_is_upper = false) { - String ret; - Vector<String> parts = p_identifier.split("_", true); - - for (int i = 0; i < parts.size(); i++) { - String part = parts[i]; - - if (part.length()) { - part[0] = _find_upper(part[0]); - if (p_input_is_upper) { - for (int j = 1; j < part.length(); j++) { - part[j] = _find_lower(part[j]); - } - } - ret += part; - } else { - if (i == 0 || i == (parts.size() - 1)) { - // Preserve underscores at the beginning and end - ret += "_"; - } else { - // Preserve contiguous underscores - if (parts[i - 1].length()) { - ret += "__"; - } else { - ret += "_"; - } - } - } - } - - return ret; -} - -static String snake_to_camel_case(const String &p_identifier, bool p_input_is_upper = false) { - String ret; - Vector<String> parts = p_identifier.split("_", true); - - for (int i = 0; i < parts.size(); i++) { - String part = parts[i]; - - if (part.length()) { - if (i != 0) { - part[0] = _find_upper(part[0]); - } - if (p_input_is_upper) { - for (int j = i != 0 ? 1 : 0; j < part.length(); j++) { - part[j] = _find_lower(part[j]); - } - } - ret += part; - } else { - if (i == 0 || i == (parts.size() - 1)) { - // Preserve underscores at the beginning and end - ret += "_"; - } else { - // Preserve contiguous underscores - if (parts[i - 1].length()) { - ret += "__"; - } else { - ret += "_"; - } - } - } - } - - return ret; -} - String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) { // Based on the version in EditorHelp @@ -229,6 +163,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf List<String> tag_stack; bool code_tag = false; + bool line_del = false; int pos = 0; while (pos < bbcode.length()) { @@ -239,20 +174,22 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf } if (brk_pos > pos) { - String text = bbcode.substr(pos, brk_pos - pos); - if (code_tag || tag_stack.size() > 0) { - xml_output.append(text.xml_escape()); - } else { - Vector<String> lines = text.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (i != 0) { - xml_output.append("<para>"); - } + if (!line_del) { + String text = bbcode.substr(pos, brk_pos - pos); + if (code_tag || tag_stack.size() > 0) { + xml_output.append(text.xml_escape()); + } else { + Vector<String> lines = text.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) { + xml_output.append("<para>"); + } - xml_output.append(lines[i].xml_escape()); + xml_output.append(lines[i].xml_escape()); - if (i != lines.size() - 1) { - xml_output.append("</para>\n"); + if (i != lines.size() - 1) { + xml_output.append("</para>\n"); + } } } } @@ -265,20 +202,22 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf int brk_end = bbcode.find("]", brk_pos + 1); if (brk_end == -1) { - String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos); - if (code_tag || tag_stack.size() > 0) { - xml_output.append(text.xml_escape()); - } else { - Vector<String> lines = text.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (i != 0) { - xml_output.append("<para>"); - } + if (!line_del) { + String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos); + if (code_tag || tag_stack.size() > 0) { + xml_output.append(text.xml_escape()); + } else { + Vector<String> lines = text.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) { + xml_output.append("<para>"); + } - xml_output.append(lines[i].xml_escape()); + xml_output.append(lines[i].xml_escape()); - if (i != lines.size() - 1) { - xml_output.append("</para>\n"); + if (i != lines.size() - 1) { + xml_output.append("</para>\n"); + } } } } @@ -292,7 +231,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length()); if (!tag_ok) { - xml_output.append("["); + if (!line_del) { + xml_output.append("["); + } pos = brk_pos + 1; continue; } @@ -307,11 +248,20 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf xml_output.append("</c>"); } else if (tag == "/codeblock") { xml_output.append("</code>"); + } else if (tag == "/b") { + xml_output.append("</b>"); + } else if (tag == "/i") { + xml_output.append("</i>"); + } else if (tag == "/csharp") { + xml_output.append("</code>"); + line_del = true; + } else if (tag == "/codeblocks") { + line_del = false; } } else if (code_tag) { xml_output.append("["); pos = brk_pos + 1; - } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ")) { + } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ") || tag.begins_with("param ")) { const int tag_end = tag.find(" "); const String link_tag = tag.substr(0, tag_end); const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" "); @@ -356,6 +306,8 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf } else if (link_tag == "theme_item") { // We do not declare theme_items in any way in C#, so there is nothing to reference _append_xml_undeclared(xml_output, link_target); + } else if (link_tag == "param") { + _append_xml_undeclared(xml_output, snake_to_camel_case(link_target, false)); } pos = brk_end + 1; @@ -377,8 +329,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf #endif "\"/>"); } else if (tag == "Variant") { - // We use System.Object for Variant, so there is no Variant type in C# - xml_output.append("<c>Variant</c>"); + xml_output.append("<see cref=\"Godot.Variant\"/>"); } else if (tag == "String") { xml_output.append("<see cref=\"string\"/>"); } else if (tag == "Nil") { @@ -428,11 +379,13 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf pos = brk_end + 1; } else if (tag == "b") { - // bold is not supported in xml comments + xml_output.append("<b>"); + pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "i") { - // italics is not supported in xml comments + xml_output.append("<i>"); + pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "code") { @@ -447,6 +400,17 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf code_tag = true; pos = brk_end + 1; tag_stack.push_front(tag); + } else if (tag == "codeblocks") { + line_del = true; + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "csharp") { + xml_output.append("<code>"); + + line_del = false; + code_tag = true; + pos = brk_end + 1; + tag_stack.push_front(tag); } else if (tag == "kbd") { // keyboard combinations are not supported in xml comments pos = brk_end + 1; @@ -459,7 +423,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf xml_output.append("\n"); // FIXME: Should use <para> instead. Luckily this tag isn't used for now. pos = brk_end + 1; } else if (tag == "u") { - // underline is not supported in xml comments + // underline is not supported in Rider xml comments pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "s") { @@ -510,7 +474,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf pos = brk_end + 1; tag_stack.push_front("font"); } else { - xml_output.append("["); // ignore + if (!line_del) { + xml_output.append("["); // ignore + } pos = brk_pos + 1; } } @@ -531,7 +497,7 @@ void BindingsGenerator::_append_xml_method(StringBuilder &p_xml_output, const Ty } else if (!p_target_itype || !p_target_itype->is_object_type) { if (OS::get_singleton()->is_stdout_verbose()) { if (p_target_itype) { - OS::get_singleton()->print("Cannot resolve method reference for non-Godot.Object type in documentation: %s\n", p_link_target.utf8().get_data()); + OS::get_singleton()->print("Cannot resolve method reference for non-GodotObject type in documentation: %s\n", p_link_target.utf8().get_data()); } else { OS::get_singleton()->print("Cannot resolve type from method reference in documentation: %s\n", p_link_target.utf8().get_data()); } @@ -571,7 +537,7 @@ void BindingsGenerator::_append_xml_member(StringBuilder &p_xml_output, const Ty } else if (!p_target_itype || !p_target_itype->is_object_type) { if (OS::get_singleton()->is_stdout_verbose()) { if (p_target_itype) { - OS::get_singleton()->print("Cannot resolve member reference for non-Godot.Object type in documentation: %s\n", p_link_target.utf8().get_data()); + OS::get_singleton()->print("Cannot resolve member reference for non-GodotObject type in documentation: %s\n", p_link_target.utf8().get_data()); } else { OS::get_singleton()->print("Cannot resolve type from member reference in documentation: %s\n", p_link_target.utf8().get_data()); } @@ -607,7 +573,7 @@ void BindingsGenerator::_append_xml_signal(StringBuilder &p_xml_output, const Ty if (!p_target_itype || !p_target_itype->is_object_type) { if (OS::get_singleton()->is_stdout_verbose()) { if (p_target_itype) { - OS::get_singleton()->print("Cannot resolve signal reference for non-Godot.Object type in documentation: %s\n", p_link_target.utf8().get_data()); + OS::get_singleton()->print("Cannot resolve signal reference for non-GodotObject type in documentation: %s\n", p_link_target.utf8().get_data()); } else { OS::get_singleton()->print("Cannot resolve type from signal reference in documentation: %s\n", p_link_target.utf8().get_data()); } @@ -664,7 +630,7 @@ void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const if (OS::get_singleton()->is_stdout_verbose()) { if (p_target_itype) { - OS::get_singleton()->print("Cannot resolve constant reference for non-Godot.Object type in documentation: %s\n", p_link_target.utf8().get_data()); + OS::get_singleton()->print("Cannot resolve constant reference for non-GodotObject type in documentation: %s\n", p_link_target.utf8().get_data()); } else { OS::get_singleton()->print("Cannot resolve type from constant reference in documentation: %s\n", p_link_target.utf8().get_data()); } @@ -933,11 +899,11 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) { ARRAY_ALL(string); ARRAY_ALL(Color); ARRAY_ALL(Vector2); - ARRAY_ALL(Vector2i); + ARRAY_ALL(Vector2I); ARRAY_ALL(Vector3); - ARRAY_ALL(Vector3i); + ARRAY_ALL(Vector3I); ARRAY_ALL(Vector4); - ARRAY_ALL(Vector4i); + ARRAY_ALL(Vector4I); #undef ARRAY_ALL #undef ARRAY_IS_EMPTY @@ -1007,7 +973,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { _log("Declaring global enum '%s' inside struct '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data()); p_output.append("\npublic partial struct "); - p_output.append(enum_class_name); + p_output.append(pascal_to_pascal_case(enum_class_name)); p_output.append("\n" OPEN_BLOCK); } @@ -1016,7 +982,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { } p_output.append("\npublic enum "); - p_output.append(enum_proxy_name); + p_output.append(pascal_to_pascal_case(enum_proxy_name)); p_output.append(" : long"); p_output.append("\n" OPEN_BLOCK); @@ -1351,7 +1317,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str bool is_derived_type = itype.base_name != StringName(); if (!is_derived_type) { - // Some Godot.Object assertions + // Some GodotObject assertions CRASH_COND(itype.cname != name_cache.type_Object); CRASH_COND(!itype.is_instantiable); CRASH_COND(itype.api_type != ClassDB::API_CORE); @@ -1468,7 +1434,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output.append(MEMBER_BEGIN "public enum "); - output.append(ienum.cname.operator String()); + output.append(pascal_to_pascal_case(ienum.cname.operator String())); output.append(" : long"); output.append(MEMBER_BEGIN OPEN_BLOCK); @@ -1513,9 +1479,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str if (itype.is_singleton) { // Add the type name and the singleton pointer as static fields - output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n"); + output.append(MEMBER_BEGIN "private static GodotObject singleton;\n"); - output << MEMBER_BEGIN "public static Godot.Object " CS_PROPERTY_SINGLETON "\n" INDENT1 "{\n" + output << MEMBER_BEGIN "public static GodotObject " CS_PROPERTY_SINGLETON "\n" INDENT1 "{\n" << INDENT2 "get\n" INDENT2 "{\n" INDENT3 "if (singleton == null)\n" << INDENT4 "singleton = " C_METHOD_ENGINE_GET_SINGLETON "(typeof(" << itype.proxy_name @@ -1525,8 +1491,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.append(itype.name); output.append("\";\n"); } else { - // IMPORTANT: We also generate the static fields for Godot.Object instead of declaring - // them manually in the `Object.base.cs` partial class declaration, because they're + // IMPORTANT: We also generate the static fields for GodotObject instead of declaring + // them manually in the `GodotObject.base.cs` partial class declaration, because they're // required by other static fields in this generated partial class declaration. // Static fields are initialized in order of declaration, but when they're in different // partial class declarations then it becomes harder to tell (Rider warns about this). @@ -1608,6 +1574,16 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str << " = \"" << imethod.proxy_name << "\";\n"; } + // Generate signal names cache fields + + for (const SignalInterface &isignal : itype.signals_) { + output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n" + << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n" + << INDENT1 "private static readonly StringName " + << CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX << isignal.name + << " = \"" << isignal.proxy_name << "\";\n"; + } + // TODO: Only generate HasGodotClassMethod and InvokeGodotClassMethod if there's any method // Generate InvokeGodotClassMethod @@ -1721,6 +1697,34 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output << INDENT1 "}\n"; + + // Generate HasGodotClassSignal + + output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual") + << " bool " CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(in godot_string_name signal)\n" + << INDENT1 "{\n"; + + for (const SignalInterface &isignal : itype.signals_) { + // We check for native names (snake_case). If we detect one, we call HasGodotClassSignal + // again, but this time with the respective proxy name (PascalCase). It's the job of + // user derived classes to override the method and check for those. Our C# source + // generators take care of generating those override methods. + output << INDENT2 "if (signal == SignalName." << isignal.proxy_name + << ")\n" INDENT2 "{\n" + << INDENT3 "if (" CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(" + << CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX << isignal.name + << ".NativeValue.DangerousSelfRef))\n" INDENT3 "{\n" + << INDENT4 "return true;\n" + << INDENT3 "}\n" INDENT2 "}\n"; + } + + if (is_derived_type) { + output << INDENT2 "return base." CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(signal);\n"; + } else { + output << INDENT2 "return false;\n"; + } + + output << INDENT1 "}\n"; } //Generate StringName for all class members @@ -2058,9 +2062,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf << INDENT1 "private static readonly IntPtr " << method_bind_field << " = "; if (p_itype.is_singleton) { - // Singletons are static classes. They don't derive Godot.Object, + // Singletons are static classes. They don't derive GodotObject, // so we need to specify the type to call the static method. - p_output << "Object."; + p_output << "GodotObject."; } p_output << ICALL_CLASSDB_GET_METHOD "(" BINDINGS_NATIVE_NAME_FIELD ", MethodName." @@ -2775,6 +2779,18 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant & return false; } +bool method_has_ptr_parameter(MethodInfo p_method_info) { + if (p_method_info.return_val.type == Variant::INT && p_method_info.return_val.hint == PROPERTY_HINT_INT_IS_POINTER) { + return true; + } + for (PropertyInfo arg : p_method_info.arguments) { + if (arg.type == Variant::INT && arg.hint == PROPERTY_HINT_INT_IS_POINTER) { + return true; + } + } + return false; +} + bool BindingsGenerator::_populate_object_type_interfaces() { obj_types.clear(); @@ -2812,7 +2828,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(type_cname); - TypeInterface itype = TypeInterface::create_object_type(type_cname, api_type); + TypeInterface itype = TypeInterface::create_object_type(type_cname, pascal_to_pascal_case(type_cname), api_type); itype.base_name = ClassDB::get_parent_class(type_cname); itype.is_singleton = Engine::get_singleton()->has_singleton(itype.proxy_name); @@ -2827,9 +2843,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() { itype.cs_type = itype.proxy_name; if (itype.is_singleton) { - itype.cs_in_expr = "Object." CS_STATIC_METHOD_GETINSTANCE "(" CS_PROPERTY_SINGLETON ")"; + itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(" CS_PROPERTY_SINGLETON ")"; } else { - itype.cs_in_expr = "Object." CS_STATIC_METHOD_GETINSTANCE "(%0)"; + itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(%0)"; } itype.cs_out = "%5return (%2)%0(%1);"; @@ -2837,7 +2853,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { itype.c_arg_in = "(void*)%s"; itype.c_type = "IntPtr"; itype.c_type_in = itype.c_type; - itype.c_type_out = "Object"; + itype.c_type_out = "GodotObject"; // Populate properties @@ -2918,6 +2934,11 @@ bool BindingsGenerator::_populate_object_type_interfaces() { continue; } + if (method_has_ptr_parameter(method_info)) { + // Pointers are not supported. + continue; + } + MethodInterface imethod; imethod.name = method_info.name; imethod.cname = cname; @@ -2979,7 +3000,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (return_info.type == Variant::NIL) { imethod.return_type.cname = name_cache.type_void; } else { - imethod.return_type.cname = _get_type_name_from_meta(return_info.type, m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE); + imethod.return_type.cname = _get_type_name_from_meta(return_info.type, m ? m->get_argument_meta(-1) : (GodotTypeInfo::Metadata)method_info.return_val_metadata); } for (int i = 0; i < argc; i++) { @@ -3003,7 +3024,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.type == Variant::NIL) { iarg.type.cname = name_cache.type_Variant; } else { - iarg.type.cname = _get_type_name_from_meta(arginfo.type, m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE); + iarg.type.cname = _get_type_name_from_meta(arginfo.type, m ? m->get_argument_meta(i) : (GodotTypeInfo::Metadata)method_info.get_argument_meta(i)); } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); @@ -3103,7 +3124,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.type == Variant::NIL) { iarg.type.cname = name_cache.type_Variant; } else { - iarg.type.cname = _get_type_name_from_meta(arginfo.type, GodotTypeInfo::METADATA_NONE); + iarg.type.cname = _get_type_name_from_meta(arginfo.type, (GodotTypeInfo::Metadata)method_info.get_argument_meta(i)); } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); @@ -3152,7 +3173,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &E : enum_map) { StringName enum_proxy_cname = E.key; - String enum_proxy_name = enum_proxy_cname.operator String(); + String enum_proxy_name = pascal_to_pascal_case(enum_proxy_cname.operator String()); if (itype.find_property_by_proxy_name(enum_proxy_name) || itype.find_method_by_proxy_name(enum_proxy_name) || itype.find_signal_by_proxy_name(enum_proxy_name)) { // In case the enum name conflicts with other PascalCase members, // we append 'Enum' to the enum name in those cases. @@ -3280,7 +3301,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar } break; case Variant::AABB: { AABB aabb = p_val.operator ::AABB(); - r_iarg.default_argument = "new AABB(new Vector3" + aabb.position.operator String() + ", new Vector3" + aabb.size.operator String() + ")"; + r_iarg.default_argument = "new Aabb(new Vector3" + aabb.position.operator String() + ", new Vector3" + aabb.size.operator String() + ")"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; } break; case Variant::RECT2: { @@ -3290,7 +3311,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar } break; case Variant::RECT2I: { Rect2i rect = p_val.operator Rect2i(); - r_iarg.default_argument = "new Rect2i(new Vector2i" + rect.position.operator String() + ", new Vector2i" + rect.size.operator String() + ")"; + r_iarg.default_argument = "new Rect2I(new Vector2I" + rect.position.operator String() + ", new Vector2I" + rect.size.operator String() + ")"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; } break; case Variant::COLOR: @@ -3424,32 +3445,30 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { TypeInterface itype; -#define INSERT_STRUCT_TYPE(m_type) \ - { \ - itype = TypeInterface::create_value_type(String(#m_type)); \ - itype.c_type_in = #m_type "*"; \ - itype.c_type_out = itype.cs_type; \ - itype.cs_in_expr = "&%0"; \ - itype.cs_in_expr_is_unsafe = true; \ - builtin_types.insert(itype.cname, itype); \ - } - - INSERT_STRUCT_TYPE(Vector2) - INSERT_STRUCT_TYPE(Vector2i) - INSERT_STRUCT_TYPE(Rect2) - INSERT_STRUCT_TYPE(Rect2i) - INSERT_STRUCT_TYPE(Transform2D) - INSERT_STRUCT_TYPE(Vector3) - INSERT_STRUCT_TYPE(Vector3i) - INSERT_STRUCT_TYPE(Basis) - INSERT_STRUCT_TYPE(Quaternion) - INSERT_STRUCT_TYPE(Transform3D) - INSERT_STRUCT_TYPE(AABB) - INSERT_STRUCT_TYPE(Color) - INSERT_STRUCT_TYPE(Plane) - INSERT_STRUCT_TYPE(Vector4) - INSERT_STRUCT_TYPE(Vector4i) - INSERT_STRUCT_TYPE(Projection) +#define INSERT_STRUCT_TYPE(m_type, m_proxy_name) \ + { \ + itype = TypeInterface::create_value_type(String(#m_type), String(#m_proxy_name)); \ + itype.cs_in_expr = "&%0"; \ + itype.cs_in_expr_is_unsafe = true; \ + builtin_types.insert(itype.cname, itype); \ + } + + INSERT_STRUCT_TYPE(Vector2, Vector2) + INSERT_STRUCT_TYPE(Vector2i, Vector2I) + INSERT_STRUCT_TYPE(Rect2, Rect2) + INSERT_STRUCT_TYPE(Rect2i, Rect2I) + INSERT_STRUCT_TYPE(Transform2D, Transform2D) + INSERT_STRUCT_TYPE(Vector3, Vector3) + INSERT_STRUCT_TYPE(Vector3i, Vector3I) + INSERT_STRUCT_TYPE(Basis, Basis) + INSERT_STRUCT_TYPE(Quaternion, Quaternion) + INSERT_STRUCT_TYPE(Transform3D, Transform3D) + INSERT_STRUCT_TYPE(AABB, Aabb) + INSERT_STRUCT_TYPE(Color, Color) + INSERT_STRUCT_TYPE(Plane, Plane) + INSERT_STRUCT_TYPE(Vector4, Vector4) + INSERT_STRUCT_TYPE(Vector4i, Vector4I) + INSERT_STRUCT_TYPE(Projection, Projection) #undef INSERT_STRUCT_TYPE @@ -3588,7 +3607,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype = TypeInterface(); itype.name = "RID"; itype.cname = itype.name; - itype.proxy_name = "RID"; + itype.proxy_name = "Rid"; itype.cs_type = itype.proxy_name; itype.c_arg_in = "&%s"; itype.c_type = itype.cs_type; @@ -3802,7 +3821,7 @@ void BindingsGenerator::_populate_global_constants() { enum_itype.is_enum = true; enum_itype.name = ienum.cname.operator String(); enum_itype.cname = ienum.cname; - enum_itype.proxy_name = enum_itype.name; + enum_itype.proxy_name = pascal_to_pascal_case(enum_itype.name); TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); @@ -3824,9 +3843,9 @@ void BindingsGenerator::_populate_global_constants() { // HARDCODED List<StringName> hardcoded_enums; hardcoded_enums.push_back("Vector2.Axis"); - hardcoded_enums.push_back("Vector2i.Axis"); + hardcoded_enums.push_back("Vector2I.Axis"); hardcoded_enums.push_back("Vector3.Axis"); - hardcoded_enums.push_back("Vector3i.Axis"); + hardcoded_enums.push_back("Vector3I.Axis"); for (const StringName &enum_cname : hardcoded_enums) { // These enums are not generated and must be written manually (e.g.: Vector3.Axis) // Here, we assume core types do not begin with underscore @@ -3834,7 +3853,7 @@ void BindingsGenerator::_populate_global_constants() { enum_itype.is_enum = true; enum_itype.name = enum_cname.operator String(); enum_itype.cname = enum_cname; - enum_itype.proxy_name = enum_itype.name; + enum_itype.proxy_name = pascal_to_pascal_case(enum_itype.name); TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); } diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index cef8246032..5c266ed31f 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -472,43 +472,88 @@ class BindingsGenerator { } private: + static DocData::ClassDoc *_get_type_doc(TypeInterface &itype) { + String doc_name = itype.name.begins_with("_") ? itype.name.substr(1) : itype.name; + return &EditorHelp::get_doc_data()->class_list[doc_name]; + } + static void _init_value_type(TypeInterface &itype) { - itype.proxy_name = itype.name; + if (itype.proxy_name.is_empty()) { + itype.proxy_name = itype.name; + } - itype.c_type = itype.name; itype.cs_type = itype.proxy_name; - itype.c_type_in = itype.proxy_name + "*"; - itype.c_type_out = itype.proxy_name; - itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name]; + itype.c_type = itype.cs_type; + itype.c_type_in = itype.cs_type + "*"; + itype.c_type_out = itype.cs_type; + + itype.class_doc = _get_type_doc(itype); + } + + static void _init_object_type(TypeInterface &itype, ClassDB::APIType p_api_type) { + if (itype.proxy_name.is_empty()) { + itype.proxy_name = itype.name; + } + + if (itype.proxy_name.begins_with("_")) { + itype.proxy_name = itype.proxy_name.substr(1); + } + + itype.api_type = p_api_type; + itype.is_object_type = true; + + itype.class_doc = _get_type_doc(itype); } public: - static TypeInterface create_value_type(const String &p_name) { + static TypeInterface create_value_type(const String &p_name, const String &p_proxy_name) { TypeInterface itype; itype.name = p_name; - itype.cname = StringName(p_name); + itype.cname = p_name; + itype.proxy_name = p_proxy_name; _init_value_type(itype); return itype; } - static TypeInterface create_value_type(const StringName &p_name) { + static TypeInterface create_value_type(const StringName &p_cname, const String &p_proxy_name) { TypeInterface itype; - itype.name = p_name.operator String(); + itype.name = p_cname; + itype.cname = p_cname; + itype.proxy_name = p_proxy_name; + _init_value_type(itype); + return itype; + } + + static TypeInterface create_value_type(const String &p_name) { + TypeInterface itype; + itype.name = p_name; itype.cname = p_name; _init_value_type(itype); return itype; } - static TypeInterface create_object_type(const StringName &p_cname, ClassDB::APIType p_api_type) { + static TypeInterface create_value_type(const StringName &p_cname) { TypeInterface itype; + itype.name = p_cname; + itype.cname = p_cname; + _init_value_type(itype); + return itype; + } + static TypeInterface create_object_type(const StringName &p_cname, const String &p_proxy_name, ClassDB::APIType p_api_type) { + TypeInterface itype; itype.name = p_cname; itype.cname = p_cname; - itype.proxy_name = itype.name.begins_with("_") ? itype.name.substr(1, itype.name.length()) : itype.name; - itype.api_type = p_api_type; - itype.is_object_type = true; - itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name]; + itype.proxy_name = p_proxy_name; + _init_object_type(itype, p_api_type); + return itype; + } + static TypeInterface create_object_type(const StringName &p_cname, ClassDB::APIType p_api_type) { + TypeInterface itype; + itype.name = p_cname; + itype.cname = p_cname; + _init_object_type(itype, p_api_type); return itype; } diff --git a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs index fbad482cf6..87468fb433 100644 --- a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs +++ b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs @@ -17,22 +17,22 @@ public partial class _CLASS_ : _BASE_ // Add the gravity. if (!IsOnFloor()) - velocity.y += gravity * (float)delta; + velocity.Y += gravity * (float)delta; // Handle Jump. if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) - velocity.y = JumpVelocity; + velocity.Y = JumpVelocity; // Get the input direction and handle the movement/deceleration. // As good practice, you should replace UI actions with custom gameplay actions. Vector2 direction = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); if (direction != Vector2.Zero) { - velocity.x = direction.x * Speed; + velocity.X = direction.X * Speed; } else { - velocity.x = Mathf.MoveToward(Velocity.x, 0, Speed); + velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed); } Velocity = velocity; diff --git a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs index abed246a1e..ddeb9d7e00 100644 --- a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs +++ b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs @@ -17,25 +17,25 @@ public partial class _CLASS_ : _BASE_ // Add the gravity. if (!IsOnFloor()) - velocity.y -= gravity * (float)delta; + velocity.Y -= gravity * (float)delta; // Handle Jump. if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) - velocity.y = JumpVelocity; + velocity.Y = JumpVelocity; // Get the input direction and handle the movement/deceleration. // As good practice, you should replace UI actions with custom gameplay actions. Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); - Vector3 direction = (Transform.basis * new Vector3(inputDir.x, 0, inputDir.y)).Normalized(); + Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized(); if (direction != Vector3.Zero) { - velocity.x = direction.x * Speed; - velocity.z = direction.z * Speed; + velocity.X = direction.X * Speed; + velocity.Z = direction.Z * Speed; } else { - velocity.x = Mathf.MoveToward(Velocity.x, 0, Speed); - velocity.z = Mathf.MoveToward(Velocity.z, 0, Speed); + velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed); + velocity.Z = Mathf.MoveToward(Velocity.Z, 0, Speed); } Velocity = velocity; diff --git a/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs b/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs index 9e1b7ef580..fa2ff30a09 100644 --- a/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs +++ b/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs @@ -7,7 +7,7 @@ using System; [Tool] public partial class _CLASS_ : _BASE_ { - public override Godot.Object _PostImport(Node scene) + public override GodotObject _PostImport(Node scene) { // Modify the contents of the scene upon import. return scene; // Return the modified root node when you're done. diff --git a/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs b/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs index bf2c9434e4..4365ba5a04 100644 --- a/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs +++ b/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs @@ -7,7 +7,7 @@ using System; [Tool] public partial class _CLASS_ : _BASE_ { - public override Godot.Object _PostImport(Node scene) + public override GodotObject _PostImport(Node scene) { return scene; } diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj index e720d3878c..e58d730ee3 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj +++ b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj @@ -8,6 +8,7 @@ <!-- To generate the .runtimeconfig.json file--> <EnableDynamicLoading>true</EnableDynamicLoading> + <RollForward>LatestMajor</RollForward> </PropertyGroup> <ItemGroup> diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs index 4ce02d221e..2a72b7c53e 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs +++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs @@ -65,7 +65,7 @@ namespace GodotPlugins } private static readonly List<AssemblyName> SharedAssemblies = new(); - private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly; + private static readonly Assembly CoreApiAssembly = typeof(global::Godot.GodotObject).Assembly; private static Assembly? _editorApiAssembly; private static PluginLoadContextWrapper? _projectLoadContext; private static bool _editorHint = false; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs index 0e46b63b59..af83cc24bf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs @@ -9,7 +9,7 @@ namespace Godot /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential)] - public struct AABB : IEquatable<AABB> + public struct Aabb : IEquatable<Aabb> { private Vector3 _position; private Vector3 _size; @@ -50,19 +50,28 @@ namespace Godot } /// <summary> - /// Returns an <see cref="AABB"/> with equivalent position and size, modified so that + /// The volume of this <see cref="Aabb"/>. + /// See also <see cref="HasVolume"/>. + /// </summary> + public readonly real_t Volume + { + get { return _size.X * _size.Y * _size.Z; } + } + + /// <summary> + /// Returns an <see cref="Aabb"/> with equivalent position and size, modified so that /// the most-negative corner is the origin and the size is positive. /// </summary> - /// <returns>The modified <see cref="AABB"/>.</returns> - public readonly AABB Abs() + /// <returns>The modified <see cref="Aabb"/>.</returns> + public readonly Aabb Abs() { Vector3 end = End; - Vector3 topLeft = new Vector3(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y), Mathf.Min(_position.z, end.z)); - return new AABB(topLeft, _size.Abs()); + Vector3 topLeft = new Vector3(Mathf.Min(_position.X, end.X), Mathf.Min(_position.Y, end.Y), Mathf.Min(_position.Z, end.Z)); + return new Aabb(topLeft, _size.Abs()); } /// <summary> - /// Returns the center of the <see cref="AABB"/>, which is equal + /// Returns the center of the <see cref="Aabb"/>, which is equal /// to <see cref="Position"/> + (<see cref="Size"/> / 2). /// </summary> /// <returns>The center.</returns> @@ -72,94 +81,94 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if this <see cref="AABB"/> completely encloses another one. + /// Returns <see langword="true"/> if this <see cref="Aabb"/> completely encloses another one. /// </summary> - /// <param name="with">The other <see cref="AABB"/> that may be enclosed.</param> + /// <param name="with">The other <see cref="Aabb"/> that may be enclosed.</param> /// <returns> - /// A <see langword="bool"/> for whether or not this <see cref="AABB"/> encloses <paramref name="with"/>. + /// A <see langword="bool"/> for whether or not this <see cref="Aabb"/> encloses <paramref name="with"/>. /// </returns> - public readonly bool Encloses(AABB with) + public readonly bool Encloses(Aabb with) { Vector3 srcMin = _position; Vector3 srcMax = _position + _size; Vector3 dstMin = with._position; Vector3 dstMax = with._position + with._size; - return srcMin.x <= dstMin.x && - srcMax.x > dstMax.x && - srcMin.y <= dstMin.y && - srcMax.y > dstMax.y && - srcMin.z <= dstMin.z && - srcMax.z > dstMax.z; + return srcMin.X <= dstMin.X && + srcMax.X > dstMax.X && + srcMin.Y <= dstMin.Y && + srcMax.Y > dstMax.Y && + srcMin.Z <= dstMin.Z && + srcMax.Z > dstMax.Z; } /// <summary> - /// Returns this <see cref="AABB"/> expanded to include a given point. + /// Returns this <see cref="Aabb"/> expanded to include a given point. /// </summary> /// <param name="point">The point to include.</param> - /// <returns>The expanded <see cref="AABB"/>.</returns> - public readonly AABB Expand(Vector3 point) + /// <returns>The expanded <see cref="Aabb"/>.</returns> + public readonly Aabb Expand(Vector3 point) { Vector3 begin = _position; Vector3 end = _position + _size; - if (point.x < begin.x) + if (point.X < begin.X) { - begin.x = point.x; + begin.X = point.X; } - if (point.y < begin.y) + if (point.Y < begin.Y) { - begin.y = point.y; + begin.Y = point.Y; } - if (point.z < begin.z) + if (point.Z < begin.Z) { - begin.z = point.z; + begin.Z = point.Z; } - if (point.x > end.x) + if (point.X > end.X) { - end.x = point.x; + end.X = point.X; } - if (point.y > end.y) + if (point.Y > end.Y) { - end.y = point.y; + end.Y = point.Y; } - if (point.z > end.z) + if (point.Z > end.Z) { - end.z = point.z; + end.Z = point.Z; } - return new AABB(begin, end - begin); + return new Aabb(begin, end - begin); } /// <summary> - /// Gets the position of one of the 8 endpoints of the <see cref="AABB"/>. + /// Gets the position of one of the 8 endpoints of the <see cref="Aabb"/>. /// </summary> /// <param name="idx">Which endpoint to get.</param> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="idx"/> is less than 0 or greater than 7. /// </exception> - /// <returns>An endpoint of the <see cref="AABB"/>.</returns> + /// <returns>An endpoint of the <see cref="Aabb"/>.</returns> public readonly Vector3 GetEndpoint(int idx) { switch (idx) { case 0: - return new Vector3(_position.x, _position.y, _position.z); + return new Vector3(_position.X, _position.Y, _position.Z); case 1: - return new Vector3(_position.x, _position.y, _position.z + _size.z); + return new Vector3(_position.X, _position.Y, _position.Z + _size.Z); case 2: - return new Vector3(_position.x, _position.y + _size.y, _position.z); + return new Vector3(_position.X, _position.Y + _size.Y, _position.Z); case 3: - return new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z); + return new Vector3(_position.X, _position.Y + _size.Y, _position.Z + _size.Z); case 4: - return new Vector3(_position.x + _size.x, _position.y, _position.z); + return new Vector3(_position.X + _size.X, _position.Y, _position.Z); case 5: - return new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z); + return new Vector3(_position.X + _size.X, _position.Y, _position.Z + _size.Z); case 6: - return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z); + return new Vector3(_position.X + _size.X, _position.Y + _size.Y, _position.Z); case 7: - return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z); + return new Vector3(_position.X + _size.X, _position.Y + _size.Y, _position.Z + _size.Z); default: { throw new ArgumentOutOfRangeException(nameof(idx), @@ -169,21 +178,21 @@ namespace Godot } /// <summary> - /// Returns the normalized longest axis of the <see cref="AABB"/>. + /// Returns the normalized longest axis of the <see cref="Aabb"/>. /// </summary> - /// <returns>A vector representing the normalized longest axis of the <see cref="AABB"/>.</returns> + /// <returns>A vector representing the normalized longest axis of the <see cref="Aabb"/>.</returns> public readonly Vector3 GetLongestAxis() { var axis = new Vector3(1f, 0f, 0f); - real_t maxSize = _size.x; + real_t maxSize = _size.X; - if (_size.y > maxSize) + if (_size.Y > maxSize) { axis = new Vector3(0f, 1f, 0f); - maxSize = _size.y; + maxSize = _size.Y; } - if (_size.z > maxSize) + if (_size.Z > maxSize) { axis = new Vector3(0f, 0f, 1f); } @@ -192,21 +201,21 @@ namespace Godot } /// <summary> - /// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the <see cref="AABB"/>. + /// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the <see cref="Aabb"/>. /// </summary> /// <returns>A <see cref="Vector3.Axis"/> index for which axis is longest.</returns> public readonly Vector3.Axis GetLongestAxisIndex() { var axis = Vector3.Axis.X; - real_t maxSize = _size.x; + real_t maxSize = _size.X; - if (_size.y > maxSize) + if (_size.Y > maxSize) { axis = Vector3.Axis.Y; - maxSize = _size.y; + maxSize = _size.Y; } - if (_size.z > maxSize) + if (_size.Z > maxSize) { axis = Vector3.Axis.Z; } @@ -215,38 +224,38 @@ namespace Godot } /// <summary> - /// Returns the scalar length of the longest axis of the <see cref="AABB"/>. + /// Returns the scalar length of the longest axis of the <see cref="Aabb"/>. /// </summary> - /// <returns>The scalar length of the longest axis of the <see cref="AABB"/>.</returns> + /// <returns>The scalar length of the longest axis of the <see cref="Aabb"/>.</returns> public readonly real_t GetLongestAxisSize() { - real_t maxSize = _size.x; + real_t maxSize = _size.X; - if (_size.y > maxSize) - maxSize = _size.y; + if (_size.Y > maxSize) + maxSize = _size.Y; - if (_size.z > maxSize) - maxSize = _size.z; + if (_size.Z > maxSize) + maxSize = _size.Z; return maxSize; } /// <summary> - /// Returns the normalized shortest axis of the <see cref="AABB"/>. + /// Returns the normalized shortest axis of the <see cref="Aabb"/>. /// </summary> - /// <returns>A vector representing the normalized shortest axis of the <see cref="AABB"/>.</returns> + /// <returns>A vector representing the normalized shortest axis of the <see cref="Aabb"/>.</returns> public readonly Vector3 GetShortestAxis() { var axis = new Vector3(1f, 0f, 0f); - real_t maxSize = _size.x; + real_t maxSize = _size.X; - if (_size.y < maxSize) + if (_size.Y < maxSize) { axis = new Vector3(0f, 1f, 0f); - maxSize = _size.y; + maxSize = _size.Y; } - if (_size.z < maxSize) + if (_size.Z < maxSize) { axis = new Vector3(0f, 0f, 1f); } @@ -255,21 +264,21 @@ namespace Godot } /// <summary> - /// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the <see cref="AABB"/>. + /// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the <see cref="Aabb"/>. /// </summary> /// <returns>A <see cref="Vector3.Axis"/> index for which axis is shortest.</returns> public readonly Vector3.Axis GetShortestAxisIndex() { var axis = Vector3.Axis.X; - real_t maxSize = _size.x; + real_t maxSize = _size.X; - if (_size.y < maxSize) + if (_size.Y < maxSize) { axis = Vector3.Axis.Y; - maxSize = _size.y; + maxSize = _size.Y; } - if (_size.z < maxSize) + if (_size.Z < maxSize) { axis = Vector3.Axis.Z; } @@ -278,18 +287,18 @@ namespace Godot } /// <summary> - /// Returns the scalar length of the shortest axis of the <see cref="AABB"/>. + /// Returns the scalar length of the shortest axis of the <see cref="Aabb"/>. /// </summary> - /// <returns>The scalar length of the shortest axis of the <see cref="AABB"/>.</returns> + /// <returns>The scalar length of the shortest axis of the <see cref="Aabb"/>.</returns> public readonly real_t GetShortestAxisSize() { - real_t maxSize = _size.x; + real_t maxSize = _size.X; - if (_size.y < maxSize) - maxSize = _size.y; + if (_size.Y < maxSize) + maxSize = _size.Y; - if (_size.z < maxSize) - maxSize = _size.z; + if (_size.Z < maxSize) + maxSize = _size.Z; return maxSize; } @@ -306,99 +315,90 @@ namespace Godot Vector3 ofs = _position + halfExtents; return ofs + new Vector3( - dir.x > 0f ? -halfExtents.x : halfExtents.x, - dir.y > 0f ? -halfExtents.y : halfExtents.y, - dir.z > 0f ? -halfExtents.z : halfExtents.z); - } - - /// <summary> - /// Returns the volume of the <see cref="AABB"/>. - /// </summary> - /// <returns>The volume.</returns> - public readonly real_t GetVolume() - { - return _size.x * _size.y * _size.z; + dir.X > 0f ? -halfExtents.X : halfExtents.X, + dir.Y > 0f ? -halfExtents.Y : halfExtents.Y, + dir.Z > 0f ? -halfExtents.Z : halfExtents.Z); } /// <summary> - /// Returns a copy of the <see cref="AABB"/> grown a given amount of units towards all the sides. + /// Returns a copy of the <see cref="Aabb"/> grown a given amount of units towards all the sides. /// </summary> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown <see cref="AABB"/>.</returns> - public readonly AABB Grow(real_t by) + /// <returns>The grown <see cref="Aabb"/>.</returns> + public readonly Aabb Grow(real_t by) { - AABB res = this; + Aabb res = this; - res._position.x -= by; - res._position.y -= by; - res._position.z -= by; - res._size.x += 2.0f * by; - res._size.y += 2.0f * by; - res._size.z += 2.0f * by; + res._position.X -= by; + res._position.Y -= by; + res._position.Z -= by; + res._size.X += 2.0f * by; + res._size.Y += 2.0f * by; + res._size.Z += 2.0f * by; return res; } /// <summary> - /// Returns <see langword="true"/> if the <see cref="AABB"/> contains a point, + /// Returns <see langword="true"/> if the <see cref="Aabb"/> contains a point, /// or <see langword="false"/> otherwise. /// </summary> /// <param name="point">The point to check.</param> /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> contains <paramref name="point"/>. + /// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> contains <paramref name="point"/>. /// </returns> public readonly bool HasPoint(Vector3 point) { - if (point.x < _position.x) + if (point.X < _position.X) return false; - if (point.y < _position.y) + if (point.Y < _position.Y) return false; - if (point.z < _position.z) + if (point.Z < _position.Z) return false; - if (point.x > _position.x + _size.x) + if (point.X > _position.X + _size.X) return false; - if (point.y > _position.y + _size.y) + if (point.Y > _position.Y + _size.Y) return false; - if (point.z > _position.z + _size.z) + if (point.Z > _position.Z + _size.Z) return false; return true; } /// <summary> - /// Returns <see langword="true"/> if the <see cref="AABB"/> + /// Returns <see langword="true"/> if the <see cref="Aabb"/> /// has a surface or a length, and <see langword="false"/> - /// if the <see cref="AABB"/> is empty (all components + /// if the <see cref="Aabb"/> is empty (all components /// of <see cref="Size"/> are zero or negative). /// </summary> /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has surface. + /// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> has surface. /// </returns> public readonly bool HasSurface() { - return _size.x > 0.0f || _size.y > 0.0f || _size.z > 0.0f; + return _size.X > 0.0f || _size.Y > 0.0f || _size.Z > 0.0f; } /// <summary> - /// Returns <see langword="true"/> if the <see cref="AABB"/> has - /// area, and <see langword="false"/> if the <see cref="AABB"/> + /// Returns <see langword="true"/> if the <see cref="Aabb"/> has + /// area, and <see langword="false"/> if the <see cref="Aabb"/> /// is linear, empty, or has a negative <see cref="Size"/>. - /// See also <see cref="GetVolume"/>. + /// See also <see cref="Volume"/>. /// </summary> /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has volume. + /// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> has volume. /// </returns> public readonly bool HasVolume() { - return _size.x > 0.0f && _size.y > 0.0f && _size.z > 0.0f; + return _size.X > 0.0f && _size.Y > 0.0f && _size.Z > 0.0f; } /// <summary> - /// Returns the intersection of this <see cref="AABB"/> and <paramref name="with"/>. + /// Returns the intersection of this <see cref="Aabb"/> and <paramref name="with"/>. /// </summary> - /// <param name="with">The other <see cref="AABB"/>.</param> - /// <returns>The clipped <see cref="AABB"/>.</returns> - public readonly AABB Intersection(AABB with) + /// <param name="with">The other <see cref="Aabb"/>.</param> + /// <returns>The clipped <see cref="Aabb"/>.</returns> + public readonly Aabb Intersection(Aabb with) { Vector3 srcMin = _position; Vector3 srcMax = _position + _size; @@ -407,78 +407,78 @@ namespace Godot Vector3 min, max; - if (srcMin.x > dstMax.x || srcMax.x < dstMin.x) + if (srcMin.X > dstMax.X || srcMax.X < dstMin.X) { - return new AABB(); + return new Aabb(); } - min.x = srcMin.x > dstMin.x ? srcMin.x : dstMin.x; - max.x = srcMax.x < dstMax.x ? srcMax.x : dstMax.x; + min.X = srcMin.X > dstMin.X ? srcMin.X : dstMin.X; + max.X = srcMax.X < dstMax.X ? srcMax.X : dstMax.X; - if (srcMin.y > dstMax.y || srcMax.y < dstMin.y) + if (srcMin.Y > dstMax.Y || srcMax.Y < dstMin.Y) { - return new AABB(); + return new Aabb(); } - min.y = srcMin.y > dstMin.y ? srcMin.y : dstMin.y; - max.y = srcMax.y < dstMax.y ? srcMax.y : dstMax.y; + min.Y = srcMin.Y > dstMin.Y ? srcMin.Y : dstMin.Y; + max.Y = srcMax.Y < dstMax.Y ? srcMax.Y : dstMax.Y; - if (srcMin.z > dstMax.z || srcMax.z < dstMin.z) + if (srcMin.Z > dstMax.Z || srcMax.Z < dstMin.Z) { - return new AABB(); + return new Aabb(); } - min.z = srcMin.z > dstMin.z ? srcMin.z : dstMin.z; - max.z = srcMax.z < dstMax.z ? srcMax.z : dstMax.z; + min.Z = srcMin.Z > dstMin.Z ? srcMin.Z : dstMin.Z; + max.Z = srcMax.Z < dstMax.Z ? srcMax.Z : dstMax.Z; - return new AABB(min, max - min); + return new Aabb(min, max - min); } /// <summary> - /// Returns <see langword="true"/> if the <see cref="AABB"/> overlaps with <paramref name="with"/> + /// Returns <see langword="true"/> if the <see cref="Aabb"/> overlaps with <paramref name="with"/> /// (i.e. they have at least one point in common). /// </summary> - /// <param name="with">The other <see cref="AABB"/> to check for intersections with.</param> + /// <param name="with">The other <see cref="Aabb"/> to check for intersections with.</param> /// <returns> /// A <see langword="bool"/> for whether or not they are intersecting. /// </returns> - public readonly bool Intersects(AABB with) + public readonly bool Intersects(Aabb with) { - if (_position.x >= with._position.x + with._size.x) + if (_position.X >= with._position.X + with._size.X) return false; - if (_position.x + _size.x <= with._position.x) + if (_position.X + _size.X <= with._position.X) return false; - if (_position.y >= with._position.y + with._size.y) + if (_position.Y >= with._position.Y + with._size.Y) return false; - if (_position.y + _size.y <= with._position.y) + if (_position.Y + _size.Y <= with._position.Y) return false; - if (_position.z >= with._position.z + with._size.z) + if (_position.Z >= with._position.Z + with._size.Z) return false; - if (_position.z + _size.z <= with._position.z) + if (_position.Z + _size.Z <= with._position.Z) return false; return true; } /// <summary> - /// Returns <see langword="true"/> if the <see cref="AABB"/> is on both sides of <paramref name="plane"/>. + /// Returns <see langword="true"/> if the <see cref="Aabb"/> is on both sides of <paramref name="plane"/>. /// </summary> /// <param name="plane">The <see cref="Plane"/> to check for intersection.</param> /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> intersects the <see cref="Plane"/>. + /// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> intersects the <see cref="Plane"/>. /// </returns> public readonly bool IntersectsPlane(Plane plane) { Vector3[] points = { - new Vector3(_position.x, _position.y, _position.z), - new Vector3(_position.x, _position.y, _position.z + _size.z), - new Vector3(_position.x, _position.y + _size.y, _position.z), - new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z), - new Vector3(_position.x + _size.x, _position.y, _position.z), - new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z), - new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z), - new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z) + new Vector3(_position.X, _position.Y, _position.Z), + new Vector3(_position.X, _position.Y, _position.Z + _size.Z), + new Vector3(_position.X, _position.Y + _size.Y, _position.Z), + new Vector3(_position.X, _position.Y + _size.Y, _position.Z + _size.Z), + new Vector3(_position.X + _size.X, _position.Y, _position.Z), + new Vector3(_position.X + _size.X, _position.Y, _position.Z + _size.Z), + new Vector3(_position.X + _size.X, _position.Y + _size.Y, _position.Z), + new Vector3(_position.X + _size.X, _position.Y + _size.Y, _position.Z + _size.Z) }; bool over = false; @@ -500,13 +500,13 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if the <see cref="AABB"/> intersects + /// Returns <see langword="true"/> if the <see cref="Aabb"/> intersects /// the line segment between <paramref name="from"/> and <paramref name="to"/>. /// </summary> /// <param name="from">The start of the line segment.</param> /// <param name="to">The end of the line segment.</param> /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> intersects the line segment. + /// A <see langword="bool"/> for whether or not the <see cref="Aabb"/> intersects the line segment. /// </returns> public readonly bool IntersectsSegment(Vector3 from, Vector3 to) { @@ -563,7 +563,7 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if this <see cref="AABB"/> is finite, by calling + /// Returns <see langword="true"/> if this <see cref="Aabb"/> is finite, by calling /// <see cref="Mathf.IsFinite"/> on each component. /// </summary> /// <returns>Whether this vector is finite or not.</returns> @@ -573,73 +573,73 @@ namespace Godot } /// <summary> - /// Returns a larger <see cref="AABB"/> that contains this <see cref="AABB"/> and <paramref name="with"/>. + /// Returns a larger <see cref="Aabb"/> that contains this <see cref="Aabb"/> and <paramref name="with"/>. /// </summary> - /// <param name="with">The other <see cref="AABB"/>.</param> - /// <returns>The merged <see cref="AABB"/>.</returns> - public readonly AABB Merge(AABB with) + /// <param name="with">The other <see cref="Aabb"/>.</param> + /// <returns>The merged <see cref="Aabb"/>.</returns> + public readonly Aabb Merge(Aabb with) { Vector3 beg1 = _position; Vector3 beg2 = with._position; - var end1 = new Vector3(_size.x, _size.y, _size.z) + beg1; - var end2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg2; + var end1 = new Vector3(_size.X, _size.Y, _size.Z) + beg1; + var end2 = new Vector3(with._size.X, with._size.Y, with._size.Z) + beg2; var min = new Vector3( - beg1.x < beg2.x ? beg1.x : beg2.x, - beg1.y < beg2.y ? beg1.y : beg2.y, - beg1.z < beg2.z ? beg1.z : beg2.z + beg1.X < beg2.X ? beg1.X : beg2.X, + beg1.Y < beg2.Y ? beg1.Y : beg2.Y, + beg1.Z < beg2.Z ? beg1.Z : beg2.Z ); var max = new Vector3( - end1.x > end2.x ? end1.x : end2.x, - end1.y > end2.y ? end1.y : end2.y, - end1.z > end2.z ? end1.z : end2.z + end1.X > end2.X ? end1.X : end2.X, + end1.Y > end2.Y ? end1.Y : end2.Y, + end1.Z > end2.Z ? end1.Z : end2.Z ); - return new AABB(min, max - min); + return new Aabb(min, max - min); } /// <summary> - /// Constructs an <see cref="AABB"/> from a position and size. + /// Constructs an <see cref="Aabb"/> from a position and size. /// </summary> /// <param name="position">The position.</param> /// <param name="size">The size, typically positive.</param> - public AABB(Vector3 position, Vector3 size) + public Aabb(Vector3 position, Vector3 size) { _position = position; _size = size; } /// <summary> - /// Constructs an <see cref="AABB"/> from a <paramref name="position"/>, + /// Constructs an <see cref="Aabb"/> from a <paramref name="position"/>, /// <paramref name="width"/>, <paramref name="height"/>, and <paramref name="depth"/>. /// </summary> /// <param name="position">The position.</param> /// <param name="width">The width, typically positive.</param> /// <param name="height">The height, typically positive.</param> /// <param name="depth">The depth, typically positive.</param> - public AABB(Vector3 position, real_t width, real_t height, real_t depth) + public Aabb(Vector3 position, real_t width, real_t height, real_t depth) { _position = position; _size = new Vector3(width, height, depth); } /// <summary> - /// Constructs an <see cref="AABB"/> from <paramref name="x"/>, + /// Constructs an <see cref="Aabb"/> from <paramref name="x"/>, /// <paramref name="y"/>, <paramref name="z"/>, and <paramref name="size"/>. /// </summary> /// <param name="x">The position's X coordinate.</param> /// <param name="y">The position's Y coordinate.</param> /// <param name="z">The position's Z coordinate.</param> /// <param name="size">The size, typically positive.</param> - public AABB(real_t x, real_t y, real_t z, Vector3 size) + public Aabb(real_t x, real_t y, real_t z, Vector3 size) { _position = new Vector3(x, y, z); _size = size; } /// <summary> - /// Constructs an <see cref="AABB"/> from <paramref name="x"/>, + /// Constructs an <see cref="Aabb"/> from <paramref name="x"/>, /// <paramref name="y"/>, <paramref name="z"/>, <paramref name="width"/>, /// <paramref name="height"/>, and <paramref name="depth"/>. /// </summary> @@ -649,7 +649,7 @@ namespace Godot /// <param name="width">The width, typically positive.</param> /// <param name="height">The height, typically positive.</param> /// <param name="depth">The depth, typically positive.</param> - public AABB(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth) + public Aabb(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth) { _position = new Vector3(x, y, z); _size = new Vector3(width, height, depth); @@ -663,7 +663,7 @@ namespace Godot /// <param name="left">The left AABB.</param> /// <param name="right">The right AABB.</param> /// <returns>Whether or not the AABBs are exactly equal.</returns> - public static bool operator ==(AABB left, AABB right) + public static bool operator ==(Aabb left, Aabb right) { return left.Equals(right); } @@ -676,7 +676,7 @@ namespace Godot /// <param name="left">The left AABB.</param> /// <param name="right">The right AABB.</param> /// <returns>Whether or not the AABBs are not equal.</returns> - public static bool operator !=(AABB left, AABB right) + public static bool operator !=(Aabb left, Aabb right) { return !left.Equals(right); } @@ -691,7 +691,7 @@ namespace Godot /// <returns>Whether or not the AABB and the object are equal.</returns> public override readonly bool Equals(object obj) { - return obj is AABB other && Equals(other); + return obj is Aabb other && Equals(other); } /// <summary> @@ -701,7 +701,7 @@ namespace Godot /// </summary> /// <param name="other">The other AABB.</param> /// <returns>Whether or not the AABBs are exactly equal.</returns> - public readonly bool Equals(AABB other) + public readonly bool Equals(Aabb other) { return _position == other._position && _size == other._size; } @@ -712,13 +712,13 @@ namespace Godot /// </summary> /// <param name="other">The other AABB to compare.</param> /// <returns>Whether or not the AABBs structures are approximately equal.</returns> - public readonly bool IsEqualApprox(AABB other) + public readonly bool IsEqualApprox(Aabb other) { return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size); } /// <summary> - /// Serves as the hash function for <see cref="AABB"/>. + /// Serves as the hash function for <see cref="Aabb"/>. /// </summary> /// <returns>A hash code for this AABB.</returns> public override readonly int GetHashCode() @@ -727,7 +727,7 @@ namespace Godot } /// <summary> - /// Converts this <see cref="AABB"/> to a string. + /// Converts this <see cref="Aabb"/> to a string. /// </summary> /// <returns>A string representation of this AABB.</returns> public override readonly string ToString() @@ -736,7 +736,7 @@ namespace Godot } /// <summary> - /// Converts this <see cref="AABB"/> to a string with the given <paramref name="format"/>. + /// Converts this <see cref="Aabb"/> to a string with the given <paramref name="format"/>. /// </summary> /// <returns>A string representation of this AABB.</returns> public readonly string ToString(string format) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index df306e5244..a61c5403b9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -99,7 +99,7 @@ namespace Godot.Collections this[i] = array[i]; } - public Array(Span<RID> array) : this() + public Array(Span<Rid> array) : this() { if (array == null) throw new ArgumentNullException(nameof(array)); @@ -120,7 +120,7 @@ namespace Godot.Collections // fine as long as the array is not mutated. However, Span does this type checking at // instantiation, so it's not possible to use it even when not mutating anything. // ReSharper disable once RedundantNameQualifier - public Array(ReadOnlySpan<Godot.Object> array) : this() + public Array(ReadOnlySpan<GodotObject> array) : this() { if (array == null) throw new ArgumentNullException(nameof(array)); @@ -189,10 +189,15 @@ namespace Godot.Collections /// <summary> /// Resizes this <see cref="Array"/> to the given size. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="newSize">The new size of the array.</param> /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns> public Error Resize(int newSize) { + ThrowIfReadOnly(); + var self = (godot_array)NativeValue; return NativeFuncs.godotsharp_array_resize(ref self, newSize); } @@ -200,8 +205,13 @@ namespace Godot.Collections /// <summary> /// Shuffles the contents of this <see cref="Array"/> into a random order. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> public void Shuffle() { + ThrowIfReadOnly(); + var self = (godot_array)NativeValue; NativeFuncs.godotsharp_array_shuffle(ref self); } @@ -240,6 +250,9 @@ namespace Godot.Collections /// <summary> /// Returns the item at the given <paramref name="index"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The property is assigned and the array is read-only. + /// </exception> /// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value> public unsafe Variant this[int index] { @@ -250,8 +263,11 @@ namespace Godot.Collections } set { + ThrowIfReadOnly(); + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index)); + var self = (godot_array)NativeValue; godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self); godot_variant* itemPtr = &ptrw[index]; @@ -264,9 +280,14 @@ namespace Godot.Collections /// Adds an item to the end of this <see cref="Array"/>. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="item">The <see cref="Variant"/> item to add.</param> public void Add(Variant item) { + ThrowIfReadOnly(); + godot_variant variantValue = (godot_variant)item.NativeVar; var self = (godot_array)NativeValue; _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); @@ -282,6 +303,9 @@ namespace Godot.Collections /// <summary> /// Erases all items from this <see cref="Array"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> public void Clear() => Resize(0); /// <summary> @@ -303,10 +327,15 @@ namespace Godot.Collections /// or the position at the end of the array. /// Existing items will be moved to the right. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="index">The index to insert at.</param> /// <param name="item">The <see cref="Variant"/> item to insert.</param> public void Insert(int index, Variant item) { + ThrowIfReadOnly(); + if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); @@ -319,9 +348,14 @@ namespace Godot.Collections /// Removes the first occurrence of the specified <paramref name="item"/> /// from this <see cref="Array"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="item">The value to remove.</param> public bool Remove(Variant item) { + ThrowIfReadOnly(); + int index = IndexOf(item); if (index >= 0) { @@ -335,9 +369,14 @@ namespace Godot.Collections /// <summary> /// Removes an element from this <see cref="Array"/> by index. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="index">The index of the element to remove.</param> public void RemoveAt(int index) { + ThrowIfReadOnly(); + if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); @@ -358,7 +397,28 @@ namespace Godot.Collections object ICollection.SyncRoot => false; - bool ICollection<Variant>.IsReadOnly => false; + /// <summary> + /// Returns <see langword="true"/> if the array is read-only. + /// See <see cref="MakeReadOnly"/>. + /// </summary> + public bool IsReadOnly => NativeValue.DangerousSelfRef.IsReadOnly; + + /// <summary> + /// Makes the <see cref="Array"/> read-only, i.e. disabled modying of the + /// array's elements. Does not apply to nested content, e.g. content of + /// nested arrays. + /// </summary> + public void MakeReadOnly() + { + if (IsReadOnly) + { + // Avoid interop call when the array is already read-only. + return; + } + + var self = (godot_array)NativeValue; + NativeFuncs.godotsharp_array_make_read_only(ref self); + } /// <summary> /// Copies the elements of this <see cref="Array"/> to the given @@ -472,6 +532,14 @@ namespace Godot.Collections { elem = NativeValue.DangerousSelfRef.Elements[index]; } + + private void ThrowIfReadOnly() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Array instance is read-only."); + } + } } internal interface IGenericGodotArray @@ -592,6 +660,9 @@ namespace Godot.Collections /// <summary> /// Resizes this <see cref="Array{T}"/> to the given size. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="newSize">The new size of the array.</param> /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns> public Error Resize(int newSize) @@ -602,6 +673,9 @@ namespace Godot.Collections /// <summary> /// Shuffles the contents of this <see cref="Array{T}"/> into a random order. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> public void Shuffle() { _underlyingArray.Shuffle(); @@ -634,6 +708,9 @@ namespace Godot.Collections /// <summary> /// Returns the value at the given <paramref name="index"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The property is assigned and the array is read-only. + /// </exception> /// <value>The value at the given <paramref name="index"/>.</value> public unsafe T this[int index] { @@ -644,8 +721,11 @@ namespace Godot.Collections } set { + ThrowIfReadOnly(); + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index)); + var self = (godot_array)_underlyingArray.NativeValue; godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self); godot_variant* itemPtr = &ptrw[index]; @@ -673,10 +753,15 @@ namespace Godot.Collections /// or the position at the end of the array. /// Existing items will be moved to the right. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="index">The index to insert at.</param> /// <param name="item">The item to insert.</param> public void Insert(int index, T item) { + ThrowIfReadOnly(); + if (index < 0 || index > Count) throw new ArgumentOutOfRangeException(nameof(index)); @@ -688,6 +773,9 @@ namespace Godot.Collections /// <summary> /// Removes an element from this <see cref="Array{T}"/> by index. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="index">The index of the element to remove.</param> public void RemoveAt(int index) { @@ -703,16 +791,35 @@ namespace Godot.Collections /// <returns>The number of elements.</returns> public int Count => _underlyingArray.Count; - bool ICollection<T>.IsReadOnly => false; + /// <summary> + /// Returns <see langword="true"/> if the array is read-only. + /// See <see cref="MakeReadOnly"/>. + /// </summary> + public bool IsReadOnly => _underlyingArray.IsReadOnly; + + /// <summary> + /// Makes the <see cref="Array{T}"/> read-only, i.e. disabled modying of the + /// array's elements. Does not apply to nested content, e.g. content of + /// nested arrays. + /// </summary> + public void MakeReadOnly() + { + _underlyingArray.MakeReadOnly(); + } /// <summary> /// Adds an item to the end of this <see cref="Array{T}"/>. /// This is the same as <c>append</c> or <c>push_back</c> in GDScript. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="item">The item to add.</param> /// <returns>The new size after adding the item.</returns> public void Add(T item) { + ThrowIfReadOnly(); + using var variantValue = VariantUtils.CreateFrom(item); var self = (godot_array)_underlyingArray.NativeValue; _ = NativeFuncs.godotsharp_array_add(ref self, variantValue); @@ -721,6 +828,9 @@ namespace Godot.Collections /// <summary> /// Erases all items from this <see cref="Array{T}"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> public void Clear() { _underlyingArray.Clear(); @@ -769,10 +879,15 @@ namespace Godot.Collections /// Removes the first occurrence of the specified value /// from this <see cref="Array{T}"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The array is read-only. + /// </exception> /// <param name="item">The value to remove.</param> /// <returns>A <see langword="bool"/> indicating success or failure.</returns> public bool Remove(T item) { + ThrowIfReadOnly(); + int index = IndexOf(item); if (index >= 0) { @@ -812,5 +927,13 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Array<T>(Variant from) => from.AsGodotArray<T>(); + + private void ThrowIfReadOnly() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Array instance is read-only."); + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs index acdae83d2e..81659f74de 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs @@ -10,7 +10,7 @@ namespace Godot /// collection of types that implement scripts; otherwise, retrieving the types requires lookup. /// </summary> [AttributeUsage(AttributeTargets.Assembly)] - public class AssemblyHasScriptsAttribute : Attribute + public sealed class AssemblyHasScriptsAttribute : Attribute { /// <summary> /// If the Godot scripts contained in the assembly require lookup diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotClassNameAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotClassNameAttribute.cs new file mode 100644 index 0000000000..b19427f60d --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotClassNameAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace Godot +{ + /// <summary> + /// Attribute that specifies the engine class name when it's not the same + /// as the generated C# class name. This allows introspection code to find + /// the name associated with the class. If the attribute is not present, + /// the C# class name can be used instead. + /// </summary> + [AttributeUsage(AttributeTargets.Class)] + public class GodotClassNameAttribute : Attribute + { + /// <summary> + /// Original engine class name. + /// </summary> + public string Name { get; } + + public GodotClassNameAttribute(string name) + { + Name = name; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs index 23088378d1..0070223c95 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs @@ -7,5 +7,5 @@ namespace Godot /// that can be marshaled from/to a <see cref="Variant"/>. /// </summary> [AttributeUsage(AttributeTargets.GenericParameter)] - public class MustBeVariantAttribute : Attribute { } + public sealed class MustBeVariantAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RpcAttribute.cs index afee926464..6a73d6f70c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RpcAttribute.cs @@ -5,16 +5,16 @@ namespace Godot /// <summary> /// Attribute that changes the RPC mode for the annotated <c>method</c> to the given <see cref="Mode"/>, /// optionally specifying the <see cref="TransferMode"/> and <see cref="TransferChannel"/> (on supported peers). - /// See <see cref="MultiplayerAPI.RPCMode"/> and <see cref="MultiplayerPeer.TransferModeEnum"/>. + /// See <see cref="MultiplayerApi.RpcMode"/> and <see cref="MultiplayerPeer.TransferModeEnum"/>. /// By default, methods are not exposed to networking (and RPCs). /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public class RPCAttribute : Attribute + public sealed class RpcAttribute : Attribute { /// <summary> /// RPC mode for the annotated method. /// </summary> - public MultiplayerAPI.RPCMode Mode { get; } = MultiplayerAPI.RPCMode.Disabled; + public MultiplayerApi.RpcMode Mode { get; } = MultiplayerApi.RpcMode.Disabled; /// <summary> /// If the method will also be called locally; otherwise, it is only called remotely. @@ -32,10 +32,10 @@ namespace Godot public int TransferChannel { get; init; } = 0; /// <summary> - /// Constructs a <see cref="RPCAttribute"/> instance. + /// Constructs a <see cref="RpcAttribute"/> instance. /// </summary> /// <param name="mode">The RPC mode to use.</param> - public RPCAttribute(MultiplayerAPI.RPCMode mode = MultiplayerAPI.RPCMode.Authority) + public RpcAttribute(MultiplayerApi.RpcMode mode = MultiplayerApi.RpcMode.Authority) { Mode = mode; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs index f05bcdac38..d363e14c5d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs @@ -6,7 +6,7 @@ namespace Godot /// An attribute that contains the path to the object's script. /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ScriptPathAttribute : Attribute + public sealed class ScriptPathAttribute : Attribute { /// <summary> /// File path to the script. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs index 38e68a89d5..0a08bb5df8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs @@ -3,5 +3,5 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Delegate)] - public class SignalAttribute : Attribute { } + public sealed class SignalAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs index d2344389f4..4c56201727 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs @@ -3,5 +3,5 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Class)] - public class ToolAttribute : Attribute { } + public sealed class ToolAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index b57317e1d0..ca963cbf4f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -27,7 +27,7 @@ namespace Godot /// The basis matrix's X vector (column 0). /// </summary> /// <value>Equivalent to <see cref="Column0"/> and array index <c>[0]</c>.</value> - public Vector3 x + public Vector3 X { readonly get => Column0; set => Column0 = value; @@ -37,7 +37,7 @@ namespace Godot /// The basis matrix's Y vector (column 1). /// </summary> /// <value>Equivalent to <see cref="Column1"/> and array index <c>[1]</c>.</value> - public Vector3 y + public Vector3 Y { readonly get => Column1; set => Column1 = value; @@ -47,7 +47,7 @@ namespace Godot /// The basis matrix's Z vector (column 2). /// </summary> /// <value>Equivalent to <see cref="Column2"/> and array index <c>[2]</c>.</value> - public Vector3 z + public Vector3 Z { readonly get => Column2; set => Column2 = value; @@ -77,45 +77,63 @@ namespace Godot /// <summary> /// Column 0 of the basis matrix (the X vector). /// </summary> - /// <value>Equivalent to <see cref="x"/> and array index <c>[0]</c>.</value> + /// <value>Equivalent to <see cref="X"/> and array index <c>[0]</c>.</value> public Vector3 Column0 { - readonly get => new Vector3(Row0.x, Row1.x, Row2.x); + readonly get => new Vector3(Row0.X, Row1.X, Row2.X); set { - Row0.x = value.x; - Row1.x = value.y; - Row2.x = value.z; + Row0.X = value.X; + Row1.X = value.Y; + Row2.X = value.Z; } } /// <summary> /// Column 1 of the basis matrix (the Y vector). /// </summary> - /// <value>Equivalent to <see cref="y"/> and array index <c>[1]</c>.</value> + /// <value>Equivalent to <see cref="Y"/> and array index <c>[1]</c>.</value> public Vector3 Column1 { - readonly get => new Vector3(Row0.y, Row1.y, Row2.y); + readonly get => new Vector3(Row0.Y, Row1.Y, Row2.Y); set { - Row0.y = value.x; - Row1.y = value.y; - Row2.y = value.z; + Row0.Y = value.X; + Row1.Y = value.Y; + Row2.Y = value.Z; } } /// <summary> /// Column 2 of the basis matrix (the Z vector). /// </summary> - /// <value>Equivalent to <see cref="z"/> and array index <c>[2]</c>.</value> + /// <value>Equivalent to <see cref="Z"/> and array index <c>[2]</c>.</value> public Vector3 Column2 { - readonly get => new Vector3(Row0.z, Row1.z, Row2.z); + readonly get => new Vector3(Row0.Z, Row1.Z, Row2.Z); set { - Row0.z = value.x; - Row1.z = value.y; - Row2.z = value.z; + Row0.Z = value.X; + Row1.Z = value.Y; + Row2.Z = value.Z; + } + } + + /// <summary> + /// Assuming that the matrix is the combination of a rotation and scaling, + /// return the absolute value of scaling factors along each axis. + /// </summary> + public readonly Vector3 Scale + { + get + { + real_t detSign = Mathf.Sign(Determinant()); + return detSign * new Vector3 + ( + Column0.Length(), + Column1.Length(), + Column2.Length() + ); } } @@ -195,9 +213,9 @@ namespace Godot private void SetDiagonal(Vector3 diagonal) { - Row0 = new Vector3(diagonal.x, 0, 0); - Row1 = new Vector3(0, diagonal.y, 0); - Row2 = new Vector3(0, 0, diagonal.z); + Row0 = new Vector3(diagonal.X, 0, 0); + Row1 = new Vector3(0, diagonal.Y, 0); + Row2 = new Vector3(0, 0, diagonal.Z); } /// <summary> @@ -252,29 +270,29 @@ namespace Godot if (Row1[0] == 0 && Row0[1] == 0 && Row1[2] == 0 && Row2[1] == 0 && Row1[1] == 1) { // return the simplest form (human friendlier in editor and scripts) - euler.x = 0; - euler.y = Mathf.Atan2(Row0[2], Row0[0]); - euler.z = 0; + euler.X = 0; + euler.Y = Mathf.Atan2(Row0[2], Row0[0]); + euler.Z = 0; } else { - euler.x = Mathf.Atan2(-Row1[2], Row2[2]); - euler.y = Mathf.Asin(sy); - euler.z = Mathf.Atan2(-Row0[1], Row0[0]); + euler.X = Mathf.Atan2(-Row1[2], Row2[2]); + euler.Y = Mathf.Asin(sy); + euler.Z = Mathf.Atan2(-Row0[1], Row0[0]); } } else { - euler.x = Mathf.Atan2(Row2[1], Row1[1]); - euler.y = -Mathf.Tau / 4.0f; - euler.z = 0.0f; + euler.X = Mathf.Atan2(Row2[1], Row1[1]); + euler.Y = -Mathf.Tau / 4.0f; + euler.Z = 0.0f; } } else { - euler.x = Mathf.Atan2(Row2[1], Row1[1]); - euler.y = Mathf.Tau / 4.0f; - euler.z = 0.0f; + euler.X = Mathf.Atan2(Row2[1], Row1[1]); + euler.Y = Mathf.Tau / 4.0f; + euler.Z = 0.0f; } return euler; } @@ -292,24 +310,24 @@ namespace Godot { if (sz > -(1.0f - Mathf.Epsilon)) { - euler.x = Mathf.Atan2(Row2[1], Row1[1]); - euler.y = Mathf.Atan2(Row0[2], Row0[0]); - euler.z = Mathf.Asin(-sz); + euler.X = Mathf.Atan2(Row2[1], Row1[1]); + euler.Y = Mathf.Atan2(Row0[2], Row0[0]); + euler.Z = Mathf.Asin(-sz); } else { // It's -1 - euler.x = -Mathf.Atan2(Row1[2], Row2[2]); - euler.y = 0.0f; - euler.z = Mathf.Tau / 4.0f; + euler.X = -Mathf.Atan2(Row1[2], Row2[2]); + euler.Y = 0.0f; + euler.Z = Mathf.Tau / 4.0f; } } else { // It's 1 - euler.x = -Mathf.Atan2(Row1[2], Row2[2]); - euler.y = 0.0f; - euler.z = -Mathf.Tau / 4.0f; + euler.X = -Mathf.Atan2(Row1[2], Row2[2]); + euler.Y = 0.0f; + euler.Z = -Mathf.Tau / 4.0f; } return euler; } @@ -331,29 +349,29 @@ namespace Godot if (Row1[0] == 0 && Row0[1] == 0 && Row0[2] == 0 && Row2[0] == 0 && Row0[0] == 1) { // return the simplest form (human friendlier in editor and scripts) - euler.x = Mathf.Atan2(-m12, Row1[1]); - euler.y = 0; - euler.z = 0; + euler.X = Mathf.Atan2(-m12, Row1[1]); + euler.Y = 0; + euler.Z = 0; } else { - euler.x = Mathf.Asin(-m12); - euler.y = Mathf.Atan2(Row0[2], Row2[2]); - euler.z = Mathf.Atan2(Row1[0], Row1[1]); + euler.X = Mathf.Asin(-m12); + euler.Y = Mathf.Atan2(Row0[2], Row2[2]); + euler.Z = Mathf.Atan2(Row1[0], Row1[1]); } } else { // m12 == -1 - euler.x = Mathf.Tau / 4.0f; - euler.y = Mathf.Atan2(Row0[1], Row0[0]); - euler.z = 0; + euler.X = Mathf.Tau / 4.0f; + euler.Y = Mathf.Atan2(Row0[1], Row0[0]); + euler.Z = 0; } } else { // m12 == 1 - euler.x = -Mathf.Tau / 4.0f; - euler.y = -Mathf.Atan2(Row0[1], Row0[0]); - euler.z = 0; + euler.X = -Mathf.Tau / 4.0f; + euler.Y = -Mathf.Atan2(Row0[1], Row0[0]); + euler.Z = 0; } return euler; @@ -372,24 +390,24 @@ namespace Godot { if (sz > -(1.0f - Mathf.Epsilon)) { - euler.x = Mathf.Atan2(-Row1[2], Row1[1]); - euler.y = Mathf.Atan2(-Row2[0], Row0[0]); - euler.z = Mathf.Asin(sz); + euler.X = Mathf.Atan2(-Row1[2], Row1[1]); + euler.Y = Mathf.Atan2(-Row2[0], Row0[0]); + euler.Z = Mathf.Asin(sz); } else { // It's -1 - euler.x = Mathf.Atan2(Row2[1], Row2[2]); - euler.y = 0.0f; - euler.z = -Mathf.Tau / 4.0f; + euler.X = Mathf.Atan2(Row2[1], Row2[2]); + euler.Y = 0.0f; + euler.Z = -Mathf.Tau / 4.0f; } } else { // It's 1 - euler.x = Mathf.Atan2(Row2[1], Row2[2]); - euler.y = 0.0f; - euler.z = Mathf.Tau / 4.0f; + euler.X = Mathf.Atan2(Row2[1], Row2[2]); + euler.Y = 0.0f; + euler.Z = Mathf.Tau / 4.0f; } return euler; } @@ -407,24 +425,24 @@ namespace Godot { if (sx > -(1.0f - Mathf.Epsilon)) { - euler.x = Mathf.Asin(sx); - euler.y = Mathf.Atan2(-Row2[0], Row2[2]); - euler.z = Mathf.Atan2(-Row0[1], Row1[1]); + euler.X = Mathf.Asin(sx); + euler.Y = Mathf.Atan2(-Row2[0], Row2[2]); + euler.Z = Mathf.Atan2(-Row0[1], Row1[1]); } else { // It's -1 - euler.x = -Mathf.Tau / 4.0f; - euler.y = Mathf.Atan2(Row0[2], Row0[0]); - euler.z = 0; + euler.X = -Mathf.Tau / 4.0f; + euler.Y = Mathf.Atan2(Row0[2], Row0[0]); + euler.Z = 0; } } else { // It's 1 - euler.x = Mathf.Tau / 4.0f; - euler.y = Mathf.Atan2(Row0[2], Row0[0]); - euler.z = 0; + euler.X = Mathf.Tau / 4.0f; + euler.Y = Mathf.Atan2(Row0[2], Row0[0]); + euler.Z = 0; } return euler; } @@ -442,24 +460,24 @@ namespace Godot { if (sy > -(1.0f - Mathf.Epsilon)) { - euler.x = Mathf.Atan2(Row2[1], Row2[2]); - euler.y = Mathf.Asin(-sy); - euler.z = Mathf.Atan2(Row1[0], Row0[0]); + euler.X = Mathf.Atan2(Row2[1], Row2[2]); + euler.Y = Mathf.Asin(-sy); + euler.Z = Mathf.Atan2(Row1[0], Row0[0]); } else { // It's -1 - euler.x = 0; - euler.y = Mathf.Tau / 4.0f; - euler.z = -Mathf.Atan2(Row0[1], Row1[1]); + euler.X = 0; + euler.Y = Mathf.Tau / 4.0f; + euler.Z = -Mathf.Atan2(Row0[1], Row1[1]); } } else { // It's 1 - euler.x = 0; - euler.y = -Mathf.Tau / 4.0f; - euler.z = -Mathf.Atan2(Row0[1], Row1[1]); + euler.X = 0; + euler.Y = -Mathf.Tau / 4.0f; + euler.Z = -Mathf.Atan2(Row0[1], Row1[1]); } return euler; } @@ -542,21 +560,6 @@ namespace Godot } /// <summary> - /// Assuming that the matrix is the combination of a rotation and scaling, - /// return the absolute value of scaling factors along each axis. - /// </summary> - public readonly Vector3 GetScale() - { - real_t detSign = Mathf.Sign(Determinant()); - return detSign * new Vector3 - ( - Column0.Length(), - Column1.Length(), - Column2.Length() - ); - } - - /// <summary> /// Returns the inverse of the matrix. /// </summary> /// <returns>The inverse matrix.</returns> @@ -650,9 +653,9 @@ namespace Godot public readonly Basis Scaled(Vector3 scale) { Basis b = this; - b.Row0 *= scale.x; - b.Row1 *= scale.y; - b.Row2 *= scale.z; + b.Row0 *= scale.X; + b.Row1 *= scale.Y; + b.Row2 *= scale.Z; return b; } @@ -789,18 +792,18 @@ namespace Godot { real_t s = 2.0f / quaternion.LengthSquared(); - real_t xs = quaternion.x * s; - real_t ys = quaternion.y * s; - real_t zs = quaternion.z * s; - real_t wx = quaternion.w * xs; - real_t wy = quaternion.w * ys; - real_t wz = quaternion.w * zs; - real_t xx = quaternion.x * xs; - real_t xy = quaternion.x * ys; - real_t xz = quaternion.x * zs; - real_t yy = quaternion.y * ys; - real_t yz = quaternion.y * zs; - real_t zz = quaternion.z * zs; + real_t xs = quaternion.X * s; + real_t ys = quaternion.Y * s; + real_t zs = quaternion.Z * s; + real_t wx = quaternion.W * xs; + real_t wy = quaternion.W * ys; + real_t wz = quaternion.W * zs; + real_t xx = quaternion.X * xs; + real_t xy = quaternion.X * ys; + real_t xz = quaternion.X * zs; + real_t yy = quaternion.Y * ys; + real_t yz = quaternion.Y * zs; + real_t zz = quaternion.Z * zs; Row0 = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); Row1 = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); @@ -815,29 +818,29 @@ namespace Godot /// <param name="angle">The angle to rotate, in radians.</param> public Basis(Vector3 axis, real_t angle) { - Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); + Vector3 axisSq = new Vector3(axis.X * axis.X, axis.Y * axis.Y, axis.Z * axis.Z); (real_t sin, real_t cos) = Mathf.SinCos(angle); - Row0.x = axisSq.x + cos * (1.0f - axisSq.x); - Row1.y = axisSq.y + cos * (1.0f - axisSq.y); - Row2.z = axisSq.z + cos * (1.0f - axisSq.z); + Row0.X = axisSq.X + cos * (1.0f - axisSq.X); + Row1.Y = axisSq.Y + cos * (1.0f - axisSq.Y); + Row2.Z = axisSq.Z + cos * (1.0f - axisSq.Z); real_t t = 1.0f - cos; - real_t xyzt = axis.x * axis.y * t; - real_t zyxs = axis.z * sin; - Row0.y = xyzt - zyxs; - Row1.x = xyzt + zyxs; + real_t xyzt = axis.X * axis.Y * t; + real_t zyxs = axis.Z * sin; + Row0.Y = xyzt - zyxs; + Row1.X = xyzt + zyxs; - xyzt = axis.x * axis.z * t; - zyxs = axis.y * sin; - Row0.z = xyzt + zyxs; - Row2.x = xyzt - zyxs; + xyzt = axis.X * axis.Z * t; + zyxs = axis.Y * sin; + Row0.Z = xyzt + zyxs; + Row2.X = xyzt - zyxs; - xyzt = axis.y * axis.z * t; - zyxs = axis.x * sin; - Row1.z = xyzt - zyxs; - Row2.y = xyzt + zyxs; + xyzt = axis.Y * axis.Z * t; + zyxs = axis.X * sin; + Row1.Z = xyzt - zyxs; + Row2.Y = xyzt + zyxs; } /// <summary> @@ -848,9 +851,9 @@ namespace Godot /// <param name="column2">The Z vector, or Column2.</param> public Basis(Vector3 column0, Vector3 column1, Vector3 column2) { - Row0 = new Vector3(column0.x, column1.x, column2.x); - Row1 = new Vector3(column0.y, column1.y, column2.y); - Row2 = new Vector3(column0.z, column1.z, column2.z); + Row0 = new Vector3(column0.X, column1.X, column2.X); + Row1 = new Vector3(column0.Y, column1.Y, column2.Y); + Row2 = new Vector3(column0.Z, column1.Z, column2.Z); // Same as: // Column0 = column0; // Column1 = column1; @@ -860,17 +863,17 @@ namespace Godot /// <summary> /// Constructs a transformation matrix from the given components. - /// Arguments are named such that xy is equal to calling <c>x.y</c>. + /// Arguments are named such that xy is equal to calling <c>X.Y</c>. /// </summary> - /// <param name="xx">The X component of the X column vector, accessed via <c>b.x.x</c> or <c>[0][0]</c>.</param> - /// <param name="yx">The X component of the Y column vector, accessed via <c>b.y.x</c> or <c>[1][0]</c>.</param> - /// <param name="zx">The X component of the Z column vector, accessed via <c>b.z.x</c> or <c>[2][0]</c>.</param> - /// <param name="xy">The Y component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][1]</c>.</param> - /// <param name="yy">The Y component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][1]</c>.</param> - /// <param name="zy">The Y component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][1]</c>.</param> - /// <param name="xz">The Z component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][2]</c>.</param> - /// <param name="yz">The Z component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][2]</c>.</param> - /// <param name="zz">The Z component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][2]</c>.</param> + /// <param name="xx">The X component of the X column vector, accessed via <c>b.X.X</c> or <c>[0][0]</c>.</param> + /// <param name="yx">The X component of the Y column vector, accessed via <c>b.Y.X</c> or <c>[1][0]</c>.</param> + /// <param name="zx">The X component of the Z column vector, accessed via <c>b.Z.X</c> or <c>[2][0]</c>.</param> + /// <param name="xy">The Y component of the X column vector, accessed via <c>b.X.Y</c> or <c>[0][1]</c>.</param> + /// <param name="yy">The Y component of the Y column vector, accessed via <c>b.Y.Y</c> or <c>[1][1]</c>.</param> + /// <param name="zy">The Y component of the Z column vector, accessed via <c>b.Y.Y</c> or <c>[2][1]</c>.</param> + /// <param name="xz">The Z component of the X column vector, accessed via <c>b.X.Y</c> or <c>[0][2]</c>.</param> + /// <param name="yz">The Z component of the Y column vector, accessed via <c>b.Y.Y</c> or <c>[1][2]</c>.</param> + /// <param name="zz">The Z component of the Z column vector, accessed via <c>b.Y.Y</c> or <c>[2][2]</c>.</param> public Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz) { Row0 = new Vector3(xx, yx, zx); @@ -885,7 +888,7 @@ namespace Godot /// <param name="order">The order to compose the Euler angles.</param> public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.Yxz) { - (real_t sin, real_t cos) = Mathf.SinCos(euler.x); + (real_t sin, real_t cos) = Mathf.SinCos(euler.X); Basis xmat = new Basis ( new Vector3(1, 0, 0), @@ -893,7 +896,7 @@ namespace Godot new Vector3(0, -sin, cos) ); - (sin, cos) = Mathf.SinCos(euler.y); + (sin, cos) = Mathf.SinCos(euler.Y); Basis ymat = new Basis ( new Vector3(cos, 0, -sin), @@ -901,7 +904,7 @@ namespace Godot new Vector3(sin, 0, cos) ); - (sin, cos) = Mathf.SinCos(euler.z); + (sin, cos) = Mathf.SinCos(euler.Z); Basis zmat = new Basis ( new Vector3(cos, sin, 0), @@ -938,9 +941,9 @@ namespace Godot public static Basis FromScale(Vector3 scale) { return new Basis( - scale.x, 0, 0, - 0, scale.y, 0, - 0, 0, scale.z + scale.X, 0, 0, + 0, scale.Y, 0, + 0, 0, scale.Z ); } @@ -991,9 +994,9 @@ namespace Godot { return new Vector3 ( - basis.Row0[0] * vector.x + basis.Row1[0] * vector.y + basis.Row2[0] * vector.z, - basis.Row0[1] * vector.x + basis.Row1[1] * vector.y + basis.Row2[1] * vector.z, - basis.Row0[2] * vector.x + basis.Row1[2] * vector.y + basis.Row2[2] * vector.z + basis.Row0[0] * vector.X + basis.Row1[0] * vector.Y + basis.Row2[0] * vector.Z, + basis.Row0[1] * vector.X + basis.Row1[1] * vector.Y + basis.Row2[1] * vector.Z, + basis.Row0[2] * vector.X + basis.Row1[2] * vector.Y + basis.Row2[2] * vector.Z ); } @@ -1074,7 +1077,7 @@ namespace Godot /// <returns>A string representation of this basis.</returns> public override readonly string ToString() { - return $"[X: {x}, Y: {y}, Z: {z}]"; + return $"[X: {X}, Y: {Y}, Z: {Z}]"; } /// <summary> @@ -1083,7 +1086,7 @@ namespace Godot /// <returns>A string representation of this basis.</returns> public readonly string ToString(string format) { - return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, Z: {z.ToString(format)}]"; + return $"[X: {X.ToString(format)}, Y: {Y.ToString(format)}, Z: {Z.ToString(format)}]"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs index 354212da1b..16be494d99 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs @@ -12,7 +12,7 @@ namespace Godot.Bridge { try { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) { @@ -49,7 +49,7 @@ namespace Godot.Bridge { try { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) throw new InvalidOperationException(); @@ -79,17 +79,34 @@ namespace Godot.Bridge { try { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) throw new InvalidOperationException(); + // Properties if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue)) { *outRet = outRetValue; return godot_bool.True; } + // Signals + if (godotObject.HasGodotClassSignal(CustomUnsafe.AsRef(name))) + { + godot_signal signal = new godot_signal(*name, godotObject.GetInstanceId()); + *outRet = VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue(signal); + return godot_bool.True; + } + + // Methods + if (godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(name))) + { + godot_callable method = new godot_callable(*name, godotObject.GetInstanceId()); + *outRet = VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue(method); + return godot_bool.True; + } + var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name))); @@ -117,7 +134,7 @@ namespace Godot.Bridge { try { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (okIfNull.ToBool()) godotObject?.Dispose(); @@ -135,7 +152,7 @@ namespace Godot.Bridge { try { - var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + var self = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (self == null) { @@ -169,7 +186,7 @@ namespace Godot.Bridge { try { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) return godot_bool.False; @@ -192,7 +209,7 @@ namespace Godot.Bridge { try { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) return; @@ -225,7 +242,7 @@ namespace Godot.Bridge { try { - var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target; + var godotObject = (GodotObject)GCHandle.FromIntPtr(godotObjectGCHandle).Target; if (godotObject == null) return; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs index 6d20f95007..47feb1902f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs @@ -48,7 +48,7 @@ public sealed class GodotSerializationInfo : IDisposable { _signalEvents[name] = serializedData; } - else if (OS.IsStdoutVerbose()) + else if (OS.IsStdOutVerbose()) { Console.WriteLine($"Failed to serialize event signal delegate: {name}"); } @@ -72,7 +72,7 @@ public sealed class GodotSerializationInfo : IDisposable return true; } - else if (OS.IsStdoutVerbose()) + else if (OS.IsStdOutVerbose()) { Console.WriteLine($"Failed to deserialize event signal delegate: {name}"); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index dafa83431b..ec2728140e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -100,7 +100,7 @@ namespace Godot.Bridge Type nativeType = TypeGetProxyClass(nativeTypeNameStr) ?? throw new InvalidOperationException( "Wrapper class not found for type: " + nativeTypeNameStr); - var obj = (Object)FormatterServices.GetUninitializedObject(nativeType); + var obj = (GodotObject)FormatterServices.GetUninitializedObject(nativeType); var ctor = nativeType.GetConstructor( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, @@ -150,7 +150,7 @@ namespace Godot.Bridge } } - var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); + var obj = (GodotObject)FormatterServices.GetUninitializedObject(scriptType); var parameters = ctor.GetParameters(); int paramCount = parameters.Length; @@ -189,7 +189,7 @@ namespace Godot.Bridge return; } - var native = Object.InternalGetClassNativeBase(scriptType); + var native = GodotObject.InternalGetClassNativeBase(scriptType); var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); @@ -222,7 +222,7 @@ namespace Godot.Bridge { try { - var target = (Object?)GCHandle.FromIntPtr(gcHandlePtr).Target; + var target = (GodotObject?)GCHandle.FromIntPtr(gcHandlePtr).Target; if (target != null) target.NativePtr = newPtr; } @@ -239,21 +239,45 @@ namespace Godot.Bridge if (nativeTypeNameStr[0] == '_') nativeTypeNameStr = nativeTypeNameStr.Substring(1); - Type? wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr); + Type? wrapperType = typeof(GodotObject).Assembly.GetType("Godot." + nativeTypeNameStr); if (wrapperType == null) { - wrapperType = AppDomain.CurrentDomain.GetAssemblies() - .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor")? - .GetType("Godot." + nativeTypeNameStr); + wrapperType = GetTypeByGodotClassAttr(typeof(GodotObject).Assembly, nativeTypeNameStr); + } + + if (wrapperType == null) + { + var editorAssembly = AppDomain.CurrentDomain.GetAssemblies() + .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor"); + wrapperType = editorAssembly?.GetType("Godot." + nativeTypeNameStr); + + if (wrapperType == null) + { + wrapperType = GetTypeByGodotClassAttr(editorAssembly, nativeTypeNameStr); + } + } + + static Type? GetTypeByGodotClassAttr(Assembly assembly, string nativeTypeNameStr) + { + var types = assembly.GetTypes(); + foreach (var type in types) + { + var attr = type.GetCustomAttribute<GodotClassNameAttribute>(); + if (attr?.Name == nativeTypeNameStr) + { + return type; + } + } + return null; } static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed; if (wrapperType != null && IsStatic(wrapperType)) { - // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object. - return typeof(Object); + // A static class means this is a Godot singleton class. If an instance is needed we use GodotObject. + return typeof(GodotObject); } return wrapperType; @@ -293,7 +317,7 @@ namespace Godot.Bridge // such as when disabling C# source generators (for whatever reason) or when using a // language other than C# that has nothing similar to source generators to automate it. - var typeOfGodotObject = typeof(Object); + var typeOfGodotObject = typeof(GodotObject); foreach (var type in assembly.GetTypes()) { @@ -331,7 +355,7 @@ namespace Godot.Bridge { try { - var owner = (Object?)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; + var owner = (GodotObject?)GCHandle.FromIntPtr(ownerGCHandlePtr).Target; if (owner == null) { @@ -539,9 +563,9 @@ namespace Godot.Bridge } // ReSharper disable once RedundantNameQualifier - if (!typeof(Godot.Object).IsAssignableFrom(scriptType)) + if (!typeof(GodotObject).IsAssignableFrom(scriptType)) { - // The class no longer inherits Godot.Object, can't reload + // The class no longer inherits GodotObject, can't reload return godot_bool.False; } @@ -589,7 +613,7 @@ namespace Godot.Bridge using var methods = new Collections.Array(); Type? top = scriptType; - Type native = Object.InternalGetClassNativeBase(top); + Type native = GodotObject.InternalGetClassNativeBase(top); while (top != null && top != native) { @@ -650,7 +674,7 @@ namespace Godot.Bridge continue; var rpcAttr = method.GetCustomAttributes(inherit: false) - .OfType<RPCAttribute>().FirstOrDefault(); + .OfType<RpcAttribute>().FirstOrDefault(); if (rpcAttr == null) continue; @@ -899,7 +923,7 @@ namespace Godot.Bridge try { Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr); - Type native = Object.InternalGetClassNativeBase(top); + Type native = GodotObject.InternalGetClassNativeBase(top); while (top != null && top != native) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs index 23b0aa9204..219a9a8c15 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs @@ -28,7 +28,7 @@ namespace Godot /// </example> public readonly partial struct Callable { - private readonly Object _target; + private readonly GodotObject _target; private readonly StringName _method; private readonly Delegate _delegate; private readonly unsafe delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> _trampoline; @@ -36,7 +36,7 @@ namespace Godot /// <summary> /// Object that contains the method. /// </summary> - public Object Target => _target; + public GodotObject Target => _target; /// <summary> /// Name of the method that will be called. @@ -60,7 +60,7 @@ namespace Godot /// </summary> /// <param name="target">Object that contains the method.</param> /// <param name="method">Name of the method that will be called.</param> - public unsafe Callable(Object target, StringName method) + public unsafe Callable(GodotObject target, StringName method) { _target = target; _method = method; @@ -71,7 +71,7 @@ namespace Godot private unsafe Callable(Delegate @delegate, delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> trampoline) { - _target = @delegate?.Target as Object; + _target = @delegate?.Target as GodotObject; _method = null; _delegate = @delegate; _trampoline = trampoline; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs index ff385da1c9..3005582bea 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs @@ -45,7 +45,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0>( + public static unsafe Callable From<[MustBeVariant] T0>( Action<T0> action ) { @@ -64,7 +64,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1>( Action<T0, T1> action ) { @@ -84,7 +84,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2>( Action<T0, T1, T2> action ) { @@ -105,7 +105,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3>( Action<T0, T1, T2, T3> action ) { @@ -127,7 +127,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4>( Action<T0, T1, T2, T3, T4> action ) { @@ -150,7 +150,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5>( Action<T0, T1, T2, T3, T4, T5> action ) { @@ -174,7 +174,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6>( Action<T0, T1, T2, T3, T4, T5, T6> action ) { @@ -199,7 +199,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7>( Action<T0, T1, T2, T3, T4, T5, T6, T7> action ) { @@ -225,7 +225,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8>( Action<T0, T1, T2, T3, T4, T5, T6, T7, T8> action ) { @@ -255,7 +255,7 @@ public readonly partial struct Callable /// Constructs a new <see cref="Callable"/> for the given <paramref name="func"/>. /// </summary> /// <param name="func">Action method that will be called.</param> - public static unsafe Callable From<TResult>( + public static unsafe Callable From<[MustBeVariant] TResult>( Func<TResult> func ) { @@ -272,7 +272,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] TResult>( Func<T0, TResult> func ) { @@ -291,7 +291,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] TResult>( Func<T0, T1, TResult> func ) { @@ -311,7 +311,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] TResult>( Func<T0, T1, T2, TResult> func ) { @@ -332,7 +332,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, TResult> func ) { @@ -354,7 +354,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, TResult> func ) { @@ -377,7 +377,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, T5, TResult> func ) { @@ -401,7 +401,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, T5, T6, TResult> func ) { @@ -426,7 +426,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult> func ) { @@ -452,7 +452,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult> func ) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index 6a7863112a..555811bab2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -21,97 +21,97 @@ namespace Godot /// <summary> /// The color's red component, typically on the range of 0 to 1. /// </summary> - public float r; + public float R; /// <summary> /// The color's green component, typically on the range of 0 to 1. /// </summary> - public float g; + public float G; /// <summary> /// The color's blue component, typically on the range of 0 to 1. /// </summary> - public float b; + public float B; /// <summary> /// The color's alpha (transparency) component, typically on the range of 0 to 1. /// </summary> - public float a; + public float A; /// <summary> - /// Wrapper for <see cref="r"/> that uses the range 0 to 255 instead of 0 to 1. + /// Wrapper for <see cref="R"/> that uses the range 0 to 255 instead of 0 to 1. /// </summary> /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value> - public int r8 + public int R8 { readonly get { - return (int)Math.Round(r * 255.0f); + return (int)Math.Round(R * 255.0f); } set { - r = value / 255.0f; + R = value / 255.0f; } } /// <summary> - /// Wrapper for <see cref="g"/> that uses the range 0 to 255 instead of 0 to 1. + /// Wrapper for <see cref="G"/> that uses the range 0 to 255 instead of 0 to 1. /// </summary> /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value> - public int g8 + public int G8 { readonly get { - return (int)Math.Round(g * 255.0f); + return (int)Math.Round(G * 255.0f); } set { - g = value / 255.0f; + G = value / 255.0f; } } /// <summary> - /// Wrapper for <see cref="b"/> that uses the range 0 to 255 instead of 0 to 1. + /// Wrapper for <see cref="B"/> that uses the range 0 to 255 instead of 0 to 1. /// </summary> /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value> - public int b8 + public int B8 { readonly get { - return (int)Math.Round(b * 255.0f); + return (int)Math.Round(B * 255.0f); } set { - b = value / 255.0f; + B = value / 255.0f; } } /// <summary> - /// Wrapper for <see cref="a"/> that uses the range 0 to 255 instead of 0 to 1. + /// Wrapper for <see cref="A"/> that uses the range 0 to 255 instead of 0 to 1. /// </summary> /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value> - public int a8 + public int A8 { readonly get { - return (int)Math.Round(a * 255.0f); + return (int)Math.Round(A * 255.0f); } set { - a = value / 255.0f; + A = value / 255.0f; } } /// <summary> /// The HSV hue of this color, on the range 0 to 1. /// </summary> - /// <value>Getting is a long process, refer to the source code for details. Setting uses <see cref="FromHSV"/>.</value> - public float h + /// <value>Getting is a long process, refer to the source code for details. Setting uses <see cref="FromHsv"/>.</value> + public float H { readonly get { - float max = Math.Max(r, Math.Max(g, b)); - float min = Math.Min(r, Math.Min(g, b)); + float max = Math.Max(R, Math.Max(G, B)); + float min = Math.Min(R, Math.Min(G, B)); float delta = max - min; @@ -122,17 +122,17 @@ namespace Godot float h; - if (r == max) + if (R == max) { - h = (g - b) / delta; // Between yellow & magenta + h = (G - B) / delta; // Between yellow & magenta } - else if (g == max) + else if (G == max) { - h = 2 + ((b - r) / delta); // Between cyan & yellow + h = 2 + ((B - R) / delta); // Between cyan & yellow } else { - h = 4 + ((r - g) / delta); // Between magenta & cyan + h = 4 + ((R - G) / delta); // Between magenta & cyan } h /= 6.0f; @@ -146,20 +146,20 @@ namespace Godot } set { - this = FromHSV(value, s, v, a); + this = FromHsv(value, S, V, A); } } /// <summary> /// The HSV saturation of this color, on the range 0 to 1. /// </summary> - /// <value>Getting is equivalent to the ratio between the min and max RGB value. Setting uses <see cref="FromHSV"/>.</value> - public float s + /// <value>Getting is equivalent to the ratio between the min and max RGB value. Setting uses <see cref="FromHsv"/>.</value> + public float S { readonly get { - float max = Math.Max(r, Math.Max(g, b)); - float min = Math.Min(r, Math.Min(g, b)); + float max = Math.Max(R, Math.Max(G, B)); + float min = Math.Min(R, Math.Min(G, B)); float delta = max - min; @@ -167,23 +167,23 @@ namespace Godot } set { - this = FromHSV(h, value, v, a); + this = FromHsv(H, value, V, A); } } /// <summary> /// The HSV value (brightness) of this color, on the range 0 to 1. /// </summary> - /// <value>Getting is equivalent to using <see cref="Math.Max(float, float)"/> on the RGB components. Setting uses <see cref="FromHSV"/>.</value> - public float v + /// <value>Getting is equivalent to using <see cref="Math.Max(float, float)"/> on the RGB components. Setting uses <see cref="FromHsv"/>.</value> + public float V { readonly get { - return Math.Max(r, Math.Max(g, b)); + return Math.Max(R, Math.Max(G, B)); } set { - this = FromHSV(h, s, value, a); + this = FromHsv(H, S, value, A); } } @@ -197,17 +197,17 @@ namespace Godot /// </summary> public readonly float Luminance { - get { return 0.2126f * r + 0.7152f * g + 0.0722f * b; } + get { return 0.2126f * R + 0.7152f * G + 0.0722f * B; } } /// <summary> /// Access color components using their index. /// </summary> /// <value> - /// <c>[0]</c> is equivalent to <see cref="r"/>, - /// <c>[1]</c> is equivalent to <see cref="g"/>, - /// <c>[2]</c> is equivalent to <see cref="b"/>, - /// <c>[3]</c> is equivalent to <see cref="a"/>. + /// <c>[0]</c> is equivalent to <see cref="R"/>, + /// <c>[1]</c> is equivalent to <see cref="G"/>, + /// <c>[2]</c> is equivalent to <see cref="B"/>, + /// <c>[3]</c> is equivalent to <see cref="A"/>. /// </value> public float this[int index] { @@ -216,13 +216,13 @@ namespace Godot switch (index) { case 0: - return r; + return R; case 1: - return g; + return G; case 2: - return b; + return B; case 3: - return a; + return A; default: throw new ArgumentOutOfRangeException(nameof(index)); } @@ -232,16 +232,16 @@ namespace Godot switch (index) { case 0: - r = value; + R = value; return; case 1: - g = value; + G = value; return; case 2: - b = value; + B = value; return; case 3: - a = value; + A = value; return; default: throw new ArgumentOutOfRangeException(nameof(index)); @@ -260,17 +260,17 @@ namespace Godot { Color res; - float sa = 1.0f - over.a; - res.a = (a * sa) + over.a; + float sa = 1.0f - over.A; + res.A = (A * sa) + over.A; - if (res.a == 0) + if (res.A == 0) { return new Color(0, 0, 0, 0); } - res.r = ((r * a * sa) + (over.r * over.a)) / res.a; - res.g = ((g * a * sa) + (over.g * over.a)) / res.a; - res.b = ((b * a * sa) + (over.b * over.a)) / res.a; + res.R = ((R * A * sa) + (over.R * over.A)) / res.A; + res.G = ((G * A * sa) + (over.G * over.A)) / res.A; + res.B = ((B * A * sa) + (over.B * over.A)) / res.A; return res; } @@ -289,10 +289,10 @@ namespace Godot Color maximum = max ?? new Color(1, 1, 1, 1); return new Color ( - (float)Mathf.Clamp(r, minimum.r, maximum.r), - (float)Mathf.Clamp(g, minimum.g, maximum.g), - (float)Mathf.Clamp(b, minimum.b, maximum.b), - (float)Mathf.Clamp(a, minimum.a, maximum.a) + (float)Mathf.Clamp(R, minimum.R, maximum.R), + (float)Mathf.Clamp(G, minimum.G, maximum.G), + (float)Mathf.Clamp(B, minimum.B, maximum.B), + (float)Mathf.Clamp(A, minimum.A, maximum.A) ); } @@ -305,9 +305,9 @@ namespace Godot public readonly Color Darkened(float amount) { Color res = this; - res.r *= 1.0f - amount; - res.g *= 1.0f - amount; - res.b *= 1.0f - amount; + res.R *= 1.0f - amount; + res.G *= 1.0f - amount; + res.B *= 1.0f - amount; return res; } @@ -318,10 +318,10 @@ namespace Godot public readonly Color Inverted() { return new Color( - 1.0f - r, - 1.0f - g, - 1.0f - b, - a + 1.0f - R, + 1.0f - G, + 1.0f - B, + A ); } @@ -334,9 +334,9 @@ namespace Godot public readonly Color Lightened(float amount) { Color res = this; - res.r += (1.0f - res.r) * amount; - res.g += (1.0f - res.g) * amount; - res.b += (1.0f - res.b) * amount; + res.R += (1.0f - res.R) * amount; + res.G += (1.0f - res.G) * amount; + res.B += (1.0f - res.B) * amount; return res; } @@ -351,10 +351,10 @@ namespace Godot { return new Color ( - (float)Mathf.Lerp(r, to.r, weight), - (float)Mathf.Lerp(g, to.g, weight), - (float)Mathf.Lerp(b, to.b, weight), - (float)Mathf.Lerp(a, to.a, weight) + (float)Mathf.Lerp(R, to.R, weight), + (float)Mathf.Lerp(G, to.G, weight), + (float)Mathf.Lerp(B, to.B, weight), + (float)Mathf.Lerp(A, to.A, weight) ); } @@ -367,9 +367,9 @@ namespace Godot public readonly Color LinearToSrgb() { return new Color( - r < 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * (float)Mathf.Pow(r, 1.0f / 2.4f) - 0.055f, - g < 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * (float)Mathf.Pow(g, 1.0f / 2.4f) - 0.055f, - b < 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * (float)Mathf.Pow(b, 1.0f / 2.4f) - 0.055f, a); + R < 0.0031308f ? 12.92f * R : (1.0f + 0.055f) * (float)Mathf.Pow(R, 1.0f / 2.4f) - 0.055f, + G < 0.0031308f ? 12.92f * G : (1.0f + 0.055f) * (float)Mathf.Pow(G, 1.0f / 2.4f) - 0.055f, + B < 0.0031308f ? 12.92f * B : (1.0f + 0.055f) * (float)Mathf.Pow(B, 1.0f / 2.4f) - 0.055f, A); } /// <summary> @@ -381,10 +381,10 @@ namespace Godot public readonly Color SrgbToLinear() { return new Color( - r < 0.04045f ? r * (1.0f / 12.92f) : (float)Mathf.Pow((r + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), - g < 0.04045f ? g * (1.0f / 12.92f) : (float)Mathf.Pow((g + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), - b < 0.04045f ? b * (1.0f / 12.92f) : (float)Mathf.Pow((b + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), - a); + R < 0.04045f ? R * (1.0f / 12.92f) : (float)Mathf.Pow((R + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), + G < 0.04045f ? G * (1.0f / 12.92f) : (float)Mathf.Pow((G + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), + B < 0.04045f ? B * (1.0f / 12.92f) : (float)Mathf.Pow((B + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f), + A); } /// <summary> @@ -395,13 +395,13 @@ namespace Godot /// <returns>A <see langword="uint"/> representing this color in ABGR32 format.</returns> public readonly uint ToAbgr32() { - uint c = (byte)Math.Round(a * 255); + uint c = (byte)Math.Round(A * 255); c <<= 8; - c |= (byte)Math.Round(b * 255); + c |= (byte)Math.Round(B * 255); c <<= 8; - c |= (byte)Math.Round(g * 255); + c |= (byte)Math.Round(G * 255); c <<= 8; - c |= (byte)Math.Round(r * 255); + c |= (byte)Math.Round(R * 255); return c; } @@ -414,13 +414,13 @@ namespace Godot /// <returns>A <see langword="ulong"/> representing this color in ABGR64 format.</returns> public readonly ulong ToAbgr64() { - ulong c = (ushort)Math.Round(a * 65535); + ulong c = (ushort)Math.Round(A * 65535); c <<= 16; - c |= (ushort)Math.Round(b * 65535); + c |= (ushort)Math.Round(B * 65535); c <<= 16; - c |= (ushort)Math.Round(g * 65535); + c |= (ushort)Math.Round(G * 65535); c <<= 16; - c |= (ushort)Math.Round(r * 65535); + c |= (ushort)Math.Round(R * 65535); return c; } @@ -433,13 +433,13 @@ namespace Godot /// <returns>A <see langword="uint"/> representing this color in ARGB32 format.</returns> public readonly uint ToArgb32() { - uint c = (byte)Math.Round(a * 255); + uint c = (byte)Math.Round(A * 255); c <<= 8; - c |= (byte)Math.Round(r * 255); + c |= (byte)Math.Round(R * 255); c <<= 8; - c |= (byte)Math.Round(g * 255); + c |= (byte)Math.Round(G * 255); c <<= 8; - c |= (byte)Math.Round(b * 255); + c |= (byte)Math.Round(B * 255); return c; } @@ -452,13 +452,13 @@ namespace Godot /// <returns>A <see langword="ulong"/> representing this color in ARGB64 format.</returns> public readonly ulong ToArgb64() { - ulong c = (ushort)Math.Round(a * 65535); + ulong c = (ushort)Math.Round(A * 65535); c <<= 16; - c |= (ushort)Math.Round(r * 65535); + c |= (ushort)Math.Round(R * 65535); c <<= 16; - c |= (ushort)Math.Round(g * 65535); + c |= (ushort)Math.Round(G * 65535); c <<= 16; - c |= (ushort)Math.Round(b * 65535); + c |= (ushort)Math.Round(B * 65535); return c; } @@ -471,13 +471,13 @@ namespace Godot /// <returns>A <see langword="uint"/> representing this color in RGBA32 format.</returns> public readonly uint ToRgba32() { - uint c = (byte)Math.Round(r * 255); + uint c = (byte)Math.Round(R * 255); c <<= 8; - c |= (byte)Math.Round(g * 255); + c |= (byte)Math.Round(G * 255); c <<= 8; - c |= (byte)Math.Round(b * 255); + c |= (byte)Math.Round(B * 255); c <<= 8; - c |= (byte)Math.Round(a * 255); + c |= (byte)Math.Round(A * 255); return c; } @@ -490,13 +490,13 @@ namespace Godot /// <returns>A <see langword="ulong"/> representing this color in RGBA64 format.</returns> public readonly ulong ToRgba64() { - ulong c = (ushort)Math.Round(r * 65535); + ulong c = (ushort)Math.Round(R * 65535); c <<= 16; - c |= (ushort)Math.Round(g * 65535); + c |= (ushort)Math.Round(G * 65535); c <<= 16; - c |= (ushort)Math.Round(b * 65535); + c |= (ushort)Math.Round(B * 65535); c <<= 16; - c |= (ushort)Math.Round(a * 65535); + c |= (ushort)Math.Round(A * 65535); return c; } @@ -508,17 +508,17 @@ namespace Godot /// Whether or not to include alpha. If <see langword="false"/>, the color is RGB instead of RGBA. /// </param> /// <returns>A string for the HTML hexadecimal representation of this color.</returns> - public readonly string ToHTML(bool includeAlpha = true) + public readonly string ToHtml(bool includeAlpha = true) { string txt = string.Empty; - txt += ToHex32(r); - txt += ToHex32(g); - txt += ToHex32(b); + txt += ToHex32(R); + txt += ToHex32(G); + txt += ToHex32(B); if (includeAlpha) { - txt += ToHex32(a); + txt += ToHex32(A); } return txt; @@ -533,10 +533,10 @@ namespace Godot /// <param name="a">The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1.</param> public Color(float r, float g, float b, float a = 1.0f) { - this.r = r; - this.g = g; - this.b = b; - this.a = a; + R = r; + G = g; + B = b; + A = a; } /// <summary> @@ -546,10 +546,10 @@ namespace Godot /// <param name="a">The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1.</param> public Color(Color c, float a = 1.0f) { - r = c.r; - g = c.g; - b = c.b; - this.a = a; + R = c.R; + G = c.G; + B = c.B; + A = a; } /// <summary> @@ -559,13 +559,13 @@ namespace Godot /// <param name="rgba">The <see langword="uint"/> representing the color.</param> public Color(uint rgba) { - a = (rgba & 0xFF) / 255.0f; + A = (rgba & 0xFF) / 255.0f; rgba >>= 8; - b = (rgba & 0xFF) / 255.0f; + B = (rgba & 0xFF) / 255.0f; rgba >>= 8; - g = (rgba & 0xFF) / 255.0f; + G = (rgba & 0xFF) / 255.0f; rgba >>= 8; - r = (rgba & 0xFF) / 255.0f; + R = (rgba & 0xFF) / 255.0f; } /// <summary> @@ -575,13 +575,13 @@ namespace Godot /// <param name="rgba">The <see langword="ulong"/> representing the color.</param> public Color(ulong rgba) { - a = (rgba & 0xFFFF) / 65535.0f; + A = (rgba & 0xFFFF) / 65535.0f; rgba >>= 16; - b = (rgba & 0xFFFF) / 65535.0f; + B = (rgba & 0xFFFF) / 65535.0f; rgba >>= 16; - g = (rgba & 0xFFFF) / 65535.0f; + G = (rgba & 0xFFFF) / 65535.0f; rgba >>= 16; - r = (rgba & 0xFFFF) / 65535.0f; + R = (rgba & 0xFFFF) / 65535.0f; } /// <summary> @@ -598,7 +598,7 @@ namespace Godot { if (HtmlIsValid(code)) { - this = FromHTML(code); + this = FromHtml(code); } else { @@ -616,7 +616,7 @@ namespace Godot public Color(string code, float alpha) { this = new Color(code); - a = alpha; + A = alpha; } /// <summary> @@ -626,15 +626,15 @@ namespace Godot /// <exception name="ArgumentOutOfRangeException"> /// <paramref name="rgba"/> color code is invalid. /// </exception> - public static Color FromHTML(ReadOnlySpan<char> rgba) + public static Color FromHtml(ReadOnlySpan<char> rgba) { Color c; if (rgba.Length == 0) { - c.r = 0f; - c.g = 0f; - c.b = 0f; - c.a = 1.0f; + c.R = 0f; + c.G = 0f; + c.B = 0f; + c.A = 1.0f; return c; } @@ -670,44 +670,44 @@ namespace Godot $"Invalid color code. Length is {rgba.Length}, but a length of 6 or 8 is expected: {rgba}"); } - c.a = 1.0f; + c.A = 1.0f; if (isShorthand) { - c.r = ParseCol4(rgba, 0) / 15f; - c.g = ParseCol4(rgba, 1) / 15f; - c.b = ParseCol4(rgba, 2) / 15f; + c.R = ParseCol4(rgba, 0) / 15f; + c.G = ParseCol4(rgba, 1) / 15f; + c.B = ParseCol4(rgba, 2) / 15f; if (alpha) { - c.a = ParseCol4(rgba, 3) / 15f; + c.A = ParseCol4(rgba, 3) / 15f; } } else { - c.r = ParseCol8(rgba, 0) / 255f; - c.g = ParseCol8(rgba, 2) / 255f; - c.b = ParseCol8(rgba, 4) / 255f; + c.R = ParseCol8(rgba, 0) / 255f; + c.G = ParseCol8(rgba, 2) / 255f; + c.B = ParseCol8(rgba, 4) / 255f; if (alpha) { - c.a = ParseCol8(rgba, 6) / 255f; + c.A = ParseCol8(rgba, 6) / 255f; } } - if (c.r < 0) + if (c.R < 0) { throw new ArgumentOutOfRangeException($"Invalid color code. Red part is not valid hexadecimal: {rgba}"); } - if (c.g < 0) + if (c.G < 0) { throw new ArgumentOutOfRangeException($"Invalid color code. Green part is not valid hexadecimal: {rgba}"); } - if (c.b < 0) + if (c.B < 0) { throw new ArgumentOutOfRangeException($"Invalid color code. Blue part is not valid hexadecimal: {rgba}"); } - if (c.a < 0) + if (c.A < 0) { throw new ArgumentOutOfRangeException($"Invalid color code. Alpha part is not valid hexadecimal: {rgba}"); } @@ -793,7 +793,7 @@ namespace Godot /// <param name="value">The HSV value (brightness), typically on the range of 0 to 1.</param> /// <param name="alpha">The alpha (transparency) value, typically on the range of 0 to 1.</param> /// <returns>The constructed color.</returns> - public static Color FromHSV(float hue, float saturation, float value, float alpha = 1.0f) + public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f) { if (saturation == 0) { @@ -837,10 +837,10 @@ namespace Godot /// <param name="hue">Output parameter for the HSV hue.</param> /// <param name="saturation">Output parameter for the HSV saturation.</param> /// <param name="value">Output parameter for the HSV value.</param> - public readonly void ToHSV(out float hue, out float saturation, out float value) + public readonly void ToHsv(out float hue, out float saturation, out float value) { - float max = (float)Mathf.Max(r, Mathf.Max(g, b)); - float min = (float)Mathf.Min(r, Mathf.Min(g, b)); + float max = (float)Mathf.Max(R, Mathf.Max(G, B)); + float min = (float)Mathf.Min(R, Mathf.Min(G, B)); float delta = max - min; @@ -850,17 +850,17 @@ namespace Godot } else { - if (r == max) + if (R == max) { - hue = (g - b) / delta; // Between yellow & magenta + hue = (G - B) / delta; // Between yellow & magenta } - else if (g == max) + else if (G == max) { - hue = 2 + ((b - r) / delta); // Between cyan & yellow + hue = 2 + ((B - R) / delta); // Between cyan & yellow } else { - hue = 4 + ((r - g) / delta); // Between magenta & cyan + hue = 4 + ((R - G) / delta); // Between magenta & cyan } hue /= 6.0f; @@ -950,7 +950,7 @@ namespace Godot { if (HtmlIsValid(str)) { - return FromHTML(str); + return FromHtml(str); } else { @@ -1012,10 +1012,10 @@ namespace Godot /// <returns>The added color.</returns> public static Color operator +(Color left, Color right) { - left.r += right.r; - left.g += right.g; - left.b += right.b; - left.a += right.a; + left.R += right.R; + left.G += right.G; + left.B += right.B; + left.A += right.A; return left; } @@ -1028,17 +1028,17 @@ namespace Godot /// <returns>The subtracted color.</returns> public static Color operator -(Color left, Color right) { - left.r -= right.r; - left.g -= right.g; - left.b -= right.b; - left.a -= right.a; + left.R -= right.R; + left.G -= right.G; + left.B -= right.B; + left.A -= right.A; return left; } /// <summary> /// Inverts the given color. This is equivalent to /// <c>Colors.White - c</c> or - /// <c>new Color(1 - c.r, 1 - c.g, 1 - c.b, 1 - c.a)</c>. + /// <c>new Color(1 - c.R, 1 - c.G, 1 - c.B, 1 - c.A)</c>. /// </summary> /// <param name="color">The color to invert.</param> /// <returns>The inverted color.</returns> @@ -1056,10 +1056,10 @@ namespace Godot /// <returns>The multiplied color.</returns> public static Color operator *(Color color, float scale) { - color.r *= scale; - color.g *= scale; - color.b *= scale; - color.a *= scale; + color.R *= scale; + color.G *= scale; + color.B *= scale; + color.A *= scale; return color; } @@ -1072,10 +1072,10 @@ namespace Godot /// <returns>The multiplied color.</returns> public static Color operator *(float scale, Color color) { - color.r *= scale; - color.g *= scale; - color.b *= scale; - color.a *= scale; + color.R *= scale; + color.G *= scale; + color.B *= scale; + color.A *= scale; return color; } @@ -1088,10 +1088,10 @@ namespace Godot /// <returns>The multiplied color.</returns> public static Color operator *(Color left, Color right) { - left.r *= right.r; - left.g *= right.g; - left.b *= right.b; - left.a *= right.a; + left.R *= right.R; + left.G *= right.G; + left.B *= right.B; + left.A *= right.A; return left; } @@ -1104,10 +1104,10 @@ namespace Godot /// <returns>The divided color.</returns> public static Color operator /(Color color, float scale) { - color.r /= scale; - color.g /= scale; - color.b /= scale; - color.a /= scale; + color.R /= scale; + color.G /= scale; + color.B /= scale; + color.A /= scale; return color; } @@ -1120,10 +1120,10 @@ namespace Godot /// <returns>The divided color.</returns> public static Color operator /(Color left, Color right) { - left.r /= right.r; - left.g /= right.g; - left.b /= right.b; - left.a /= right.a; + left.R /= right.R; + left.G /= right.G; + left.B /= right.B; + left.A /= right.A; return left; } @@ -1167,19 +1167,19 @@ namespace Godot /// <returns>Whether or not the left is less than the right.</returns> public static bool operator <(Color left, Color right) { - if (left.r == right.r) + if (left.R == right.R) { - if (left.g == right.g) + if (left.G == right.G) { - if (left.b == right.b) + if (left.B == right.B) { - return left.a < right.a; + return left.A < right.A; } - return left.b < right.b; + return left.B < right.B; } - return left.g < right.g; + return left.G < right.G; } - return left.r < right.r; + return left.R < right.R; } /// <summary> @@ -1196,19 +1196,19 @@ namespace Godot /// <returns>Whether or not the left is greater than the right.</returns> public static bool operator >(Color left, Color right) { - if (left.r == right.r) + if (left.R == right.R) { - if (left.g == right.g) + if (left.G == right.G) { - if (left.b == right.b) + if (left.B == right.B) { - return left.a > right.a; + return left.A > right.A; } - return left.b > right.b; + return left.B > right.B; } - return left.g > right.g; + return left.G > right.G; } - return left.r > right.r; + return left.R > right.R; } /// <summary> @@ -1225,19 +1225,19 @@ namespace Godot /// <returns>Whether or not the left is less than or equal to the right.</returns> public static bool operator <=(Color left, Color right) { - if (left.r == right.r) + if (left.R == right.R) { - if (left.g == right.g) + if (left.G == right.G) { - if (left.b == right.b) + if (left.B == right.B) { - return left.a <= right.a; + return left.A <= right.A; } - return left.b < right.b; + return left.B < right.B; } - return left.g < right.g; + return left.G < right.G; } - return left.r < right.r; + return left.R < right.R; } /// <summary> @@ -1254,19 +1254,19 @@ namespace Godot /// <returns>Whether or not the left is greater than or equal to the right.</returns> public static bool operator >=(Color left, Color right) { - if (left.r == right.r) + if (left.R == right.R) { - if (left.g == right.g) + if (left.G == right.G) { - if (left.b == right.b) + if (left.B == right.B) { - return left.a >= right.a; + return left.A >= right.A; } - return left.b > right.b; + return left.B > right.B; } - return left.g > right.g; + return left.G > right.G; } - return left.r > right.r; + return left.R > right.R; } /// <summary> @@ -1288,7 +1288,7 @@ namespace Godot /// <returns>Whether or not the colors are equal.</returns> public readonly bool Equals(Color other) { - return r == other.r && g == other.g && b == other.b && a == other.a; + return R == other.R && G == other.G && B == other.B && A == other.A; } /// <summary> @@ -1299,7 +1299,7 @@ namespace Godot /// <returns>Whether or not the colors are approximately equal.</returns> public readonly bool IsEqualApprox(Color other) { - return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a); + return Mathf.IsEqualApprox(R, other.R) && Mathf.IsEqualApprox(G, other.G) && Mathf.IsEqualApprox(B, other.B) && Mathf.IsEqualApprox(A, other.A); } /// <summary> @@ -1308,7 +1308,7 @@ namespace Godot /// <returns>A hash code for this color.</returns> public override readonly int GetHashCode() { - return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode(); + return R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode(); } /// <summary> @@ -1317,7 +1317,7 @@ namespace Godot /// <returns>A string representation of this color.</returns> public override readonly string ToString() { - return $"({r}, {g}, {b}, {a})"; + return $"({R}, {G}, {B}, {A})"; } /// <summary> @@ -1326,7 +1326,7 @@ namespace Godot /// <returns>A string representation of this color.</returns> public readonly string ToString(string format) { - return $"({r.ToString(format)}, {g.ToString(format)}, {b.ToString(format)}, {a.ToString(format)})"; + return $"({R.ToString(format)}, {G.ToString(format)}, {B.ToString(format)}, {A.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index d94fbff331..4a54f67cc9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -126,7 +126,7 @@ namespace Godot } } // ReSharper disable once RedundantNameQualifier - case Godot.Object godotObject: + case GodotObject godotObject: { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) @@ -399,7 +399,7 @@ namespace Godot { ulong objectId = reader.ReadUInt64(); // ReSharper disable once RedundantNameQualifier - Godot.Object godotObject = GD.InstanceFromId(objectId); + GodotObject godotObject = GodotObject.InstanceFromId(objectId); if (godotObject == null) return false; @@ -581,31 +581,31 @@ namespace Godot return VariantUtils.CreateFrom(@double); case Vector2 vector2: return VariantUtils.CreateFrom(vector2); - case Vector2i vector2I: + case Vector2I vector2I: return VariantUtils.CreateFrom(vector2I); case Rect2 rect2: return VariantUtils.CreateFrom(rect2); - case Rect2i rect2I: + case Rect2I rect2I: return VariantUtils.CreateFrom(rect2I); case Transform2D transform2D: return VariantUtils.CreateFrom(transform2D); case Vector3 vector3: return VariantUtils.CreateFrom(vector3); - case Vector3i vector3I: + case Vector3I vector3I: return VariantUtils.CreateFrom(vector3I); case Vector4 vector4: return VariantUtils.CreateFrom(vector4); - case Vector4i vector4I: + case Vector4I vector4I: return VariantUtils.CreateFrom(vector4I); case Basis basis: return VariantUtils.CreateFrom(basis); case Quaternion quaternion: return VariantUtils.CreateFrom(quaternion); - case Transform3D transform3d: - return VariantUtils.CreateFrom(transform3d); + case Transform3D transform3D: + return VariantUtils.CreateFrom(transform3D); case Projection projection: return VariantUtils.CreateFrom(projection); - case AABB aabb: + case Aabb aabb: return VariantUtils.CreateFrom(aabb); case Color color: return VariantUtils.CreateFrom(color); @@ -639,15 +639,15 @@ namespace Godot return VariantUtils.CreateFrom(stringNameArray); case NodePath[] nodePathArray: return VariantUtils.CreateFrom(nodePathArray); - case RID[] ridArray: + case Rid[] ridArray: return VariantUtils.CreateFrom(ridArray); - case Godot.Object[] godotObjectArray: + case GodotObject[] godotObjectArray: return VariantUtils.CreateFrom(godotObjectArray); case StringName stringName: return VariantUtils.CreateFrom(stringName); case NodePath nodePath: return VariantUtils.CreateFrom(nodePath); - case RID rid: + case Rid rid: return VariantUtils.CreateFrom(rid); case Collections.Dictionary godotDictionary: return VariantUtils.CreateFrom(godotDictionary); @@ -655,7 +655,7 @@ namespace Godot return VariantUtils.CreateFrom(godotArray); case Variant variant: return VariantUtils.CreateFrom(variant); - case Godot.Object godotObject: + case GodotObject godotObject: return VariantUtils.CreateFrom(godotObject); case Enum @enum: return VariantUtils.CreateFrom(Convert.ToInt64(@enum)); @@ -690,18 +690,18 @@ namespace Godot [typeof(float)] = (in godot_variant variant) => VariantUtils.ConvertTo<float>(variant), [typeof(double)] = (in godot_variant variant) => VariantUtils.ConvertTo<double>(variant), [typeof(Vector2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2>(variant), - [typeof(Vector2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2i>(variant), + [typeof(Vector2I)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2I>(variant), [typeof(Rect2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2>(variant), - [typeof(Rect2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2i>(variant), + [typeof(Rect2I)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2I>(variant), [typeof(Transform2D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform2D>(variant), [typeof(Vector3)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3>(variant), - [typeof(Vector3i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3i>(variant), + [typeof(Vector3I)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3I>(variant), [typeof(Basis)] = (in godot_variant variant) => VariantUtils.ConvertTo<Basis>(variant), [typeof(Quaternion)] = (in godot_variant variant) => VariantUtils.ConvertTo<Quaternion>(variant), [typeof(Transform3D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform3D>(variant), [typeof(Vector4)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4>(variant), - [typeof(Vector4i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4i>(variant), - [typeof(AABB)] = (in godot_variant variant) => VariantUtils.ConvertTo<AABB>(variant), + [typeof(Vector4I)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4I>(variant), + [typeof(Aabb)] = (in godot_variant variant) => VariantUtils.ConvertTo<Aabb>(variant), [typeof(Color)] = (in godot_variant variant) => VariantUtils.ConvertTo<Color>(variant), [typeof(Plane)] = (in godot_variant variant) => VariantUtils.ConvertTo<Plane>(variant), [typeof(Callable)] = (in godot_variant variant) => VariantUtils.ConvertTo<Callable>(variant), @@ -719,10 +719,10 @@ namespace Godot [typeof(StringName[])] = (in godot_variant variant) => VariantUtils.ConvertTo<StringName[]>(variant), [typeof(NodePath[])] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath[]>(variant), - [typeof(RID[])] = (in godot_variant variant) => VariantUtils.ConvertTo<RID[]>(variant), + [typeof(Rid[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Rid[]>(variant), [typeof(StringName)] = (in godot_variant variant) => VariantUtils.ConvertTo<StringName>(variant), [typeof(NodePath)] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath>(variant), - [typeof(RID)] = (in godot_variant variant) => VariantUtils.ConvertTo<RID>(variant), + [typeof(Rid)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rid>(variant), [typeof(Godot.Collections.Dictionary)] = (in godot_variant variant) => VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant), [typeof(Godot.Collections.Array)] = @@ -736,18 +736,18 @@ namespace Godot if (ToSystemObjectFuncByType.TryGetValue(type, out var func)) return func(variant); - if (typeof(Godot.Object).IsAssignableFrom(type)) - return Convert.ChangeType(VariantUtils.ConvertTo<Godot.Object>(variant), type); + if (typeof(GodotObject).IsAssignableFrom(type)) + return Convert.ChangeType(VariantUtils.ConvertTo<GodotObject>(variant), type); - if (typeof(Godot.Object[]).IsAssignableFrom(type)) + if (typeof(GodotObject[]).IsAssignableFrom(type)) { - static Godot.Object[] ConvertToSystemArrayOfGodotObject(in godot_array nativeArray, Type type) + static GodotObject[] ConvertToSystemArrayOfGodotObject(in godot_array nativeArray, Type type) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(nativeArray)); int length = array.Count; - var ret = (Godot.Object[])Activator.CreateInstance(type, length)!; + var ret = (GodotObject[])Activator.CreateInstance(type, length)!; for (int i = 0; i < length; i++) ret[i] = array[i].AsGodotObject(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index b5a8742d3d..923b2adafd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -68,7 +68,15 @@ namespace Godot.Collections } /// <summary> - /// Duplicates this <see cref="Dictionary"/>. + /// Returns a copy of the <see cref="Dictionary"/>. + /// If <paramref name="deep"/> is <see langword="true"/>, a deep copy is performed: + /// all nested arrays and dictionaries are duplicated and will not be shared with + /// the original dictionary. If <see langword="false"/>, a shallow copy is made and + /// references to the original nested arrays and dictionaries are kept, so that + /// modifying a sub-array or dictionary in the copy will also impact those + /// referenced in the source dictionary. Note that any <see cref="GodotObject"/> derived + /// elements will be shallow copied regardless of the <paramref name="deep"/> + /// setting. /// </summary> /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param> /// <returns>A new Godot Dictionary.</returns> @@ -80,6 +88,44 @@ namespace Godot.Collections return CreateTakingOwnershipOfDisposableValue(newDictionary); } + /// <summary> + /// Adds entries from <paramref name="dictionary"/> to this dictionary. + /// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/> + /// is <see langword="true"/>. + /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> + /// <param name="dictionary">Dictionary to copy entries from.</param> + /// <param name="overwrite">If duplicate keys should be copied over as well.</param> + public void Merge(Dictionary dictionary, bool overwrite = false) + { + ThrowIfReadOnly(); + + var self = (godot_dictionary)NativeValue; + var other = (godot_dictionary)dictionary.NativeValue; + NativeFuncs.godotsharp_dictionary_merge(ref self, in other, overwrite.ToGodotBool()); + } + + /// <summary> + /// Compares this <see cref="Dictionary"/> against the <paramref name="other"/> + /// <see cref="Dictionary"/> recursively. Returns <see langword="true"/> if the + /// two dictionaries contain the same keys and values. The order of the entries + /// does not matter. + /// otherwise. + /// </summary> + /// <param name="other">The other dictionary to compare against.</param> + /// <returns> + /// <see langword="true"/> if the dictionaries contain the same keys and values, + /// <see langword="false"/> otherwise. + /// </returns> + public bool RecursiveEqual(Dictionary other) + { + var self = (godot_dictionary)NativeValue; + var otherVariant = (godot_dictionary)other.NativeValue; + return NativeFuncs.godotsharp_dictionary_recursive_equal(ref self, otherVariant).ToBool(); + } + // IDictionary /// <summary> @@ -134,6 +180,13 @@ namespace Godot.Collections /// <summary> /// Returns the value at the given <paramref name="key"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The property is assigned and the dictionary is read-only. + /// </exception> + /// <exception cref="KeyNotFoundException"> + /// The property is retrieved and an entry for <paramref name="key"/> + /// does not exist in the dictionary. + /// </exception> /// <value>The value at the given <paramref name="key"/>.</value> public Variant this[Variant key] { @@ -153,6 +206,8 @@ namespace Godot.Collections } set { + ThrowIfReadOnly(); + var self = (godot_dictionary)NativeValue; NativeFuncs.godotsharp_dictionary_set_value(ref self, (godot_variant)key.NativeVar, (godot_variant)value.NativeVar); @@ -163,10 +218,18 @@ namespace Godot.Collections /// Adds an value <paramref name="value"/> at key <paramref name="key"/> /// to this <see cref="Dictionary"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> + /// <exception cref="ArgumentException"> + /// An entry for <paramref name="key"/> already exists in the dictionary. + /// </exception> /// <param name="key">The key at which to add the value.</param> /// <param name="value">The value to add.</param> public void Add(Variant key, Variant value) { + ThrowIfReadOnly(); + var variantKey = (godot_variant)key.NativeVar; var self = (godot_dictionary)NativeValue; @@ -181,10 +244,15 @@ namespace Godot.Collections => Add(item.Key, item.Value); /// <summary> - /// Erases all items from this <see cref="Dictionary"/>. + /// Clears the dictionary, removing all entries from it. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> public void Clear() { + ThrowIfReadOnly(); + var self = (godot_dictionary)NativeValue; NativeFuncs.godotsharp_dictionary_clear(ref self); } @@ -200,7 +268,7 @@ namespace Godot.Collections return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool(); } - public bool Contains(KeyValuePair<Variant, Variant> item) + bool ICollection<KeyValuePair<Variant, Variant>>.Contains(KeyValuePair<Variant, Variant> item) { godot_variant variantKey = (godot_variant)item.Key.NativeVar; var self = (godot_dictionary)NativeValue; @@ -220,15 +288,22 @@ namespace Godot.Collections /// <summary> /// Removes an element from this <see cref="Dictionary"/> by key. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <param name="key">The key of the element to remove.</param> public bool Remove(Variant key) { + ThrowIfReadOnly(); + var self = (godot_dictionary)NativeValue; return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool(); } - public bool Remove(KeyValuePair<Variant, Variant> item) + bool ICollection<KeyValuePair<Variant, Variant>>.Remove(KeyValuePair<Variant, Variant> item) { + ThrowIfReadOnly(); + godot_variant variantKey = (godot_variant)item.Key.NativeVar; var self = (godot_dictionary)NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, @@ -264,8 +339,37 @@ namespace Godot.Collections } } - bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false; + /// <summary> + /// Returns <see langword="true"/> if the dictionary is read-only. + /// See <see cref="MakeReadOnly"/>. + /// </summary> + public bool IsReadOnly => NativeValue.DangerousSelfRef.IsReadOnly; + + /// <summary> + /// Makes the <see cref="Dictionary"/> read-only, i.e. disabled modying of the + /// dictionary's elements. Does not apply to nested content, e.g. content of + /// nested dictionaries. + /// </summary> + public void MakeReadOnly() + { + if (IsReadOnly) + { + // Avoid interop call when the dictionary is already read-only. + return; + } + + var self = (godot_dictionary)NativeValue; + NativeFuncs.godotsharp_dictionary_make_read_only(ref self); + } + /// <summary> + /// Gets the value for the given <paramref name="key"/> in the dictionary. + /// Returns <see langword="true"/> if an entry for the given key exists in + /// the dictionary; otherwise, returns <see langword="false"/>. + /// </summary> + /// <param name="key">The key of the element to get.</param> + /// <param name="value">The value at the given <paramref name="key"/>.</param> + /// <returns>If an entry was found for the given <paramref name="key"/>.</returns> public bool TryGetValue(Variant key, out Variant value) { var self = (godot_dictionary)NativeValue; @@ -283,7 +387,7 @@ namespace Godot.Collections /// </summary> /// <param name="array">The array to copy to.</param> /// <param name="arrayIndex">The index to start at.</param> - public void CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex) + void ICollection<KeyValuePair<Variant, Variant>>.CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); @@ -342,6 +446,14 @@ namespace Godot.Collections using (str) return Marshaling.ConvertStringToManaged(str); } + + private void ThrowIfReadOnly() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Dictionary instance is read-only."); + } + } } internal interface IGenericGodotDictionary @@ -433,20 +545,64 @@ namespace Godot.Collections } /// <summary> - /// Duplicates this <see cref="Dictionary{TKey, TValue}"/>. + /// Returns a copy of the <see cref="Dictionary{TKey, TValue}"/>. + /// If <paramref name="deep"/> is <see langword="true"/>, a deep copy is performed: + /// all nested arrays and dictionaries are duplicated and will not be shared with + /// the original dictionary. If <see langword="false"/>, a shallow copy is made and + /// references to the original nested arrays and dictionaries are kept, so that + /// modifying a sub-array or dictionary in the copy will also impact those + /// referenced in the source dictionary. Note that any <see cref="GodotObject"/> derived + /// elements will be shallow copied regardless of the <paramref name="deep"/> + /// setting. /// </summary> - /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param> - /// <returns>A new Godot Dictionary.</returns> public Dictionary<TKey, TValue> Duplicate(bool deep = false) { return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep)); } + /// <summary> + /// Adds entries from <paramref name="dictionary"/> to this dictionary. + /// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/> + /// is <see langword="true"/>. + /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> + /// <param name="dictionary">Dictionary to copy entries from.</param> + /// <param name="overwrite">If duplicate keys should be copied over as well.</param> + public void Merge(Dictionary<TKey, TValue> dictionary, bool overwrite = false) + { + _underlyingDict.Merge(dictionary._underlyingDict, overwrite); + } + + /// <summary> + /// Compares this <see cref="Dictionary{TKey, TValue}"/> against the <paramref name="other"/> + /// <see cref="Dictionary{TKey, TValue}"/> recursively. Returns <see langword="true"/> if the + /// two dictionaries contain the same keys and values. The order of the entries does not matter. + /// otherwise. + /// </summary> + /// <param name="other">The other dictionary to compare against.</param> + /// <returns> + /// <see langword="true"/> if the dictionaries contain the same keys and values, + /// <see langword="false"/> otherwise. + /// </returns> + public bool RecursiveEqual(Dictionary<TKey, TValue> other) + { + return _underlyingDict.RecursiveEqual(other._underlyingDict); + } + // IDictionary<TKey, TValue> /// <summary> /// Returns the value at the given <paramref name="key"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The property is assigned and the dictionary is read-only. + /// </exception> + /// <exception cref="KeyNotFoundException"> + /// The property is retrieved and an entry for <paramref name="key"/> + /// does not exist in the dictionary. + /// </exception> /// <value>The value at the given <paramref name="key"/>.</value> public TValue this[TKey key] { @@ -468,6 +624,8 @@ namespace Godot.Collections } set { + ThrowIfReadOnly(); + using var variantKey = VariantUtils.CreateFrom(key); using var variantValue = VariantUtils.CreateFrom(value); var self = (godot_dictionary)_underlyingDict.NativeValue; @@ -527,10 +685,15 @@ namespace Godot.Collections /// Adds an object <paramref name="value"/> at key <paramref name="key"/> /// to this <see cref="Dictionary{TKey, TValue}"/>. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <param name="key">The key at which to add the object.</param> /// <param name="value">The object to add.</param> public void Add(TKey key, TValue value) { + ThrowIfReadOnly(); + using var variantKey = VariantUtils.CreateFrom(key); var self = (godot_dictionary)_underlyingDict.NativeValue; @@ -556,20 +719,27 @@ namespace Godot.Collections /// <summary> /// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> /// <param name="key">The key of the element to remove.</param> public bool Remove(TKey key) { + ThrowIfReadOnly(); + using var variantKey = VariantUtils.CreateFrom(key); var self = (godot_dictionary)_underlyingDict.NativeValue; return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool(); } /// <summary> - /// Gets the object at the given <paramref name="key"/>. + /// Gets the value for the given <paramref name="key"/> in the dictionary. + /// Returns <see langword="true"/> if an entry for the given key exists in + /// the dictionary; otherwise, returns <see langword="false"/>. /// </summary> /// <param name="key">The key of the element to get.</param> /// <param name="value">The value at the given <paramref name="key"/>.</param> - /// <returns>If an object was found for the given <paramref name="key"/>.</returns> + /// <returns>If an entry was found for the given <paramref name="key"/>.</returns> public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { using var variantKey = VariantUtils.CreateFrom(key); @@ -592,14 +762,31 @@ namespace Godot.Collections /// <returns>The number of elements.</returns> public int Count => _underlyingDict.Count; - bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false; + /// <summary> + /// Returns <see langword="true"/> if the dictionary is read-only. + /// See <see cref="MakeReadOnly"/>. + /// </summary> + public bool IsReadOnly => _underlyingDict.IsReadOnly; + + /// <summary> + /// Makes the <see cref="Dictionary{TKey, TValue}"/> read-only, i.e. disabled + /// modying of the dictionary's elements. Does not apply to nested content, + /// e.g. content of nested dictionaries. + /// </summary> + public void MakeReadOnly() + { + _underlyingDict.MakeReadOnly(); + } void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value); /// <summary> - /// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>. + /// Clears the dictionary, removing all entries from it. /// </summary> + /// <exception cref="InvalidOperationException"> + /// The dictionary is read-only. + /// </exception> public void Clear() => _underlyingDict.Clear(); bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) @@ -625,7 +812,7 @@ namespace Godot.Collections /// </summary> /// <param name="array">The array to copy to.</param> /// <param name="arrayIndex">The index to start at.</param> - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) + void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); @@ -649,6 +836,8 @@ namespace Godot.Collections bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) { + ThrowIfReadOnly(); + using var variantKey = VariantUtils.CreateFrom(item.Key); var self = (godot_dictionary)_underlyingDict.NativeValue; bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self, @@ -698,5 +887,13 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Dictionary<TKey, TValue>(Variant from) => from.AsGodotDictionary<TKey, TValue>(); + + private void ThrowIfReadOnly() + { + if (IsReadOnly) + { + throw new InvalidOperationException("Dictionary instance is read-only."); + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs index e6cb4171a7..e7370b2b05 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -9,7 +9,10 @@ namespace Godot internal static GodotTaskScheduler DefaultGodotTaskScheduler; internal static void InitializeDefaultGodotTaskScheduler() - => DefaultGodotTaskScheduler = new GodotTaskScheduler(); + { + DefaultGodotTaskScheduler?.Dispose(); + DefaultGodotTaskScheduler = new GodotTaskScheduler(); + } public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs index 421b588560..53292e10cf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs @@ -28,7 +28,7 @@ namespace Godot try { - isStdoutVerbose = OS.IsStdoutVerbose(); + isStdoutVerbose = OS.IsStdOutVerbose(); } catch (ObjectDisposedException) { @@ -43,9 +43,9 @@ namespace Godot // like StringName, NodePath, Godot.Collections.Array/Dictionary, etc. // The Godot Object Dispose() method may need any of the later instances. - foreach (WeakReference<Object> item in GodotObjectInstances.Keys) + foreach (WeakReference<GodotObject> item in GodotObjectInstances.Keys) { - if (item.TryGetTarget(out Object? self)) + if (item.TryGetTarget(out GodotObject? self)) self.Dispose(); } @@ -60,15 +60,15 @@ namespace Godot } // ReSharper disable once RedundantNameQualifier - private static ConcurrentDictionary<WeakReference<Godot.Object>, byte> GodotObjectInstances { get; } = + private static ConcurrentDictionary<WeakReference<GodotObject>, byte> GodotObjectInstances { get; } = new(); private static ConcurrentDictionary<WeakReference<IDisposable>, byte> OtherInstances { get; } = new(); - public static WeakReference<Object> RegisterGodotObject(Object godotObject) + public static WeakReference<GodotObject> RegisterGodotObject(GodotObject godotObject) { - var weakReferenceToSelf = new WeakReference<Object>(godotObject); + var weakReferenceToSelf = new WeakReference<GodotObject>(godotObject); GodotObjectInstances.TryAdd(weakReferenceToSelf, 0); return weakReferenceToSelf; } @@ -80,7 +80,7 @@ namespace Godot return weakReferenceToSelf; } - public static void UnregisterGodotObject(Object godotObject, WeakReference<Object> weakReferenceToSelf) + public static void UnregisterGodotObject(GodotObject godotObject, WeakReference<GodotObject> weakReferenceToSelf) { if (!GodotObjectInstances.TryRemove(weakReferenceToSelf, out _)) throw new ArgumentException("Godot Object not registered.", nameof(weakReferenceToSelf)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs new file mode 100644 index 0000000000..6c90c17078 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs @@ -0,0 +1,86 @@ +using System; +using Godot.NativeInterop; + +namespace Godot +{ + public partial class GodotObject + { + /// <summary> + /// Returns the <see cref="GodotObject"/> that corresponds to <paramref name="instanceId"/>. + /// All Objects have a unique instance ID. See also <see cref="GetInstanceId"/>. + /// </summary> + /// <example> + /// <code> + /// public partial class MyNode : Node + /// { + /// public string Foo { get; set; } = "bar"; + /// + /// public override void _Ready() + /// { + /// ulong id = GetInstanceId(); + /// var inst = (MyNode)InstanceFromId(Id); + /// GD.Print(inst.Foo); // Prints bar + /// } + /// } + /// </code> + /// </example> + /// <param name="instanceId">Instance ID of the Object to retrieve.</param> + /// <returns>The <see cref="GodotObject"/> instance.</returns> + public static GodotObject InstanceFromId(ulong instanceId) + { + return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId)); + } + + /// <summary> + /// Returns <see langword="true"/> if the <see cref="GodotObject"/> that corresponds + /// to <paramref name="id"/> is a valid object (e.g. has not been deleted from + /// memory). All Objects have a unique instance ID. + /// </summary> + /// <param name="id">The Object ID to check.</param> + /// <returns>If the instance with the given ID is a valid object.</returns> + public static bool IsInstanceIdValid(ulong id) + { + return IsInstanceValid(InstanceFromId(id)); + } + + /// <summary> + /// Returns <see langword="true"/> if <paramref name="instance"/> is a + /// valid <see cref="GodotObject"/> (e.g. has not been deleted from memory). + /// </summary> + /// <param name="instance">The instance to check.</param> + /// <returns>If the instance is a valid object.</returns> + public static bool IsInstanceValid(GodotObject instance) + { + return instance != null && instance.NativeInstance != IntPtr.Zero; + } + + /// <summary> + /// Returns a weak reference to an object, or <see langword="null"/> + /// if the argument is invalid. + /// A weak reference to an object is not enough to keep the object alive: + /// when the only remaining references to a referent are weak references, + /// garbage collection is free to destroy the referent and reuse its memory + /// for something else. However, until the object is actually destroyed the + /// weak reference may return the object even if there are no strong references + /// to it. + /// </summary> + /// <param name="obj">The object.</param> + /// <returns> + /// The <see cref="WeakRef"/> reference to the object or <see langword="null"/>. + /// </returns> + public static WeakRef WeakRef(GodotObject obj) + { + if (!IsInstanceValid(obj)) + return null; + + NativeFuncs.godotsharp_weakref(GetPtr(obj), out godot_ref weakRef); + using (weakRef) + { + if (weakRef.IsNull) + return null; + + return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef.Reference); + } + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs deleted file mode 100644 index 4094ceeb22..0000000000 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Godot.NativeInterop; - -namespace Godot -{ - public partial class Object - { - /// <summary> - /// Returns whether <paramref name="instance"/> is a valid object - /// (e.g. has not been deleted from memory). - /// </summary> - /// <param name="instance">The instance to check.</param> - /// <returns>If the instance is a valid object.</returns> - public static bool IsInstanceValid(Object instance) - { - return instance != null && instance.NativeInstance != IntPtr.Zero; - } - - /// <summary> - /// Returns a weak reference to an object, or <see langword="null"/> - /// if the argument is invalid. - /// A weak reference to an object is not enough to keep the object alive: - /// when the only remaining references to a referent are weak references, - /// garbage collection is free to destroy the referent and reuse its memory - /// for something else. However, until the object is actually destroyed the - /// weak reference may return the object even if there are no strong references - /// to it. - /// </summary> - /// <param name="obj">The object.</param> - /// <returns> - /// The <see cref="WeakRef"/> reference to the object or <see langword="null"/>. - /// </returns> - public static WeakRef WeakRef(Object obj) - { - if (!IsInstanceValid(obj)) - return null; - - NativeFuncs.godotsharp_weakref(GetPtr(obj), out godot_ref weakRef); - using (weakRef) - { - if (weakRef.IsNull) - return null; - - return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef.Reference); - } - } - } -} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index f3f124d5ad..9425b7424c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using Godot.NativeInterop; namespace Godot @@ -10,36 +11,45 @@ namespace Godot public static partial class GD { /// <summary> - /// Decodes a byte array back to a <c>Variant</c> value. - /// If <paramref name="allowObjects"/> is <see langword="true"/> decoding objects is allowed. - /// - /// WARNING: Deserialized object can contain code which gets executed. - /// Do not set <paramref name="allowObjects"/> to <see langword="true"/> - /// if the serialized object comes from untrusted sources to avoid - /// potential security threats (remote code execution). + /// Decodes a byte array back to a <see cref="Variant"/> value, without decoding objects. + /// Note: If you need object deserialization, see <see cref="BytesToVarWithObjects"/>. /// </summary> - /// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param> - /// <param name="allowObjects">If objects should be decoded.</param> - /// <returns>The decoded <c>Variant</c>.</returns> - public static Variant BytesToVar(Span<byte> bytes, bool allowObjects = false) + /// <param name="bytes">Byte array that will be decoded to a <see cref="Variant"/>.</param> + /// <returns>The decoded <see cref="Variant"/>.</returns> + public static Variant BytesToVar(Span<byte> bytes) + { + using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes); + NativeFuncs.godotsharp_bytes_to_var(varBytes, godot_bool.False, out godot_variant ret); + return Variant.CreateTakingOwnershipOfDisposableValue(ret); + } + + /// <summary> + /// Decodes a byte array back to a <see cref="Variant"/> value. Decoding objects is allowed. + /// Warning: Deserialized object can contain code which gets executed. Do not use this + /// option if the serialized object comes from untrusted sources to avoid potential security + /// threats (remote code execution). + /// </summary> + /// <param name="bytes">Byte array that will be decoded to a <see cref="Variant"/>.</param> + /// <returns>The decoded <see cref="Variant"/>.</returns> + public static Variant BytesToVarWithObjects(Span<byte> bytes) { using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes); - NativeFuncs.godotsharp_bytes_to_var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret); + NativeFuncs.godotsharp_bytes_to_var(varBytes, godot_bool.True, out godot_variant ret); return Variant.CreateTakingOwnershipOfDisposableValue(ret); } /// <summary> - /// Converts from a <c>Variant</c> type to another in the best way possible. + /// Converts <paramref name="what"/> to <paramref name="type"/> in the best way possible. /// The <paramref name="type"/> parameter uses the <see cref="Variant.Type"/> values. /// </summary> /// <example> /// <code> - /// var a = new Vector2(1, 0); - /// // Prints 1 - /// GD.Print(a.Length()); - /// var b = GD.Convert(a, Variant.Type.String) - /// // Prints 6 as "(1, 0)" is 6 characters - /// GD.Print(b.Length); + /// Variant a = new Godot.Collections.Array { 4, 2.5, 1.2 }; + /// GD.Print(a.VariantType == Variant.Type.Array); // Prints true + /// + /// var b = GD.Convert(a, Variant.Type.PackedByteArray); + /// GD.Print(b); // Prints [4, 2, 1] + /// GD.Print(b.VariantType == Variant.Type.Array); // Prints false /// </code> /// </example> /// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns> @@ -49,18 +59,8 @@ namespace Godot return Variant.CreateTakingOwnershipOfDisposableValue(ret); } - private static string[] GetPrintParams(object[] parameters) - { - if (parameters == null) - { - return new[] { "null" }; - } - - return Array.ConvertAll(parameters, x => x?.ToString() ?? "null"); - } - /// <summary> - /// Returns the integer hash of the variable passed. + /// Returns the integer hash of the passed <paramref name="var"/>. /// </summary> /// <example> /// <code> @@ -75,32 +75,6 @@ namespace Godot } /// <summary> - /// Returns the <see cref="Object"/> that corresponds to <paramref name="instanceId"/>. - /// All Objects have a unique instance ID. - /// </summary> - /// <example> - /// <code> - /// public class MyNode : Node - /// { - /// public string foo = "bar"; - /// - /// public override void _Ready() - /// { - /// ulong id = GetInstanceId(); - /// var inst = (MyNode)GD.InstanceFromId(Id); - /// GD.Print(inst.foo); // Prints bar - /// } - /// } - /// </code> - /// </example> - /// <param name="instanceId">Instance ID of the Object to retrieve.</param> - /// <returns>The <see cref="Object"/> instance.</returns> - public static Object InstanceFromId(ulong instanceId) - { - return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId)); - } - - /// <summary> /// Loads a resource from the filesystem located at <paramref name="path"/>. /// The resource is loaded on the method call (unless it's referenced already /// elsewhere, e.g. in another script or in the scene), which might cause slight delay, @@ -154,57 +128,96 @@ namespace Godot return ResourceLoader.Load<T>(path); } - /// <summary> - /// Pushes an error message to Godot's built-in debugger and to the OS terminal. - /// - /// Note: Errors printed this way will not pause project execution. - /// </summary> - /// <example> - /// <code> - /// GD.PushError("test_error"); // Prints "test error" to debugger and terminal as error call - /// </code> - /// </example> - /// <param name="message">Error message.</param> - public static void PushError(string message) + private static string AppendPrintParams(object[] parameters) { - using var godotStr = Marshaling.ConvertStringToNative(message); - NativeFuncs.godotsharp_pusherror(godotStr); + if (parameters == null) + { + return "null"; + } + + var sb = new StringBuilder(); + for (int i = 0; i < parameters.Length; i++) + { + sb.Append(parameters[i]?.ToString() ?? "null"); + } + return sb.ToString(); + } + + private static string AppendPrintParams(char separator, object[] parameters) + { + if (parameters == null) + { + return "null"; + } + + var sb = new StringBuilder(); + for (int i = 0; i < parameters.Length; i++) + { + if (i != 0) + sb.Append(separator); + sb.Append(parameters[i]?.ToString() ?? "null"); + } + return sb.ToString(); } /// <summary> - /// Pushes a warning message to Godot's built-in debugger and to the OS terminal. + /// Prints a message to the console. + /// + /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> + /// to print error and warning messages instead of <see cref="Print(string)"/>. + /// This distinguishes them from print messages used for debugging purposes, + /// while also displaying a stack trace when an error or warning is printed. /// </summary> - /// <example> - /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call - /// </example> - /// <param name="message">Warning message.</param> - public static void PushWarning(string message) + /// <param name="what">Message that will be printed.</param> + public static void Print(string what) { - using var godotStr = Marshaling.ConvertStringToNative(message); - NativeFuncs.godotsharp_pushwarning(godotStr); + using var godotStr = Marshaling.ConvertStringToNative(what); + NativeFuncs.godotsharp_print(godotStr); } /// <summary> /// Converts one or more arguments of any type to string in the best way possible /// and prints them to the console. /// - /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> + /// Note: Consider using <see cref="PushError(object[])"/> and <see cref="PushWarning(object[])"/> /// to print error and warning messages instead of <see cref="Print(object[])"/>. /// This distinguishes them from print messages used for debugging purposes, /// while also displaying a stack trace when an error or warning is printed. /// </summary> /// <example> /// <code> - /// var a = new int[] { 1, 2, 3 }; + /// var a = new Godot.Collections.Array { 1, 2, 3 }; /// GD.Print("a", "b", a); // Prints ab[1, 2, 3] /// </code> /// </example> /// <param name="what">Arguments that will be printed.</param> public static void Print(params object[] what) { - string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); - NativeFuncs.godotsharp_print(godotStr); + Print(AppendPrintParams(what)); + } + + /// <summary> + /// Prints a message to the console. + /// The following BBCode tags are supported: b, i, u, s, indent, code, url, center, + /// right, color, bgcolor, fgcolor. + /// Color tags only support named colors such as <c>red</c>, not hexadecimal color codes. + /// Unsupported tags will be left as-is in standard output. + /// When printing to standard output, the supported subset of BBCode is converted to + /// ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes + /// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary + /// across terminal emulators, especially for italic and strikethrough. + /// + /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> + /// to print error and warning messages instead of <see cref="Print(string)"/> or + /// <see cref="PrintRich(string)"/>. + /// This distinguishes them from print messages used for debugging purposes, + /// while also displaying a stack trace when an error or warning is printed. + /// </summary> + /// <param name="what">Message that will be printed.</param> + public static void PrintRich(string what) + { + using var godotStr = Marshaling.ConvertStringToNative(what); + NativeFuncs.godotsharp_print_rich(godotStr); } /// <summary> @@ -219,7 +232,7 @@ namespace Godot /// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary /// across terminal emulators, especially for italic and strikethrough. /// - /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> + /// Note: Consider using <see cref="PushError(object[])"/> and <see cref="PushWarning(object[])"/> /// to print error and warning messages instead of <see cref="Print(object[])"/> or /// <see cref="PrintRich(object[])"/>. /// This distinguishes them from print messages used for debugging purposes, @@ -227,23 +240,23 @@ namespace Godot /// </summary> /// <example> /// <code> - /// GD.PrintRich("[b]Hello world![/b]"); // Prints out "Hello world!" in bold. + /// GD.PrintRich("[code][b]Hello world![/b][/code]"); // Prints out: [b]Hello world![/b] /// </code> /// </example> /// <param name="what">Arguments that will be printed.</param> public static void PrintRich(params object[] what) { - string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); - NativeFuncs.godotsharp_print_rich(godotStr); + PrintRich(AppendPrintParams(what)); } /// <summary> - /// Prints the current stack trace information to the console. + /// Prints a message to standard error line. /// </summary> - public static void PrintStack() + /// <param name="what">Message that will be printed.</param> + public static void PrintErr(string what) { - Print(System.Environment.StackTrace); + using var godotStr = Marshaling.ConvertStringToNative(what); + NativeFuncs.godotsharp_printerr(godotStr); } /// <summary> @@ -257,31 +270,36 @@ namespace Godot /// <param name="what">Arguments that will be printed.</param> public static void PrintErr(params object[] what) { - string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); - NativeFuncs.godotsharp_printerr(godotStr); + PrintErr(AppendPrintParams(what)); } /// <summary> - /// Prints one or more arguments to strings in the best way possible to console. - /// No newline is added at the end. - /// - /// Note: Due to limitations with Godot's built-in console, this only prints to the terminal. - /// If you need to print in the editor, use another method, such as <see cref="Print(object[])"/>. + /// Prints a message to the OS terminal. + /// Unlike <see cref="Print(string)"/>, no newline is added at the end. + /// </summary> + /// <param name="what">Message that will be printed.</param> + public static void PrintRaw(string what) + { + using var godotStr = Marshaling.ConvertStringToNative(what); + NativeFuncs.godotsharp_printraw(godotStr); + } + + /// <summary> + /// Prints one or more arguments to strings in the best way possible to the OS terminal. + /// Unlike <see cref="Print(object[])"/>, no newline is added at the end. /// </summary> /// <example> /// <code> /// GD.PrintRaw("A"); /// GD.PrintRaw("B"); - /// // Prints AB + /// GD.PrintRaw("C"); + /// // Prints ABC to terminal /// </code> /// </example> /// <param name="what">Arguments that will be printed.</param> public static void PrintRaw(params object[] what) { - string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); - NativeFuncs.godotsharp_printraw(godotStr); + PrintRaw(AppendPrintParams(what)); } /// <summary> @@ -295,8 +313,8 @@ namespace Godot /// <param name="what">Arguments that will be printed.</param> public static void PrintS(params object[] what) { - string str = string.Join(' ', GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); + string message = AppendPrintParams(' ', what); + using var godotStr = Marshaling.ConvertStringToNative(message); NativeFuncs.godotsharp_prints(godotStr); } @@ -311,12 +329,74 @@ namespace Godot /// <param name="what">Arguments that will be printed.</param> public static void PrintT(params object[] what) { - string str = string.Join('\t', GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); + string message = AppendPrintParams('\t', what); + using var godotStr = Marshaling.ConvertStringToNative(message); NativeFuncs.godotsharp_printt(godotStr); } /// <summary> + /// Pushes an error message to Godot's built-in debugger and to the OS terminal. + /// + /// Note: Errors printed this way will not pause project execution. + /// </summary> + /// <example> + /// <code> + /// GD.PushError("test error"); // Prints "test error" to debugger and terminal as error call + /// </code> + /// </example> + /// <param name="message">Error message.</param> + public static void PushError(string message) + { + using var godotStr = Marshaling.ConvertStringToNative(message); + NativeFuncs.godotsharp_pusherror(godotStr); + } + + /// <summary> + /// Pushes an error message to Godot's built-in debugger and to the OS terminal. + /// + /// Note: Errors printed this way will not pause project execution. + /// </summary> + /// <example> + /// <code> + /// GD.PushError("test_error"); // Prints "test error" to debugger and terminal as error call + /// </code> + /// </example> + /// <param name="what">Arguments that form the error message.</param> + public static void PushError(params object[] what) + { + PushError(AppendPrintParams(what)); + } + + /// <summary> + /// Pushes a warning message to Godot's built-in debugger and to the OS terminal. + /// </summary> + /// <example> + /// <code> + /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call + /// </code> + /// </example> + /// <param name="message">Warning message.</param> + public static void PushWarning(string message) + { + using var godotStr = Marshaling.ConvertStringToNative(message); + NativeFuncs.godotsharp_pushwarning(godotStr); + } + + /// <summary> + /// Pushes a warning message to Godot's built-in debugger and to the OS terminal. + /// </summary> + /// <example> + /// <code> + /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call + /// </code> + /// </example> + /// <param name="what">Arguments that form the warning message.</param> + public static void PushWarning(params object[] what) + { + PushWarning(AppendPrintParams(what)); + } + + /// <summary> /// Returns a random floating point value between <c>0.0</c> and <c>1.0</c> (inclusive). /// </summary> /// <example> @@ -331,7 +411,9 @@ namespace Godot } /// <summary> - /// Returns a normally-distributed pseudo-random number, using Box-Muller transform with the specified <c>mean</c> and a standard <c>deviation</c>. + /// Returns a normally-distributed pseudo-random floating point value + /// using Box-Muller transform with the specified <pararmref name="mean"/> + /// and a standard <paramref name="deviation"/>. /// This is also called Gaussian distribution. /// </summary> /// <returns>A random normally-distributed <see langword="float"/> number.</returns> @@ -342,7 +424,8 @@ namespace Godot /// <summary> /// Returns a random unsigned 32-bit integer. - /// Use remainder to obtain a random value in the interval <c>[0, N - 1]</c> (where N is smaller than 2^32). + /// Use remainder to obtain a random value in the interval <c>[0, N - 1]</c> + /// (where N is smaller than 2^32). /// </summary> /// <example> /// <code> @@ -360,11 +443,11 @@ namespace Godot /// <summary> /// Randomizes the seed (or the internal state) of the random number generator. - /// Current implementation reseeds using a number based on time. + /// The current implementation uses a number based on the device's time. /// /// Note: This method is called automatically when the project is run. - /// If you need to fix the seed to have reproducible results, use <see cref="Seed(ulong)"/> - /// to initialize the random number generator. + /// If you need to fix the seed to have consistent, reproducible results, + /// use <see cref="Seed(ulong)"/> to initialize the random number generator. /// </summary> public static void Randomize() { @@ -372,12 +455,13 @@ namespace Godot } /// <summary> - /// Returns a random floating point value on the interval between <paramref name="from"/> + /// Returns a random floating point value between <paramref name="from"/> /// and <paramref name="to"/> (inclusive). /// </summary> /// <example> /// <code> - /// GD.PrintS(GD.RandRange(-10.0, 10.0), GD.RandRange(-10.0, 10.0)); // Prints e.g. -3.844535 7.45315 + /// GD.RandRange(0.0, 20.5); // Returns e.g. 7.45315 + /// GD.RandRange(-10.0, 10.0); // Returns e.g. -3.844535 /// </code> /// </example> /// <returns>A random <see langword="double"/> number inside the given range.</returns> @@ -393,8 +477,8 @@ namespace Godot /// </summary> /// <example> /// <code> - /// GD.Print(GD.RandRange(0, 1)); // Prints 0 or 1 - /// GD.Print(GD.RandRange(-10, 1000)); // Prints any number from -10 to 1000 + /// GD.RandRange(0, 1); // Returns either 0 or 1 + /// GD.RandRange(-10, 1000); // Returns random integer between -10 and 1000 /// </code> /// </example> /// <returns>A random <see langword="int"/> number inside the given range.</returns> @@ -404,8 +488,18 @@ namespace Godot } /// <summary> - /// Returns a random unsigned 32-bit integer, using the given <paramref name="seed"/>. + /// Given a <paramref name="seed"/>, returns a randomized <see langword="uint"/> + /// value. The <paramref name="seed"/> may be modified. + /// Passing the same <paramref name="seed"/> consistently returns the same value. + /// + /// Note: "Seed" here refers to the internal state of the pseudo random number + /// generator, currently implemented as a 64 bit integer. /// </summary> + /// <example> + /// <code> + /// var a = GD.RandFromSeed(4); + /// </code> + /// </example> /// <param name="seed"> /// Seed to use to generate the random number. /// If a different seed is used, its value will be modified. @@ -418,7 +512,8 @@ namespace Godot /// <summary> /// Returns a <see cref="IEnumerable{T}"/> that iterates from - /// <c>0</c> to <paramref name="end"/> in steps of <c>1</c>. + /// <c>0</c> (inclusive) to <paramref name="end"/> (exclusive) + /// in steps of <c>1</c>. /// </summary> /// <param name="end">The last index.</param> public static IEnumerable<int> Range(int end) @@ -428,7 +523,8 @@ namespace Godot /// <summary> /// Returns a <see cref="IEnumerable{T}"/> that iterates from - /// <paramref name="start"/> to <paramref name="end"/> in steps of <c>1</c>. + /// <paramref name="start"/> (inclusive) to <paramref name="end"/> (exclusive) + /// in steps of <c>1</c>. /// </summary> /// <param name="start">The first index.</param> /// <param name="end">The last index.</param> @@ -439,13 +535,21 @@ namespace Godot /// <summary> /// Returns a <see cref="IEnumerable{T}"/> that iterates from - /// <paramref name="start"/> to <paramref name="end"/> in steps of <paramref name="step"/>. + /// <paramref name="start"/> (inclusive) to <paramref name="end"/> (exclusive) + /// in steps of <paramref name="step"/>. + /// The argument <paramref name="step"/> can be negative, but not <c>0</c>. /// </summary> + /// <exception cref="ArgumentException"> + /// <paramref name="step"/> is 0. + /// </exception> /// <param name="start">The first index.</param> /// <param name="end">The last index.</param> /// <param name="step">The amount by which to increment the index on each iteration.</param> public static IEnumerable<int> Range(int start, int end, int step) { + if (step == 0) + throw new ArgumentException("step cannot be 0.", nameof(step)); + if (end < start && step > 0) yield break; @@ -465,8 +569,20 @@ namespace Godot } /// <summary> - /// Sets seed for the random number generator. + /// Sets seed for the random number generator to <paramref name="seed"/>. + /// Setting the seed manually can ensure consistent, repeatable results for + /// most random functions. /// </summary> + /// <example> + /// <code> + /// ulong mySeed = (ulong)GD.Hash("Godot Rocks"); + /// GD.Seed(mySeed); + /// var a = GD.Randf() + GD.Randi(); + /// GD.Seed(mySeed); + /// var b = GD.Randf() + GD.Randi(); + /// // a and b are now identical + /// </code> + /// </example> /// <param name="seed">Seed that will be used.</param> public static void Seed(ulong seed) { @@ -474,26 +590,14 @@ namespace Godot } /// <summary> - /// Converts one or more arguments of any type to string in the best way possible. - /// </summary> - /// <param name="what">Arguments that will converted to string.</param> - /// <returns>The string formed by the given arguments.</returns> - public static string Str(params Variant[] what) - { - using var whatGodot = new Godot.Collections.Array(what); - NativeFuncs.godotsharp_str((godot_array)whatGodot.NativeValue, out godot_string ret); - using (ret) - return Marshaling.ConvertStringToManaged(ret); - } - - /// <summary> - /// Converts a formatted string that was returned by <see cref="VarToStr(Variant)"/> to the original value. + /// Converts a formatted string that was returned by <see cref="VarToStr(Variant)"/> + /// to the original value. /// </summary> /// <example> /// <code> - /// string a = "{\"a\": 1, \"b\": 2 }"; - /// var b = (Godot.Collections.Dictionary)GD.StrToVar(a); - /// GD.Print(b["a"]); // Prints 1 + /// string a = "{ \"a\": 1, \"b\": 2 }"; // a is a string + /// var b = GD.StrToVar(a).AsGodotDictionary(); // b is a Dictionary + /// GD.Print(b["a"]); // Prints 1 /// </code> /// </example> /// <param name="str">String that will be converted to Variant.</param> @@ -506,38 +610,49 @@ namespace Godot } /// <summary> - /// Encodes a <c>Variant</c> value to a byte array. - /// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed - /// (and can potentially include code). - /// Deserialization can be done with <see cref="BytesToVar(Span{byte}, bool)"/>. + /// Encodes a <see cref="Variant"/> value to a byte array, without encoding objects. + /// Deserialization can be done with <see cref="BytesToVar"/>. + /// Note: If you need object serialization, see <see cref="VarToBytesWithObjects"/>. + /// </summary> + /// <param name="var"><see cref="Variant"/> that will be encoded.</param> + /// <returns>The <see cref="Variant"/> encoded as an array of bytes.</returns> + public static byte[] VarToBytes(Variant var) + { + NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, godot_bool.False, out var varBytes); + using (varBytes) + return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes); + } + + /// <summary> + /// Encodes a <see cref="Variant"/>. Encoding objects is allowed (and can potentially + /// include executable code). Deserialization can be done with <see cref="BytesToVarWithObjects"/>. /// </summary> - /// <param name="var">Variant that will be encoded.</param> - /// <param name="fullObjects">If objects should be serialized.</param> - /// <returns>The <c>Variant</c> encoded as an array of bytes.</returns> - public static byte[] VarToBytes(Variant var, bool fullObjects = false) + /// <param name="var"><see cref="Variant"/> that will be encoded.</param> + /// <returns>The <see cref="Variant"/> encoded as an array of bytes.</returns> + public static byte[] VarToBytesWithObjects(Variant var) { - NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, fullObjects.ToGodotBool(), out var varBytes); + NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, godot_bool.True, out var varBytes); using (varBytes) return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes); } /// <summary> - /// Converts a <c>Variant</c> <paramref name="var"/> to a formatted string that + /// Converts a <see cref="Variant"/> <paramref name="var"/> to a formatted string that /// can later be parsed using <see cref="StrToVar(string)"/>. /// </summary> /// <example> /// <code> /// var a = new Godot.Collections.Dictionary { ["a"] = 1, ["b"] = 2 }; /// GD.Print(GD.VarToStr(a)); - /// // Prints + /// // Prints: /// // { - /// // "a": 1, - /// // "b": 2 + /// // "a": 1, + /// // "b": 2 /// // } /// </code> /// </example> /// <param name="var">Variant that will be converted to string.</param> - /// <returns>The <c>Variant</c> encoded as a string.</returns> + /// <returns>The <see cref="Variant"/> encoded as a string.</returns> public static string VarToStr(Variant var) { NativeFuncs.godotsharp_var_to_str((godot_variant)var.NativeVar, out godot_string ret); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs index 60ee6eb6f4..b9a5ac82d1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs @@ -5,20 +5,20 @@ using Godot.NativeInterop; namespace Godot { - public partial class Object : IDisposable + public partial class GodotObject : IDisposable { private bool _disposed = false; - private static readonly Type CachedType = typeof(Object); + private static readonly Type CachedType = typeof(GodotObject); internal IntPtr NativePtr; private bool _memoryOwn; - private WeakReference<Object> _weakReferenceToSelf; + private WeakReference<GodotObject> _weakReferenceToSelf; /// <summary> - /// Constructs a new <see cref="Object"/>. + /// Constructs a new <see cref="GodotObject"/>. /// </summary> - public Object() : this(false) + public GodotObject() : this(false) { unsafe { @@ -49,17 +49,17 @@ namespace Godot _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this); } - internal Object(bool memoryOwn) + internal GodotObject(bool memoryOwn) { _memoryOwn = memoryOwn; } /// <summary> - /// The pointer to the native instance of this <see cref="Object"/>. + /// The pointer to the native instance of this <see cref="GodotObject"/>. /// </summary> public IntPtr NativeInstance => NativePtr; - internal static IntPtr GetPtr(Object instance) + internal static IntPtr GetPtr(GodotObject instance) { if (instance == null) return IntPtr.Zero; @@ -75,13 +75,13 @@ namespace Godot return instance.NativePtr; } - ~Object() + ~GodotObject() { Dispose(false); } /// <summary> - /// Disposes of this <see cref="Object"/>. + /// Disposes of this <see cref="GodotObject"/>. /// </summary> public void Dispose() { @@ -90,7 +90,7 @@ namespace Godot } /// <summary> - /// Disposes implementation of this <see cref="Object"/>. + /// Disposes implementation of this <see cref="GodotObject"/>. /// </summary> protected virtual void Dispose(bool disposing) { @@ -129,7 +129,7 @@ namespace Godot } /// <summary> - /// Converts this <see cref="Object"/> to a string. + /// Converts this <see cref="GodotObject"/> to a string. /// </summary> /// <returns>A string representation of this object.</returns> public override string ToString() @@ -166,7 +166,7 @@ namespace Godot /// A <see cref="SignalAwaiter"/> that completes when /// <paramref name="source"/> emits the <paramref name="signal"/>. /// </returns> - public SignalAwaiter ToSignal(Object source, StringName signal) + public SignalAwaiter ToSignal(GodotObject source, StringName signal) { return new SignalAwaiter(source, signal, this); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.exceptions.cs index 0fcc4ee01b..a7640043ce 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.exceptions.cs @@ -5,7 +5,7 @@ using System.Text; namespace Godot { - public partial class Object + public partial class GodotObject { public class NativeMemberNotFoundException : Exception { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs index 1b599beab5..027eab30fc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs @@ -1,13 +1,13 @@ +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; namespace Godot { - public class GodotSynchronizationContext : SynchronizationContext + public sealed class GodotSynchronizationContext : SynchronizationContext, IDisposable { - private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = - new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); + private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = new(); public override void Post(SendOrPostCallback d, object state) { @@ -24,5 +24,10 @@ namespace Godot workItem.Key(workItem.Value); } } + + public void Dispose() + { + _queue.Dispose(); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs index 408bed71b2..f6c36455b2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs @@ -10,7 +10,7 @@ namespace Godot /// GodotTaskScheduler contains a linked list of tasks to perform as a queue. Methods /// within the class are used to control the queue and perform the contained tasks. /// </summary> - public class GodotTaskScheduler : TaskScheduler + public sealed class GodotTaskScheduler : TaskScheduler, IDisposable { /// <summary> /// The current synchronization context. @@ -108,5 +108,10 @@ namespace Godot } } } + + public void Dispose() + { + Context.Dispose(); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 137a42a6de..ca0032df73 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Godot { @@ -35,15 +36,18 @@ namespace Godot public const real_t NaN = real_t.NaN; // 0.0174532924f and 0.0174532925199433 - private const real_t _degToRadConst = (real_t)0.0174532925199432957692369077M; + private const float _degToRadConstF = (float)0.0174532925199432957692369077M; + private const double _degToRadConstD = (double)0.0174532925199432957692369077M; // 57.29578f and 57.2957795130823 - private const real_t _radToDegConst = (real_t)57.295779513082320876798154814M; + private const float _radToDegConstF = (float)57.295779513082320876798154814M; + private const double _radToDegConstD = (double)57.295779513082320876798154814M; /// <summary> /// Returns the absolute value of <paramref name="s"/> (i.e. positive value). /// </summary> /// <param name="s">The input number.</param> /// <returns>The absolute value of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Abs(int s) { return Math.Abs(s); @@ -54,7 +58,19 @@ namespace Godot /// </summary> /// <param name="s">The input number.</param> /// <returns>The absolute value of <paramref name="s"/>.</returns> - public static real_t Abs(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Abs(float s) + { + return Math.Abs(s); + } + + /// <summary> + /// Returns the absolute value of <paramref name="s"/> (i.e. positive value). + /// </summary> + /// <param name="s">The input number.</param> + /// <returns>The absolute value of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Abs(double s) { return Math.Abs(s); } @@ -67,9 +83,38 @@ namespace Godot /// <returns> /// An angle that would result in the given cosine value. On the range <c>0</c> to <c>Tau/2</c>. /// </returns> - public static real_t Acos(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Acos(float s) { - return (real_t)Math.Acos(s); + return MathF.Acos(s); + } + + /// <summary> + /// Returns the arc cosine of <paramref name="s"/> in radians. + /// Use to get the angle of cosine <paramref name="s"/>. + /// </summary> + /// <param name="s">The input cosine value. Must be on the range of -1.0 to 1.0.</param> + /// <returns> + /// An angle that would result in the given cosine value. On the range <c>0</c> to <c>Tau/2</c>. + /// </returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Acos(double s) + { + return Math.Acos(s); + } + + /// <summary> + /// Returns the arc sine of <paramref name="s"/> in radians. + /// Use to get the angle of sine <paramref name="s"/>. + /// </summary> + /// <param name="s">The input sine value. Must be on the range of -1.0 to 1.0.</param> + /// <returns> + /// An angle that would result in the given sine value. On the range <c>-Tau/4</c> to <c>Tau/4</c>. + /// </returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Asin(float s) + { + return MathF.Asin(s); } /// <summary> @@ -80,9 +125,10 @@ namespace Godot /// <returns> /// An angle that would result in the given sine value. On the range <c>-Tau/4</c> to <c>Tau/4</c>. /// </returns> - public static real_t Asin(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Asin(double s) { - return (real_t)Math.Asin(s); + return Math.Asin(s); } /// <summary> @@ -90,15 +136,51 @@ namespace Godot /// Use to get the angle of tangent <paramref name="s"/>. /// /// The method cannot know in which quadrant the angle should fall. - /// See <see cref="Atan2(real_t, real_t)"/> if you have both <c>y</c> and <c>x</c>. + /// See <see cref="Atan2(float, float)"/> if you have both <c>y</c> and <c>x</c>. /// </summary> /// <param name="s">The input tangent value.</param> /// <returns> /// An angle that would result in the given tangent value. On the range <c>-Tau/4</c> to <c>Tau/4</c>. /// </returns> - public static real_t Atan(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Atan(float s) { - return (real_t)Math.Atan(s); + return MathF.Atan(s); + } + + /// <summary> + /// Returns the arc tangent of <paramref name="s"/> in radians. + /// Use to get the angle of tangent <paramref name="s"/>. + /// + /// The method cannot know in which quadrant the angle should fall. + /// See <see cref="Atan2(double, double)"/> if you have both <c>y</c> and <c>x</c>. + /// </summary> + /// <param name="s">The input tangent value.</param> + /// <returns> + /// An angle that would result in the given tangent value. On the range <c>-Tau/4</c> to <c>Tau/4</c>. + /// </returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Atan(double s) + { + return Math.Atan(s); + } + + /// <summary> + /// Returns the arc tangent of <paramref name="y"/> and <paramref name="x"/> in radians. + /// Use to get the angle of the tangent of <c>y/x</c>. To compute the value, the method takes into + /// account the sign of both arguments in order to determine the quadrant. + /// + /// Important note: The Y coordinate comes first, by convention. + /// </summary> + /// <param name="y">The Y coordinate of the point to find the angle to.</param> + /// <param name="x">The X coordinate of the point to find the angle to.</param> + /// <returns> + /// An angle that would result in the given tangent value. On the range <c>-Tau/2</c> to <c>Tau/2</c>. + /// </returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Atan2(float y, float x) + { + return MathF.Atan2(y, x); } /// <summary> @@ -113,9 +195,21 @@ namespace Godot /// <returns> /// An angle that would result in the given tangent value. On the range <c>-Tau/2</c> to <c>Tau/2</c>. /// </returns> - public static real_t Atan2(real_t y, real_t x) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Atan2(double y, double x) + { + return Math.Atan2(y, x); + } + + /// <summary> + /// Rounds <paramref name="s"/> upward (towards positive infinity). + /// </summary> + /// <param name="s">The number to ceil.</param> + /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Ceil(float s) { - return (real_t)Math.Atan2(y, x); + return MathF.Ceiling(s); } /// <summary> @@ -123,9 +217,10 @@ namespace Godot /// </summary> /// <param name="s">The number to ceil.</param> /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> - public static real_t Ceil(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Ceil(double s) { - return (real_t)Math.Ceiling(s); + return Math.Ceiling(s); } /// <summary> @@ -136,9 +231,24 @@ namespace Godot /// <param name="min">The minimum allowed value.</param> /// <param name="max">The maximum allowed value.</param> /// <returns>The clamped value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Clamp(int value, int min, int max) { - return value < min ? min : value > max ? max : value; + return Math.Clamp(value, min, max); + } + + /// <summary> + /// Clamps a <paramref name="value"/> so that it is not less than <paramref name="min"/> + /// and not more than <paramref name="max"/>. + /// </summary> + /// <param name="value">The value to clamp.</param> + /// <param name="min">The minimum allowed value.</param> + /// <param name="max">The maximum allowed value.</param> + /// <returns>The clamped value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Clamp(float value, float min, float max) + { + return Math.Clamp(value, min, max); } /// <summary> @@ -149,9 +259,10 @@ namespace Godot /// <param name="min">The minimum allowed value.</param> /// <param name="max">The maximum allowed value.</param> /// <returns>The clamped value.</returns> - public static real_t Clamp(real_t value, real_t min, real_t max) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp(double value, double min, double max) { - return value < min ? min : value > max ? max : value; + return Math.Clamp(value, min, max); } /// <summary> @@ -159,9 +270,21 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The cosine of that angle.</returns> - public static real_t Cos(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Cos(float s) { - return (real_t)Math.Cos(s); + return MathF.Cos(s); + } + + /// <summary> + /// Returns the cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The cosine of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Cos(double s) + { + return Math.Cos(s); } /// <summary> @@ -169,9 +292,21 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The hyperbolic cosine of that angle.</returns> - public static real_t Cosh(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Cosh(float s) { - return (real_t)Math.Cosh(s); + return MathF.Cosh(s); + } + + /// <summary> + /// Returns the hyperbolic cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The hyperbolic cosine of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Cosh(double s) + { + return Math.Cosh(s); } /// <summary> @@ -184,7 +319,7 @@ namespace Godot /// <param name="post">The value which after "to" value for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t CubicInterpolate(real_t from, real_t to, real_t pre, real_t post, real_t weight) + public static float CubicInterpolate(float from, float to, float pre, float post, float weight) { return 0.5f * ((from * 2.0f) + @@ -194,9 +329,28 @@ namespace Godot } /// <summary> + /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/> + /// with pre and post values. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="pre">The value which before "from" value for interpolation.</param> + /// <param name="post">The value which after "to" value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static double CubicInterpolate(double from, double to, double pre, double post, double weight) + { + return 0.5 * + ((from * 2.0) + + (-pre + to) * weight + + (2.0 * pre - 5.0 * from + 4.0 * to - post) * (weight * weight) + + (-pre + 3.0 * from - 3.0 * to + post) * (weight * weight * weight)); + } + + /// <summary> /// Cubic interpolates between two rotation values with shortest path /// by the factor defined in <paramref name="weight"/> with pre and post values. - /// See also <see cref="LerpAngle"/>. + /// See also <see cref="LerpAngle(float, float, float)"/>. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> @@ -204,18 +358,45 @@ namespace Godot /// <param name="post">The value which after "to" value for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t CubicInterpolateAngle(real_t from, real_t to, real_t pre, real_t post, real_t weight) + public static float CubicInterpolateAngle(float from, float to, float pre, float post, float weight) { - real_t fromRot = from % Mathf.Tau; + float fromRot = from % MathF.Tau; - real_t preDiff = (pre - fromRot) % Mathf.Tau; - real_t preRot = fromRot + (2.0f * preDiff) % Mathf.Tau - preDiff; + float preDiff = (pre - fromRot) % MathF.Tau; + float preRot = fromRot + (2.0f * preDiff) % MathF.Tau - preDiff; - real_t toDiff = (to - fromRot) % Mathf.Tau; - real_t toRot = fromRot + (2.0f * toDiff) % Mathf.Tau - toDiff; + float toDiff = (to - fromRot) % MathF.Tau; + float toRot = fromRot + (2.0f * toDiff) % MathF.Tau - toDiff; - real_t postDiff = (post - toRot) % Mathf.Tau; - real_t postRot = toRot + (2.0f * postDiff) % Mathf.Tau - postDiff; + float postDiff = (post - toRot) % MathF.Tau; + float postRot = toRot + (2.0f * postDiff) % MathF.Tau - postDiff; + + return CubicInterpolate(fromRot, toRot, preRot, postRot, weight); + } + + /// <summary> + /// Cubic interpolates between two rotation values with shortest path + /// by the factor defined in <paramref name="weight"/> with pre and post values. + /// See also <see cref="LerpAngle(double, double, double)"/>. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="pre">The value which before "from" value for interpolation.</param> + /// <param name="post">The value which after "to" value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static double CubicInterpolateAngle(double from, double to, double pre, double post, double weight) + { + double fromRot = from % Math.Tau; + + double preDiff = (pre - fromRot) % Math.Tau; + double preRot = fromRot + (2.0 * preDiff) % Math.Tau - preDiff; + + double toDiff = (to - fromRot) % Math.Tau; + double toRot = fromRot + (2.0 * toDiff) % Math.Tau - toDiff; + + double postDiff = (post - toRot) % Math.Tau; + double postRot = toRot + (2.0 * postDiff) % Math.Tau - postDiff; return CubicInterpolate(fromRot, toRot, preRot, postRot, weight); } @@ -223,7 +404,8 @@ namespace Godot /// <summary> /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/> /// with pre and post values. - /// It can perform smoother interpolation than <see cref="CubicInterpolate"/> + /// It can perform smoother interpolation than + /// <see cref="CubicInterpolate(float, float, float, float, float)"/> /// by the time values. /// </summary> /// <param name="from">The start value for interpolation.</param> @@ -235,23 +417,52 @@ namespace Godot /// <param name="preT"></param> /// <param name="postT"></param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t CubicInterpolateInTime(real_t from, real_t to, real_t pre, real_t post, real_t weight, real_t toT, real_t preT, real_t postT) + public static float CubicInterpolateInTime(float from, float to, float pre, float post, float weight, float toT, float preT, float postT) { /* Barry-Goldman method */ - real_t t = Lerp(0.0f, toT, weight); - real_t a1 = Lerp(pre, from, preT == 0 ? 0.0f : (t - preT) / -preT); - real_t a2 = Lerp(from, to, toT == 0 ? 0.5f : t / toT); - real_t a3 = Lerp(to, post, postT - toT == 0 ? 1.0f : (t - toT) / (postT - toT)); - real_t b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0f : (t - preT) / (toT - preT)); - real_t b2 = Lerp(a2, a3, postT == 0 ? 1.0f : t / postT); + float t = Lerp(0.0f, toT, weight); + float a1 = Lerp(pre, from, preT == 0 ? 0.0f : (t - preT) / -preT); + float a2 = Lerp(from, to, toT == 0 ? 0.5f : t / toT); + float a3 = Lerp(to, post, postT - toT == 0 ? 1.0f : (t - toT) / (postT - toT)); + float b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0f : (t - preT) / (toT - preT)); + float b2 = Lerp(a2, a3, postT == 0 ? 1.0f : t / postT); return Lerp(b1, b2, toT == 0 ? 0.5f : t / toT); } /// <summary> + /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/> + /// with pre and post values. + /// It can perform smoother interpolation than + /// <see cref="CubicInterpolate(double, double, double, double, double)"/> + /// by the time values. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="pre">The value which before "from" value for interpolation.</param> + /// <param name="post">The value which after "to" value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <param name="toT"></param> + /// <param name="preT"></param> + /// <param name="postT"></param> + /// <returns>The resulting value of the interpolation.</returns> + public static double CubicInterpolateInTime(double from, double to, double pre, double post, double weight, double toT, double preT, double postT) + { + /* Barry-Goldman method */ + double t = Lerp(0.0, toT, weight); + double a1 = Lerp(pre, from, preT == 0 ? 0.0 : (t - preT) / -preT); + double a2 = Lerp(from, to, toT == 0 ? 0.5 : t / toT); + double a3 = Lerp(to, post, postT - toT == 0 ? 1.0 : (t - toT) / (postT - toT)); + double b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0 : (t - preT) / (toT - preT)); + double b2 = Lerp(a2, a3, postT == 0 ? 1.0 : t / postT); + return Lerp(b1, b2, toT == 0 ? 0.5 : t / toT); + } + + /// <summary> /// Cubic interpolates between two rotation values with shortest path /// by the factor defined in <paramref name="weight"/> with pre and post values. - /// See also <see cref="LerpAngle"/>. - /// It can perform smoother interpolation than <see cref="CubicInterpolateAngle"/> + /// See also <see cref="LerpAngle(float, float, float)"/>. + /// It can perform smoother interpolation than + /// <see cref="CubicInterpolateAngle(float, float, float, float, float)"/> /// by the time values. /// </summary> /// <param name="from">The start value for interpolation.</param> @@ -263,24 +474,78 @@ namespace Godot /// <param name="preT"></param> /// <param name="postT"></param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t CubicInterpolateAngleInTime(real_t from, real_t to, real_t pre, real_t post, real_t weight, - real_t toT, real_t preT, real_t postT) + public static float CubicInterpolateAngleInTime(float from, float to, float pre, float post, float weight, float toT, float preT, float postT) { - real_t fromRot = from % Mathf.Tau; + float fromRot = from % MathF.Tau; - real_t preDiff = (pre - fromRot) % Mathf.Tau; - real_t preRot = fromRot + (2.0f * preDiff) % Mathf.Tau - preDiff; + float preDiff = (pre - fromRot) % MathF.Tau; + float preRot = fromRot + (2.0f * preDiff) % MathF.Tau - preDiff; - real_t toDiff = (to - fromRot) % Mathf.Tau; - real_t toRot = fromRot + (2.0f * toDiff) % Mathf.Tau - toDiff; + float toDiff = (to - fromRot) % MathF.Tau; + float toRot = fromRot + (2.0f * toDiff) % MathF.Tau - toDiff; - real_t postDiff = (post - toRot) % Mathf.Tau; - real_t postRot = toRot + (2.0f * postDiff) % Mathf.Tau - postDiff; + float postDiff = (post - toRot) % MathF.Tau; + float postRot = toRot + (2.0f * postDiff) % MathF.Tau - postDiff; return CubicInterpolateInTime(fromRot, toRot, preRot, postRot, weight, toT, preT, postT); } /// <summary> + /// Cubic interpolates between two rotation values with shortest path + /// by the factor defined in <paramref name="weight"/> with pre and post values. + /// See also <see cref="LerpAngle(double, double, double)"/>. + /// It can perform smoother interpolation than + /// <see cref="CubicInterpolateAngle(double, double, double, double, double)"/> + /// by the time values. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="pre">The value which before "from" value for interpolation.</param> + /// <param name="post">The value which after "to" value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <param name="toT"></param> + /// <param name="preT"></param> + /// <param name="postT"></param> + /// <returns>The resulting value of the interpolation.</returns> + public static double CubicInterpolateAngleInTime(double from, double to, double pre, double post, double weight, double toT, double preT, double postT) + { + double fromRot = from % Math.Tau; + + double preDiff = (pre - fromRot) % Math.Tau; + double preRot = fromRot + (2.0 * preDiff) % Math.Tau - preDiff; + + double toDiff = (to - fromRot) % Math.Tau; + double toRot = fromRot + (2.0 * toDiff) % Math.Tau - toDiff; + + double postDiff = (post - toRot) % Math.Tau; + double postRot = toRot + (2.0 * postDiff) % Math.Tau - postDiff; + + return CubicInterpolateInTime(fromRot, toRot, preRot, postRot, weight, toT, preT, postT); + } + + /// <summary> + /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by + /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points. + /// </summary> + /// <param name="start">The start value for the interpolation.</param> + /// <param name="control1">Control point that defines the bezier curve.</param> + /// <param name="control2">Control point that defines the bezier curve.</param> + /// <param name="end">The destination value for the interpolation.</param> + /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static float BezierInterpolate(float start, float control1, float control2, float end, float t) + { + // Formula from Wikipedia article on Bezier curves + float omt = 1.0f - t; + float omt2 = omt * omt; + float omt3 = omt2 * omt; + float t2 = t * t; + float t3 = t2 * t; + + return start * omt3 + control1 * omt2 * t * 3.0f + control2 * omt * t2 * 3.0f + end * t3; + } + + /// <summary> /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points. /// </summary> @@ -290,16 +555,16 @@ namespace Godot /// <param name="end">The destination value for the interpolation.</param> /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t BezierInterpolate(real_t start, real_t control1, real_t control2, real_t end, real_t t) + public static double BezierInterpolate(double start, double control1, double control2, double end, double t) { // Formula from Wikipedia article on Bezier curves - real_t omt = 1 - t; - real_t omt2 = omt * omt; - real_t omt3 = omt2 * omt; - real_t t2 = t * t; - real_t t3 = t2 * t; + double omt = 1.0 - t; + double omt2 = omt * omt; + double omt3 = omt2 * omt; + double t2 = t * t; + double t3 = t2 * t; - return start * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3; + return start * omt3 + control1 * omt2 * t * 3.0 + control2 * omt * t2 * 3.0 + end * t3; } /// <summary> @@ -312,26 +577,68 @@ namespace Godot /// <param name="end">The destination value for the interpolation.</param> /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t BezierDerivative(real_t start, real_t control1, real_t control2, real_t end, real_t t) + public static float BezierDerivative(float start, float control1, float control2, float end, float t) { // Formula from Wikipedia article on Bezier curves - real_t omt = 1 - t; - real_t omt2 = omt * omt; - real_t t2 = t * t; + float omt = 1.0f - t; + float omt2 = omt * omt; + float t2 = t * t; - real_t d = (control1 - start) * 3 * omt2 + (control2 - control1) * 6 * omt * t + (end - control2) * 3 * t2; + float d = (control1 - start) * 3.0f * omt2 + (control2 - control1) * 6.0f * omt * t + (end - control2) * 3.0f * t2; return d; } /// <summary> + /// Returns the derivative at the given <paramref name="t"/> on a one dimensional Bezier curve defined by + /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points. + /// </summary> + /// <param name="start">The start value for the interpolation.</param> + /// <param name="control1">Control point that defines the bezier curve.</param> + /// <param name="control2">Control point that defines the bezier curve.</param> + /// <param name="end">The destination value for the interpolation.</param> + /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static double BezierDerivative(double start, double control1, double control2, double end, double t) + { + // Formula from Wikipedia article on Bezier curves + double omt = 1.0 - t; + double omt2 = omt * omt; + double t2 = t * t; + + double d = (control1 - start) * 3.0 * omt2 + (control2 - control1) * 6.0 * omt * t + (end - control2) * 3.0 * t2; + return d; + } + + /// <summary> + /// Converts from decibels to linear energy (audio). + /// </summary> + /// <seealso cref="LinearToDb(float)"/> + /// <param name="db">Decibels to convert.</param> + /// <returns>Audio volume as linear energy.</returns> + public static float DbToLinear(float db) + { + return MathF.Exp(db * 0.11512925464970228420089957273422f); + } + + /// <summary> /// Converts from decibels to linear energy (audio). /// </summary> - /// <seealso cref="LinearToDb(real_t)"/> + /// <seealso cref="LinearToDb(double)"/> /// <param name="db">Decibels to convert.</param> /// <returns>Audio volume as linear energy.</returns> - public static real_t DbToLinear(real_t db) + public static double DbToLinear(double db) + { + return Math.Exp(db * 0.11512925464970228420089957273422); + } + + /// <summary> + /// Converts an angle expressed in degrees to radians. + /// </summary> + /// <param name="deg">An angle expressed in degrees.</param> + /// <returns>The same angle expressed in radians.</returns> + public static float DegToRad(float deg) { - return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); + return deg * _degToRadConstF; } /// <summary> @@ -339,9 +646,9 @@ namespace Godot /// </summary> /// <param name="deg">An angle expressed in degrees.</param> /// <returns>The same angle expressed in radians.</returns> - public static real_t DegToRad(real_t deg) + public static double DegToRad(double deg) { - return deg * _degToRadConst; + return deg * _degToRadConstD; } /// <summary> @@ -354,38 +661,94 @@ namespace Godot /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out. /// </param> /// <returns>The eased value.</returns> - public static real_t Ease(real_t s, real_t curve) + public static float Ease(float s, float curve) { - if (s < 0f) + if (s < 0.0f) { - s = 0f; + s = 0.0f; } else if (s > 1.0f) { s = 1.0f; } - if (curve > 0f) + if (curve > 0.0f) { if (curve < 1.0f) { - return 1.0f - Pow(1.0f - s, 1.0f / curve); + return 1.0f - MathF.Pow(1.0f - s, 1.0f / curve); } - return Pow(s, curve); + return MathF.Pow(s, curve); } - if (curve < 0f) + if (curve < 0.0f) { if (s < 0.5f) { - return Pow(s * 2.0f, -curve) * 0.5f; + return MathF.Pow(s * 2.0f, -curve) * 0.5f; } - return ((1.0f - Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f; + return ((1.0f - MathF.Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f; } - return 0f; + return 0.0f; + } + + /// <summary> + /// Easing function, based on exponent. The <paramref name="curve"/> values are: + /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out. + /// Negative values are in-out/out-in. + /// </summary> + /// <param name="s">The value to ease.</param> + /// <param name="curve"> + /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out. + /// </param> + /// <returns>The eased value.</returns> + public static double Ease(double s, double curve) + { + if (s < 0.0) + { + s = 0.0; + } + else if (s > 1.0) + { + s = 1.0; + } + + if (curve > 0) + { + if (curve < 1.0) + { + return 1.0 - Math.Pow(1.0 - s, 1.0 / curve); + } + + return Math.Pow(s, curve); + } + + if (curve < 0.0) + { + if (s < 0.5) + { + return Math.Pow(s * 2.0, -curve) * 0.5; + } + + return ((1.0 - Math.Pow(1.0 - ((s - 0.5) * 2.0), -curve)) * 0.5) + 0.5; + } + + return 0.0; + } + + /// <summary> + /// The natural exponential function. It raises the mathematical + /// constant <c>e</c> to the power of <paramref name="s"/> and returns it. + /// </summary> + /// <param name="s">The exponent to raise <c>e</c> to.</param> + /// <returns><c>e</c> raised to the power of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Exp(float s) + { + return MathF.Exp(s); } /// <summary> @@ -394,9 +757,21 @@ namespace Godot /// </summary> /// <param name="s">The exponent to raise <c>e</c> to.</param> /// <returns><c>e</c> raised to the power of <paramref name="s"/>.</returns> - public static real_t Exp(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Exp(double s) + { + return Math.Exp(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> downward (towards negative infinity). + /// </summary> + /// <param name="s">The number to floor.</param> + /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Floor(float s) { - return (real_t)Math.Exp(s); + return MathF.Floor(s); } /// <summary> @@ -404,14 +779,32 @@ namespace Godot /// </summary> /// <param name="s">The number to floor.</param> /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> - public static real_t Floor(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Floor(double s) + { + return Math.Floor(s); + } + + /// <summary> + /// Returns a normalized value considering the given range. + /// This is the opposite of <see cref="Lerp(float, float, float)"/>. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="weight">The interpolated value.</param> + /// <returns> + /// The resulting value of the inverse interpolation. + /// The returned value will be between 0.0 and 1.0 if <paramref name="weight"/> is + /// between <paramref name="from"/> and <paramref name="to"/> (inclusive). + /// </returns> + public static float InverseLerp(float from, float to, float weight) { - return (real_t)Math.Floor(s); + return (weight - from) / (to - from); } /// <summary> /// Returns a normalized value considering the given range. - /// This is the opposite of <see cref="Lerp(real_t, real_t, real_t)"/>. + /// This is the opposite of <see cref="Lerp(double, double, double)"/>. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> @@ -421,7 +814,7 @@ namespace Godot /// The returned value will be between 0.0 and 1.0 if <paramref name="weight"/> is /// between <paramref name="from"/> and <paramref name="to"/> (inclusive). /// </returns> - public static real_t InverseLerp(real_t from, real_t to, real_t weight) + public static double InverseLerp(double from, double to, double weight) { return (weight - from) / (to - from); } @@ -434,7 +827,7 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>A <see langword="bool"/> for whether or not the two values are approximately equal.</returns> - public static bool IsEqualApprox(real_t a, real_t b) + public static bool IsEqualApprox(float a, float b) { // Check for exact equality first, required to handle "infinity" values. if (a == b) @@ -442,12 +835,36 @@ namespace Godot return true; } // Then check for approximate equality. - real_t tolerance = Epsilon * Abs(a); - if (tolerance < Epsilon) + float tolerance = _epsilonF * Math.Abs(a); + if (tolerance < _epsilonF) { - tolerance = Epsilon; + tolerance = _epsilonF; } - return Abs(a - b) < tolerance; + return Math.Abs(a - b) < tolerance; + } + + /// <summary> + /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately equal + /// to each other. + /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>. + /// </summary> + /// <param name="a">One of the values.</param> + /// <param name="b">The other value.</param> + /// <returns>A <see langword="bool"/> for whether or not the two values are approximately equal.</returns> + public static bool IsEqualApprox(double a, double b) + { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) + { + return true; + } + // Then check for approximate equality. + double tolerance = _epsilonD * Math.Abs(a); + if (tolerance < _epsilonD) + { + tolerance = _epsilonD; + } + return Math.Abs(a - b) < tolerance; } /// <summary> @@ -456,9 +873,22 @@ namespace Godot /// </summary> /// <param name="s">The value to check.</param> /// <returns>A <see langword="bool"/> for whether or not the value is a finite value.</returns> - public static bool IsFinite(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(float s) { - return real_t.IsFinite(s); + return float.IsFinite(s); + } + + /// <summary> + /// Returns whether <paramref name="s"/> is a finite value, i.e. it is not + /// <see cref="NaN"/>, positive infinite, or negative infinity. + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is a finite value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(double s) + { + return double.IsFinite(s); } /// <summary> @@ -466,9 +896,32 @@ namespace Godot /// </summary> /// <param name="s">The value to check.</param> /// <returns>A <see langword="bool"/> for whether or not the value is an infinity value.</returns> - public static bool IsInf(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInf(float s) { - return real_t.IsInfinity(s); + return float.IsInfinity(s); + } + + /// <summary> + /// Returns whether <paramref name="s"/> is an infinity value (either positive infinity or negative infinity). + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is an infinity value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInf(double s) + { + return double.IsInfinity(s); + } + + /// <summary> + /// Returns whether <paramref name="s"/> is a <c>NaN</c> ("Not a Number" or invalid) value. + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is a <c>NaN</c> value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNaN(float s) + { + return float.IsNaN(s); } /// <summary> @@ -476,34 +929,64 @@ namespace Godot /// </summary> /// <param name="s">The value to check.</param> /// <returns>A <see langword="bool"/> for whether or not the value is a <c>NaN</c> value.</returns> - public static bool IsNaN(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNaN(double s) { - return real_t.IsNaN(s); + return double.IsNaN(s); } /// <summary> /// Returns <see langword="true"/> if <paramref name="s"/> is zero or almost zero. /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>. /// - /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with + /// This method is faster than using <see cref="IsEqualApprox(float, float)"/> with /// one value as zero. /// </summary> /// <param name="s">The value to check.</param> /// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns> - public static bool IsZeroApprox(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsZeroApprox(float s) { - return Abs(s) < Epsilon; + return Math.Abs(s) < _epsilonF; + } + + /// <summary> + /// Returns <see langword="true"/> if <paramref name="s"/> is zero or almost zero. + /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>. + /// + /// This method is faster than using <see cref="IsEqualApprox(double, double)"/> with + /// one value as zero. + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsZeroApprox(double s) + { + return Math.Abs(s) < _epsilonD; + } + + /// <summary> + /// Linearly interpolates between two values by a normalized value. + /// This is the opposite <see cref="InverseLerp(float, float, float)"/>. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static float Lerp(float from, float to, float weight) + { + return from + ((to - from) * weight); } /// <summary> /// Linearly interpolates between two values by a normalized value. - /// This is the opposite <see cref="InverseLerp(real_t, real_t, real_t)"/>. + /// This is the opposite <see cref="InverseLerp(double, double, double)"/>. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t Lerp(real_t from, real_t to, real_t weight) + public static double Lerp(double from, double to, double weight) { return from + ((to - from) * weight); } @@ -511,17 +994,34 @@ namespace Godot /// <summary> /// Linearly interpolates between two angles (in radians) by a normalized value. /// - /// Similar to <see cref="Lerp(real_t, real_t, real_t)"/>, + /// Similar to <see cref="Lerp(float, float, float)"/>, + /// but interpolates correctly when the angles wrap around <see cref="Tau"/>. + /// </summary> + /// <param name="from">The start angle for interpolation.</param> + /// <param name="to">The destination angle for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting angle of the interpolation.</returns> + public static float LerpAngle(float from, float to, float weight) + { + float difference = (to - from) % MathF.Tau; + float distance = ((2 * difference) % MathF.Tau) - difference; + return from + (distance * weight); + } + + /// <summary> + /// Linearly interpolates between two angles (in radians) by a normalized value. + /// + /// Similar to <see cref="Lerp(double, double, double)"/>, /// but interpolates correctly when the angles wrap around <see cref="Tau"/>. /// </summary> /// <param name="from">The start angle for interpolation.</param> /// <param name="to">The destination angle for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting angle of the interpolation.</returns> - public static real_t LerpAngle(real_t from, real_t to, real_t weight) + public static double LerpAngle(double from, double to, double weight) { - real_t difference = (to - from) % Mathf.Tau; - real_t distance = ((2 * difference) % Mathf.Tau) - difference; + double difference = (to - from) % Math.Tau; + double distance = ((2 * difference) % Math.Tau) - difference; return from + (distance * weight); } @@ -529,7 +1029,7 @@ namespace Godot /// Converts from linear energy to decibels (audio). /// This can be used to implement volume sliders that behave as expected (since volume isn't linear). /// </summary> - /// <seealso cref="DbToLinear(real_t)"/> + /// <seealso cref="DbToLinear(float)"/> /// <example> /// <code> /// // "slider" refers to a node that inherits Range such as HSlider or VSlider. @@ -540,9 +1040,42 @@ namespace Godot /// </example> /// <param name="linear">The linear energy to convert.</param> /// <returns>Audio as decibels.</returns> - public static real_t LinearToDb(real_t linear) + public static float LinearToDb(float linear) { - return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321); + return MathF.Log(linear) * 8.6858896380650365530225783783321f; + } + + /// <summary> + /// Converts from linear energy to decibels (audio). + /// This can be used to implement volume sliders that behave as expected (since volume isn't linear). + /// </summary> + /// <seealso cref="DbToLinear(double)"/> + /// <example> + /// <code> + /// // "slider" refers to a node that inherits Range such as HSlider or VSlider. + /// // Its range must be configured to go from 0 to 1. + /// // Change the bus name if you'd like to change the volume of a specific bus only. + /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.LinearToDb(slider.value)); + /// </code> + /// </example> + /// <param name="linear">The linear energy to convert.</param> + /// <returns>Audio as decibels.</returns> + public static double LinearToDb(double linear) + { + return Math.Log(linear) * 8.6858896380650365530225783783321; + } + + /// <summary> + /// Natural logarithm. The amount of time needed to reach a certain level of continuous growth. + /// + /// Note: This is not the same as the "log" function on most calculators, which uses a base 10 logarithm. + /// </summary> + /// <param name="s">The input value.</param> + /// <returns>The natural log of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Log(float s) + { + return MathF.Log(s); } /// <summary> @@ -552,9 +1085,10 @@ namespace Godot /// </summary> /// <param name="s">The input value.</param> /// <returns>The natural log of <paramref name="s"/>.</returns> - public static real_t Log(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Log(double s) { - return (real_t)Math.Log(s); + return Math.Log(s); } /// <summary> @@ -563,9 +1097,22 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>Whichever of the two values is higher.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Max(int a, int b) { - return a > b ? a : b; + return Math.Max(a, b); + } + + /// <summary> + /// Returns the maximum of two values. + /// </summary> + /// <param name="a">One of the values.</param> + /// <param name="b">The other value.</param> + /// <returns>Whichever of the two values is higher.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Max(float a, float b) + { + return Math.Max(a, b); } /// <summary> @@ -574,9 +1121,10 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>Whichever of the two values is higher.</returns> - public static real_t Max(real_t a, real_t b) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Max(double a, double b) { - return a > b ? a : b; + return Math.Max(a, b); } /// <summary> @@ -585,9 +1133,22 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>Whichever of the two values is lower.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Min(int a, int b) { - return a < b ? a : b; + return Math.Min(a, b); + } + + /// <summary> + /// Returns the minimum of two values. + /// </summary> + /// <param name="a">One of the values.</param> + /// <param name="b">The other value.</param> + /// <returns>Whichever of the two values is lower.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Min(float a, float b) + { + return Math.Min(a, b); } /// <summary> @@ -596,9 +1157,27 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>Whichever of the two values is lower.</returns> - public static real_t Min(real_t a, real_t b) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Min(double a, double b) + { + return Math.Min(a, b); + } + + /// <summary> + /// Moves <paramref name="from"/> toward <paramref name="to"/> by the <paramref name="delta"/> value. + /// + /// Use a negative <paramref name="delta"/> value to move away. + /// </summary> + /// <param name="from">The start value.</param> + /// <param name="to">The value to move towards.</param> + /// <param name="delta">The amount to move by.</param> + /// <returns>The value after moving.</returns> + public static float MoveToward(float from, float to, float delta) { - return a < b ? a : b; + if (Math.Abs(to - from) <= delta) + return to; + + return from + (Math.Sign(to - from) * delta); } /// <summary> @@ -610,12 +1189,12 @@ namespace Godot /// <param name="to">The value to move towards.</param> /// <param name="delta">The amount to move by.</param> /// <returns>The value after moving.</returns> - public static real_t MoveToward(real_t from, real_t to, real_t delta) + public static double MoveToward(double from, double to, double delta) { - if (Abs(to - from) <= delta) + if (Math.Abs(to - from) <= delta) return to; - return from + (Sign(to - from) * delta); + return from + (Math.Sign(to - from) * delta); } /// <summary> @@ -657,9 +1236,25 @@ namespace Godot /// <param name="a">The dividend, the primary input.</param> /// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param> /// <returns>The resulting output.</returns> - public static real_t PosMod(real_t a, real_t b) + public static float PosMod(float a, float b) + { + float c = a % b; + if ((c < 0 && b > 0) || (c > 0 && b < 0)) + { + c += b; + } + return c; + } + + /// <summary> + /// Performs a canonical Modulus operation, where the output is on the range [0, <paramref name="b"/>). + /// </summary> + /// <param name="a">The dividend, the primary input.</param> + /// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param> + /// <returns>The resulting output.</returns> + public static double PosMod(double a, double b) { - real_t c = a % b; + double c = a % b; if ((c < 0 && b > 0) || (c > 0 && b < 0)) { c += b; @@ -673,9 +1268,22 @@ namespace Godot /// <param name="x">The base.</param> /// <param name="y">The exponent.</param> /// <returns><paramref name="x"/> raised to the power of <paramref name="y"/>.</returns> - public static real_t Pow(real_t x, real_t y) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Pow(float x, float y) + { + return MathF.Pow(x, y); + } + + /// <summary> + /// Returns the result of <paramref name="x"/> raised to the power of <paramref name="y"/>. + /// </summary> + /// <param name="x">The base.</param> + /// <param name="y">The exponent.</param> + /// <returns><paramref name="x"/> raised to the power of <paramref name="y"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Pow(double x, double y) { - return (real_t)Math.Pow(x, y); + return Math.Pow(x, y); } /// <summary> @@ -683,9 +1291,21 @@ namespace Godot /// </summary> /// <param name="rad">An angle expressed in radians.</param> /// <returns>The same angle expressed in degrees.</returns> - public static real_t RadToDeg(real_t rad) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float RadToDeg(float rad) { - return rad * _radToDegConst; + return rad * _radToDegConstF; + } + + /// <summary> + /// Converts an angle expressed in radians to degrees. + /// </summary> + /// <param name="rad">An angle expressed in radians.</param> + /// <returns>The same angle expressed in degrees.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double RadToDeg(double rad) + { + return rad * _radToDegConstD; } /// <summary> @@ -698,20 +1318,48 @@ namespace Godot /// <param name="outFrom">The start value for the output interpolation.</param> /// <param name="outTo">The destination value for the output interpolation.</param> /// <returns>The resulting mapped value mapped.</returns> - public static real_t Remap(real_t value, real_t inFrom, real_t inTo, real_t outFrom, real_t outTo) + public static float Remap(float value, float inFrom, float inTo, float outFrom, float outTo) { return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value)); } /// <summary> + /// Maps a <paramref name="value"/> from [<paramref name="inFrom"/>, <paramref name="inTo"/>] + /// to [<paramref name="outFrom"/>, <paramref name="outTo"/>]. + /// </summary> + /// <param name="value">The value to map.</param> + /// <param name="inFrom">The start value for the input interpolation.</param> + /// <param name="inTo">The destination value for the input interpolation.</param> + /// <param name="outFrom">The start value for the output interpolation.</param> + /// <param name="outTo">The destination value for the output interpolation.</param> + /// <returns>The resulting mapped value mapped.</returns> + public static double Remap(double value, double inFrom, double inTo, double outFrom, double outTo) + { + return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value)); + } + + /// <summary> + /// Rounds <paramref name="s"/> to the nearest whole number, + /// with halfway cases rounded towards the nearest multiple of two. + /// </summary> + /// <param name="s">The number to round.</param> + /// <returns>The rounded number.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Round(float s) + { + return MathF.Round(s); + } + + /// <summary> /// Rounds <paramref name="s"/> to the nearest whole number, /// with halfway cases rounded towards the nearest multiple of two. /// </summary> /// <param name="s">The number to round.</param> /// <returns>The rounded number.</returns> - public static real_t Round(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Round(double s) { - return (real_t)Math.Round(s); + return Math.Round(s); } /// <summary> @@ -720,11 +1368,10 @@ namespace Godot /// </summary> /// <param name="s">The input number.</param> /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Sign(int s) { - if (s == 0) - return 0; - return s < 0 ? -1 : 1; + return Math.Sign(s); } /// <summary> @@ -733,11 +1380,33 @@ namespace Godot /// </summary> /// <param name="s">The input number.</param> /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> - public static int Sign(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(float s) { - if (s == 0) - return 0; - return s < 0 ? -1 : 1; + return Math.Sign(s); + } + + /// <summary> + /// Returns the sign of <paramref name="s"/>: <c>-1</c> or <c>1</c>. + /// Returns <c>0</c> if <paramref name="s"/> is <c>0</c>. + /// </summary> + /// <param name="s">The input number.</param> + /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(double s) + { + return Math.Sign(s); + } + + /// <summary> + /// Returns the sine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The sine of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sin(float s) + { + return MathF.Sin(s); } /// <summary> @@ -745,9 +1414,21 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The sine of that angle.</returns> - public static real_t Sin(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Sin(double s) + { + return Math.Sin(s); + } + + /// <summary> + /// Returns the hyperbolic sine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The hyperbolic sine of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sinh(float s) { - return (real_t)Math.Sin(s); + return MathF.Sinh(s); } /// <summary> @@ -755,27 +1436,47 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The hyperbolic sine of that angle.</returns> - public static real_t Sinh(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Sinh(double s) + { + return Math.Sinh(s); + } + + /// <summary> + /// Returns a number smoothly interpolated between <paramref name="from"/> and <paramref name="to"/>, + /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(float, float, float)"/>, + /// but interpolates faster at the beginning and slower at the end. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="weight">A value representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static float SmoothStep(float from, float to, float weight) { - return (real_t)Math.Sinh(s); + if (IsEqualApprox(from, to)) + { + return from; + } + float x = Math.Clamp((weight - from) / (to - from), 0.0f, 1.0f); + return x * x * (3 - (2 * x)); } /// <summary> /// Returns a number smoothly interpolated between <paramref name="from"/> and <paramref name="to"/>, - /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(real_t, real_t, real_t)"/>, + /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(double, double, double)"/>, /// but interpolates faster at the beginning and slower at the end. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> /// <param name="weight">A value representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t SmoothStep(real_t from, real_t to, real_t weight) + public static double SmoothStep(double from, double to, double weight) { if (IsEqualApprox(from, to)) { return from; } - real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0); + double x = Math.Clamp((weight - from) / (to - from), 0.0, 1.0); return x * x * (3 - (2 * x)); } @@ -786,9 +1487,23 @@ namespace Godot /// </summary> /// <param name="s">The input number. Must not be negative.</param> /// <returns>The square root of <paramref name="s"/>.</returns> - public static real_t Sqrt(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sqrt(float s) { - return (real_t)Math.Sqrt(s); + return MathF.Sqrt(s); + } + + /// <summary> + /// Returns the square root of <paramref name="s"/>, where <paramref name="s"/> is a non-negative number. + /// + /// If you need negative inputs, use <see cref="System.Numerics.Complex"/>. + /// </summary> + /// <param name="s">The input number. Must not be negative.</param> + /// <returns>The square root of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Sqrt(double s) + { + return Math.Sqrt(s); } /// <summary> @@ -798,7 +1513,7 @@ namespace Godot /// </summary> /// <param name="step">The input value.</param> /// <returns>The position of the first non-zero digit.</returns> - public static int StepDecimals(real_t step) + public static int StepDecimals(double step) { double[] sd = new double[] { @@ -812,7 +1527,7 @@ namespace Godot 0.00000009999, 0.000000009999, }; - double abs = Abs(step); + double abs = Math.Abs(step); double decs = abs - (int)abs; // Strip away integer part for (int i = 0; i < sd.Length; i++) { @@ -831,11 +1546,28 @@ namespace Godot /// <param name="s">The value to snap.</param> /// <param name="step">The step size to snap to.</param> /// <returns>The snapped value.</returns> - public static real_t Snapped(real_t s, real_t step) + public static float Snapped(float s, float step) + { + if (step != 0f) + { + return MathF.Floor((s / step) + 0.5f) * step; + } + + return s; + } + + /// <summary> + /// Snaps float value <paramref name="s"/> to a given <paramref name="step"/>. + /// This can also be used to round a floating point number to an arbitrary number of decimals. + /// </summary> + /// <param name="s">The value to snap.</param> + /// <param name="step">The step size to snap to.</param> + /// <returns>The snapped value.</returns> + public static double Snapped(double s, double step) { if (step != 0f) { - return Floor((s / step) + 0.5f) * step; + return Math.Floor((s / step) + 0.5f) * step; } return s; @@ -846,9 +1578,32 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The tangent of that angle.</returns> - public static real_t Tan(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Tan(float s) + { + return MathF.Tan(s); + } + + /// <summary> + /// Returns the tangent of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The tangent of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Tan(double s) + { + return Math.Tan(s); + } + + /// <summary> + /// Returns the hyperbolic tangent of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The hyperbolic tangent of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Tanh(float s) { - return (real_t)Math.Tan(s); + return MathF.Tanh(s); } /// <summary> @@ -856,9 +1611,10 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The hyperbolic tangent of that angle.</returns> - public static real_t Tanh(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Tanh(double s) { - return (real_t)Math.Tanh(s); + return Math.Tanh(s); } /// <summary> @@ -884,15 +1640,35 @@ namespace Godot /// Wraps <paramref name="value"/> between <paramref name="min"/> and <paramref name="max"/>. /// Usable for creating loop-alike behavior or infinite surfaces. /// If <paramref name="min"/> is <c>0</c>, this is equivalent - /// to <see cref="PosMod(real_t, real_t)"/>, so prefer using that instead. + /// to <see cref="PosMod(float, float)"/>, so prefer using that instead. + /// </summary> + /// <param name="value">The value to wrap.</param> + /// <param name="min">The minimum allowed value and lower bound of the range.</param> + /// <param name="max">The maximum allowed value and upper bound of the range.</param> + /// <returns>The wrapped value.</returns> + public static float Wrap(float value, float min, float max) + { + float range = max - min; + if (IsZeroApprox(range)) + { + return min; + } + return min + ((((value - min) % range) + range) % range); + } + + /// <summary> + /// Wraps <paramref name="value"/> between <paramref name="min"/> and <paramref name="max"/>. + /// Usable for creating loop-alike behavior or infinite surfaces. + /// If <paramref name="min"/> is <c>0</c>, this is equivalent + /// to <see cref="PosMod(double, double)"/>, so prefer using that instead. /// </summary> /// <param name="value">The value to wrap.</param> /// <param name="min">The minimum allowed value and lower bound of the range.</param> /// <param name="max">The maximum allowed value and upper bound of the range.</param> /// <returns>The wrapped value.</returns> - public static real_t Wrap(real_t value, real_t min, real_t max) + public static double Wrap(double value, double min, double max) { - real_t range = max - min; + double range = max - min; if (IsZeroApprox(range)) { return min; @@ -900,9 +1676,23 @@ namespace Godot return min + ((((value - min) % range) + range) % range); } - private static real_t Fract(real_t value) + /// <summary> + /// Returns the <paramref name="value"/> wrapped between <c>0</c> and the <paramref name="length"/>. + /// If the limit is reached, the next value the function returned is decreased to the <c>0</c> side + /// or increased to the <paramref name="length"/> side (like a triangle wave). + /// If <paramref name="length"/> is less than zero, it becomes positive. + /// </summary> + /// <param name="value">The value to pingpong.</param> + /// <param name="length">The maximum value of the function.</param> + /// <returns>The ping-ponged value.</returns> + public static float PingPong(float value, float length) { - return value - (real_t)Math.Floor(value); + return (length != 0.0f) ? Math.Abs(Fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f; + + static float Fract(float value) + { + return value - MathF.Floor(value); + } } /// <summary> @@ -914,9 +1704,14 @@ namespace Godot /// <param name="value">The value to pingpong.</param> /// <param name="length">The maximum value of the function.</param> /// <returns>The ping-ponged value.</returns> - public static real_t PingPong(real_t value, real_t length) + public static double PingPong(double value, double length) { - return (length != (real_t)0.0) ? Abs(Fract((value - length) / (length * (real_t)2.0)) * length * (real_t)2.0 - length) : (real_t)0.0; + return (length != 0.0) ? Math.Abs(Fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0; + + static double Fract(double value) + { + return value - Math.Floor(value); + } } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs index 72a1868964..cc2d61f58d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs @@ -1,4 +1,8 @@ using System; +using System.Runtime.CompilerServices; + +// This file contains extra members for the Mathf class that aren't part of Godot's Core API. +// Math API that is also part of Core should go into Mathf.cs. namespace Godot { @@ -16,14 +20,18 @@ namespace Godot /// </summary> public const real_t Sqrt2 = (real_t)1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095 + // Epsilon size should depend on the precision used. + private const float _epsilonF = 1e-06f; + private const double _epsilonD = 1e-14; + /// <summary> /// A very small number used for float comparison with error tolerance. /// 1e-06 with single-precision floats, but 1e-14 if <c>REAL_T_IS_DOUBLE</c>. /// </summary> #if REAL_T_IS_DOUBLE - public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used. + public const real_t Epsilon = _epsilonD; #else - public const real_t Epsilon = 1e-06f; + public const real_t Epsilon = _epsilonF; #endif /// <summary> @@ -31,7 +39,8 @@ namespace Godot /// </summary> /// <param name="s">The input value.</param> /// <returns>The amount of digits.</returns> - public static int DecimalCount(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int DecimalCount(double s) { return DecimalCount((decimal)s); } @@ -41,6 +50,7 @@ namespace Godot /// </summary> /// <param name="s">The input <see langword="decimal"/> value.</param> /// <returns>The amount of digits.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int DecimalCount(decimal s) { return BitConverter.GetBytes(decimal.GetBits(s)[3])[2]; @@ -49,11 +59,25 @@ namespace Godot /// <summary> /// Rounds <paramref name="s"/> upward (towards positive infinity). /// - /// This is the same as <see cref="Ceil(real_t)"/>, but returns an <see langword="int"/>. + /// This is the same as <see cref="Ceil(float)"/>, but returns an <see langword="int"/>. + /// </summary> + /// <param name="s">The number to ceil.</param> + /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CeilToInt(float s) + { + return (int)MathF.Ceiling(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> upward (towards positive infinity). + /// + /// This is the same as <see cref="Ceil(double)"/>, but returns an <see langword="int"/>. /// </summary> /// <param name="s">The number to ceil.</param> /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> - public static int CeilToInt(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CeilToInt(double s) { return (int)Math.Ceiling(s); } @@ -61,11 +85,25 @@ namespace Godot /// <summary> /// Rounds <paramref name="s"/> downward (towards negative infinity). /// - /// This is the same as <see cref="Floor(real_t)"/>, but returns an <see langword="int"/>. + /// This is the same as <see cref="Floor(float)"/>, but returns an <see langword="int"/>. + /// </summary> + /// <param name="s">The number to floor.</param> + /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FloorToInt(float s) + { + return (int)MathF.Floor(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> downward (towards negative infinity). + /// + /// This is the same as <see cref="Floor(double)"/>, but returns an <see langword="int"/>. /// </summary> /// <param name="s">The number to floor.</param> /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> - public static int FloorToInt(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FloorToInt(double s) { return (int)Math.Floor(s); } @@ -73,11 +111,25 @@ namespace Godot /// <summary> /// Rounds <paramref name="s"/> to the nearest whole number. /// - /// This is the same as <see cref="Round(real_t)"/>, but returns an <see langword="int"/>. + /// This is the same as <see cref="Round(float)"/>, but returns an <see langword="int"/>. /// </summary> /// <param name="s">The number to round.</param> /// <returns>The rounded number.</returns> - public static int RoundToInt(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int RoundToInt(float s) + { + return (int)MathF.Round(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> to the nearest whole number. + /// + /// This is the same as <see cref="Round(double)"/>, but returns an <see langword="int"/>. + /// </summary> + /// <param name="s">The number to round.</param> + /// <returns>The rounded number.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int RoundToInt(double s) { return (int)Math.Round(s); } @@ -87,23 +139,53 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The sine and cosine of that angle.</returns> - public static (real_t Sin, real_t Cos) SinCos(real_t s) + public static (float Sin, float Cos) SinCos(float s) + { + return MathF.SinCos(s); + } + + /// <summary> + /// Returns the sine and cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The sine and cosine of that angle.</returns> + public static (double Sin, double Cos) SinCos(double s) { - (double sin, double cos) = Math.SinCos(s); - return ((real_t)sin, (real_t)cos); + return Math.SinCos(s); + } + + /// <summary> + /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately + /// equal to each other. + /// The comparison is done using the provided tolerance value. + /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(float, float)"/>. + /// </summary> + /// <param name="a">One of the values.</param> + /// <param name="b">The other value.</param> + /// <param name="tolerance">The pre-calculated tolerance value.</param> + /// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns> + public static bool IsEqualApprox(float a, float b, float tolerance) + { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) + { + return true; + } + // Then check for approximate equality. + return Math.Abs(a - b) < tolerance; } /// <summary> /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately /// equal to each other. /// The comparison is done using the provided tolerance value. - /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(real_t, real_t)"/>. + /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(double, double)"/>. /// </summary> /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <param name="tolerance">The pre-calculated tolerance value.</param> /// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns> - public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance) + public static bool IsEqualApprox(double a, double b, double tolerance) { // Check for exact equality first, required to handle "infinity" values. if (a == b) @@ -111,7 +193,7 @@ namespace Godot return true; } // Then check for approximate equality. - return Abs(a - b) < tolerance; + return Math.Abs(a - b) < tolerance; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs index 5a0ea2ba13..71a2adadcb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -68,7 +68,7 @@ namespace Godot.NativeInterop string file = globalFrames.Count > 0 ? globalFrames[0].File ?? "" : ""; string func = globalFrames.Count > 0 ? globalFrames[0].Func : ""; int line = globalFrames.Count > 0 ? globalFrames[0].Line : 0; - string errorMsg = "Exception"; + string errorMsg = e.GetType().FullName ?? ""; using godot_string nFile = Marshaling.ConvertStringToNative(file); using godot_string nFunc = Marshaling.ConvertStringToNative(func); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index fa79c2efbc..43e7c7eb9a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -104,7 +104,7 @@ namespace Godot.NativeInterop } } - [StructLayout(LayoutKind.Explicit)] + [StructLayout(LayoutKind.Sequential, Pack = 8)] // ReSharper disable once InconsistentNaming public ref struct godot_variant { @@ -113,11 +113,11 @@ namespace Godot.NativeInterop => (godot_variant*)Unsafe.AsPointer(ref Unsafe.AsRef(in _typeField)); // Variant.Type is generated as an enum of type long, so we can't use for the field as it must only take 32-bits. - [FieldOffset(0)] private int _typeField; + private int _typeField; // There's padding here - [FieldOffset(8)] private godot_variant_data _data; + private godot_variant_data _data; [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming @@ -126,10 +126,10 @@ namespace Godot.NativeInterop [FieldOffset(0)] public godot_bool _bool; [FieldOffset(0)] public long _int; [FieldOffset(0)] public double _float; - [FieldOffset(0)] public Transform2D* _transform2D; - [FieldOffset(0)] public AABB* _aabb; + [FieldOffset(0)] public Transform2D* _transform2d; + [FieldOffset(0)] public Aabb* _aabb; [FieldOffset(0)] public Basis* _basis; - [FieldOffset(0)] public Transform3D* _transform3D; + [FieldOffset(0)] public Transform3D* _transform3d; [FieldOffset(0)] public Projection* _projection; [FieldOffset(0)] private godot_variant_data_mem _mem; @@ -137,18 +137,18 @@ namespace Godot.NativeInterop [FieldOffset(0)] public godot_string_name _m_string_name; [FieldOffset(0)] public godot_string _m_string; [FieldOffset(0)] public Vector4 _m_vector4; - [FieldOffset(0)] public Vector4i _m_vector4i; + [FieldOffset(0)] public Vector4I _m_vector4i; [FieldOffset(0)] public Vector3 _m_vector3; - [FieldOffset(0)] public Vector3i _m_vector3i; + [FieldOffset(0)] public Vector3I _m_vector3i; [FieldOffset(0)] public Vector2 _m_vector2; - [FieldOffset(0)] public Vector2i _m_vector2i; + [FieldOffset(0)] public Vector2I _m_vector2i; [FieldOffset(0)] public Rect2 _m_rect2; - [FieldOffset(0)] public Rect2i _m_rect2i; + [FieldOffset(0)] public Rect2I _m_rect2i; [FieldOffset(0)] public Plane _m_plane; [FieldOffset(0)] public Quaternion _m_quaternion; [FieldOffset(0)] public Color _m_color; [FieldOffset(0)] public godot_node_path _m_node_path; - [FieldOffset(0)] public RID _m_rid; + [FieldOffset(0)] public Rid _m_rid; [FieldOffset(0)] public godot_variant_obj_data _m_obj_data; [FieldOffset(0)] public godot_callable _m_callable; [FieldOffset(0)] public godot_signal _m_signal; @@ -211,10 +211,10 @@ namespace Godot.NativeInterop public readonly unsafe Transform2D* Transform2D { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _data._transform2D; + get => _data._transform2d; } - public readonly unsafe AABB* AABB + public readonly unsafe Aabb* Aabb { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _data._aabb; @@ -229,7 +229,7 @@ namespace Godot.NativeInterop public readonly unsafe Transform3D* Transform3D { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _data._transform3D; + get => _data._transform3d; } public readonly unsafe Projection* Projection @@ -262,7 +262,7 @@ namespace Godot.NativeInterop set => _data._m_vector4 = value; } - public Vector4i Vector4i + public Vector4I Vector4I { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => _data._m_vector4i; @@ -278,7 +278,7 @@ namespace Godot.NativeInterop set => _data._m_vector3 = value; } - public Vector3i Vector3i + public Vector3I Vector3I { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => _data._m_vector3i; @@ -294,7 +294,7 @@ namespace Godot.NativeInterop set => _data._m_vector2 = value; } - public Vector2i Vector2i + public Vector2I Vector2I { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => _data._m_vector2i; @@ -310,7 +310,7 @@ namespace Godot.NativeInterop set => _data._m_rect2 = value; } - public Rect2i Rect2i + public Rect2I Rect2I { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => _data._m_rect2i; @@ -350,7 +350,7 @@ namespace Godot.NativeInterop set => _data._m_node_path = value; } - public RID RID + public Rid Rid { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get => _data._m_rid; @@ -405,13 +405,13 @@ namespace Godot.NativeInterop case Variant.Type.Int: case Variant.Type.Float: case Variant.Type.Vector2: - case Variant.Type.Vector2i: + case Variant.Type.Vector2I: case Variant.Type.Rect2: - case Variant.Type.Rect2i: + case Variant.Type.Rect2I: case Variant.Type.Vector3: - case Variant.Type.Vector3i: + case Variant.Type.Vector3I: case Variant.Type.Vector4: - case Variant.Type.Vector4i: + case Variant.Type.Vector4I: case Variant.Type.Plane: case Variant.Type.Quaternion: case Variant.Type.Color: @@ -697,6 +697,9 @@ namespace Godot.NativeInterop private uint _safeRefCount; public VariantVector _arrayVector; + + private unsafe godot_variant* _readOnly; + // There are more fields here, but we don't care as we never store this in C# public readonly int Size @@ -704,6 +707,12 @@ namespace Godot.NativeInterop [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _arrayVector.Size; } + + public readonly unsafe bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _readOnly != null; + } } [StructLayout(LayoutKind.Sequential)] @@ -737,6 +746,12 @@ namespace Godot.NativeInterop get => _p != null ? _p->Size : 0; } + public readonly unsafe bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _p != null && _p->IsReadOnly; + } + public unsafe void Dispose() { if (_p == null) @@ -766,35 +781,59 @@ namespace Godot.NativeInterop // A correctly constructed value needs to call the native default constructor to allocate `_p`. // Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine). - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Explicit)] // ReSharper disable once InconsistentNaming public ref struct godot_dictionary { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal readonly unsafe godot_dictionary* GetUnsafeAddress() - => (godot_dictionary*)Unsafe.AsPointer(ref Unsafe.AsRef(in _p)); + => (godot_dictionary*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper)); - private IntPtr _p; + [FieldOffset(0)] private byte _getUnsafeAddressHelper; - public readonly bool IsAllocated + [FieldOffset(0)] private unsafe DictionaryPrivate* _p; + + [StructLayout(LayoutKind.Sequential)] + private struct DictionaryPrivate + { + private uint _safeRefCount; + + private unsafe godot_variant* _readOnly; + + // There are more fields here, but we don't care as we never store this in C# + + public readonly unsafe bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _readOnly != null; + } + } + + public readonly unsafe bool IsAllocated { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _p != IntPtr.Zero; + get => _p != null; } - public void Dispose() + public readonly unsafe bool IsReadOnly { - if (_p == IntPtr.Zero) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _p != null && _p->IsReadOnly; + } + + public unsafe void Dispose() + { + if (_p == null) return; NativeFuncs.godotsharp_dictionary_destroy(ref this); - _p = IntPtr.Zero; + _p = null; } [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming internal struct movable { - private IntPtr _p; + private unsafe DictionaryPrivate* _p; public static unsafe explicit operator movable(in godot_dictionary value) => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs index 82f1c04d40..cc308bfdb3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs @@ -8,7 +8,7 @@ namespace Godot.NativeInterop { internal static class InteropUtils { - public static Object UnmanagedGetManaged(IntPtr unmanaged) + public static GodotObject UnmanagedGetManaged(IntPtr unmanaged) { // The native pointer may be null if (unmanaged == IntPtr.Zero) @@ -23,7 +23,7 @@ namespace Godot.NativeInterop unmanaged, out hasCsScriptInstance); if (gcHandlePtr != IntPtr.Zero) - return (Object)GCHandle.FromIntPtr(gcHandlePtr).Target; + return (GodotObject)GCHandle.FromIntPtr(gcHandlePtr).Target; // Otherwise, if the object has a CSharpInstance script instance, return null @@ -37,17 +37,17 @@ namespace Godot.NativeInterop object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null; if (target != null) - return (Object)target; + return (GodotObject)target; // If the native instance binding GC handle target was collected, create a new one gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed( unmanaged, gcHandlePtr); - return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null; + return gcHandlePtr != IntPtr.Zero ? (GodotObject)GCHandle.FromIntPtr(gcHandlePtr).Target : null; } - public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged, + public static void TieManagedToUnmanaged(GodotObject managed, IntPtr unmanaged, StringName nativeName, bool refCounted, Type type, Type nativeType) { var gcHandle = refCounted ? @@ -76,7 +76,7 @@ namespace Godot.NativeInterop } } - public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged, + public static void TieManagedToUnmanagedWithPreSetup(GodotObject managed, IntPtr unmanaged, Type type, Type nativeType) { if (type == nativeType) @@ -87,7 +87,7 @@ namespace Godot.NativeInterop GCHandle.ToIntPtr(strongGCHandle), unmanaged); } - public static Object EngineGetSingleton(string name) + public static GodotObject EngineGetSingleton(string name) { using godot_string src = Marshaling.ConvertStringToNative(name); return UnmanagedGetManaged(NativeFuncs.godotsharp_engine_get_singleton(src)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 0d9a698af0..93a83b701b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -51,29 +51,29 @@ namespace Godot.NativeInterop if (type == typeof(Vector2)) return Variant.Type.Vector2; - if (type == typeof(Vector2i)) - return Variant.Type.Vector2i; + if (type == typeof(Vector2I)) + return Variant.Type.Vector2I; if (type == typeof(Rect2)) return Variant.Type.Rect2; - if (type == typeof(Rect2i)) - return Variant.Type.Rect2i; + if (type == typeof(Rect2I)) + return Variant.Type.Rect2I; if (type == typeof(Transform2D)) - return Variant.Type.Transform2d; + return Variant.Type.Transform2D; if (type == typeof(Vector3)) return Variant.Type.Vector3; - if (type == typeof(Vector3i)) - return Variant.Type.Vector3i; + if (type == typeof(Vector3I)) + return Variant.Type.Vector3I; if (type == typeof(Vector4)) return Variant.Type.Vector4; - if (type == typeof(Vector4i)) - return Variant.Type.Vector4i; + if (type == typeof(Vector4I)) + return Variant.Type.Vector4I; if (type == typeof(Basis)) return Variant.Type.Basis; @@ -82,12 +82,12 @@ namespace Godot.NativeInterop return Variant.Type.Quaternion; if (type == typeof(Transform3D)) - return Variant.Type.Transform3d; + return Variant.Type.Transform3D; if (type == typeof(Projection)) return Variant.Type.Projection; - if (type == typeof(AABB)) + if (type == typeof(Aabb)) return Variant.Type.Aabb; if (type == typeof(Color)) @@ -140,15 +140,15 @@ namespace Godot.NativeInterop if (type == typeof(NodePath[])) return Variant.Type.Array; - if (type == typeof(RID[])) + if (type == typeof(Rid[])) return Variant.Type.Array; - if (typeof(Godot.Object[]).IsAssignableFrom(type)) + if (typeof(GodotObject[]).IsAssignableFrom(type)) return Variant.Type.Array; } else if (type.IsGenericType) { - if (typeof(Godot.Object).IsAssignableFrom(type)) + if (typeof(GodotObject).IsAssignableFrom(type)) return Variant.Type.Object; // We use `IsAssignableFrom` with our helper interfaces to detect generic Godot collections @@ -167,7 +167,7 @@ namespace Godot.NativeInterop } else { - if (typeof(Godot.Object).IsAssignableFrom(type)) + if (typeof(GodotObject).IsAssignableFrom(type)) return Variant.Type.Object; if (typeof(StringName) == type) @@ -176,7 +176,7 @@ namespace Godot.NativeInterop if (typeof(NodePath) == type) return Variant.Type.NodePath; - if (typeof(RID) == type) + if (typeof(Rid) == type) return Variant.Type.Rid; if (typeof(Collections.Dictionary) == type) @@ -232,7 +232,7 @@ namespace Godot.NativeInterop var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate); IntPtr objectPtr = p_managed_callable.Target != null ? - Object.GetPtr(p_managed_callable.Target) : + GodotObject.GetPtr(p_managed_callable.Target) : IntPtr.Zero; unsafe @@ -310,7 +310,7 @@ namespace Godot.NativeInterop public static Signal ConvertSignalToManaged(in godot_signal p_signal) { - var owner = GD.InstanceFromId(p_signal.ObjectId); + var owner = GodotObject.InstanceFromId(p_signal.ObjectId); var name = StringName.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_string_name_new_copy(p_signal.Name)); return new Signal(owner, name); @@ -319,7 +319,7 @@ namespace Godot.NativeInterop // Array internal static T[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(in godot_array p_array) - where T : Godot.Object + where T : GodotObject { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); @@ -361,16 +361,16 @@ namespace Godot.NativeInterop return ret; } - internal static RID[] ConvertNativeGodotArrayToSystemArrayOfRID(in godot_array p_array) + internal static Rid[] ConvertNativeGodotArrayToSystemArrayOfRid(in godot_array p_array) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); int length = array.Count; - var ret = new RID[length]; + var ret = new Rid[length]; for (int i = 0; i < length; i++) - ret[i] = array[i].AsRID(); + ret[i] = array[i].AsRid(); return ret; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 57488bd586..1e23689c95 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -184,7 +184,7 @@ namespace Godot.NativeInterop public static partial void godotsharp_variant_new_projection(out godot_variant r_dest, in Projection p_proj); - public static partial void godotsharp_variant_new_aabb(out godot_variant r_dest, in AABB p_aabb); + public static partial void godotsharp_variant_new_aabb(out godot_variant r_dest, in Aabb p_aabb); public static partial void godotsharp_variant_new_dictionary(out godot_variant r_dest, in godot_dictionary p_dict); @@ -228,27 +228,27 @@ namespace Godot.NativeInterop public static partial Vector2 godotsharp_variant_as_vector2(in godot_variant p_self); - public static partial Vector2i godotsharp_variant_as_vector2i(in godot_variant p_self); + public static partial Vector2I godotsharp_variant_as_vector2i(in godot_variant p_self); public static partial Rect2 godotsharp_variant_as_rect2(in godot_variant p_self); - public static partial Rect2i godotsharp_variant_as_rect2i(in godot_variant p_self); + public static partial Rect2I godotsharp_variant_as_rect2i(in godot_variant p_self); public static partial Vector3 godotsharp_variant_as_vector3(in godot_variant p_self); - public static partial Vector3i godotsharp_variant_as_vector3i(in godot_variant p_self); + public static partial Vector3I godotsharp_variant_as_vector3i(in godot_variant p_self); public static partial Transform2D godotsharp_variant_as_transform2d(in godot_variant p_self); public static partial Vector4 godotsharp_variant_as_vector4(in godot_variant p_self); - public static partial Vector4i godotsharp_variant_as_vector4i(in godot_variant p_self); + public static partial Vector4I godotsharp_variant_as_vector4i(in godot_variant p_self); public static partial Plane godotsharp_variant_as_plane(in godot_variant p_self); public static partial Quaternion godotsharp_variant_as_quaternion(in godot_variant p_self); - public static partial AABB godotsharp_variant_as_aabb(in godot_variant p_self); + public static partial Aabb godotsharp_variant_as_aabb(in godot_variant p_self); public static partial Basis godotsharp_variant_as_basis(in godot_variant p_self); @@ -262,7 +262,7 @@ namespace Godot.NativeInterop public static partial godot_node_path godotsharp_variant_as_node_path(in godot_variant p_self); - public static partial RID godotsharp_variant_as_rid(in godot_variant p_self); + public static partial Rid godotsharp_variant_as_rid(in godot_variant p_self); public static partial godot_callable godotsharp_variant_as_callable(in godot_variant p_self); @@ -376,6 +376,8 @@ namespace Godot.NativeInterop public static partial Error godotsharp_array_resize(ref godot_array p_self, int p_new_size); + public static partial void godotsharp_array_make_read_only(ref godot_array p_self); + public static partial void godotsharp_array_shuffle(ref godot_array p_self); public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str); @@ -409,9 +411,15 @@ namespace Godot.NativeInterop public static partial void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep, out godot_dictionary r_dest); + public static partial void godotsharp_dictionary_merge(ref godot_dictionary p_self, in godot_dictionary p_dictionary, godot_bool p_overwrite); + + public static partial godot_bool godotsharp_dictionary_recursive_equal(ref godot_dictionary p_self, in godot_dictionary p_other); + public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, in godot_variant p_key); + public static partial void godotsharp_dictionary_make_read_only(ref godot_dictionary p_self); + public static partial void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str); // StringExtensions @@ -494,8 +502,6 @@ namespace Godot.NativeInterop internal static partial void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref); - internal static partial void godotsharp_str(in godot_array p_what, out godot_string r_ret); - internal static partial void godotsharp_str_to_var(in godot_string p_str, out godot_variant r_ret); internal static partial void godotsharp_var_to_bytes(in godot_variant p_what, godot_bool p_full_objects, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs index 9f0b55431b..44ec16dca9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -18,20 +18,20 @@ namespace Godot.NativeInterop return new godot_variant() { Float = src.Float, Type = Variant.Type.Float }; case Variant.Type.Vector2: return new godot_variant() { Vector2 = src.Vector2, Type = Variant.Type.Vector2 }; - case Variant.Type.Vector2i: - return new godot_variant() { Vector2i = src.Vector2i, Type = Variant.Type.Vector2i }; + case Variant.Type.Vector2I: + return new godot_variant() { Vector2I = src.Vector2I, Type = Variant.Type.Vector2I }; case Variant.Type.Rect2: return new godot_variant() { Rect2 = src.Rect2, Type = Variant.Type.Rect2 }; - case Variant.Type.Rect2i: - return new godot_variant() { Rect2i = src.Rect2i, Type = Variant.Type.Rect2i }; + case Variant.Type.Rect2I: + return new godot_variant() { Rect2I = src.Rect2I, Type = Variant.Type.Rect2I }; case Variant.Type.Vector3: return new godot_variant() { Vector3 = src.Vector3, Type = Variant.Type.Vector3 }; - case Variant.Type.Vector3i: - return new godot_variant() { Vector3i = src.Vector3i, Type = Variant.Type.Vector3i }; + case Variant.Type.Vector3I: + return new godot_variant() { Vector3I = src.Vector3I, Type = Variant.Type.Vector3I }; case Variant.Type.Vector4: return new godot_variant() { Vector4 = src.Vector4, Type = Variant.Type.Vector4 }; - case Variant.Type.Vector4i: - return new godot_variant() { Vector4i = src.Vector4i, Type = Variant.Type.Vector4i }; + case Variant.Type.Vector4I: + return new godot_variant() { Vector4I = src.Vector4I, Type = Variant.Type.Vector4I }; case Variant.Type.Plane: return new godot_variant() { Plane = src.Plane, Type = Variant.Type.Plane }; case Variant.Type.Quaternion: @@ -39,7 +39,7 @@ namespace Godot.NativeInterop case Variant.Type.Color: return new godot_variant() { Color = src.Color, Type = Variant.Type.Color }; case Variant.Type.Rid: - return new godot_variant() { RID = src.RID, Type = Variant.Type.Rid }; + return new godot_variant() { Rid = src.Rid, Type = Variant.Type.Rid }; } godotsharp_variant_new_copy(out godot_variant ret, src); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 9c9258dd9e..e6bcd9393d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -10,8 +10,8 @@ namespace Godot.NativeInterop { public static partial class VariantUtils { - public static godot_variant CreateFromRID(RID from) - => new() { Type = Variant.Type.Rid, RID = from }; + public static godot_variant CreateFromRid(Rid from) + => new() { Type = Variant.Type.Rid, Rid = from }; public static godot_variant CreateFromBool(bool from) => new() { Type = Variant.Type.Bool, Bool = from.ToGodotBool() }; @@ -28,26 +28,26 @@ namespace Godot.NativeInterop public static godot_variant CreateFromVector2(Vector2 from) => new() { Type = Variant.Type.Vector2, Vector2 = from }; - public static godot_variant CreateFromVector2i(Vector2i from) - => new() { Type = Variant.Type.Vector2i, Vector2i = from }; + public static godot_variant CreateFromVector2I(Vector2I from) + => new() { Type = Variant.Type.Vector2I, Vector2I = from }; public static godot_variant CreateFromVector3(Vector3 from) => new() { Type = Variant.Type.Vector3, Vector3 = from }; - public static godot_variant CreateFromVector3i(Vector3i from) - => new() { Type = Variant.Type.Vector3i, Vector3i = from }; + public static godot_variant CreateFromVector3I(Vector3I from) + => new() { Type = Variant.Type.Vector3I, Vector3I = from }; public static godot_variant CreateFromVector4(Vector4 from) => new() { Type = Variant.Type.Vector4, Vector4 = from }; - public static godot_variant CreateFromVector4i(Vector4i from) - => new() { Type = Variant.Type.Vector4i, Vector4i = from }; + public static godot_variant CreateFromVector4I(Vector4I from) + => new() { Type = Variant.Type.Vector4I, Vector4I = from }; public static godot_variant CreateFromRect2(Rect2 from) => new() { Type = Variant.Type.Rect2, Rect2 = from }; - public static godot_variant CreateFromRect2i(Rect2i from) - => new() { Type = Variant.Type.Rect2i, Rect2i = from }; + public static godot_variant CreateFromRect2I(Rect2I from) + => new() { Type = Variant.Type.Rect2I, Rect2I = from }; public static godot_variant CreateFromQuaternion(Quaternion from) => new() { Type = Variant.Type.Quaternion, Quaternion = from }; @@ -82,7 +82,7 @@ namespace Godot.NativeInterop return ret; } - public static godot_variant CreateFromAABB(AABB from) + public static godot_variant CreateFromAabb(Aabb from) { NativeFuncs.godotsharp_variant_new_aabb(out godot_variant ret, from); return ret; @@ -237,11 +237,11 @@ namespace Godot.NativeInterop public static godot_variant CreateFromSystemArrayOfNodePath(Span<NodePath> from) => CreateFromArray(new Collections.Array(from)); - public static godot_variant CreateFromSystemArrayOfRID(Span<RID> from) + public static godot_variant CreateFromSystemArrayOfRid(Span<Rid> from) => CreateFromArray(new Collections.Array(from)); // ReSharper disable once RedundantNameQualifier - public static godot_variant CreateFromSystemArrayOfGodotObject(Godot.Object[]? from) + public static godot_variant CreateFromSystemArrayOfGodotObject(GodotObject[]? from) { if (from == null) return default; // Nil @@ -260,7 +260,7 @@ namespace Godot.NativeInterop => from != null ? CreateFromArray((godot_array)from.NativeValue) : default; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_variant CreateFromArray<T>(Array<T>? from) + public static godot_variant CreateFromArray<[MustBeVariant] T>(Array<T>? from) => from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default; public static godot_variant CreateFromDictionary(godot_dictionary from) @@ -274,7 +274,7 @@ namespace Godot.NativeInterop => from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_variant CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue>? from) + public static godot_variant CreateFromDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>(Dictionary<TKey, TValue>? from) => from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default; public static godot_variant CreateFromStringName(godot_string_name from) @@ -307,8 +307,8 @@ namespace Godot.NativeInterop [MethodImpl(MethodImplOptions.AggressiveInlining)] // ReSharper disable once RedundantNameQualifier - public static godot_variant CreateFromGodotObject(Godot.Object? from) - => from != null ? CreateFromGodotObjectPtr(Object.GetPtr(from)) : default; + public static godot_variant CreateFromGodotObject(GodotObject? from) + => from != null ? CreateFromGodotObjectPtr(GodotObject.GetPtr(from)) : default; // We avoid the internal call if the stored type is the same we want. @@ -375,9 +375,9 @@ namespace Godot.NativeInterop p_var.Vector2 : NativeFuncs.godotsharp_variant_as_vector2(p_var); - public static Vector2i ConvertToVector2i(in godot_variant p_var) - => p_var.Type == Variant.Type.Vector2i ? - p_var.Vector2i : + public static Vector2I ConvertToVector2I(in godot_variant p_var) + => p_var.Type == Variant.Type.Vector2I ? + p_var.Vector2I : NativeFuncs.godotsharp_variant_as_vector2i(p_var); public static Rect2 ConvertToRect2(in godot_variant p_var) @@ -385,13 +385,13 @@ namespace Godot.NativeInterop p_var.Rect2 : NativeFuncs.godotsharp_variant_as_rect2(p_var); - public static Rect2i ConvertToRect2i(in godot_variant p_var) - => p_var.Type == Variant.Type.Rect2i ? - p_var.Rect2i : + public static Rect2I ConvertToRect2I(in godot_variant p_var) + => p_var.Type == Variant.Type.Rect2I ? + p_var.Rect2I : NativeFuncs.godotsharp_variant_as_rect2i(p_var); public static unsafe Transform2D ConvertToTransform2D(in godot_variant p_var) - => p_var.Type == Variant.Type.Transform2d ? + => p_var.Type == Variant.Type.Transform2D ? *p_var.Transform2D : NativeFuncs.godotsharp_variant_as_transform2d(p_var); @@ -400,9 +400,9 @@ namespace Godot.NativeInterop p_var.Vector3 : NativeFuncs.godotsharp_variant_as_vector3(p_var); - public static Vector3i ConvertToVector3i(in godot_variant p_var) - => p_var.Type == Variant.Type.Vector3i ? - p_var.Vector3i : + public static Vector3I ConvertToVector3I(in godot_variant p_var) + => p_var.Type == Variant.Type.Vector3I ? + p_var.Vector3I : NativeFuncs.godotsharp_variant_as_vector3i(p_var); public static unsafe Vector4 ConvertToVector4(in godot_variant p_var) @@ -410,9 +410,9 @@ namespace Godot.NativeInterop p_var.Vector4 : NativeFuncs.godotsharp_variant_as_vector4(p_var); - public static unsafe Vector4i ConvertToVector4i(in godot_variant p_var) - => p_var.Type == Variant.Type.Vector4i ? - p_var.Vector4i : + public static unsafe Vector4I ConvertToVector4I(in godot_variant p_var) + => p_var.Type == Variant.Type.Vector4I ? + p_var.Vector4I : NativeFuncs.godotsharp_variant_as_vector4i(p_var); public static unsafe Basis ConvertToBasis(in godot_variant p_var) @@ -426,7 +426,7 @@ namespace Godot.NativeInterop NativeFuncs.godotsharp_variant_as_quaternion(p_var); public static unsafe Transform3D ConvertToTransform3D(in godot_variant p_var) - => p_var.Type == Variant.Type.Transform3d ? + => p_var.Type == Variant.Type.Transform3D ? *p_var.Transform3D : NativeFuncs.godotsharp_variant_as_transform3d(p_var); @@ -435,9 +435,9 @@ namespace Godot.NativeInterop *p_var.Projection : NativeFuncs.godotsharp_variant_as_projection(p_var); - public static unsafe AABB ConvertToAABB(in godot_variant p_var) + public static unsafe Aabb ConvertToAabb(in godot_variant p_var) => p_var.Type == Variant.Type.Aabb ? - *p_var.AABB : + *p_var.Aabb : NativeFuncs.godotsharp_variant_as_aabb(p_var); public static Color ConvertToColor(in godot_variant p_var) @@ -450,9 +450,9 @@ namespace Godot.NativeInterop p_var.Plane : NativeFuncs.godotsharp_variant_as_plane(p_var); - public static RID ConvertToRID(in godot_variant p_var) + public static Rid ConvertToRid(in godot_variant p_var) => p_var.Type == Variant.Type.Rid ? - p_var.RID : + p_var.Rid : NativeFuncs.godotsharp_variant_as_rid(p_var); public static IntPtr ConvertToGodotObjectPtr(in godot_variant p_var) @@ -460,7 +460,7 @@ namespace Godot.NativeInterop [MethodImpl(MethodImplOptions.AggressiveInlining)] // ReSharper disable once RedundantNameQualifier - public static Godot.Object ConvertToGodotObject(in godot_variant p_var) + public static GodotObject ConvertToGodotObject(in godot_variant p_var) => InteropUtils.UnmanagedGetManaged(ConvertToGodotObjectPtr(p_var)); public static string ConvertToString(in godot_variant p_var) @@ -526,7 +526,7 @@ namespace Godot.NativeInterop => Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Array<T> ConvertToArray<T>(in godot_variant p_var) + public static Array<T> ConvertToArray<[MustBeVariant] T>(in godot_variant p_var) => Array<T>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var)); public static godot_dictionary ConvertToNativeDictionary(in godot_variant p_var) @@ -539,7 +539,7 @@ namespace Godot.NativeInterop => Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Dictionary<TKey, TValue> ConvertToDictionary<TKey, TValue>(in godot_variant p_var) + public static Dictionary<TKey, TValue> ConvertToDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>(in godot_variant p_var) => Dictionary<TKey, TValue>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var)); public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var) @@ -608,15 +608,15 @@ namespace Godot.NativeInterop return Marshaling.ConvertNativeGodotArrayToSystemArrayOfNodePath(godotArray); } - public static RID[] ConvertToSystemArrayOfRID(in godot_variant p_var) + public static Rid[] ConvertToSystemArrayOfRid(in godot_variant p_var) { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); - return Marshaling.ConvertNativeGodotArrayToSystemArrayOfRID(godotArray); + return Marshaling.ConvertNativeGodotArrayToSystemArrayOfRid(godotArray); } public static T[] ConvertToSystemArrayOfGodotObject<T>(in godot_variant p_var) // ReSharper disable once RedundantNameQualifier - where T : Godot.Object + where T : GodotObject { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(godotArray); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs index 3d64533269..12b0a47079 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs @@ -80,14 +80,14 @@ public partial class VariantUtils if (typeof(T) == typeof(Vector2)) return CreateFromVector2(UnsafeAs<Vector2>(from)); - if (typeof(T) == typeof(Vector2i)) - return CreateFromVector2i(UnsafeAs<Vector2i>(from)); + if (typeof(T) == typeof(Vector2I)) + return CreateFromVector2I(UnsafeAs<Vector2I>(from)); if (typeof(T) == typeof(Rect2)) return CreateFromRect2(UnsafeAs<Rect2>(from)); - if (typeof(T) == typeof(Rect2i)) - return CreateFromRect2i(UnsafeAs<Rect2i>(from)); + if (typeof(T) == typeof(Rect2I)) + return CreateFromRect2I(UnsafeAs<Rect2I>(from)); if (typeof(T) == typeof(Transform2D)) return CreateFromTransform2D(UnsafeAs<Transform2D>(from)); @@ -98,8 +98,8 @@ public partial class VariantUtils if (typeof(T) == typeof(Vector3)) return CreateFromVector3(UnsafeAs<Vector3>(from)); - if (typeof(T) == typeof(Vector3i)) - return CreateFromVector3i(UnsafeAs<Vector3i>(from)); + if (typeof(T) == typeof(Vector3I)) + return CreateFromVector3I(UnsafeAs<Vector3I>(from)); if (typeof(T) == typeof(Basis)) return CreateFromBasis(UnsafeAs<Basis>(from)); @@ -113,11 +113,11 @@ public partial class VariantUtils if (typeof(T) == typeof(Vector4)) return CreateFromVector4(UnsafeAs<Vector4>(from)); - if (typeof(T) == typeof(Vector4i)) - return CreateFromVector4i(UnsafeAs<Vector4i>(from)); + if (typeof(T) == typeof(Vector4I)) + return CreateFromVector4I(UnsafeAs<Vector4I>(from)); - if (typeof(T) == typeof(AABB)) - return CreateFromAABB(UnsafeAs<AABB>(from)); + if (typeof(T) == typeof(Aabb)) + return CreateFromAabb(UnsafeAs<Aabb>(from)); if (typeof(T) == typeof(Color)) return CreateFromColor(UnsafeAs<Color>(from)); @@ -167,8 +167,8 @@ public partial class VariantUtils if (typeof(T) == typeof(NodePath[])) return CreateFromSystemArrayOfNodePath(UnsafeAs<NodePath[]>(from)); - if (typeof(T) == typeof(RID[])) - return CreateFromSystemArrayOfRID(UnsafeAs<RID[]>(from)); + if (typeof(T) == typeof(Rid[])) + return CreateFromSystemArrayOfRid(UnsafeAs<Rid[]>(from)); if (typeof(T) == typeof(StringName)) return CreateFromStringName(UnsafeAs<StringName>(from)); @@ -176,8 +176,8 @@ public partial class VariantUtils if (typeof(T) == typeof(NodePath)) return CreateFromNodePath(UnsafeAs<NodePath>(from)); - if (typeof(T) == typeof(RID)) - return CreateFromRID(UnsafeAs<RID>(from)); + if (typeof(T) == typeof(Rid)) + return CreateFromRid(UnsafeAs<Rid>(from)); if (typeof(T) == typeof(Godot.Collections.Dictionary)) return CreateFromDictionary(UnsafeAs<Godot.Collections.Dictionary>(from)); @@ -192,8 +192,8 @@ public partial class VariantUtils // `typeof(X).IsAssignableFrom(typeof(T))` is optimized away - if (typeof(Godot.Object).IsAssignableFrom(typeof(T))) - return CreateFromGodotObject(UnsafeAs<Godot.Object>(from)); + if (typeof(GodotObject).IsAssignableFrom(typeof(T))) + return CreateFromGodotObject(UnsafeAs<GodotObject>(from)); // `typeof(T).IsValueType` is optimized away // `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113 @@ -269,14 +269,14 @@ public partial class VariantUtils if (typeof(T) == typeof(Vector2)) return UnsafeAsT(ConvertToVector2(variant)); - if (typeof(T) == typeof(Vector2i)) - return UnsafeAsT(ConvertToVector2i(variant)); + if (typeof(T) == typeof(Vector2I)) + return UnsafeAsT(ConvertToVector2I(variant)); if (typeof(T) == typeof(Rect2)) return UnsafeAsT(ConvertToRect2(variant)); - if (typeof(T) == typeof(Rect2i)) - return UnsafeAsT(ConvertToRect2i(variant)); + if (typeof(T) == typeof(Rect2I)) + return UnsafeAsT(ConvertToRect2I(variant)); if (typeof(T) == typeof(Transform2D)) return UnsafeAsT(ConvertToTransform2D(variant)); @@ -284,8 +284,8 @@ public partial class VariantUtils if (typeof(T) == typeof(Vector3)) return UnsafeAsT(ConvertToVector3(variant)); - if (typeof(T) == typeof(Vector3i)) - return UnsafeAsT(ConvertToVector3i(variant)); + if (typeof(T) == typeof(Vector3I)) + return UnsafeAsT(ConvertToVector3I(variant)); if (typeof(T) == typeof(Basis)) return UnsafeAsT(ConvertToBasis(variant)); @@ -302,11 +302,11 @@ public partial class VariantUtils if (typeof(T) == typeof(Vector4)) return UnsafeAsT(ConvertToVector4(variant)); - if (typeof(T) == typeof(Vector4i)) - return UnsafeAsT(ConvertToVector4i(variant)); + if (typeof(T) == typeof(Vector4I)) + return UnsafeAsT(ConvertToVector4I(variant)); - if (typeof(T) == typeof(AABB)) - return UnsafeAsT(ConvertToAABB(variant)); + if (typeof(T) == typeof(Aabb)) + return UnsafeAsT(ConvertToAabb(variant)); if (typeof(T) == typeof(Color)) return UnsafeAsT(ConvertToColor(variant)); @@ -356,8 +356,8 @@ public partial class VariantUtils if (typeof(T) == typeof(NodePath[])) return UnsafeAsT(ConvertToSystemArrayOfNodePath(variant)); - if (typeof(T) == typeof(RID[])) - return UnsafeAsT(ConvertToSystemArrayOfRID(variant)); + if (typeof(T) == typeof(Rid[])) + return UnsafeAsT(ConvertToSystemArrayOfRid(variant)); if (typeof(T) == typeof(StringName)) return UnsafeAsT(ConvertToStringName(variant)); @@ -365,8 +365,8 @@ public partial class VariantUtils if (typeof(T) == typeof(NodePath)) return UnsafeAsT(ConvertToNodePath(variant)); - if (typeof(T) == typeof(RID)) - return UnsafeAsT(ConvertToRID(variant)); + if (typeof(T) == typeof(Rid)) + return UnsafeAsT(ConvertToRid(variant)); if (typeof(T) == typeof(Godot.Collections.Dictionary)) return UnsafeAsT(ConvertToDictionary(variant)); @@ -381,7 +381,7 @@ public partial class VariantUtils // `typeof(X).IsAssignableFrom(typeof(T))` is optimized away - if (typeof(Godot.Object).IsAssignableFrom(typeof(T))) + if (typeof(GodotObject).IsAssignableFrom(typeof(T))) return (T)(object)ConvertToGodotObject(variant); // `typeof(T).IsValueType` is optimized away diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 8a125e3c73..55b7a83fc2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -13,13 +13,14 @@ namespace Godot public struct Plane : IEquatable<Plane> { private Vector3 _normal; + private real_t _d; /// <summary> /// The normal of the plane, which must be a unit vector. /// In the scalar equation of the plane <c>ax + by + cz = d</c>, this is /// the vector <c>(a, b, c)</c>, where <c>d</c> is the <see cref="D"/> property. /// </summary> - /// <value>Equivalent to <see cref="x"/>, <see cref="y"/>, and <see cref="z"/>.</value> + /// <value>Equivalent to <see cref="X"/>, <see cref="Y"/>, and <see cref="Z"/>.</value> public Vector3 Normal { readonly get { return _normal; } @@ -27,18 +28,32 @@ namespace Godot } /// <summary> + /// The distance from the origin to the plane (in the direction of + /// <see cref="Normal"/>). This value is typically non-negative. + /// In the scalar equation of the plane <c>ax + by + cz = d</c>, + /// this is <c>d</c>, while the <c>(a, b, c)</c> coordinates are represented + /// by the <see cref="Normal"/> property. + /// </summary> + /// <value>The plane's distance from the origin.</value> + public real_t D + { + readonly get { return _d; } + set { _d = value; } + } + + /// <summary> /// The X component of the plane's normal vector. /// </summary> /// <value>Equivalent to <see cref="Normal"/>'s X value.</value> - public real_t x + public real_t X { readonly get { - return _normal.x; + return _normal.X; } set { - _normal.x = value; + _normal.X = value; } } @@ -46,15 +61,15 @@ namespace Godot /// The Y component of the plane's normal vector. /// </summary> /// <value>Equivalent to <see cref="Normal"/>'s Y value.</value> - public real_t y + public real_t Y { readonly get { - return _normal.y; + return _normal.Y; } set { - _normal.y = value; + _normal.Y = value; } } @@ -62,36 +77,26 @@ namespace Godot /// The Z component of the plane's normal vector. /// </summary> /// <value>Equivalent to <see cref="Normal"/>'s Z value.</value> - public real_t z + public real_t Z { readonly get { - return _normal.z; + return _normal.Z; } set { - _normal.z = value; + _normal.Z = value; } } /// <summary> - /// The distance from the origin to the plane (in the direction of - /// <see cref="Normal"/>). This value is typically non-negative. - /// In the scalar equation of the plane <c>ax + by + cz = d</c>, - /// this is <c>d</c>, while the <c>(a, b, c)</c> coordinates are represented - /// by the <see cref="Normal"/> property. - /// </summary> - /// <value>The plane's distance from the origin.</value> - public real_t D { get; set; } - - /// <summary> /// Returns the shortest distance from this plane to the position <paramref name="point"/>. /// </summary> /// <param name="point">The position to use for the calculation.</param> /// <returns>The shortest distance.</returns> public readonly real_t DistanceTo(Vector3 point) { - return _normal.Dot(point) - D; + return _normal.Dot(point) - _d; } /// <summary> @@ -101,7 +106,7 @@ namespace Godot /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value> public readonly Vector3 GetCenter() { - return _normal * D; + return _normal * _d; } /// <summary> @@ -113,7 +118,7 @@ namespace Godot /// <returns>A <see langword="bool"/> for whether or not the plane has the point.</returns> public readonly bool HasPoint(Vector3 point, real_t tolerance = Mathf.Epsilon) { - real_t dist = _normal.Dot(point) - D; + real_t dist = _normal.Dot(point) - _d; return Mathf.Abs(dist) <= tolerance; } @@ -133,9 +138,9 @@ namespace Godot return null; } - Vector3 result = (b._normal.Cross(c._normal) * D) + - (c._normal.Cross(_normal) * b.D) + - (_normal.Cross(b._normal) * c.D); + Vector3 result = (b._normal.Cross(c._normal) * _d) + + (c._normal.Cross(_normal) * b._d) + + (_normal.Cross(b._normal) * c._d); return result / denom; } @@ -157,7 +162,7 @@ namespace Godot return null; } - real_t dist = (_normal.Dot(from) - D) / den; + real_t dist = (_normal.Dot(from) - _d) / den; // This is a ray, before the emitting pos (from) does not exist if (dist > Mathf.Epsilon) @@ -186,7 +191,7 @@ namespace Godot return null; } - real_t dist = (_normal.Dot(begin) - D) / den; + real_t dist = (_normal.Dot(begin) - _d) / den; // Only allow dist to be in the range of 0 to 1, with tolerance. if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) @@ -214,7 +219,7 @@ namespace Godot /// <returns>A <see langword="bool"/> for whether or not the point is above the plane.</returns> public readonly bool IsPointOver(Vector3 point) { - return _normal.Dot(point) > D; + return _normal.Dot(point) > _d; } /// <summary> @@ -230,7 +235,7 @@ namespace Godot return new Plane(0, 0, 0, 0); } - return new Plane(_normal / len, D / len); + return new Plane(_normal / len, _d / len); } /// <summary> @@ -279,7 +284,7 @@ namespace Godot public Plane(real_t a, real_t b, real_t c, real_t d) { _normal = new Vector3(a, b, c); - D = d; + _d = d; } /// <summary> @@ -290,7 +295,7 @@ namespace Godot public Plane(Vector3 normal) { _normal = normal; - D = 0; + _d = 0; } /// <summary> @@ -302,7 +307,7 @@ namespace Godot public Plane(Vector3 normal, real_t d) { _normal = normal; - D = d; + _d = d; } /// <summary> @@ -314,7 +319,7 @@ namespace Godot public Plane(Vector3 normal, Vector3 point) { _normal = normal; - D = _normal.Dot(point); + _d = _normal.Dot(point); } /// <summary> @@ -327,7 +332,7 @@ namespace Godot { _normal = (v1 - v3).Cross(v1 - v2); _normal.Normalize(); - D = _normal.Dot(v1); + _d = _normal.Dot(v1); } /// <summary> @@ -341,7 +346,7 @@ namespace Godot /// <returns>The negated/flipped plane.</returns> public static Plane operator -(Plane plane) { - return new Plane(-plane._normal, -plane.D); + return new Plane(-plane._normal, -plane._d); } /// <summary> @@ -389,7 +394,7 @@ namespace Godot /// <returns>Whether or not the planes are exactly equal.</returns> public readonly bool Equals(Plane other) { - return _normal == other._normal && D == other.D; + return _normal == other._normal && _d == other._d; } /// <summary> @@ -400,7 +405,7 @@ namespace Godot /// <returns>Whether or not the planes are approximately equal.</returns> public readonly bool IsEqualApprox(Plane other) { - return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D); + return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(_d, other._d); } /// <summary> @@ -409,7 +414,7 @@ namespace Godot /// <returns>A hash code for this plane.</returns> public override readonly int GetHashCode() { - return _normal.GetHashCode() ^ D.GetHashCode(); + return _normal.GetHashCode() ^ _d.GetHashCode(); } /// <summary> @@ -418,7 +423,7 @@ namespace Godot /// <returns>A string representation of this plane.</returns> public override readonly string ToString() { - return $"{_normal}, {D}"; + return $"{_normal}, {_d}"; } /// <summary> @@ -427,7 +432,7 @@ namespace Godot /// <returns>A string representation of this plane.</returns> public readonly string ToString(string format) { - return $"{_normal.ToString(format)}, {D.ToString(format)}"; + return $"{_normal.ToString(format)}, {_d.ToString(format)}"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs index f11b3c553a..84fc73b87a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs @@ -49,22 +49,22 @@ namespace Godot /// <summary> /// The projection's X column. Also accessible by using the index position <c>[0]</c>. /// </summary> - public Vector4 x; + public Vector4 X; /// <summary> /// The projection's Y column. Also accessible by using the index position <c>[1]</c>. /// </summary> - public Vector4 y; + public Vector4 Y; /// <summary> /// The projection's Z column. Also accessible by using the index position <c>[2]</c>. /// </summary> - public Vector4 z; + public Vector4 Z; /// <summary> /// The projection's W column. Also accessible by using the index position <c>[3]</c>. /// </summary> - public Vector4 w; + public Vector4 W; /// <summary> /// Access whole columns in the form of <see cref="Vector4"/>. @@ -80,13 +80,13 @@ namespace Godot switch (column) { case 0: - return x; + return X; case 1: - return y; + return Y; case 2: - return z; + return Z; case 3: - return w; + return W; default: throw new ArgumentOutOfRangeException(nameof(column)); } @@ -96,16 +96,16 @@ namespace Godot switch (column) { case 0: - x = value; + X = value; return; case 1: - y = value; + Y = value; return; case 2: - z = value; + Z = value; return; case 3: - w = value; + W = value; return; default: throw new ArgumentOutOfRangeException(nameof(column)); @@ -128,13 +128,13 @@ namespace Godot switch (column) { case 0: - return x[row]; + return X[row]; case 1: - return y[row]; + return Y[row]; case 2: - return z[row]; + return Z[row]; case 3: - return w[row]; + return W[row]; default: throw new ArgumentOutOfRangeException(nameof(column)); } @@ -144,16 +144,16 @@ namespace Godot switch (column) { case 0: - x[row] = value; + X[row] = value; return; case 1: - y[row] = value; + Y[row] = value; return; case 2: - z[row] = value; + Z[row] = value; return; case 3: - w[row] = value; + W[row] = value; return; default: throw new ArgumentOutOfRangeException(nameof(column)); @@ -180,20 +180,20 @@ namespace Godot /// <summary> /// Creates a new <see cref="Projection"/> that scales a given projection to fit around - /// a given <see cref="AABB"/> in projection space. + /// a given <see cref="Aabb"/> in projection space. /// </summary> - /// <param name="aabb">The AABB to fit the projection around.</param> + /// <param name="aabb">The Aabb to fit the projection around.</param> /// <returns>The created projection.</returns> - public static Projection CreateFitAabb(AABB aabb) + public static Projection CreateFitAabb(Aabb aabb) { Vector3 min = aabb.Position; Vector3 max = aabb.Position + aabb.Size; return new Projection( - new Vector4(2 / (max.x - min.x), 0, 0, 0), - new Vector4(0, 2 / (max.y - min.y), 0, 0), - new Vector4(0, 0, 2 / (max.z - min.z), 0), - new Vector4(-(max.x + min.x) / (max.x - min.x), -(max.y + min.y) / (max.y - min.y), -(max.z + min.z) / (max.z - min.z), 1) + new Vector4(2 / (max.X - min.X), 0, 0, 0), + new Vector4(0, 2 / (max.Y - min.Y), 0, 0), + new Vector4(0, 0, 2 / (max.Z - min.Z), 0), + new Vector4(-(max.X + min.X) / (max.X - min.X), -(max.Y + min.Y) / (max.Y - min.Y), -(max.Z + min.Z) / (max.Z - min.Z), 1) ); } @@ -300,7 +300,7 @@ namespace Godot { size *= aspect; } - return CreateFrustum(-size / 2 + offset.x, +size / 2 + offset.x, -size / aspect / 2 + offset.y, +size / aspect / 2 + offset.y, near, far); + return CreateFrustum(-size / 2 + offset.X, +size / 2 + offset.X, -size / aspect / 2 + offset.Y, +size / aspect / 2 + offset.Y, near, far); } /// <summary> @@ -311,10 +311,10 @@ namespace Godot public static Projection CreateLightAtlasRect(Rect2 rect) { return new Projection( - new Vector4(rect.Size.x, 0, 0, 0), - new Vector4(0, rect.Size.y, 0, 0), + new Vector4(rect.Size.X, 0, 0, 0), + new Vector4(0, rect.Size.Y, 0, 0), new Vector4(0, 0, 1, 0), - new Vector4(rect.Position.x, rect.Position.y, 0, 1) + new Vector4(rect.Position.X, rect.Position.Y, 0, 1) ); } @@ -332,13 +332,13 @@ namespace Godot public static Projection CreateOrthogonal(real_t left, real_t right, real_t bottom, real_t top, real_t zNear, real_t zFar) { Projection proj = Projection.Identity; - proj.x.x = (real_t)2.0 / (right - left); - proj.w.x = -((right + left) / (right - left)); - proj.y.y = (real_t)2.0 / (top - bottom); - proj.w.y = -((top + bottom) / (top - bottom)); - proj.z.z = (real_t)(-2.0) / (zFar - zNear); - proj.w.z = -((zFar + zNear) / (zFar - zNear)); - proj.w.w = (real_t)1.0; + proj.X.X = (real_t)2.0 / (right - left); + proj.W.X = -((right + left) / (right - left)); + proj.Y.Y = (real_t)2.0 / (top - bottom); + proj.W.Y = -((top + bottom) / (top - bottom)); + proj.Z.Z = (real_t)(-2.0) / (zFar - zNear); + proj.W.Z = -((zFar + zNear) / (zFar - zNear)); + proj.W.W = (real_t)1.0; return proj; } @@ -392,12 +392,12 @@ namespace Godot Projection proj = Projection.Identity; - proj.x.x = cotangent / aspect; - proj.y.y = cotangent; - proj.z.z = -(zFar + zNear) / deltaZ; - proj.z.w = -1; - proj.w.z = -2 * zNear * zFar / deltaZ; - proj.w.w = 0; + proj.X.X = cotangent / aspect; + proj.Y.Y = cotangent; + proj.Z.Z = -(zFar + zNear) / deltaZ; + proj.Z.W = -1; + proj.W.Z = -2 * zNear * zFar / deltaZ; + proj.W.W = 0; return proj; } @@ -456,7 +456,7 @@ namespace Godot } Projection proj = CreateFrustum(left, right, -ymax, ymax, zNear, zFar); Projection cm = Projection.Identity; - cm.w.x = modeltranslation; + cm.W.X = modeltranslation; return proj * cm; } @@ -469,18 +469,18 @@ namespace Godot /// <returns>The determinant calculated from this projection.</returns> public readonly real_t Determinant() { - return x.w * y.z * z.y * w.x - x.z * y.w * z.y * w.x - - x.w * y.y * z.z * w.x + x.y * y.w * z.z * w.x + - x.z * y.y * z.w * w.x - x.y * y.z * z.w * w.x - - x.w * y.z * z.x * w.y + x.z * y.w * z.x * w.y + - x.w * y.x * z.z * w.y - x.x * y.w * z.z * w.y - - x.z * y.x * z.w * w.y + x.x * y.z * z.w * w.y + - x.w * y.y * z.x * w.z - x.y * y.w * z.x * w.z - - x.w * y.x * z.y * w.z + x.x * y.w * z.y * w.z + - x.y * y.x * z.w * w.z - x.x * y.y * z.w * w.z - - x.z * y.y * z.x * w.w + x.y * y.z * z.x * w.w + - x.z * y.x * z.y * w.w - x.x * y.z * z.y * w.w - - x.y * y.x * z.z * w.w + x.x * y.y * z.z * w.w; + return X.W * Y.Z * Z.Y * W.X - X.Z * Y.W * Z.Y * W.X - + X.W * Y.Y * Z.Z * W.X + X.Y * Y.W * Z.Z * W.X + + X.Z * Y.Y * Z.W * W.X - X.Y * Y.Z * Z.W * W.X - + X.W * Y.Z * Z.X * W.Y + X.Z * Y.W * Z.X * W.Y + + X.W * Y.X * Z.Z * W.Y - X.X * Y.W * Z.Z * W.Y - + X.Z * Y.X * Z.W * W.Y + X.X * Y.Z * Z.W * W.Y + + X.W * Y.Y * Z.X * W.Z - X.Y * Y.W * Z.X * W.Z - + X.W * Y.X * Z.Y * W.Z + X.X * Y.W * Z.Y * W.Z + + X.Y * Y.X * Z.W * W.Z - X.X * Y.Y * Z.W * W.Z - + X.Z * Y.Y * Z.X * W.W + X.Y * Y.Z * Z.X * W.W + + X.Z * Y.X * Z.Y * W.W - X.X * Y.Z * Z.Y * W.W - + X.Y * Y.X * Z.Z * W.W + X.X * Y.Y * Z.Z * W.W; } /// <summary> @@ -490,7 +490,7 @@ namespace Godot public readonly real_t GetAspect() { Vector2 vpHe = GetViewportHalfExtents(); - return vpHe.x / vpHe.y; + return vpHe.X / vpHe.Y; } /// <summary> @@ -499,15 +499,15 @@ namespace Godot /// <returns>The horizontal field of view of this projection.</returns> public readonly real_t GetFov() { - Plane rightPlane = new Plane(x.w - x.x, y.w - y.x, z.w - z.x, -w.w + w.x).Normalized(); - if (z.x == 0 && z.y == 0) + Plane rightPlane = new Plane(X.W - X.X, Y.W - Y.X, Z.W - Z.X, -W.W + W.X).Normalized(); + if (Z.X == 0 && Z.Y == 0) { - return Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.x))) * (real_t)2.0; + return Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.X))) * (real_t)2.0; } else { - Plane leftPlane = new Plane(x.w + x.x, y.w + y.x, z.w + z.x, w.w + w.x).Normalized(); - return Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(leftPlane.Normal.x))) + Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.x))); + Plane leftPlane = new Plane(X.W + X.X, Y.W + Y.X, Z.W + Z.X, W.W + W.X).Normalized(); + return Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(leftPlane.Normal.X))) + Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.X))); } } @@ -531,12 +531,12 @@ namespace Godot { if (IsOrthogonal()) { - return GetViewportHalfExtents().x; + return GetViewportHalfExtents().X; } else { real_t zn = GetZNear(); - real_t width = GetViewportHalfExtents().x * (real_t)2.0; + real_t width = GetViewportHalfExtents().X * (real_t)2.0; return (real_t)1.0 / (zn / width); } } @@ -551,7 +551,7 @@ namespace Godot { Vector3 result = this * new Vector3(1, 0, -1); - return (int)((result.x * (real_t)0.5 + (real_t)0.5) * forPixelWidth); + return (int)((result.X * (real_t)0.5 + (real_t)0.5) * forPixelWidth); } /// <summary> @@ -567,12 +567,12 @@ namespace Godot { Plane newPlane = plane switch { - Planes.Near => new Plane(x.w + x.z, y.w + y.z, z.w + z.z, w.w + w.z), - Planes.Far => new Plane(x.w - x.z, y.w - y.z, z.w - z.z, w.w - w.z), - Planes.Left => new Plane(x.w + x.x, y.w + y.x, z.w + z.x, w.w + w.x), - Planes.Top => new Plane(x.w - x.y, y.w - y.y, z.w - z.y, w.w - w.y), - Planes.Right => new Plane(x.w - x.x, y.w - y.x, z.w - z.x, w.w - w.x), - Planes.Bottom => new Plane(x.w + x.y, y.w + y.y, z.w + z.y, w.w + w.y), + Planes.Near => new Plane(X.W + X.Z, Y.W + Y.Z, Z.W + Z.Z, W.W + W.Z), + Planes.Far => new Plane(X.W - X.Z, Y.W - Y.Z, Z.W - Z.Z, W.W - W.Z), + Planes.Left => new Plane(X.W + X.X, Y.W + Y.X, Z.W + Z.X, W.W + W.X), + Planes.Top => new Plane(X.W - X.Y, Y.W - Y.Y, Z.W - Z.Y, W.W - W.Y), + Planes.Right => new Plane(X.W - X.X, Y.W - Y.X, Z.W - Z.X, W.W - W.X), + Planes.Bottom => new Plane(X.W + X.Y, Y.W + Y.Y, Z.W + Z.Y, W.W + W.Y), _ => new Plane(), }; newPlane.Normal = -newPlane.Normal; @@ -586,7 +586,7 @@ namespace Godot public readonly Vector2 GetFarPlaneHalfExtents() { var res = GetProjectionPlane(Planes.Far).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top)); - return new Vector2(res.Value.x, res.Value.y); + return new Vector2(res.Value.X, res.Value.Y); } /// <summary> @@ -597,7 +597,7 @@ namespace Godot public readonly Vector2 GetViewportHalfExtents() { var res = GetProjectionPlane(Planes.Near).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top)); - return new Vector2(res.Value.x, res.Value.y); + return new Vector2(res.Value.X, res.Value.Y); } /// <summary> @@ -625,7 +625,7 @@ namespace Godot public readonly Projection FlippedY() { Projection proj = this; - proj.y = -proj.y; + proj.Y = -proj.Y; return proj; } @@ -642,8 +642,8 @@ namespace Godot real_t zFar = GetZFar(); real_t zNear = newZNear; real_t deltaZ = zFar - zNear; - proj.z.z = -(zFar + zNear) / deltaZ; - proj.w.z = -2 * zNear * zFar / deltaZ; + proj.Z.Z = -(zFar + zNear) / deltaZ; + proj.W.Z = -2 * zNear * zFar / deltaZ; return proj; } @@ -656,8 +656,8 @@ namespace Godot public readonly Projection JitterOffseted(Vector2 offset) { Projection proj = this; - proj.w.x += offset.x; - proj.w.y += offset.y; + proj.W.X += offset.X; + proj.W.Y += offset.Y; return proj; } @@ -795,7 +795,7 @@ namespace Godot /// <returns>If the projection performs an orthogonal projection.</returns> public readonly bool IsOrthogonal() { - return w.w == (real_t)1.0; + return W.W == (real_t)1.0; } // Constants @@ -835,10 +835,10 @@ namespace Godot /// <param name="w">The W column, or column index 3.</param> public Projection(Vector4 x, Vector4 y, Vector4 z, Vector4 w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; + X = x; + Y = y; + Z = z; + W = w; } /// <summary> @@ -847,10 +847,10 @@ namespace Godot /// <param name="transform">The <see cref="Transform3D"/>.</param> public Projection(Transform3D transform) { - x = new Vector4(transform.basis.Row0.x, transform.basis.Row1.x, transform.basis.Row2.x, 0); - y = new Vector4(transform.basis.Row0.y, transform.basis.Row1.y, transform.basis.Row2.y, 0); - z = new Vector4(transform.basis.Row0.z, transform.basis.Row1.z, transform.basis.Row2.z, 0); - w = new Vector4(transform.origin.x, transform.origin.y, transform.origin.z, 1); + X = new Vector4(transform.Basis.Row0.X, transform.Basis.Row1.X, transform.Basis.Row2.X, 0); + Y = new Vector4(transform.Basis.Row0.Y, transform.Basis.Row1.Y, transform.Basis.Row2.Y, 0); + Z = new Vector4(transform.Basis.Row0.Z, transform.Basis.Row1.Z, transform.Basis.Row2.Z, 0); + W = new Vector4(transform.Origin.X, transform.Origin.Y, transform.Origin.Z, 1); } /// <summary> @@ -865,25 +865,25 @@ namespace Godot { return new Projection( new Vector4( - left.x.x * right.x.x + left.y.x * right.x.y + left.z.x * right.x.z + left.w.x * right.x.w, - left.x.y * right.x.x + left.y.y * right.x.y + left.z.y * right.x.z + left.w.y * right.x.w, - left.x.z * right.x.x + left.y.z * right.x.y + left.z.z * right.x.z + left.w.z * right.x.w, - left.x.w * right.x.x + left.y.w * right.x.y + left.z.w * right.x.z + left.w.w * right.x.w + left.X.X * right.X.X + left.Y.X * right.X.Y + left.Z.X * right.X.Z + left.W.X * right.X.W, + left.X.Y * right.X.X + left.Y.Y * right.X.Y + left.Z.Y * right.X.Z + left.W.Y * right.X.W, + left.X.Z * right.X.X + left.Y.Z * right.X.Y + left.Z.Z * right.X.Z + left.W.Z * right.X.W, + left.X.W * right.X.X + left.Y.W * right.X.Y + left.Z.W * right.X.Z + left.W.W * right.X.W ), new Vector4( - left.x.x * right.y.x + left.y.x * right.y.y + left.z.x * right.y.z + left.w.x * right.y.w, - left.x.y * right.y.x + left.y.y * right.y.y + left.z.y * right.y.z + left.w.y * right.y.w, - left.x.z * right.y.x + left.y.z * right.y.y + left.z.z * right.y.z + left.w.z * right.y.w, - left.x.w * right.y.x + left.y.w * right.y.y + left.z.w * right.y.z + left.w.w * right.y.w + left.X.X * right.Y.X + left.Y.X * right.Y.Y + left.Z.X * right.Y.Z + left.W.X * right.Y.W, + left.X.Y * right.Y.X + left.Y.Y * right.Y.Y + left.Z.Y * right.Y.Z + left.W.Y * right.Y.W, + left.X.Z * right.Y.X + left.Y.Z * right.Y.Y + left.Z.Z * right.Y.Z + left.W.Z * right.Y.W, + left.X.W * right.Y.X + left.Y.W * right.Y.Y + left.Z.W * right.Y.Z + left.W.W * right.Y.W ), new Vector4( - left.x.x * right.z.x + left.y.x * right.z.y + left.z.x * right.z.z + left.w.x * right.z.w, - left.x.y * right.z.x + left.y.y * right.z.y + left.z.y * right.z.z + left.w.y * right.z.w, - left.x.z * right.z.x + left.y.z * right.z.y + left.z.z * right.z.z + left.w.z * right.z.w, - left.x.w * right.z.x + left.y.w * right.z.y + left.z.w * right.z.z + left.w.w * right.z.w + left.X.X * right.Z.X + left.Y.X * right.Z.Y + left.Z.X * right.Z.Z + left.W.X * right.Z.W, + left.X.Y * right.Z.X + left.Y.Y * right.Z.Y + left.Z.Y * right.Z.Z + left.W.Y * right.Z.W, + left.X.Z * right.Z.X + left.Y.Z * right.Z.Y + left.Z.Z * right.Z.Z + left.W.Z * right.Z.W, + left.X.W * right.Z.X + left.Y.W * right.Z.Y + left.Z.W * right.Z.Z + left.W.W * right.Z.W ), new Vector4( - left.x.x * right.w.x + left.y.x * right.w.y + left.z.x * right.w.z + left.w.x * right.w.w, - left.x.y * right.w.x + left.y.y * right.w.y + left.z.y * right.w.z + left.w.y * right.w.w, - left.x.z * right.w.x + left.y.z * right.w.y + left.z.z * right.w.z + left.w.z * right.w.w, - left.x.w * right.w.x + left.y.w * right.w.y + left.z.w * right.w.z + left.w.w * right.w.w + left.X.X * right.W.X + left.Y.X * right.W.Y + left.Z.X * right.W.Z + left.W.X * right.W.W, + left.X.Y * right.W.X + left.Y.Y * right.W.Y + left.Z.Y * right.W.Z + left.W.Y * right.W.W, + left.X.Z * right.W.X + left.Y.Z * right.W.Y + left.Z.Z * right.W.Z + left.W.Z * right.W.W, + left.X.W * right.W.X + left.Y.W * right.W.Y + left.Z.W * right.W.Z + left.W.W * right.W.W ) ); } @@ -897,10 +897,10 @@ namespace Godot public static Vector4 operator *(Projection proj, Vector4 vector) { return new Vector4( - proj.x.x * vector.x + proj.y.x * vector.y + proj.z.x * vector.z + proj.w.x * vector.w, - proj.x.y * vector.x + proj.y.y * vector.y + proj.z.y * vector.z + proj.w.y * vector.w, - proj.x.z * vector.x + proj.y.z * vector.y + proj.z.z * vector.z + proj.w.z * vector.w, - proj.x.w * vector.x + proj.y.w * vector.y + proj.z.w * vector.z + proj.w.w * vector.w + proj.X.X * vector.X + proj.Y.X * vector.Y + proj.Z.X * vector.Z + proj.W.X * vector.W, + proj.X.Y * vector.X + proj.Y.Y * vector.Y + proj.Z.Y * vector.Z + proj.W.Y * vector.W, + proj.X.Z * vector.X + proj.Y.Z * vector.Y + proj.Z.Z * vector.Z + proj.W.Z * vector.W, + proj.X.W * vector.X + proj.Y.W * vector.Y + proj.Z.W * vector.Z + proj.W.W * vector.W ); } @@ -913,10 +913,10 @@ namespace Godot public static Vector4 operator *(Vector4 vector, Projection proj) { return new Vector4( - proj.x.x * vector.x + proj.x.y * vector.y + proj.x.z * vector.z + proj.x.w * vector.w, - proj.y.x * vector.x + proj.y.y * vector.y + proj.y.z * vector.z + proj.y.w * vector.w, - proj.z.x * vector.x + proj.z.y * vector.y + proj.z.z * vector.z + proj.z.w * vector.w, - proj.w.x * vector.x + proj.w.y * vector.y + proj.w.z * vector.z + proj.w.w * vector.w + proj.X.X * vector.X + proj.X.Y * vector.Y + proj.X.Z * vector.Z + proj.X.W * vector.W, + proj.Y.X * vector.X + proj.Y.Y * vector.Y + proj.Y.Z * vector.Z + proj.Y.W * vector.W, + proj.Z.X * vector.X + proj.Z.Y * vector.Y + proj.Z.Z * vector.Z + proj.Z.W * vector.W, + proj.W.X * vector.X + proj.W.Y * vector.Y + proj.W.Z * vector.Z + proj.W.W * vector.W ); } @@ -929,11 +929,11 @@ namespace Godot public static Vector3 operator *(Projection proj, Vector3 vector) { Vector3 ret = new Vector3( - proj.x.x * vector.x + proj.y.x * vector.y + proj.z.x * vector.z + proj.w.x, - proj.x.y * vector.x + proj.y.y * vector.y + proj.z.y * vector.z + proj.w.y, - proj.x.z * vector.x + proj.y.z * vector.y + proj.z.z * vector.z + proj.w.z + proj.X.X * vector.X + proj.Y.X * vector.Y + proj.Z.X * vector.Z + proj.W.X, + proj.X.Y * vector.X + proj.Y.Y * vector.Y + proj.Z.Y * vector.Z + proj.W.Y, + proj.X.Z * vector.X + proj.Y.Z * vector.Y + proj.Z.Z * vector.Z + proj.W.Z ); - return ret / (proj.x.w * vector.x + proj.y.w * vector.y + proj.z.w * vector.z + proj.w.w); + return ret / (proj.X.W * vector.X + proj.Y.W * vector.Y + proj.Z.W * vector.Z + proj.W.W); } /// <summary> @@ -966,11 +966,11 @@ namespace Godot { return new Transform3D( new Basis( - new Vector3(proj.x.x, proj.x.y, proj.x.z), - new Vector3(proj.y.x, proj.y.y, proj.y.z), - new Vector3(proj.z.x, proj.z.y, proj.z.z) + new Vector3(proj.X.X, proj.X.Y, proj.X.Z), + new Vector3(proj.Y.X, proj.Y.Y, proj.Y.Z), + new Vector3(proj.Z.X, proj.Z.Y, proj.Z.Z) ), - new Vector3(proj.w.x, proj.w.y, proj.w.z) + new Vector3(proj.W.X, proj.W.Y, proj.W.Z) ); } @@ -992,7 +992,7 @@ namespace Godot /// <returns>Whether or not the projections are exactly equal.</returns> public readonly bool Equals(Projection other) { - return x == other.x && y == other.y && z == other.z && w == other.w; + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// <summary> @@ -1001,7 +1001,7 @@ namespace Godot /// <returns>A hash code for this projection.</returns> public override readonly int GetHashCode() { - return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + return Y.GetHashCode() ^ X.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode(); } /// <summary> @@ -1010,7 +1010,7 @@ namespace Godot /// <returns>A string representation of this projection.</returns> public override readonly string ToString() { - return $"{x.x}, {x.y}, {x.z}, {x.w}\n{y.x}, {y.y}, {y.z}, {y.w}\n{z.x}, {z.y}, {z.z}, {z.w}\n{w.x}, {w.y}, {w.z}, {w.w}\n"; + return $"{X.X}, {X.Y}, {X.Z}, {X.W}\n{Y.X}, {Y.Y}, {Y.Z}, {Y.W}\n{Z.X}, {Z.Y}, {Z.Z}, {Z.W}\n{W.X}, {W.Y}, {W.Z}, {W.W}\n"; } /// <summary> @@ -1019,10 +1019,10 @@ namespace Godot /// <returns>A string representation of this projection.</returns> public readonly string ToString(string format) { - return $"{x.x.ToString(format)}, {x.y.ToString(format)}, {x.z.ToString(format)}, {x.w.ToString(format)}\n" + - $"{y.x.ToString(format)}, {y.y.ToString(format)}, {y.z.ToString(format)}, {y.w.ToString(format)}\n" + - $"{z.x.ToString(format)}, {z.y.ToString(format)}, {z.z.ToString(format)}, {z.w.ToString(format)}\n" + - $"{w.x.ToString(format)}, {w.y.ToString(format)}, {w.z.ToString(format)}, {w.w.ToString(format)}\n"; + return $"{X.X.ToString(format)}, {X.Y.ToString(format)}, {X.Z.ToString(format)}, {X.W.ToString(format)}\n" + + $"{Y.X.ToString(format)}, {Y.Y.ToString(format)}, {Y.Z.ToString(format)}, {Y.W.ToString(format)}\n" + + $"{Z.X.ToString(format)}, {Z.Y.ToString(format)}, {Z.Z.ToString(format)}, {Z.W.ToString(format)}\n" + + $"{W.X.ToString(format)}, {W.Y.ToString(format)}, {W.Z.ToString(format)}, {W.W.ToString(format)}\n"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index 8e4f9178f7..9c2a6fc654 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -24,25 +24,25 @@ namespace Godot /// X component of the quaternion (imaginary <c>i</c> axis part). /// Quaternion components should usually not be manipulated directly. /// </summary> - public real_t x; + public real_t X; /// <summary> /// Y component of the quaternion (imaginary <c>j</c> axis part). /// Quaternion components should usually not be manipulated directly. /// </summary> - public real_t y; + public real_t Y; /// <summary> /// Z component of the quaternion (imaginary <c>k</c> axis part). /// Quaternion components should usually not be manipulated directly. /// </summary> - public real_t z; + public real_t Z; /// <summary> /// W component of the quaternion (real part). /// Quaternion components should usually not be manipulated directly. /// </summary> - public real_t w; + public real_t W; /// <summary> /// Access quaternion components using their index. @@ -51,10 +51,10 @@ namespace Godot /// <paramref name="index"/> is not 0, 1, 2 or 3. /// </exception> /// <value> - /// <c>[0]</c> is equivalent to <see cref="x"/>, - /// <c>[1]</c> is equivalent to <see cref="y"/>, - /// <c>[2]</c> is equivalent to <see cref="z"/>, - /// <c>[3]</c> is equivalent to <see cref="w"/>. + /// <c>[0]</c> is equivalent to <see cref="X"/>, + /// <c>[1]</c> is equivalent to <see cref="Y"/>, + /// <c>[2]</c> is equivalent to <see cref="Z"/>, + /// <c>[3]</c> is equivalent to <see cref="W"/>. /// </value> public real_t this[int index] { @@ -63,13 +63,13 @@ namespace Godot switch (index) { case 0: - return x; + return X; case 1: - return y; + return Y; case 2: - return z; + return Z; case 3: - return w; + return W; default: throw new ArgumentOutOfRangeException(nameof(index)); } @@ -79,16 +79,16 @@ namespace Godot switch (index) { case 0: - x = value; + X = value; break; case 1: - y = value; + Y = value; break; case 2: - z = value; + Z = value; break; case 3: - w = value; + W = value; break; default: throw new ArgumentOutOfRangeException(nameof(index)); @@ -155,9 +155,9 @@ namespace Godot Quaternion lnPre = (fromQ.Inverse() * preQ).Log(); Quaternion lnPost = (fromQ.Inverse() * postQ).Log(); Quaternion ln = new Quaternion( - Mathf.CubicInterpolate(lnFrom.x, lnTo.x, lnPre.x, lnPost.x, weight), - Mathf.CubicInterpolate(lnFrom.y, lnTo.y, lnPre.y, lnPost.y, weight), - Mathf.CubicInterpolate(lnFrom.z, lnTo.z, lnPre.z, lnPost.z, weight), + Mathf.CubicInterpolate(lnFrom.X, lnTo.X, lnPre.X, lnPost.X, weight), + Mathf.CubicInterpolate(lnFrom.Y, lnTo.Y, lnPre.Y, lnPost.Y, weight), + Mathf.CubicInterpolate(lnFrom.Z, lnTo.Z, lnPre.Z, lnPost.Z, weight), 0); Quaternion q1 = fromQ * ln.Exp(); @@ -167,9 +167,9 @@ namespace Godot lnPre = (toQ.Inverse() * preQ).Log(); lnPost = (toQ.Inverse() * postQ).Log(); ln = new Quaternion( - Mathf.CubicInterpolate(lnFrom.x, lnTo.x, lnPre.x, lnPost.x, weight), - Mathf.CubicInterpolate(lnFrom.y, lnTo.y, lnPre.y, lnPost.y, weight), - Mathf.CubicInterpolate(lnFrom.z, lnTo.z, lnPre.z, lnPost.z, weight), + Mathf.CubicInterpolate(lnFrom.X, lnTo.X, lnPre.X, lnPost.X, weight), + Mathf.CubicInterpolate(lnFrom.Y, lnTo.Y, lnPre.Y, lnPost.Y, weight), + Mathf.CubicInterpolate(lnFrom.Z, lnTo.Z, lnPre.Z, lnPost.Z, weight), 0); Quaternion q2 = toQ * ln.Exp(); @@ -224,9 +224,9 @@ namespace Godot Quaternion lnPre = (fromQ.Inverse() * preQ).Log(); Quaternion lnPost = (fromQ.Inverse() * postQ).Log(); Quaternion ln = new Quaternion( - Mathf.CubicInterpolateInTime(lnFrom.x, lnTo.x, lnPre.x, lnPost.x, weight, bT, preAT, postBT), - Mathf.CubicInterpolateInTime(lnFrom.y, lnTo.y, lnPre.y, lnPost.y, weight, bT, preAT, postBT), - Mathf.CubicInterpolateInTime(lnFrom.z, lnTo.z, lnPre.z, lnPost.z, weight, bT, preAT, postBT), + Mathf.CubicInterpolateInTime(lnFrom.X, lnTo.X, lnPre.X, lnPost.X, weight, bT, preAT, postBT), + Mathf.CubicInterpolateInTime(lnFrom.Y, lnTo.Y, lnPre.Y, lnPost.Y, weight, bT, preAT, postBT), + Mathf.CubicInterpolateInTime(lnFrom.Z, lnTo.Z, lnPre.Z, lnPost.Z, weight, bT, preAT, postBT), 0); Quaternion q1 = fromQ * ln.Exp(); @@ -236,9 +236,9 @@ namespace Godot lnPre = (toQ.Inverse() * preQ).Log(); lnPost = (toQ.Inverse() * postQ).Log(); ln = new Quaternion( - Mathf.CubicInterpolateInTime(lnFrom.x, lnTo.x, lnPre.x, lnPost.x, weight, bT, preAT, postBT), - Mathf.CubicInterpolateInTime(lnFrom.y, lnTo.y, lnPre.y, lnPost.y, weight, bT, preAT, postBT), - Mathf.CubicInterpolateInTime(lnFrom.z, lnTo.z, lnPre.z, lnPost.z, weight, bT, preAT, postBT), + Mathf.CubicInterpolateInTime(lnFrom.X, lnTo.X, lnPre.X, lnPost.X, weight, bT, preAT, postBT), + Mathf.CubicInterpolateInTime(lnFrom.Y, lnTo.Y, lnPre.Y, lnPost.Y, weight, bT, preAT, postBT), + Mathf.CubicInterpolateInTime(lnFrom.Z, lnTo.Z, lnPre.Z, lnPost.Z, weight, bT, preAT, postBT), 0); Quaternion q2 = toQ * ln.Exp(); @@ -253,12 +253,12 @@ namespace Godot /// <returns>The dot product.</returns> public readonly real_t Dot(Quaternion b) { - return (x * b.x) + (y * b.y) + (z * b.z) + (w * b.w); + return (X * b.X) + (Y * b.Y) + (Z * b.Z) + (W * b.W); } public readonly Quaternion Exp() { - Vector3 v = new Vector3(x, y, z); + Vector3 v = new Vector3(X, Y, Z); real_t theta = v.Length(); v = v.Normalized(); if (theta < Mathf.Epsilon || !v.IsNormalized()) @@ -270,18 +270,18 @@ namespace Godot public readonly real_t GetAngle() { - return 2 * Mathf.Acos(w); + return 2 * Mathf.Acos(W); } public readonly Vector3 GetAxis() { - if (Mathf.Abs(w) > 1 - Mathf.Epsilon) + if (Mathf.Abs(W) > 1 - Mathf.Epsilon) { - return new Vector3(x, y, z); + return new Vector3(X, Y, Z); } - real_t r = 1 / Mathf.Sqrt(1 - w * w); - return new Vector3(x * r, y * r, z * r); + real_t r = 1 / Mathf.Sqrt(1 - W * W); + return new Vector3(X * r, Y * r, Z * r); } /// <summary> @@ -315,7 +315,7 @@ namespace Godot throw new InvalidOperationException("Quaternion is not normalized."); } #endif - return new Quaternion(-x, -y, -z, w); + return new Quaternion(-X, -Y, -Z, W); } /// <summary> @@ -325,7 +325,7 @@ namespace Godot /// <returns>Whether this vector is finite or not.</returns> public readonly bool IsFinite() { - return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w); + return Mathf.IsFinite(X) && Mathf.IsFinite(Y) && Mathf.IsFinite(Z) && Mathf.IsFinite(W); } /// <summary> @@ -340,7 +340,7 @@ namespace Godot public readonly Quaternion Log() { Vector3 v = GetAxis() * GetAngle(); - return new Quaternion(v.x, v.y, v.z, 0); + return new Quaternion(v.X, v.Y, v.Z, 0); } /// <summary> @@ -432,10 +432,10 @@ namespace Godot // Calculate final values. return new Quaternion ( - (scale0 * x) + (scale1 * to1.x), - (scale0 * y) + (scale1 * to1.y), - (scale0 * z) + (scale1 * to1.z), - (scale0 * w) + (scale1 * to1.w) + (scale0 * X) + (scale1 * to1.X), + (scale0 * Y) + (scale1 * to1.Y), + (scale0 * Z) + (scale1 * to1.Z), + (scale0 * W) + (scale1 * to1.W) ); } @@ -474,10 +474,10 @@ namespace Godot return new Quaternion ( - (invFactor * x) + (newFactor * to.x), - (invFactor * y) + (newFactor * to.y), - (invFactor * z) + (newFactor * to.z), - (invFactor * w) + (newFactor * to.w) + (invFactor * X) + (newFactor * to.X), + (invFactor * Y) + (newFactor * to.Y), + (invFactor * Z) + (newFactor * to.Z), + (invFactor * W) + (newFactor * to.W) ); } @@ -501,10 +501,10 @@ namespace Godot /// <param name="w">W component of the quaternion (real part).</param> public Quaternion(real_t x, real_t y, real_t z, real_t w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; + X = x; + Y = y; + Z = z; + W = w; } /// <summary> @@ -535,20 +535,20 @@ namespace Godot if (d == 0f) { - x = 0f; - y = 0f; - z = 0f; - w = 0f; + X = 0f; + Y = 0f; + Z = 0f; + W = 0f; } else { (real_t sin, real_t cos) = Mathf.SinCos(angle * 0.5f); real_t s = sin / d; - x = axis.x * s; - y = axis.y * s; - z = axis.z * s; - w = cos; + X = axis.X * s; + Y = axis.Y * s; + Z = axis.Z * s; + W = cos; } } @@ -559,20 +559,20 @@ namespace Godot if (d < -1.0f + Mathf.Epsilon) { - x = 0f; - y = 1f; - z = 0f; - w = 0f; + X = 0f; + Y = 1f; + Z = 0f; + W = 0f; } else { real_t s = Mathf.Sqrt((1.0f + d) * 2.0f); real_t rs = 1.0f / s; - x = c.x * rs; - y = c.y * rs; - z = c.z * rs; - w = s * 0.5f; + X = c.X * rs; + Y = c.Y * rs; + Z = c.Z * rs; + W = s * 0.5f; } } @@ -584,9 +584,9 @@ namespace Godot /// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param> public static Quaternion FromEuler(Vector3 eulerYXZ) { - real_t halfA1 = eulerYXZ.y * 0.5f; - real_t halfA2 = eulerYXZ.x * 0.5f; - real_t halfA3 = eulerYXZ.z * 0.5f; + real_t halfA1 = eulerYXZ.Y * 0.5f; + real_t halfA2 = eulerYXZ.X * 0.5f; + real_t halfA3 = eulerYXZ.Z * 0.5f; // R = Y(a1).X(a2).Z(a3) convention for Euler angles. // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) @@ -616,10 +616,10 @@ namespace Godot { return new Quaternion ( - (left.w * right.x) + (left.x * right.w) + (left.y * right.z) - (left.z * right.y), - (left.w * right.y) + (left.y * right.w) + (left.z * right.x) - (left.x * right.z), - (left.w * right.z) + (left.z * right.w) + (left.x * right.y) - (left.y * right.x), - (left.w * right.w) - (left.x * right.x) - (left.y * right.y) - (left.z * right.z) + (left.W * right.X) + (left.X * right.W) + (left.Y * right.Z) - (left.Z * right.Y), + (left.W * right.Y) + (left.Y * right.W) + (left.Z * right.X) - (left.X * right.Z), + (left.W * right.Z) + (left.Z * right.W) + (left.X * right.Y) - (left.Y * right.X), + (left.W * right.W) - (left.X * right.X) - (left.Y * right.Y) - (left.Z * right.Z) ); } @@ -637,9 +637,9 @@ namespace Godot throw new InvalidOperationException("Quaternion is not normalized."); } #endif - var u = new Vector3(quaternion.x, quaternion.y, quaternion.z); + var u = new Vector3(quaternion.X, quaternion.Y, quaternion.Z); Vector3 uv = u.Cross(vector); - return vector + (((uv * quaternion.w) + u.Cross(uv)) * 2); + return vector + (((uv * quaternion.W) + u.Cross(uv)) * 2); } /// <summary> @@ -665,7 +665,7 @@ namespace Godot /// <returns>The added quaternion.</returns> public static Quaternion operator +(Quaternion left, Quaternion right) { - return new Quaternion(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + return new Quaternion(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); } /// <summary> @@ -679,20 +679,20 @@ namespace Godot /// <returns>The subtracted quaternion.</returns> public static Quaternion operator -(Quaternion left, Quaternion right) { - return new Quaternion(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + return new Quaternion(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); } /// <summary> /// Returns the negative value of the <see cref="Quaternion"/>. /// This is the same as writing - /// <c>new Quaternion(-q.x, -q.y, -q.z, -q.w)</c>. This operation + /// <c>new Quaternion(-q.X, -q.Y, -q.Z, -q.W)</c>. This operation /// results in a quaternion that represents the same rotation. /// </summary> /// <param name="quat">The quaternion to negate.</param> /// <returns>The negated quaternion.</returns> public static Quaternion operator -(Quaternion quat) { - return new Quaternion(-quat.x, -quat.y, -quat.z, -quat.w); + return new Quaternion(-quat.X, -quat.Y, -quat.Z, -quat.W); } /// <summary> @@ -706,7 +706,7 @@ namespace Godot /// <returns>The multiplied quaternion.</returns> public static Quaternion operator *(Quaternion left, real_t right) { - return new Quaternion(left.x * right, left.y * right, left.z * right, left.w * right); + return new Quaternion(left.X * right, left.Y * right, left.Z * right, left.W * right); } /// <summary> @@ -720,7 +720,7 @@ namespace Godot /// <returns>The multiplied quaternion.</returns> public static Quaternion operator *(real_t left, Quaternion right) { - return new Quaternion(right.x * left, right.y * left, right.z * left, right.w * left); + return new Quaternion(right.X * left, right.Y * left, right.Z * left, right.W * left); } /// <summary> @@ -780,7 +780,7 @@ namespace Godot /// <returns>Whether or not the quaternions are exactly equal.</returns> public readonly bool Equals(Quaternion other) { - return x == other.x && y == other.y && z == other.z && w == other.w; + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// <summary> @@ -791,7 +791,7 @@ namespace Godot /// <returns>Whether or not the quaternions are approximately equal.</returns> public readonly bool IsEqualApprox(Quaternion other) { - return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); + return Mathf.IsEqualApprox(X, other.X) && Mathf.IsEqualApprox(Y, other.Y) && Mathf.IsEqualApprox(Z, other.Z) && Mathf.IsEqualApprox(W, other.W); } /// <summary> @@ -800,7 +800,7 @@ namespace Godot /// <returns>A hash code for this quaternion.</returns> public override readonly int GetHashCode() { - return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + return Y.GetHashCode() ^ X.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode(); } /// <summary> @@ -809,7 +809,7 @@ namespace Godot /// <returns>A string representation of this quaternion.</returns> public override readonly string ToString() { - return $"({x}, {y}, {z}, {w})"; + return $"({X}, {Y}, {Z}, {W})"; } /// <summary> @@ -818,7 +818,7 @@ namespace Godot /// <returns>A string representation of this quaternion.</returns> public readonly string ToString(string format) { - return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})"; + return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}, {W.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index 1a8696d3bc..69444f8035 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -51,11 +51,11 @@ namespace Godot /// <summary> /// The area of this <see cref="Rect2"/>. + /// See also <see cref="HasArea"/>. /// </summary> - /// <value>Equivalent to <see cref="GetArea()"/>.</value> public readonly real_t Area { - get { return GetArea(); } + get { return _size.X * _size.Y; } } /// <summary> @@ -66,7 +66,7 @@ namespace Godot public readonly Rect2 Abs() { Vector2 end = End; - Vector2 topLeft = new Vector2(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y)); + Vector2 topLeft = new Vector2(Mathf.Min(_position.X, end.X), Mathf.Min(_position.Y, end.Y)); return new Rect2(topLeft, _size.Abs()); } @@ -88,14 +88,14 @@ namespace Godot return new Rect2(); } - newRect._position.x = Mathf.Max(b._position.x, _position.x); - newRect._position.y = Mathf.Max(b._position.y, _position.y); + newRect._position.X = Mathf.Max(b._position.X, _position.X); + newRect._position.Y = Mathf.Max(b._position.Y, _position.Y); Vector2 bEnd = b._position + b._size; Vector2 end = _position + _size; - newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x; - newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y; + newRect._size.X = Mathf.Min(bEnd.X, end.X) - newRect._position.X; + newRect._size.Y = Mathf.Min(bEnd.Y, end.Y) - newRect._position.Y; return newRect; } @@ -119,9 +119,9 @@ namespace Godot /// </returns> public readonly bool Encloses(Rect2 b) { - return b._position.x >= _position.x && b._position.y >= _position.y && - b._position.x + b._size.x < _position.x + _size.x && - b._position.y + b._size.y < _position.y + _size.y; + return b._position.X >= _position.X && b._position.Y >= _position.Y && + b._position.X + b._size.X < _position.X + _size.X && + b._position.Y + b._size.Y < _position.Y + _size.Y; } /// <summary> @@ -136,22 +136,22 @@ namespace Godot Vector2 begin = expanded._position; Vector2 end = expanded._position + expanded._size; - if (to.x < begin.x) + if (to.X < begin.X) { - begin.x = to.x; + begin.X = to.X; } - if (to.y < begin.y) + if (to.Y < begin.Y) { - begin.y = to.y; + begin.Y = to.Y; } - if (to.x > end.x) + if (to.X > end.X) { - end.x = to.x; + end.X = to.X; } - if (to.y > end.y) + if (to.Y > end.Y) { - end.y = to.y; + end.Y = to.Y; } expanded._position = begin; @@ -161,15 +161,6 @@ namespace Godot } /// <summary> - /// Returns the area of the <see cref="Rect2"/>. - /// </summary> - /// <returns>The area.</returns> - public readonly real_t GetArea() - { - return _size.x * _size.y; - } - - /// <summary> /// Returns the center of the <see cref="Rect2"/>, which is equal /// to <see cref="Position"/> + (<see cref="Size"/> / 2). /// </summary> @@ -191,10 +182,10 @@ namespace Godot { Rect2 g = this; - g._position.x -= by; - g._position.y -= by; - g._size.x += by * 2; - g._size.y += by * 2; + g._position.X -= by; + g._position.Y -= by; + g._size.X += by * 2; + g._size.Y += by * 2; return g; } @@ -214,10 +205,10 @@ namespace Godot { Rect2 g = this; - g._position.x -= left; - g._position.y -= top; - g._size.x += left + right; - g._size.y += top + bottom; + g._position.X -= left; + g._position.Y -= top; + g._size.X += left + right; + g._size.Y += top + bottom; return g; } @@ -247,14 +238,14 @@ namespace Godot /// Returns <see langword="true"/> if the <see cref="Rect2"/> has /// area, and <see langword="false"/> if the <see cref="Rect2"/> /// is linear, empty, or has a negative <see cref="Size"/>. - /// See also <see cref="GetArea"/>. + /// See also <see cref="Area"/>. /// </summary> /// <returns> /// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> has area. /// </returns> public readonly bool HasArea() { - return _size.x > 0.0f && _size.y > 0.0f; + return _size.X > 0.0f && _size.Y > 0.0f; } /// <summary> @@ -267,14 +258,14 @@ namespace Godot /// </returns> public readonly bool HasPoint(Vector2 point) { - if (point.x < _position.x) + if (point.X < _position.X) return false; - if (point.y < _position.y) + if (point.Y < _position.Y) return false; - if (point.x >= _position.x + _size.x) + if (point.X >= _position.X + _size.X) return false; - if (point.y >= _position.y + _size.y) + if (point.Y >= _position.Y + _size.Y) return false; return true; @@ -295,38 +286,38 @@ namespace Godot { if (includeBorders) { - if (_position.x > b._position.x + b._size.x) + if (_position.X > b._position.X + b._size.X) { return false; } - if (_position.x + _size.x < b._position.x) + if (_position.X + _size.X < b._position.X) { return false; } - if (_position.y > b._position.y + b._size.y) + if (_position.Y > b._position.Y + b._size.Y) { return false; } - if (_position.y + _size.y < b._position.y) + if (_position.Y + _size.Y < b._position.Y) { return false; } } else { - if (_position.x >= b._position.x + b._size.x) + if (_position.X >= b._position.X + b._size.X) { return false; } - if (_position.x + _size.x <= b._position.x) + if (_position.X + _size.X <= b._position.X) { return false; } - if (_position.y >= b._position.y + b._size.y) + if (_position.Y >= b._position.Y + b._size.Y) { return false; } - if (_position.y + _size.y <= b._position.y) + if (_position.Y + _size.Y <= b._position.Y) { return false; } @@ -344,11 +335,11 @@ namespace Godot { Rect2 newRect; - newRect._position.x = Mathf.Min(b._position.x, _position.x); - newRect._position.y = Mathf.Min(b._position.y, _position.y); + newRect._position.X = Mathf.Min(b._position.X, _position.X); + newRect._position.Y = Mathf.Min(b._position.Y, _position.Y); - newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x); - newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y); + newRect._size.X = Mathf.Max(b._position.X + b._size.X, _position.X + _size.X); + newRect._size.Y = Mathf.Max(b._position.Y + b._size.Y, _position.Y + _size.Y); newRect._size -= newRect._position; // Make relative again diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs index cf8939a859..2099d0abca 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs @@ -4,21 +4,21 @@ using System.Runtime.InteropServices; namespace Godot { /// <summary> - /// 2D axis-aligned bounding box using integers. Rect2i consists of a position, a size, and + /// 2D axis-aligned bounding box using integers. Rect2I consists of a position, a size, and /// several utility functions. It is typically used for fast overlap tests. /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential)] - public struct Rect2i : IEquatable<Rect2i> + public struct Rect2I : IEquatable<Rect2I> { - private Vector2i _position; - private Vector2i _size; + private Vector2I _position; + private Vector2I _size; /// <summary> /// Beginning corner. Typically has values lower than <see cref="End"/>. /// </summary> /// <value>Directly uses a private field.</value> - public Vector2i Position + public Vector2I Position { readonly get { return _position; } set { _position = value; } @@ -29,7 +29,7 @@ namespace Godot /// If the size is negative, you can use <see cref="Abs"/> to fix it. /// </summary> /// <value>Directly uses a private field.</value> - public Vector2i Size + public Vector2I Size { readonly get { return _size; } set { _size = value; } @@ -43,105 +43,105 @@ namespace Godot /// Getting is equivalent to <paramref name="value"/> = <see cref="Position"/> + <see cref="Size"/>, /// setting is equivalent to <see cref="Size"/> = <paramref name="value"/> - <see cref="Position"/> /// </value> - public Vector2i End + public Vector2I End { readonly get { return _position + _size; } set { _size = value - _position; } } /// <summary> - /// The area of this <see cref="Rect2i"/>. + /// The area of this <see cref="Rect2I"/>. + /// See also <see cref="HasArea"/>. /// </summary> - /// <value>Equivalent to <see cref="GetArea()"/>.</value> public readonly int Area { - get { return GetArea(); } + get { return _size.X * _size.Y; } } /// <summary> - /// Returns a <see cref="Rect2i"/> with equivalent position and size, modified so that + /// Returns a <see cref="Rect2I"/> with equivalent position and size, modified so that /// the top-left corner is the origin and width and height are positive. /// </summary> - /// <returns>The modified <see cref="Rect2i"/>.</returns> - public readonly Rect2i Abs() + /// <returns>The modified <see cref="Rect2I"/>.</returns> + public readonly Rect2I Abs() { - Vector2i end = End; - Vector2i topLeft = new Vector2i(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y)); - return new Rect2i(topLeft, _size.Abs()); + Vector2I end = End; + Vector2I topLeft = new Vector2I(Mathf.Min(_position.X, end.X), Mathf.Min(_position.Y, end.Y)); + return new Rect2I(topLeft, _size.Abs()); } /// <summary> - /// Returns the intersection of this <see cref="Rect2i"/> and <paramref name="b"/>. - /// If the rectangles do not intersect, an empty <see cref="Rect2i"/> is returned. + /// Returns the intersection of this <see cref="Rect2I"/> and <paramref name="b"/>. + /// If the rectangles do not intersect, an empty <see cref="Rect2I"/> is returned. /// </summary> - /// <param name="b">The other <see cref="Rect2i"/>.</param> + /// <param name="b">The other <see cref="Rect2I"/>.</param> /// <returns> - /// The intersection of this <see cref="Rect2i"/> and <paramref name="b"/>, - /// or an empty <see cref="Rect2i"/> if they do not intersect. + /// The intersection of this <see cref="Rect2I"/> and <paramref name="b"/>, + /// or an empty <see cref="Rect2I"/> if they do not intersect. /// </returns> - public readonly Rect2i Intersection(Rect2i b) + public readonly Rect2I Intersection(Rect2I b) { - Rect2i newRect = b; + Rect2I newRect = b; if (!Intersects(newRect)) { - return new Rect2i(); + return new Rect2I(); } - newRect._position.x = Mathf.Max(b._position.x, _position.x); - newRect._position.y = Mathf.Max(b._position.y, _position.y); + newRect._position.X = Mathf.Max(b._position.X, _position.X); + newRect._position.Y = Mathf.Max(b._position.Y, _position.Y); - Vector2i bEnd = b._position + b._size; - Vector2i end = _position + _size; + Vector2I bEnd = b._position + b._size; + Vector2I end = _position + _size; - newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x; - newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y; + newRect._size.X = Mathf.Min(bEnd.X, end.X) - newRect._position.X; + newRect._size.Y = Mathf.Min(bEnd.Y, end.Y) - newRect._position.Y; return newRect; } /// <summary> - /// Returns <see langword="true"/> if this <see cref="Rect2i"/> completely encloses another one. + /// Returns <see langword="true"/> if this <see cref="Rect2I"/> completely encloses another one. /// </summary> - /// <param name="b">The other <see cref="Rect2i"/> that may be enclosed.</param> + /// <param name="b">The other <see cref="Rect2I"/> that may be enclosed.</param> /// <returns> - /// A <see langword="bool"/> for whether or not this <see cref="Rect2i"/> encloses <paramref name="b"/>. + /// A <see langword="bool"/> for whether or not this <see cref="Rect2I"/> encloses <paramref name="b"/>. /// </returns> - public readonly bool Encloses(Rect2i b) + public readonly bool Encloses(Rect2I b) { - return b._position.x >= _position.x && b._position.y >= _position.y && - b._position.x + b._size.x < _position.x + _size.x && - b._position.y + b._size.y < _position.y + _size.y; + return b._position.X >= _position.X && b._position.Y >= _position.Y && + b._position.X + b._size.X < _position.X + _size.X && + b._position.Y + b._size.Y < _position.Y + _size.Y; } /// <summary> - /// Returns this <see cref="Rect2i"/> expanded to include a given point. + /// Returns this <see cref="Rect2I"/> expanded to include a given point. /// </summary> /// <param name="to">The point to include.</param> - /// <returns>The expanded <see cref="Rect2i"/>.</returns> - public readonly Rect2i Expand(Vector2i to) + /// <returns>The expanded <see cref="Rect2I"/>.</returns> + public readonly Rect2I Expand(Vector2I to) { - Rect2i expanded = this; + Rect2I expanded = this; - Vector2i begin = expanded._position; - Vector2i end = expanded._position + expanded._size; + Vector2I begin = expanded._position; + Vector2I end = expanded._position + expanded._size; - if (to.x < begin.x) + if (to.X < begin.X) { - begin.x = to.x; + begin.X = to.X; } - if (to.y < begin.y) + if (to.Y < begin.Y) { - begin.y = to.y; + begin.Y = to.Y; } - if (to.x > end.x) + if (to.X > end.X) { - end.x = to.x; + end.X = to.X; } - if (to.y > end.y) + if (to.Y > end.Y) { - end.y = to.y; + end.Y = to.Y; } expanded._position = begin; @@ -151,48 +151,39 @@ namespace Godot } /// <summary> - /// Returns the area of the <see cref="Rect2i"/>. - /// </summary> - /// <returns>The area.</returns> - public readonly int GetArea() - { - return _size.x * _size.y; - } - - /// <summary> - /// Returns the center of the <see cref="Rect2i"/>, which is equal + /// Returns the center of the <see cref="Rect2I"/>, which is equal /// to <see cref="Position"/> + (<see cref="Size"/> / 2). /// If <see cref="Size"/> is an odd number, the returned center /// value will be rounded towards <see cref="Position"/>. /// </summary> /// <returns>The center.</returns> - public readonly Vector2i GetCenter() + public readonly Vector2I GetCenter() { return _position + (_size / 2); } /// <summary> - /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount + /// Returns a copy of the <see cref="Rect2I"/> grown by the specified amount /// on all sides. /// </summary> /// <seealso cref="GrowIndividual(int, int, int, int)"/> /// <seealso cref="GrowSide(Side, int)"/> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown <see cref="Rect2i"/>.</returns> - public readonly Rect2i Grow(int by) + /// <returns>The grown <see cref="Rect2I"/>.</returns> + public readonly Rect2I Grow(int by) { - Rect2i g = this; + Rect2I g = this; - g._position.x -= by; - g._position.y -= by; - g._size.x += by * 2; - g._size.y += by * 2; + g._position.X -= by; + g._position.Y -= by; + g._size.X += by * 2; + g._size.Y += by * 2; return g; } /// <summary> - /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount + /// Returns a copy of the <see cref="Rect2I"/> grown by the specified amount /// on each side individually. /// </summary> /// <seealso cref="Grow(int)"/> @@ -201,31 +192,31 @@ namespace Godot /// <param name="top">The amount to grow by on the top side.</param> /// <param name="right">The amount to grow by on the right side.</param> /// <param name="bottom">The amount to grow by on the bottom side.</param> - /// <returns>The grown <see cref="Rect2i"/>.</returns> - public readonly Rect2i GrowIndividual(int left, int top, int right, int bottom) + /// <returns>The grown <see cref="Rect2I"/>.</returns> + public readonly Rect2I GrowIndividual(int left, int top, int right, int bottom) { - Rect2i g = this; + Rect2I g = this; - g._position.x -= left; - g._position.y -= top; - g._size.x += left + right; - g._size.y += top + bottom; + g._position.X -= left; + g._position.Y -= top; + g._size.X += left + right; + g._size.Y += top + bottom; return g; } /// <summary> - /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount + /// Returns a copy of the <see cref="Rect2I"/> grown by the specified amount /// on the specified <see cref="Side"/>. /// </summary> /// <seealso cref="Grow(int)"/> /// <seealso cref="GrowIndividual(int, int, int, int)"/> /// <param name="side">The side to grow.</param> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown <see cref="Rect2i"/>.</returns> - public readonly Rect2i GrowSide(Side side, int by) + /// <returns>The grown <see cref="Rect2I"/>.</returns> + public readonly Rect2I GrowSide(Side side, int by) { - Rect2i g = this; + Rect2I g = this; g = g.GrowIndividual(Side.Left == side ? by : 0, Side.Top == side ? by : 0, @@ -236,76 +227,76 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if the <see cref="Rect2i"/> has - /// area, and <see langword="false"/> if the <see cref="Rect2i"/> + /// Returns <see langword="true"/> if the <see cref="Rect2I"/> has + /// area, and <see langword="false"/> if the <see cref="Rect2I"/> /// is linear, empty, or has a negative <see cref="Size"/>. - /// See also <see cref="GetArea"/>. + /// See also <see cref="Area"/>. /// </summary> /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> has area. + /// A <see langword="bool"/> for whether or not the <see cref="Rect2I"/> has area. /// </returns> public readonly bool HasArea() { - return _size.x > 0 && _size.y > 0; + return _size.X > 0 && _size.Y > 0; } /// <summary> - /// Returns <see langword="true"/> if the <see cref="Rect2i"/> contains a point, + /// Returns <see langword="true"/> if the <see cref="Rect2I"/> contains a point, /// or <see langword="false"/> otherwise. /// </summary> /// <param name="point">The point to check.</param> /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> contains <paramref name="point"/>. + /// A <see langword="bool"/> for whether or not the <see cref="Rect2I"/> contains <paramref name="point"/>. /// </returns> - public readonly bool HasPoint(Vector2i point) + public readonly bool HasPoint(Vector2I point) { - if (point.x < _position.x) + if (point.X < _position.X) return false; - if (point.y < _position.y) + if (point.Y < _position.Y) return false; - if (point.x >= _position.x + _size.x) + if (point.X >= _position.X + _size.X) return false; - if (point.y >= _position.y + _size.y) + if (point.Y >= _position.Y + _size.Y) return false; return true; } /// <summary> - /// Returns <see langword="true"/> if the <see cref="Rect2i"/> overlaps with <paramref name="b"/> + /// Returns <see langword="true"/> if the <see cref="Rect2I"/> overlaps with <paramref name="b"/> /// (i.e. they have at least one point in common). /// </summary> - /// <param name="b">The other <see cref="Rect2i"/> to check for intersections with.</param> + /// <param name="b">The other <see cref="Rect2I"/> to check for intersections with.</param> /// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns> - public readonly bool Intersects(Rect2i b) + public readonly bool Intersects(Rect2I b) { - if (_position.x >= b._position.x + b._size.x) + if (_position.X >= b._position.X + b._size.X) return false; - if (_position.x + _size.x <= b._position.x) + if (_position.X + _size.X <= b._position.X) return false; - if (_position.y >= b._position.y + b._size.y) + if (_position.Y >= b._position.Y + b._size.Y) return false; - if (_position.y + _size.y <= b._position.y) + if (_position.Y + _size.Y <= b._position.Y) return false; return true; } /// <summary> - /// Returns a larger <see cref="Rect2i"/> that contains this <see cref="Rect2i"/> and <paramref name="b"/>. + /// Returns a larger <see cref="Rect2I"/> that contains this <see cref="Rect2I"/> and <paramref name="b"/>. /// </summary> - /// <param name="b">The other <see cref="Rect2i"/>.</param> - /// <returns>The merged <see cref="Rect2i"/>.</returns> - public readonly Rect2i Merge(Rect2i b) + /// <param name="b">The other <see cref="Rect2I"/>.</param> + /// <returns>The merged <see cref="Rect2I"/>.</returns> + public readonly Rect2I Merge(Rect2I b) { - Rect2i newRect; + Rect2I newRect; - newRect._position.x = Mathf.Min(b._position.x, _position.x); - newRect._position.y = Mathf.Min(b._position.y, _position.y); + newRect._position.X = Mathf.Min(b._position.X, _position.X); + newRect._position.Y = Mathf.Min(b._position.Y, _position.Y); - newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x); - newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y); + newRect._size.X = Mathf.Max(b._position.X + b._size.X, _position.X + _size.X); + newRect._size.Y = Mathf.Max(b._position.Y + b._size.Y, _position.Y + _size.Y); newRect._size -= newRect._position; // Make relative again @@ -313,93 +304,93 @@ namespace Godot } /// <summary> - /// Constructs a <see cref="Rect2i"/> from a position and size. + /// Constructs a <see cref="Rect2I"/> from a position and size. /// </summary> /// <param name="position">The position.</param> /// <param name="size">The size.</param> - public Rect2i(Vector2i position, Vector2i size) + public Rect2I(Vector2I position, Vector2I size) { _position = position; _size = size; } /// <summary> - /// Constructs a <see cref="Rect2i"/> from a position, width, and height. + /// Constructs a <see cref="Rect2I"/> from a position, width, and height. /// </summary> /// <param name="position">The position.</param> /// <param name="width">The width.</param> /// <param name="height">The height.</param> - public Rect2i(Vector2i position, int width, int height) + public Rect2I(Vector2I position, int width, int height) { _position = position; - _size = new Vector2i(width, height); + _size = new Vector2I(width, height); } /// <summary> - /// Constructs a <see cref="Rect2i"/> from x, y, and size. + /// Constructs a <see cref="Rect2I"/> from x, y, and size. /// </summary> /// <param name="x">The position's X coordinate.</param> /// <param name="y">The position's Y coordinate.</param> /// <param name="size">The size.</param> - public Rect2i(int x, int y, Vector2i size) + public Rect2I(int x, int y, Vector2I size) { - _position = new Vector2i(x, y); + _position = new Vector2I(x, y); _size = size; } /// <summary> - /// Constructs a <see cref="Rect2i"/> from x, y, width, and height. + /// Constructs a <see cref="Rect2I"/> from x, y, width, and height. /// </summary> /// <param name="x">The position's X coordinate.</param> /// <param name="y">The position's Y coordinate.</param> /// <param name="width">The width.</param> /// <param name="height">The height.</param> - public Rect2i(int x, int y, int width, int height) + public Rect2I(int x, int y, int width, int height) { - _position = new Vector2i(x, y); - _size = new Vector2i(width, height); + _position = new Vector2I(x, y); + _size = new Vector2I(width, height); } /// <summary> /// Returns <see langword="true"/> if the - /// <see cref="Rect2i"/>s are exactly equal. + /// <see cref="Rect2I"/>s are exactly equal. /// </summary> /// <param name="left">The left rect.</param> /// <param name="right">The right rect.</param> /// <returns>Whether or not the rects are equal.</returns> - public static bool operator ==(Rect2i left, Rect2i right) + public static bool operator ==(Rect2I left, Rect2I right) { return left.Equals(right); } /// <summary> /// Returns <see langword="true"/> if the - /// <see cref="Rect2i"/>s are not equal. + /// <see cref="Rect2I"/>s are not equal. /// </summary> /// <param name="left">The left rect.</param> /// <param name="right">The right rect.</param> /// <returns>Whether or not the rects are not equal.</returns> - public static bool operator !=(Rect2i left, Rect2i right) + public static bool operator !=(Rect2I left, Rect2I right) { return !left.Equals(right); } /// <summary> - /// Converts this <see cref="Rect2i"/> to a <see cref="Rect2"/>. + /// Converts this <see cref="Rect2I"/> to a <see cref="Rect2"/>. /// </summary> /// <param name="value">The rect to convert.</param> - public static implicit operator Rect2(Rect2i value) + public static implicit operator Rect2(Rect2I value) { return new Rect2(value._position, value._size); } /// <summary> - /// Converts a <see cref="Rect2"/> to a <see cref="Rect2i"/>. + /// Converts a <see cref="Rect2"/> to a <see cref="Rect2I"/>. /// </summary> /// <param name="value">The rect to convert.</param> - public static explicit operator Rect2i(Rect2 value) + public static explicit operator Rect2I(Rect2 value) { - return new Rect2i((Vector2i)value.Position, (Vector2i)value.Size); + return new Rect2I((Vector2I)value.Position, (Vector2I)value.Size); } /// <summary> @@ -409,7 +400,7 @@ namespace Godot /// <returns>Whether or not the rect and the other object are equal.</returns> public override readonly bool Equals(object obj) { - return obj is Rect2i other && Equals(other); + return obj is Rect2I other && Equals(other); } /// <summary> @@ -417,13 +408,13 @@ namespace Godot /// </summary> /// <param name="other">The other rect to compare.</param> /// <returns>Whether or not the rects are equal.</returns> - public readonly bool Equals(Rect2i other) + public readonly bool Equals(Rect2I other) { return _position.Equals(other._position) && _size.Equals(other._size); } /// <summary> - /// Serves as the hash function for <see cref="Rect2i"/>. + /// Serves as the hash function for <see cref="Rect2I"/>. /// </summary> /// <returns>A hash code for this rect.</returns> public override readonly int GetHashCode() @@ -432,7 +423,7 @@ namespace Godot } /// <summary> - /// Converts this <see cref="Rect2i"/> to a string. + /// Converts this <see cref="Rect2I"/> to a string. /// </summary> /// <returns>A string representation of this rect.</returns> public override readonly string ToString() @@ -441,7 +432,7 @@ namespace Godot } /// <summary> - /// Converts this <see cref="Rect2i"/> to a string with the given <paramref name="format"/>. + /// Converts this <see cref="Rect2I"/> to a string with the given <paramref name="format"/>. /// </summary> /// <returns>A string representation of this rect.</returns> public readonly string ToString(string format) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs index 59b9faf16c..150eb98fc7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs @@ -6,25 +6,25 @@ using Godot.NativeInterop; namespace Godot { /// <summary> - /// The RID type is used to access the unique integer ID of a resource. + /// The Rid type is used to access the unique integer ID of a resource. /// They are opaque, which means they do not grant access to the associated /// resource by themselves. They are used by and with the low-level Server /// classes such as <see cref="RenderingServer"/>. /// </summary> [StructLayout(LayoutKind.Sequential)] - public readonly struct RID + public readonly struct Rid { private readonly ulong _id; // Default is 0 - internal RID(ulong id) + internal Rid(ulong id) { _id = id; } /// <summary> - /// Constructs a new <see cref="RID"/> for the given <see cref="Object"/> <paramref name="from"/>. + /// Constructs a new <see cref="Rid"/> for the given <see cref="GodotObject"/> <paramref name="from"/>. /// </summary> - public RID(Object from) + public Rid(GodotObject from) => _id = from is Resource res ? res.GetRid()._id : default; /// <summary> @@ -34,9 +34,9 @@ namespace Godot public ulong Id => _id; /// <summary> - /// Converts this <see cref="RID"/> to a string. + /// Converts this <see cref="Rid"/> to a string. /// </summary> - /// <returns>A string representation of this RID.</returns> - public override string ToString() => $"RID({Id})"; + /// <returns>A string representation of this Rid.</returns> + public override string ToString() => $"Rid({Id})"; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs index f9b8f06603..9ac8abd37b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs @@ -5,13 +5,14 @@ namespace Godot /// </summary> public readonly struct Signal : IAwaitable<Variant[]> { - private readonly Object _owner; + private readonly GodotObject _owner; private readonly StringName _signalName; /// <summary> /// Object that contains the signal. /// </summary> - public Object Owner => _owner; + public GodotObject Owner => _owner; + /// <summary> /// Name of the signal. /// </summary> @@ -23,7 +24,7 @@ namespace Godot /// </summary> /// <param name="owner">Object that contains the signal.</param> /// <param name="name">Name of the signal.</param> - public Signal(Object owner, StringName name) + public Signal(GodotObject owner, StringName name) { _owner = owner; _signalName = name; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index 96fb891086..a67f626d35 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -10,13 +10,13 @@ namespace Godot private Variant[] _result; private Action _continuation; - public SignalAwaiter(Object source, StringName signal, Object target) + public SignalAwaiter(GodotObject source, StringName signal, GodotObject target) { var awaiterGcHandle = CustomGCHandle.AllocStrong(this); using godot_string_name signalSrc = NativeFuncs.godotsharp_string_name_new_copy( (godot_string_name)(signal?.NativeValue ?? default)); - NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), in signalSrc, - Object.GetPtr(target), GCHandle.ToIntPtr(awaiterGcHandle)); + NativeFuncs.godotsharp_internal_signal_awaiter_connect(GodotObject.GetPtr(source), in signalSrc, + GodotObject.GetPtr(target), GCHandle.ToIntPtr(awaiterGcHandle)); } public bool IsCompleted => _completed; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index d4329d78c1..df67e075ac 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -69,19 +69,6 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if the strings begins - /// with the given string <paramref name="text"/>. - /// </summary> - /// <param name="instance">The string to check.</param> - /// <param name="text">The beginning string.</param> - /// <returns>If the string begins with the given string.</returns> - [Obsolete("Use string.StartsWith instead.")] - public static bool BeginsWith(this string instance, string text) - { - return instance.StartsWith(text); - } - - /// <summary> /// Returns the bigrams (pairs of consecutive letters) of this string. /// </summary> /// <param name="instance">The string that will be used.</param> @@ -618,7 +605,7 @@ namespace Godot } else { - if (instance.BeginsWith("/")) + if (instance.StartsWith('/')) { rs = instance.Substring(1); directory = "/"; @@ -675,15 +662,15 @@ namespace Godot /// <summary> /// Converts ASCII encoded array to string. - /// Fast alternative to <see cref="GetStringFromUTF8"/> if the + /// Fast alternative to <see cref="GetStringFromUtf8"/> if the /// content is ASCII-only. Unlike the UTF-8 function this function /// maps every byte to a character in the array. Multibyte sequences /// will not be interpreted correctly. For parsing user input always - /// use <see cref="GetStringFromUTF8"/>. + /// use <see cref="GetStringFromUtf8"/>. /// </summary> /// <param name="bytes">A byte array of ASCII characters (on the range of 0-127).</param> /// <returns>A string created from the bytes.</returns> - public static string GetStringFromASCII(this byte[] bytes) + public static string GetStringFromAscii(this byte[] bytes) { return Encoding.ASCII.GetString(bytes); } @@ -693,7 +680,7 @@ namespace Godot /// </summary> /// <param name="bytes">A byte array of UTF-16 characters.</param> /// <returns>A string created from the bytes.</returns> - public static string GetStringFromUTF16(this byte[] bytes) + public static string GetStringFromUtf16(this byte[] bytes) { return Encoding.Unicode.GetString(bytes); } @@ -703,14 +690,14 @@ namespace Godot /// </summary> /// <param name="bytes">A byte array of UTF-32 characters.</param> /// <returns>A string created from the bytes.</returns> - public static string GetStringFromUTF32(this byte[] bytes) + public static string GetStringFromUtf32(this byte[] bytes) { return Encoding.UTF32.GetString(bytes); } /// <summary> /// Converts UTF-8 encoded array to string. - /// Slower than <see cref="GetStringFromASCII"/> but supports UTF-8 + /// Slower than <see cref="GetStringFromAscii"/> but supports UTF-8 /// encoded data. Use this function if you are unsure about the /// source of the data. For user input this function /// should always be preferred. @@ -719,7 +706,7 @@ namespace Godot /// A byte array of UTF-8 characters (a character may take up multiple bytes). /// </param> /// <returns>A string created from the bytes.</returns> - public static string GetStringFromUTF8(this byte[] bytes) + public static string GetStringFromUtf8(this byte[] bytes) { return Encoding.UTF8.GetString(bytes); } @@ -1199,23 +1186,6 @@ namespace Godot } /// <summary> - /// Returns a copy of the string with characters removed from the left. - /// The <paramref name="chars"/> argument is a string specifying the set of characters - /// to be removed. - /// Note: The <paramref name="chars"/> is not a prefix. See <see cref="TrimPrefix"/> - /// method that will remove a single prefix string rather than a set of characters. - /// </summary> - /// <seealso cref="RStrip(string, string)"/> - /// <param name="instance">The string to remove characters from.</param> - /// <param name="chars">The characters to be removed.</param> - /// <returns>A copy of the string with characters removed from the left.</returns> - [Obsolete("Use string.TrimStart instead.")] - public static string LStrip(this string instance, string chars) - { - return instance.TrimStart(chars.ToCharArray()); - } - - /// <summary> /// Do a simple expression match, where '*' matches zero or more /// arbitrary characters and '?' matches any single character except '.'. /// </summary> @@ -1287,10 +1257,10 @@ namespace Godot /// <summary> /// Returns the MD5 hash of the string as an array of bytes. /// </summary> - /// <seealso cref="MD5Text(string)"/> + /// <seealso cref="Md5Text(string)"/> /// <param name="instance">The string to hash.</param> /// <returns>The MD5 hash of the string.</returns> - public static byte[] MD5Buffer(this string instance) + public static byte[] Md5Buffer(this string instance) { #pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms return MD5.HashData(Encoding.UTF8.GetBytes(instance)); @@ -1300,12 +1270,12 @@ namespace Godot /// <summary> /// Returns the MD5 hash of the string as a string. /// </summary> - /// <seealso cref="MD5Buffer(string)"/> + /// <seealso cref="Md5Buffer(string)"/> /// <param name="instance">The string to hash.</param> /// <returns>The MD5 hash of the string.</returns> - public static string MD5Text(this string instance) + public static string Md5Text(this string instance) { - return instance.MD5Buffer().HexEncode(); + return instance.Md5Buffer().HexEncode(); } /// <summary> @@ -1504,29 +1474,12 @@ namespace Godot } /// <summary> - /// Returns a copy of the string with characters removed from the right. - /// The <paramref name="chars"/> argument is a string specifying the set of characters - /// to be removed. - /// Note: The <paramref name="chars"/> is not a suffix. See <see cref="TrimSuffix"/> - /// method that will remove a single suffix string rather than a set of characters. - /// </summary> - /// <seealso cref="LStrip(string, string)"/> - /// <param name="instance">The string to remove characters from.</param> - /// <param name="chars">The characters to be removed.</param> - /// <returns>A copy of the string with characters removed from the right.</returns> - [Obsolete("Use string.TrimEnd instead.")] - public static string RStrip(this string instance, string chars) - { - return instance.TrimEnd(chars.ToCharArray()); - } - - /// <summary> /// Returns the SHA-1 hash of the string as an array of bytes. /// </summary> - /// <seealso cref="SHA1Text(string)"/> + /// <seealso cref="Sha1Text(string)"/> /// <param name="instance">The string to hash.</param> /// <returns>The SHA-1 hash of the string.</returns> - public static byte[] SHA1Buffer(this string instance) + public static byte[] Sha1Buffer(this string instance) { #pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms return SHA1.HashData(Encoding.UTF8.GetBytes(instance)); @@ -1536,21 +1489,21 @@ namespace Godot /// <summary> /// Returns the SHA-1 hash of the string as a string. /// </summary> - /// <seealso cref="SHA1Buffer(string)"/> + /// <seealso cref="Sha1Buffer(string)"/> /// <param name="instance">The string to hash.</param> /// <returns>The SHA-1 hash of the string.</returns> - public static string SHA1Text(this string instance) + public static string Sha1Text(this string instance) { - return instance.SHA1Buffer().HexEncode(); + return instance.Sha1Buffer().HexEncode(); } /// <summary> /// Returns the SHA-256 hash of the string as an array of bytes. /// </summary> - /// <seealso cref="SHA256Text(string)"/> + /// <seealso cref="Sha256Text(string)"/> /// <param name="instance">The string to hash.</param> /// <returns>The SHA-256 hash of the string.</returns> - public static byte[] SHA256Buffer(this string instance) + public static byte[] Sha256Buffer(this string instance) { return SHA256.HashData(Encoding.UTF8.GetBytes(instance)); } @@ -1558,12 +1511,12 @@ namespace Godot /// <summary> /// Returns the SHA-256 hash of the string as a string. /// </summary> - /// <seealso cref="SHA256Buffer(string)"/> + /// <seealso cref="Sha256Buffer(string)"/> /// <param name="instance">The string to hash.</param> /// <returns>The SHA-256 hash of the string.</returns> - public static string SHA256Text(this string instance) + public static string Sha256Text(this string instance) { - return instance.SHA256Buffer().HexEncode(); + return instance.Sha256Buffer().HexEncode(); } /// <summary> @@ -1745,15 +1698,15 @@ namespace Godot /// <summary> /// Converts the String (which is a character array) to PackedByteArray (which is an array of bytes). - /// The conversion is faster compared to <see cref="ToUTF8Buffer(string)"/>, + /// The conversion is faster compared to <see cref="ToUtf8Buffer(string)"/>, /// as this method assumes that all the characters in the String are ASCII characters. /// </summary> - /// <seealso cref="ToUTF8Buffer(string)"/> - /// <seealso cref="ToUTF16Buffer(string)"/> - /// <seealso cref="ToUTF32Buffer(string)"/> + /// <seealso cref="ToUtf8Buffer(string)"/> + /// <seealso cref="ToUtf16Buffer(string)"/> + /// <seealso cref="ToUtf32Buffer(string)"/> /// <param name="instance">The string to convert.</param> /// <returns>The string as ASCII encoded bytes.</returns> - public static byte[] ToASCIIBuffer(this string instance) + public static byte[] ToAsciiBuffer(this string instance) { return Encoding.ASCII.GetBytes(instance); } @@ -1783,12 +1736,12 @@ namespace Godot /// <summary> /// Converts the string (which is an array of characters) to an UTF-16 encoded array of bytes. /// </summary> - /// <seealso cref="ToASCIIBuffer(string)"/> - /// <seealso cref="ToUTF32Buffer(string)"/> - /// <seealso cref="ToUTF8Buffer(string)"/> + /// <seealso cref="ToAsciiBuffer(string)"/> + /// <seealso cref="ToUtf32Buffer(string)"/> + /// <seealso cref="ToUtf8Buffer(string)"/> /// <param name="instance">The string to convert.</param> /// <returns>The string as UTF-16 encoded bytes.</returns> - public static byte[] ToUTF16Buffer(this string instance) + public static byte[] ToUtf16Buffer(this string instance) { return Encoding.Unicode.GetBytes(instance); } @@ -1796,28 +1749,28 @@ namespace Godot /// <summary> /// Converts the string (which is an array of characters) to an UTF-32 encoded array of bytes. /// </summary> - /// <seealso cref="ToASCIIBuffer(string)"/> - /// <seealso cref="ToUTF16Buffer(string)"/> - /// <seealso cref="ToUTF8Buffer(string)"/> + /// <seealso cref="ToAsciiBuffer(string)"/> + /// <seealso cref="ToUtf16Buffer(string)"/> + /// <seealso cref="ToUtf8Buffer(string)"/> /// <param name="instance">The string to convert.</param> /// <returns>The string as UTF-32 encoded bytes.</returns> - public static byte[] ToUTF32Buffer(this string instance) + public static byte[] ToUtf32Buffer(this string instance) { return Encoding.UTF32.GetBytes(instance); } /// <summary> /// Converts the string (which is an array of characters) to an UTF-8 encoded array of bytes. - /// The conversion is a bit slower than <see cref="ToASCIIBuffer(string)"/>, + /// The conversion is a bit slower than <see cref="ToAsciiBuffer(string)"/>, /// but supports all UTF-8 characters. Therefore, you should prefer this function - /// over <see cref="ToASCIIBuffer(string)"/>. + /// over <see cref="ToAsciiBuffer(string)"/>. /// </summary> - /// <seealso cref="ToASCIIBuffer(string)"/> - /// <seealso cref="ToUTF16Buffer(string)"/> - /// <seealso cref="ToUTF32Buffer(string)"/> + /// <seealso cref="ToAsciiBuffer(string)"/> + /// <seealso cref="ToUtf16Buffer(string)"/> + /// <seealso cref="ToUtf32Buffer(string)"/> /// <param name="instance">The string to convert.</param> /// <returns>The string as UTF-8 encoded bytes.</returns> - public static byte[] ToUTF8Buffer(this string instance) + public static byte[] ToUtf8Buffer(this string instance) { return Encoding.UTF8.GetBytes(instance); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index fa060e3a53..5283dc7ec6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Godot @@ -18,22 +19,51 @@ namespace Godot /// <summary> /// The basis matrix's X vector (column 0). Equivalent to array index <c>[0]</c>. /// </summary> - public Vector2 x; + public Vector2 X; /// <summary> /// The basis matrix's Y vector (column 1). Equivalent to array index <c>[1]</c>. /// </summary> - public Vector2 y; + public Vector2 Y; /// <summary> /// The origin vector (column 2, the third column). Equivalent to array index <c>[2]</c>. /// The origin vector represents translation. /// </summary> - public Vector2 origin; + public Vector2 Origin; + + /// <summary> + /// Returns the transform's rotation (in radians). + /// </summary> + public readonly real_t Rotation => Mathf.Atan2(X.Y, X.X); + + /// <summary> + /// Returns the scale. + /// </summary> + public readonly Vector2 Scale + { + get + { + real_t detSign = Mathf.Sign(BasisDeterminant()); + return new Vector2(X.Length(), detSign * Y.Length()); + } + } + + /// <summary> + /// Returns the transform's skew (in radians). + /// </summary> + public readonly real_t Skew + { + get + { + real_t detSign = Mathf.Sign(BasisDeterminant()); + return Mathf.Acos(X.Normalized().Dot(detSign * Y.Normalized())) - Mathf.Pi * 0.5f; + } + } /// <summary> /// Access whole columns in the form of <see cref="Vector2"/>. - /// The third column is the <see cref="origin"/> vector. + /// The third column is the <see cref="Origin"/> vector. /// </summary> /// <param name="column">Which column vector.</param> /// <exception cref="ArgumentOutOfRangeException"> @@ -46,11 +76,11 @@ namespace Godot switch (column) { case 0: - return x; + return X; case 1: - return y; + return Y; case 2: - return origin; + return Origin; default: throw new ArgumentOutOfRangeException(nameof(column)); } @@ -60,13 +90,13 @@ namespace Godot switch (column) { case 0: - x = value; + X = value; return; case 1: - y = value; + Y = value; return; case 2: - origin = value; + Origin = value; return; default: throw new ArgumentOutOfRangeException(nameof(column)); @@ -76,7 +106,7 @@ namespace Godot /// <summary> /// Access matrix elements in column-major order. - /// The third column is the <see cref="origin"/> vector. + /// The third column is the <see cref="Origin"/> vector. /// </summary> /// <param name="column">Which column, the matrix horizontal position.</param> /// <param name="row">Which row, the matrix vertical position.</param> @@ -131,14 +161,15 @@ namespace Godot /// and is usually considered invalid. /// </summary> /// <returns>The determinant of the basis matrix.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] private readonly real_t BasisDeterminant() { - return (x.x * y.y) - (x.y * y.x); + return (X.X * Y.Y) - (X.Y * Y.X); } /// <summary> /// Returns a vector transformed (multiplied) by the basis matrix. - /// This method does not account for translation (the <see cref="origin"/> vector). + /// This method does not account for translation (the <see cref="Origin"/> vector). /// </summary> /// <seealso cref="BasisXformInv(Vector2)"/> /// <param name="v">A vector to transform.</param> @@ -150,7 +181,7 @@ namespace Godot /// <summary> /// Returns a vector transformed (multiplied) by the inverse basis matrix. - /// This method does not account for translation (the <see cref="origin"/> vector). + /// This method does not account for translation (the <see cref="Origin"/> vector). /// /// Note: This results in a multiplication by the inverse of the /// basis matrix only if it represents a rotation-reflection. @@ -160,24 +191,7 @@ namespace Godot /// <returns>The inversely transformed vector.</returns> public readonly Vector2 BasisXformInv(Vector2 v) { - return new Vector2(x.Dot(v), y.Dot(v)); - } - - /// <summary> - /// Returns the transform's rotation (in radians). - /// </summary> - public readonly real_t GetRotation() - { - return Mathf.Atan2(x.y, x.x); - } - - /// <summary> - /// Returns the scale. - /// </summary> - public readonly Vector2 GetScale() - { - real_t detSign = Mathf.Sign(BasisDeterminant()); - return new Vector2(x.Length(), detSign * y.Length()); + return new Vector2(X.Dot(v), Y.Dot(v)); } /// <summary> @@ -188,48 +202,13 @@ namespace Godot /// <returns>The interpolated transform.</returns> public readonly Transform2D InterpolateWith(Transform2D transform, real_t weight) { - real_t r1 = GetRotation(); - real_t r2 = transform.GetRotation(); - - Vector2 s1 = GetScale(); - Vector2 s2 = transform.GetScale(); - - // Slerp rotation - (real_t sin1, real_t cos1) = Mathf.SinCos(r1); - (real_t sin2, real_t cos2) = Mathf.SinCos(r2); - var v1 = new Vector2(cos1, sin1); - var v2 = new Vector2(cos2, sin2); - - real_t dot = v1.Dot(v2); - - dot = Mathf.Clamp(dot, -1.0f, 1.0f); - - Vector2 v; - - if (dot > 0.9995f) - { - // Linearly interpolate to avoid numerical precision issues - v = v1.Lerp(v2, weight).Normalized(); - } - else - { - real_t angle = weight * Mathf.Acos(dot); - Vector2 v3 = (v2 - (v1 * dot)).Normalized(); - (real_t sine, real_t cos) = Mathf.SinCos(angle); - v = (v1 * sine) + (v3 * cos); - } - - // Extract parameters - Vector2 p1 = origin; - Vector2 p2 = transform.origin; - - // Construct matrix - var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.Lerp(p2, weight)); - Vector2 scale = s1.Lerp(s2, weight); - res.x *= scale; - res.y *= scale; - - return res; + return new Transform2D + ( + Mathf.LerpAngle(Rotation, transform.Rotation, weight), + Scale.Lerp(transform.Scale, weight), + Mathf.LerpAngle(Skew, transform.Skew, weight), + Origin.Lerp(transform.Origin, weight) + ); } /// <summary> @@ -243,10 +222,10 @@ namespace Godot Transform2D inv = this; // Swap - inv.x.y = y.x; - inv.y.x = x.y; + inv.X.Y = Y.X; + inv.Y.X = X.Y; - inv.origin = inv.BasisXform(-inv.origin); + inv.Origin = inv.BasisXform(-inv.Origin); return inv; } @@ -258,7 +237,7 @@ namespace Godot /// <returns>Whether this vector is finite or not.</returns> public readonly bool IsFinite() { - return x.IsFinite() && y.IsFinite() && origin.IsFinite(); + return X.IsFinite() && Y.IsFinite() && Origin.IsFinite(); } /// <summary> @@ -270,15 +249,15 @@ namespace Godot { Transform2D on = this; - Vector2 onX = on.x; - Vector2 onY = on.y; + Vector2 onX = on.X; + Vector2 onY = on.Y; onX.Normalize(); onY = onY - (onX * onX.Dot(onY)); onY.Normalize(); - on.x = onX; - on.y = onY; + on.X = onX; + on.Y = onY; return on; } @@ -292,7 +271,7 @@ namespace Godot /// <returns>The rotated transformation matrix.</returns> public readonly Transform2D Rotated(real_t angle) { - return this * new Transform2D(angle, new Vector2()); + return new Transform2D(angle, new Vector2()) * this; } /// <summary> @@ -304,7 +283,7 @@ namespace Godot /// <returns>The rotated transformation matrix.</returns> public readonly Transform2D RotatedLocal(real_t angle) { - return new Transform2D(angle, new Vector2()) * this; + return this * new Transform2D(angle, new Vector2()); } /// <summary> @@ -317,9 +296,9 @@ namespace Godot public readonly Transform2D Scaled(Vector2 scale) { Transform2D copy = this; - copy.x *= scale; - copy.y *= scale; - copy.origin *= scale; + copy.X *= scale; + copy.Y *= scale; + copy.Origin *= scale; return copy; } @@ -333,8 +312,8 @@ namespace Godot public readonly Transform2D ScaledLocal(Vector2 scale) { Transform2D copy = this; - copy.x *= scale; - copy.y *= scale; + copy.X *= scale; + copy.Y *= scale; return copy; } @@ -358,7 +337,7 @@ namespace Godot public readonly Transform2D Translated(Vector2 offset) { Transform2D copy = this; - copy.origin += offset; + copy.Origin += offset; return copy; } @@ -372,7 +351,7 @@ namespace Godot public readonly Transform2D TranslatedLocal(Vector2 offset) { Transform2D copy = this; - copy.origin += copy.BasisXform(offset); + copy.Origin += copy.BasisXform(offset); return copy; } @@ -407,26 +386,26 @@ namespace Godot /// <param name="originPos">The origin vector, or column index 2.</param> public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos) { - x = xAxis; - y = yAxis; - origin = originPos; + X = xAxis; + Y = yAxis; + Origin = originPos; } /// <summary> /// Constructs a transformation matrix from the given components. - /// Arguments are named such that xy is equal to calling <c>x.y</c>. - /// </summary> - /// <param name="xx">The X component of the X column vector, accessed via <c>t.x.x</c> or <c>[0][0]</c>.</param> - /// <param name="xy">The Y component of the X column vector, accessed via <c>t.x.y</c> or <c>[0][1]</c>.</param> - /// <param name="yx">The X component of the Y column vector, accessed via <c>t.y.x</c> or <c>[1][0]</c>.</param> - /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.y.y</c> or <c>[1][1]</c>.</param> - /// <param name="ox">The X component of the origin vector, accessed via <c>t.origin.x</c> or <c>[2][0]</c>.</param> - /// <param name="oy">The Y component of the origin vector, accessed via <c>t.origin.y</c> or <c>[2][1]</c>.</param> + /// Arguments are named such that xy is equal to calling <c>X.Y</c>. + /// </summary> + /// <param name="xx">The X component of the X column vector, accessed via <c>t.X.X</c> or <c>[0][0]</c>.</param> + /// <param name="xy">The Y component of the X column vector, accessed via <c>t.X.Y</c> or <c>[0][1]</c>.</param> + /// <param name="yx">The X component of the Y column vector, accessed via <c>t.Y.X</c> or <c>[1][0]</c>.</param> + /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.Y.Y</c> or <c>[1][1]</c>.</param> + /// <param name="ox">The X component of the origin vector, accessed via <c>t.Origin.X</c> or <c>[2][0]</c>.</param> + /// <param name="oy">The Y component of the origin vector, accessed via <c>t.Origin.Y</c> or <c>[2][1]</c>.</param> public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { - x = new Vector2(xx, xy); - y = new Vector2(yx, yy); - origin = new Vector2(ox, oy); + X = new Vector2(xx, xy); + Y = new Vector2(yx, yy); + Origin = new Vector2(ox, oy); } /// <summary> @@ -438,10 +417,10 @@ namespace Godot public Transform2D(real_t rotation, Vector2 origin) { (real_t sin, real_t cos) = Mathf.SinCos(rotation); - x.x = y.y = cos; - x.y = y.x = sin; - y.x *= -1; - this.origin = origin; + X.X = Y.Y = cos; + X.Y = Y.X = sin; + Y.X *= -1; + Origin = origin; } /// <summary> @@ -457,11 +436,11 @@ namespace Godot { (real_t rotationSin, real_t rotationCos) = Mathf.SinCos(rotation); (real_t rotationSkewSin, real_t rotationSkewCos) = Mathf.SinCos(rotation + skew); - x.x = rotationCos * scale.x; - y.y = rotationSkewCos * scale.y; - y.x = -rotationSkewSin * scale.y; - x.y = rotationSin * scale.x; - this.origin = origin; + X.X = rotationCos * scale.X; + Y.Y = rotationSkewCos * scale.Y; + Y.X = -rotationSkewSin * scale.Y; + X.Y = rotationSin * scale.X; + Origin = origin; } /// <summary> @@ -474,17 +453,17 @@ namespace Godot /// <returns>The composed transform.</returns> public static Transform2D operator *(Transform2D left, Transform2D right) { - left.origin = left * right.origin; + left.Origin = left * right.Origin; - real_t x0 = left.Tdotx(right.x); - real_t x1 = left.Tdoty(right.x); - real_t y0 = left.Tdotx(right.y); - real_t y1 = left.Tdoty(right.y); + real_t x0 = left.Tdotx(right.X); + real_t x1 = left.Tdoty(right.X); + real_t y0 = left.Tdotx(right.Y); + real_t y1 = left.Tdoty(right.Y); - left.x.x = x0; - left.x.y = x1; - left.y.x = y0; - left.y.y = y1; + left.X.X = x0; + left.X.Y = x1; + left.Y.X = y0; + left.Y.Y = y1; return left; } @@ -497,7 +476,7 @@ namespace Godot /// <returns>The transformed Vector2.</returns> public static Vector2 operator *(Transform2D transform, Vector2 vector) { - return new Vector2(transform.Tdotx(vector), transform.Tdoty(vector)) + transform.origin; + return new Vector2(transform.Tdotx(vector), transform.Tdoty(vector)) + transform.Origin; } /// <summary> @@ -508,8 +487,8 @@ namespace Godot /// <returns>The inversely transformed Vector2.</returns> public static Vector2 operator *(Vector2 vector, Transform2D transform) { - Vector2 vInv = vector - transform.origin; - return new Vector2(transform.x.Dot(vInv), transform.y.Dot(vInv)); + Vector2 vInv = vector - transform.Origin; + return new Vector2(transform.X.Dot(vInv), transform.Y.Dot(vInv)); } /// <summary> @@ -521,8 +500,8 @@ namespace Godot public static Rect2 operator *(Transform2D transform, Rect2 rect) { Vector2 pos = transform * rect.Position; - Vector2 toX = transform.x * rect.Size.x; - Vector2 toY = transform.y * rect.Size.y; + Vector2 toX = transform.X * rect.Size.X; + Vector2 toY = transform.Y * rect.Size.Y; return new Rect2(pos, new Vector2()).Expand(pos + toX).Expand(pos + toY).Expand(pos + toX + toY); } @@ -536,9 +515,9 @@ namespace Godot public static Rect2 operator *(Rect2 rect, Transform2D transform) { Vector2 pos = rect.Position * transform; - Vector2 to1 = new Vector2(rect.Position.x, rect.Position.y + rect.Size.y) * transform; - Vector2 to2 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y + rect.Size.y) * transform; - Vector2 to3 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y) * transform; + Vector2 to1 = new Vector2(rect.Position.X, rect.Position.Y + rect.Size.Y) * transform; + Vector2 to2 = new Vector2(rect.Position.X + rect.Size.X, rect.Position.Y + rect.Size.Y) * transform; + Vector2 to3 = new Vector2(rect.Position.X + rect.Size.X, rect.Position.Y) * transform; return new Rect2(pos, new Vector2()).Expand(to1).Expand(to2).Expand(to3); } @@ -627,7 +606,7 @@ namespace Godot /// <returns>Whether or not the matrices are exactly equal.</returns> public readonly bool Equals(Transform2D other) { - return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin); + return X.Equals(other.X) && Y.Equals(other.Y) && Origin.Equals(other.Origin); } /// <summary> @@ -638,7 +617,7 @@ namespace Godot /// <returns>Whether or not the matrices are approximately equal.</returns> public readonly bool IsEqualApprox(Transform2D other) { - return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin); + return X.IsEqualApprox(other.X) && Y.IsEqualApprox(other.Y) && Origin.IsEqualApprox(other.Origin); } /// <summary> @@ -647,7 +626,7 @@ namespace Godot /// <returns>A hash code for this transform.</returns> public override readonly int GetHashCode() { - return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode(); + return X.GetHashCode() ^ Y.GetHashCode() ^ Origin.GetHashCode(); } /// <summary> @@ -656,7 +635,7 @@ namespace Godot /// <returns>A string representation of this transform.</returns> public override readonly string ToString() { - return $"[X: {x}, Y: {y}, O: {origin}]"; + return $"[X: {X}, Y: {Y}, O: {Origin}]"; } /// <summary> @@ -665,7 +644,7 @@ namespace Godot /// <returns>A string representation of this transform.</returns> public readonly string ToString(string format) { - return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, O: {origin.ToString(format)}]"; + return $"[X: {X.ToString(format)}, Y: {Y.ToString(format)}, O: {Origin.ToString(format)}]"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 6b2475fc59..b34e95c04d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -6,7 +6,7 @@ namespace Godot /// <summary> /// 3×4 matrix (3 rows, 4 columns) used for 3D linear transformations. /// It can represent transformations such as translation, rotation, or scaling. - /// It consists of a <see cref="Basis"/> (first 3 columns) and a + /// It consists of a <see cref="Godot.Basis"/> (first 3 columns) and a /// <see cref="Vector3"/> for the origin (last column). /// /// For more information, read this documentation article: @@ -17,19 +17,19 @@ namespace Godot public struct Transform3D : IEquatable<Transform3D> { /// <summary> - /// The <see cref="Basis"/> of this transform. Contains the X, Y, and Z basis + /// The <see cref="Godot.Basis"/> of this transform. Contains the X, Y, and Z basis /// vectors (columns 0 to 2) and is responsible for rotation and scale. /// </summary> - public Basis basis; + public Basis Basis; /// <summary> /// The origin vector (column 3, the fourth column). Equivalent to array index <c>[3]</c>. /// </summary> - public Vector3 origin; + public Vector3 Origin; /// <summary> /// Access whole columns in the form of <see cref="Vector3"/>. - /// The fourth column is the <see cref="origin"/> vector. + /// The fourth column is the <see cref="Origin"/> vector. /// </summary> /// <param name="column">Which column vector.</param> /// <exception cref="ArgumentOutOfRangeException"> @@ -42,13 +42,13 @@ namespace Godot switch (column) { case 0: - return basis.Column0; + return Basis.Column0; case 1: - return basis.Column1; + return Basis.Column1; case 2: - return basis.Column2; + return Basis.Column2; case 3: - return origin; + return Origin; default: throw new ArgumentOutOfRangeException(nameof(column)); } @@ -58,16 +58,16 @@ namespace Godot switch (column) { case 0: - basis.Column0 = value; + Basis.Column0 = value; return; case 1: - basis.Column1 = value; + Basis.Column1 = value; return; case 2: - basis.Column2 = value; + Basis.Column2 = value; return; case 3: - origin = value; + Origin = value; return; default: throw new ArgumentOutOfRangeException(nameof(column)); @@ -77,7 +77,7 @@ namespace Godot /// <summary> /// Access matrix elements in column-major order. - /// The fourth column is the <see cref="origin"/> vector. + /// The fourth column is the <see cref="Origin"/> vector. /// </summary> /// <param name="column">Which column, the matrix horizontal position.</param> /// <param name="row">Which row, the matrix vertical position.</param> @@ -87,18 +87,18 @@ namespace Godot { if (column == 3) { - return origin[row]; + return Origin[row]; } - return basis[column, row]; + return Basis[column, row]; } set { if (column == 3) { - origin[row] = value; + Origin[row] = value; return; } - basis[column, row] = value; + Basis[column, row] = value; } } @@ -110,8 +110,8 @@ namespace Godot /// <returns>The inverse transformation matrix.</returns> public readonly Transform3D AffineInverse() { - Basis basisInv = basis.Inverse(); - return new Transform3D(basisInv, basisInv * -origin); + Basis basisInv = Basis.Inverse(); + return new Transform3D(basisInv, basisInv * -Origin); } /// <summary> @@ -124,19 +124,19 @@ namespace Godot /// <returns>The interpolated transform.</returns> public readonly Transform3D InterpolateWith(Transform3D transform, real_t weight) { - Vector3 sourceScale = basis.GetScale(); - Quaternion sourceRotation = basis.GetRotationQuaternion(); - Vector3 sourceLocation = origin; + Vector3 sourceScale = Basis.Scale; + Quaternion sourceRotation = Basis.GetRotationQuaternion(); + Vector3 sourceLocation = Origin; - Vector3 destinationScale = transform.basis.GetScale(); - Quaternion destinationRotation = transform.basis.GetRotationQuaternion(); - Vector3 destinationLocation = transform.origin; + Vector3 destinationScale = transform.Basis.Scale; + Quaternion destinationRotation = transform.Basis.GetRotationQuaternion(); + Vector3 destinationLocation = transform.Origin; var interpolated = new Transform3D(); Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized(); Vector3 scale = sourceScale.Lerp(destinationScale, weight); - interpolated.basis.SetQuaternionScale(quaternion, scale); - interpolated.origin = sourceLocation.Lerp(destinationLocation, weight); + interpolated.Basis.SetQuaternionScale(quaternion, scale); + interpolated.Origin = sourceLocation.Lerp(destinationLocation, weight); return interpolated; } @@ -149,8 +149,8 @@ namespace Godot /// <returns>The inverse matrix.</returns> public readonly Transform3D Inverse() { - Basis basisTr = basis.Transposed(); - return new Transform3D(basisTr, basisTr * -origin); + Basis basisTr = Basis.Transposed(); + return new Transform3D(basisTr, basisTr * -Origin); } /// <summary> @@ -160,7 +160,7 @@ namespace Godot /// <returns>Whether this vector is finite or not.</returns> public readonly bool IsFinite() { - return basis.IsFinite() && origin.IsFinite(); + return Basis.IsFinite() && Origin.IsFinite(); } /// <summary> @@ -179,7 +179,7 @@ namespace Godot public readonly Transform3D LookingAt(Vector3 target, Vector3 up) { Transform3D t = this; - t.SetLookAt(origin, target, up); + t.SetLookAt(Origin, target, up); return t; } @@ -190,7 +190,7 @@ namespace Godot /// <returns>The orthonormalized transform.</returns> public readonly Transform3D Orthonormalized() { - return new Transform3D(basis.Orthonormalized(), origin); + return new Transform3D(Basis.Orthonormalized(), Origin); } /// <summary> @@ -219,7 +219,7 @@ namespace Godot public readonly Transform3D RotatedLocal(Vector3 axis, real_t angle) { Basis tmpBasis = new Basis(axis, angle); - return new Transform3D(basis * tmpBasis, origin); + return new Transform3D(Basis * tmpBasis, Origin); } /// <summary> @@ -231,7 +231,7 @@ namespace Godot /// <returns>The scaled transformation matrix.</returns> public readonly Transform3D Scaled(Vector3 scale) { - return new Transform3D(basis.Scaled(scale), origin * scale); + return new Transform3D(Basis.Scaled(scale), Origin * scale); } /// <summary> @@ -244,7 +244,7 @@ namespace Godot public readonly Transform3D ScaledLocal(Vector3 scale) { Basis tmpBasis = Basis.FromScale(scale); - return new Transform3D(basis * tmpBasis, origin); + return new Transform3D(Basis * tmpBasis, Origin); } private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) @@ -265,9 +265,9 @@ namespace Godot column0.Normalize(); column1.Normalize(); - basis = new Basis(column0, column1, column2); + Basis = new Basis(column0, column1, column2); - origin = eye; + Origin = eye; } /// <summary> @@ -279,7 +279,7 @@ namespace Godot /// <returns>The translated matrix.</returns> public readonly Transform3D Translated(Vector3 offset) { - return new Transform3D(basis, origin + offset); + return new Transform3D(Basis, Origin + offset); } /// <summary> @@ -291,11 +291,11 @@ namespace Godot /// <returns>The translated matrix.</returns> public readonly Transform3D TranslatedLocal(Vector3 offset) { - return new Transform3D(basis, new Vector3 + return new Transform3D(Basis, new Vector3 ( - origin[0] + basis.Row0.Dot(offset), - origin[1] + basis.Row1.Dot(offset), - origin[2] + basis.Row2.Dot(offset) + Origin[0] + Basis.Row0.Dot(offset), + Origin[1] + Basis.Row1.Dot(offset), + Origin[2] + Basis.Row2.Dot(offset) )); } @@ -337,64 +337,64 @@ namespace Godot /// <param name="origin">The origin vector, or column index 3.</param> public Transform3D(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin) { - basis = new Basis(column0, column1, column2); - this.origin = origin; + Basis = new Basis(column0, column1, column2); + Origin = origin; } /// <summary> /// Constructs a transformation matrix from the given components. - /// Arguments are named such that xy is equal to calling <c>basis.x.y</c>. - /// </summary> - /// <param name="xx">The X component of the X column vector, accessed via <c>t.basis.x.x</c> or <c>[0][0]</c>.</param> - /// <param name="yx">The X component of the Y column vector, accessed via <c>t.basis.y.x</c> or <c>[1][0]</c>.</param> - /// <param name="zx">The X component of the Z column vector, accessed via <c>t.basis.z.x</c> or <c>[2][0]</c>.</param> - /// <param name="xy">The Y component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][1]</c>.</param> - /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][1]</c>.</param> - /// <param name="zy">The Y component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][1]</c>.</param> - /// <param name="xz">The Z component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][2]</c>.</param> - /// <param name="yz">The Z component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][2]</c>.</param> - /// <param name="zz">The Z component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][2]</c>.</param> - /// <param name="ox">The X component of the origin vector, accessed via <c>t.origin.x</c> or <c>[2][0]</c>.</param> - /// <param name="oy">The Y component of the origin vector, accessed via <c>t.origin.y</c> or <c>[2][1]</c>.</param> - /// <param name="oz">The Z component of the origin vector, accessed via <c>t.origin.z</c> or <c>[2][2]</c>.</param> + /// Arguments are named such that xy is equal to calling <c>Basis.X.Y</c>. + /// </summary> + /// <param name="xx">The X component of the X column vector, accessed via <c>t.Basis.X.X</c> or <c>[0][0]</c>.</param> + /// <param name="yx">The X component of the Y column vector, accessed via <c>t.Basis.Y.X</c> or <c>[1][0]</c>.</param> + /// <param name="zx">The X component of the Z column vector, accessed via <c>t.Basis.Z.X</c> or <c>[2][0]</c>.</param> + /// <param name="xy">The Y component of the X column vector, accessed via <c>t.Basis.X.Y</c> or <c>[0][1]</c>.</param> + /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.Basis.Y.Y</c> or <c>[1][1]</c>.</param> + /// <param name="zy">The Y component of the Z column vector, accessed via <c>t.Basis.Y.Y</c> or <c>[2][1]</c>.</param> + /// <param name="xz">The Z component of the X column vector, accessed via <c>t.Basis.X.Y</c> or <c>[0][2]</c>.</param> + /// <param name="yz">The Z component of the Y column vector, accessed via <c>t.Basis.Y.Y</c> or <c>[1][2]</c>.</param> + /// <param name="zz">The Z component of the Z column vector, accessed via <c>t.Basis.Y.Y</c> or <c>[2][2]</c>.</param> + /// <param name="ox">The X component of the origin vector, accessed via <c>t.Origin.X</c> or <c>[2][0]</c>.</param> + /// <param name="oy">The Y component of the origin vector, accessed via <c>t.Origin.Y</c> or <c>[2][1]</c>.</param> + /// <param name="oz">The Z component of the origin vector, accessed via <c>t.Origin.Z</c> or <c>[2][2]</c>.</param> public Transform3D(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz, real_t ox, real_t oy, real_t oz) { - basis = new Basis(xx, yx, zx, xy, yy, zy, xz, yz, zz); - origin = new Vector3(ox, oy, oz); + Basis = new Basis(xx, yx, zx, xy, yy, zy, xz, yz, zz); + Origin = new Vector3(ox, oy, oz); } /// <summary> /// Constructs a transformation matrix from the given <paramref name="basis"/> and /// <paramref name="origin"/> vector. /// </summary> - /// <param name="basis">The <see cref="Basis"/> to create the basis from.</param> + /// <param name="basis">The <see cref="Godot.Basis"/> to create the basis from.</param> /// <param name="origin">The origin vector, or column index 3.</param> public Transform3D(Basis basis, Vector3 origin) { - this.basis = basis; - this.origin = origin; + Basis = basis; + Origin = origin; } /// <summary> /// Constructs a transformation matrix from the given <paramref name="projection"/> - /// by trimming the last row of the projection matrix (<c>projection.x.w</c>, - /// <c>projection.y.w</c>, <c>projection.z.w</c>, and <c>projection.w.w</c> + /// by trimming the last row of the projection matrix (<c>projection.X.W</c>, + /// <c>projection.Y.W</c>, <c>projection.Z.W</c>, and <c>projection.W.W</c> /// are not copied over). /// </summary> /// <param name="projection">The <see cref="Projection"/> to create the transform from.</param> public Transform3D(Projection projection) { - basis = new Basis + Basis = new Basis ( - projection.x.x, projection.y.x, projection.z.x, - projection.x.y, projection.y.y, projection.z.y, - projection.x.z, projection.y.z, projection.z.z + projection.X.X, projection.Y.X, projection.Z.X, + projection.X.Y, projection.Y.Y, projection.Z.Y, + projection.X.Z, projection.Y.Z, projection.Z.Z ); - origin = new Vector3 + Origin = new Vector3 ( - projection.w.x, - projection.w.y, - projection.w.z + projection.W.X, + projection.W.Y, + projection.W.Z ); } @@ -408,8 +408,8 @@ namespace Godot /// <returns>The composed transform.</returns> public static Transform3D operator *(Transform3D left, Transform3D right) { - left.origin = left * right.origin; - left.basis *= right.basis; + left.Origin = left * right.Origin; + left.Basis *= right.Basis; return left; } @@ -423,9 +423,9 @@ namespace Godot { return new Vector3 ( - transform.basis.Row0.Dot(vector) + transform.origin.x, - transform.basis.Row1.Dot(vector) + transform.origin.y, - transform.basis.Row2.Dot(vector) + transform.origin.z + transform.Basis.Row0.Dot(vector) + transform.Origin.X, + transform.Basis.Row1.Dot(vector) + transform.Origin.Y, + transform.Basis.Row2.Dot(vector) + transform.Origin.Z ); } @@ -440,13 +440,13 @@ namespace Godot /// <returns>The inversely transformed Vector3.</returns> public static Vector3 operator *(Vector3 vector, Transform3D transform) { - Vector3 vInv = vector - transform.origin; + Vector3 vInv = vector - transform.Origin; return new Vector3 ( - (transform.basis.Row0[0] * vInv.x) + (transform.basis.Row1[0] * vInv.y) + (transform.basis.Row2[0] * vInv.z), - (transform.basis.Row0[1] * vInv.x) + (transform.basis.Row1[1] * vInv.y) + (transform.basis.Row2[1] * vInv.z), - (transform.basis.Row0[2] * vInv.x) + (transform.basis.Row1[2] * vInv.y) + (transform.basis.Row2[2] * vInv.z) + (transform.Basis.Row0[0] * vInv.X) + (transform.Basis.Row1[0] * vInv.Y) + (transform.Basis.Row2[0] * vInv.Z), + (transform.Basis.Row0[1] * vInv.X) + (transform.Basis.Row1[1] * vInv.Y) + (transform.Basis.Row2[1] * vInv.Z), + (transform.Basis.Row0[2] * vInv.X) + (transform.Basis.Row1[2] * vInv.Y) + (transform.Basis.Row2[2] * vInv.Z) ); } @@ -456,19 +456,19 @@ namespace Godot /// <param name="transform">The transformation to apply.</param> /// <param name="aabb">An AABB to transform.</param> /// <returns>The transformed AABB.</returns> - public static AABB operator *(Transform3D transform, AABB aabb) + public static Aabb operator *(Transform3D transform, Aabb aabb) { Vector3 min = aabb.Position; Vector3 max = aabb.Position + aabb.Size; - Vector3 tmin = transform.origin; - Vector3 tmax = transform.origin; + Vector3 tmin = transform.Origin; + Vector3 tmax = transform.Origin; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - real_t e = transform.basis[i][j] * min[j]; - real_t f = transform.basis[i][j] * max[j]; + real_t e = transform.Basis[i][j] * min[j]; + real_t f = transform.Basis[i][j] * max[j]; if (e < f) { tmin[i] += e; @@ -482,7 +482,7 @@ namespace Godot } } - return new AABB(tmin, tmax - tmin); + return new Aabb(tmin, tmax - tmin); } /// <summary> @@ -491,18 +491,18 @@ namespace Godot /// <param name="aabb">An AABB to inversely transform.</param> /// <param name="transform">The transformation to apply.</param> /// <returns>The inversely transformed AABB.</returns> - public static AABB operator *(AABB aabb, Transform3D transform) + public static Aabb operator *(Aabb aabb, Transform3D transform) { - Vector3 pos = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y + aabb.Size.y, aabb.Position.z + aabb.Size.z) * transform; - Vector3 to1 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y + aabb.Size.y, aabb.Position.z) * transform; - Vector3 to2 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y, aabb.Position.z + aabb.Size.z) * transform; - Vector3 to3 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y, aabb.Position.z) * transform; - Vector3 to4 = new Vector3(aabb.Position.x, aabb.Position.y + aabb.Size.y, aabb.Position.z + aabb.Size.z) * transform; - Vector3 to5 = new Vector3(aabb.Position.x, aabb.Position.y + aabb.Size.y, aabb.Position.z) * transform; - Vector3 to6 = new Vector3(aabb.Position.x, aabb.Position.y, aabb.Position.z + aabb.Size.z) * transform; - Vector3 to7 = new Vector3(aabb.Position.x, aabb.Position.y, aabb.Position.z) * transform; + Vector3 pos = new Vector3(aabb.Position.X + aabb.Size.X, aabb.Position.Y + aabb.Size.Y, aabb.Position.Z + aabb.Size.Z) * transform; + Vector3 to1 = new Vector3(aabb.Position.X + aabb.Size.X, aabb.Position.Y + aabb.Size.Y, aabb.Position.Z) * transform; + Vector3 to2 = new Vector3(aabb.Position.X + aabb.Size.X, aabb.Position.Y, aabb.Position.Z + aabb.Size.Z) * transform; + Vector3 to3 = new Vector3(aabb.Position.X + aabb.Size.X, aabb.Position.Y, aabb.Position.Z) * transform; + Vector3 to4 = new Vector3(aabb.Position.X, aabb.Position.Y + aabb.Size.Y, aabb.Position.Z + aabb.Size.Z) * transform; + Vector3 to5 = new Vector3(aabb.Position.X, aabb.Position.Y + aabb.Size.Y, aabb.Position.Z) * transform; + Vector3 to6 = new Vector3(aabb.Position.X, aabb.Position.Y, aabb.Position.Z + aabb.Size.Z) * transform; + Vector3 to7 = new Vector3(aabb.Position.X, aabb.Position.Y, aabb.Position.Z) * transform; - return new AABB(pos, new Vector3()).Expand(to1).Expand(to2).Expand(to3).Expand(to4).Expand(to5).Expand(to6).Expand(to7); + return new Aabb(pos, new Vector3()).Expand(to1).Expand(to2).Expand(to3).Expand(to4).Expand(to5).Expand(to6).Expand(to7); } /// <summary> @@ -513,7 +513,7 @@ namespace Godot /// <returns>The transformed Plane.</returns> public static Plane operator *(Transform3D transform, Plane plane) { - Basis bInvTrans = transform.basis.Inverse().Transposed(); + Basis bInvTrans = transform.Basis.Inverse().Transposed(); // Transform a single point on the plane. Vector3 point = transform * (plane.Normal * plane.D); @@ -534,7 +534,7 @@ namespace Godot public static Plane operator *(Plane plane, Transform3D transform) { Transform3D tInv = transform.AffineInverse(); - Basis bTrans = transform.basis.Transposed(); + Basis bTrans = transform.Basis.Transposed(); // Transform a single point on the plane. Vector3 point = tInv * (plane.Normal * plane.D); @@ -637,7 +637,7 @@ namespace Godot /// <returns>Whether or not the matrices are exactly equal.</returns> public readonly bool Equals(Transform3D other) { - return basis.Equals(other.basis) && origin.Equals(other.origin); + return Basis.Equals(other.Basis) && Origin.Equals(other.Origin); } /// <summary> @@ -648,7 +648,7 @@ namespace Godot /// <returns>Whether or not the matrices are approximately equal.</returns> public readonly bool IsEqualApprox(Transform3D other) { - return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin); + return Basis.IsEqualApprox(other.Basis) && Origin.IsEqualApprox(other.Origin); } /// <summary> @@ -657,7 +657,7 @@ namespace Godot /// <returns>A hash code for this transform.</returns> public override readonly int GetHashCode() { - return basis.GetHashCode() ^ origin.GetHashCode(); + return Basis.GetHashCode() ^ Origin.GetHashCode(); } /// <summary> @@ -666,7 +666,7 @@ namespace Godot /// <returns>A string representation of this transform.</returns> public override readonly string ToString() { - return $"[X: {basis.x}, Y: {basis.y}, Z: {basis.z}, O: {origin}]"; + return $"[X: {Basis.X}, Y: {Basis.Y}, Z: {Basis.Z}, O: {Origin}]"; } /// <summary> @@ -675,7 +675,7 @@ namespace Godot /// <returns>A string representation of this transform.</returns> public readonly string ToString(string format) { - return $"[X: {basis.x.ToString(format)}, Y: {basis.y.ToString(format)}, Z: {basis.z.ToString(format)}, O: {origin.ToString(format)}]"; + return $"[X: {Basis.X.ToString(format)}, Y: {Basis.Y.ToString(format)}, Z: {Basis.Z.ToString(format)}, O: {Origin.ToString(format)}]"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs index da12309217..9aad965ad0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs @@ -60,13 +60,13 @@ public partial struct Variant : IDisposable case Type.Int: case Type.Float: case Type.Vector2: - case Type.Vector2i: + case Type.Vector2I: case Type.Rect2: - case Type.Rect2i: + case Type.Rect2I: case Type.Vector3: - case Type.Vector3i: + case Type.Vector3I: case Type.Vector4: - case Type.Vector4i: + case Type.Vector4I: case Type.Plane: case Type.Quaternion: case Type.Color: @@ -117,24 +117,24 @@ public partial struct Variant : IDisposable Type.Float => AsDouble(), Type.String => AsString(), Type.Vector2 => AsVector2(), - Type.Vector2i => AsVector2i(), + Type.Vector2I => AsVector2I(), Type.Rect2 => AsRect2(), - Type.Rect2i => AsRect2i(), + Type.Rect2I => AsRect2I(), Type.Vector3 => AsVector3(), - Type.Vector3i => AsVector3i(), - Type.Transform2d => AsTransform2D(), + Type.Vector3I => AsVector3I(), + Type.Transform2D => AsTransform2D(), Type.Vector4 => AsVector4(), - Type.Vector4i => AsVector4i(), + Type.Vector4I => AsVector4I(), Type.Plane => AsPlane(), Type.Quaternion => AsQuaternion(), - Type.Aabb => AsAABB(), + Type.Aabb => AsAabb(), Type.Basis => AsBasis(), - Type.Transform3d => AsTransform3D(), + Type.Transform3D => AsTransform3D(), Type.Projection => AsProjection(), Type.Color => AsColor(), Type.StringName => AsStringName(), Type.NodePath => AsNodePath(), - Type.Rid => AsRID(), + Type.Rid => AsRid(), Type.Object => AsGodotObject(), Type.Callable => AsCallable(), Type.Signal => AsSignal(), @@ -219,16 +219,16 @@ public partial struct Variant : IDisposable VariantUtils.ConvertToVector2((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2i AsVector2i() => - VariantUtils.ConvertToVector2i((godot_variant)NativeVar); + public Vector2I AsVector2I() => + VariantUtils.ConvertToVector2I((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rect2 AsRect2() => VariantUtils.ConvertToRect2((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rect2i AsRect2i() => - VariantUtils.ConvertToRect2i((godot_variant)NativeVar); + public Rect2I AsRect2I() => + VariantUtils.ConvertToRect2I((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Transform2D AsTransform2D() => @@ -239,8 +239,8 @@ public partial struct Variant : IDisposable VariantUtils.ConvertToVector3((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector3i AsVector3i() => - VariantUtils.ConvertToVector3i((godot_variant)NativeVar); + public Vector3I AsVector3I() => + VariantUtils.ConvertToVector3I((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Basis AsBasis() => @@ -259,16 +259,16 @@ public partial struct Variant : IDisposable VariantUtils.ConvertToVector4((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4i AsVector4i() => - VariantUtils.ConvertToVector4i((godot_variant)NativeVar); + public Vector4I AsVector4I() => + VariantUtils.ConvertToVector4I((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Projection AsProjection() => VariantUtils.ConvertToProjection((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public AABB AsAABB() => - VariantUtils.ConvertToAABB((godot_variant)NativeVar); + public Aabb AsAabb() => + VariantUtils.ConvertToAabb((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color AsColor() => @@ -324,15 +324,15 @@ public partial struct Variant : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] public T[] AsGodotObjectArray<T>() - where T : Godot.Object => + where T : GodotObject => VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Collections.Dictionary<TKey, TValue> AsGodotDictionary<TKey, TValue>() => + public Collections.Dictionary<TKey, TValue> AsGodotDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>() => VariantUtils.ConvertToDictionary<TKey, TValue>((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Collections.Array<T> AsGodotArray<T>() => + public Collections.Array<T> AsGodotArray<[MustBeVariant] T>() => VariantUtils.ConvertToArray<T>((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -344,11 +344,11 @@ public partial struct Variant : IDisposable VariantUtils.ConvertToSystemArrayOfNodePath((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RID[] AsSystemArrayOfRID() => - VariantUtils.ConvertToSystemArrayOfRID((godot_variant)NativeVar); + public Rid[] AsSystemArrayOfRid() => + VariantUtils.ConvertToSystemArrayOfRid((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Godot.Object AsGodotObject() => + public GodotObject AsGodotObject() => VariantUtils.ConvertToGodotObject((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -360,8 +360,8 @@ public partial struct Variant : IDisposable VariantUtils.ConvertToNodePath((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RID AsRID() => - VariantUtils.ConvertToRID((godot_variant)NativeVar); + public Rid AsRid() => + VariantUtils.ConvertToRid((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Collections.Dictionary AsGodotDictionary() => @@ -416,13 +416,13 @@ public partial struct Variant : IDisposable public static explicit operator Vector2(Variant from) => from.AsVector2(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Vector2i(Variant from) => from.AsVector2i(); + public static explicit operator Vector2I(Variant from) => from.AsVector2I(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Rect2(Variant from) => from.AsRect2(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Rect2i(Variant from) => from.AsRect2i(); + public static explicit operator Rect2I(Variant from) => from.AsRect2I(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Transform2D(Variant from) => from.AsTransform2D(); @@ -431,7 +431,7 @@ public partial struct Variant : IDisposable public static explicit operator Vector3(Variant from) => from.AsVector3(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Vector3i(Variant from) => from.AsVector3i(); + public static explicit operator Vector3I(Variant from) => from.AsVector3I(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Basis(Variant from) => from.AsBasis(); @@ -446,13 +446,13 @@ public partial struct Variant : IDisposable public static explicit operator Vector4(Variant from) => from.AsVector4(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Vector4i(Variant from) => from.AsVector4i(); + public static explicit operator Vector4I(Variant from) => from.AsVector4I(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Projection(Variant from) => from.AsProjection(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator AABB(Variant from) => from.AsAABB(); + public static explicit operator Aabb(Variant from) => from.AsAabb(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Color(Variant from) => from.AsColor(); @@ -500,10 +500,10 @@ public partial struct Variant : IDisposable public static explicit operator NodePath[](Variant from) => from.AsSystemArrayOfNodePath(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator RID[](Variant from) => from.AsSystemArrayOfRID(); + public static explicit operator Rid[](Variant from) => from.AsSystemArrayOfRid(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator Godot.Object(Variant from) => from.AsGodotObject(); + public static explicit operator GodotObject(Variant from) => from.AsGodotObject(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator StringName(Variant from) => from.AsStringName(); @@ -512,7 +512,7 @@ public partial struct Variant : IDisposable public static explicit operator NodePath(Variant from) => from.AsNodePath(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator RID(Variant from) => from.AsRID(); + public static explicit operator Rid(Variant from) => from.AsRid(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Collections.Dictionary(Variant from) => from.AsGodotDictionary(); @@ -566,13 +566,13 @@ public partial struct Variant : IDisposable public static Variant CreateFrom(Vector2 from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(Vector2i from) => from; + public static Variant CreateFrom(Vector2I from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(Rect2 from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(Rect2i from) => from; + public static Variant CreateFrom(Rect2I from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(Transform2D from) => from; @@ -581,7 +581,7 @@ public partial struct Variant : IDisposable public static Variant CreateFrom(Vector3 from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(Vector3i from) => from; + public static Variant CreateFrom(Vector3I from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(Basis from) => from; @@ -596,13 +596,13 @@ public partial struct Variant : IDisposable public static Variant CreateFrom(Vector4 from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(Vector4i from) => from; + public static Variant CreateFrom(Vector4I from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(Projection from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(AABB from) => from; + public static Variant CreateFrom(Aabb from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(Color from) => from; @@ -644,14 +644,14 @@ public partial struct Variant : IDisposable public static Variant CreateFrom(Span<Color> from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(Godot.Object[] from) => from; + public static Variant CreateFrom(GodotObject[] from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) => + public static Variant CreateFrom<[MustBeVariant] TKey, [MustBeVariant] TValue>(Collections.Dictionary<TKey, TValue> from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom<T>(Collections.Array<T> from) => + public static Variant CreateFrom<[MustBeVariant] T>(Collections.Array<T> from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -661,10 +661,10 @@ public partial struct Variant : IDisposable public static Variant CreateFrom(Span<NodePath> from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(Span<RID> from) => from; + public static Variant CreateFrom(Span<Rid> from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(Godot.Object from) => from; + public static Variant CreateFrom(GodotObject from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(StringName from) => from; @@ -673,7 +673,7 @@ public partial struct Variant : IDisposable public static Variant CreateFrom(NodePath from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom(RID from) => from; + public static Variant CreateFrom(Rid from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant CreateFrom(Collections.Dictionary from) => from; @@ -740,16 +740,16 @@ public partial struct Variant : IDisposable CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Vector2i from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2i(from)); + public static implicit operator Variant(Vector2I from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2I(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(Rect2 from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Rect2i from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2i(from)); + public static implicit operator Variant(Rect2I from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2I(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(Transform2D from) => @@ -760,8 +760,8 @@ public partial struct Variant : IDisposable CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Vector3i from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3i(from)); + public static implicit operator Variant(Vector3I from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3I(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(Basis from) => @@ -780,16 +780,16 @@ public partial struct Variant : IDisposable CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Vector4i from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4i(from)); + public static implicit operator Variant(Vector4I from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4I(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(Projection from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromProjection(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(AABB from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAABB(from)); + public static implicit operator Variant(Aabb from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAabb(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(Color from) => @@ -844,7 +844,7 @@ public partial struct Variant : IDisposable (Variant)from.AsSpan(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Godot.Object[] from) => + public static implicit operator Variant(GodotObject[] from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfGodotObject(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -856,7 +856,7 @@ public partial struct Variant : IDisposable (Variant)from.AsSpan(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(RID[] from) => + public static implicit operator Variant(Rid[] from) => (Variant)from.AsSpan(); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -904,11 +904,11 @@ public partial struct Variant : IDisposable CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfNodePath(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Span<RID> from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRID(from)); + public static implicit operator Variant(Span<Rid> from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRid(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(Godot.Object from) => + public static implicit operator Variant(GodotObject from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromGodotObject(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -920,8 +920,8 @@ public partial struct Variant : IDisposable CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromNodePath(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Variant(RID from) => - CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRID(from)); + public static implicit operator Variant(Rid from) => + CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRid(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Variant(Collections.Dictionary from) => diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 07cb34cadd..0bf8f25f06 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -29,12 +29,12 @@ namespace Godot /// <summary> /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> - public real_t x; + public real_t X; /// <summary> /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> - public real_t y; + public real_t Y; /// <summary> /// Access vector components using their index. @@ -43,8 +43,8 @@ namespace Godot /// <paramref name="index"/> is not 0 or 1. /// </exception> /// <value> - /// <c>[0]</c> is equivalent to <see cref="x"/>, - /// <c>[1]</c> is equivalent to <see cref="y"/>. + /// <c>[0]</c> is equivalent to <see cref="X"/>, + /// <c>[1]</c> is equivalent to <see cref="Y"/>. /// </value> public real_t this[int index] { @@ -53,9 +53,9 @@ namespace Godot switch (index) { case 0: - return x; + return X; case 1: - return y; + return Y; default: throw new ArgumentOutOfRangeException(nameof(index)); } @@ -65,10 +65,10 @@ namespace Godot switch (index) { case 0: - x = value; + X = value; return; case 1: - y = value; + Y = value; return; default: throw new ArgumentOutOfRangeException(nameof(index)); @@ -81,8 +81,8 @@ namespace Godot /// </summary> public readonly void Deconstruct(out real_t x, out real_t y) { - x = this.x; - y = this.y; + x = X; + y = Y; } internal void Normalize() @@ -91,13 +91,13 @@ namespace Godot if (lengthsq == 0) { - x = y = 0f; + X = Y = 0f; } else { real_t length = Mathf.Sqrt(lengthsq); - x /= length; - y /= length; + X /= length; + Y /= length; } } @@ -107,19 +107,19 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns> public readonly Vector2 Abs() { - return new Vector2(Mathf.Abs(x), Mathf.Abs(y)); + return new Vector2(Mathf.Abs(X), Mathf.Abs(Y)); } /// <summary> /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians. /// /// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when - /// called with the vector's <see cref="y"/> and <see cref="x"/> as parameters: <c>Mathf.Atan2(v.y, v.x)</c>. + /// called with the vector's <see cref="Y"/> and <see cref="X"/> as parameters: <c>Mathf.Atan2(v.Y, v.X)</c>. /// </summary> /// <returns>The angle of this vector, in radians.</returns> public readonly real_t Angle() { - return Mathf.Atan2(y, x); + return Mathf.Atan2(Y, X); } /// <summary> @@ -139,16 +139,16 @@ namespace Godot /// <returns>The angle between the two vectors, in radians.</returns> public readonly real_t AngleToPoint(Vector2 to) { - return Mathf.Atan2(to.y - y, to.x - x); + return Mathf.Atan2(to.Y - Y, to.X - X); } /// <summary> - /// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>. + /// Returns the aspect ratio of this vector, the ratio of <see cref="X"/> to <see cref="Y"/>. /// </summary> - /// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns> + /// <returns>The <see cref="X"/> component divided by the <see cref="Y"/> component.</returns> public readonly real_t Aspect() { - return x / y; + return X / Y; } /// <summary> @@ -167,7 +167,7 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns> public readonly Vector2 Ceil() { - return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y)); + return new Vector2(Mathf.Ceil(X), Mathf.Ceil(Y)); } /// <summary> @@ -182,8 +182,8 @@ namespace Godot { return new Vector2 ( - Mathf.Clamp(x, min.x, max.x), - Mathf.Clamp(y, min.y, max.y) + Mathf.Clamp(X, min.X, max.X), + Mathf.Clamp(Y, min.Y, max.Y) ); } @@ -194,7 +194,7 @@ namespace Godot /// <returns>The cross product value.</returns> public readonly real_t Cross(Vector2 with) { - return (x * with.y) - (y * with.x); + return (X * with.Y) - (Y * with.X); } /// <summary> @@ -210,8 +210,8 @@ namespace Godot { return new Vector2 ( - Mathf.CubicInterpolate(x, b.x, preA.x, postB.x, weight), - Mathf.CubicInterpolate(y, b.y, preA.y, postB.y, weight) + Mathf.CubicInterpolate(X, b.X, preA.X, postB.X, weight), + Mathf.CubicInterpolate(Y, b.Y, preA.Y, postB.Y, weight) ); } @@ -233,8 +233,8 @@ namespace Godot { return new Vector2 ( - Mathf.CubicInterpolateInTime(x, b.x, preA.x, postB.x, weight, t, preAT, postBT), - Mathf.CubicInterpolateInTime(y, b.y, preA.y, postB.y, weight, t, preAT, postBT) + Mathf.CubicInterpolateInTime(X, b.X, preA.X, postB.X, weight, t, preAT, postBT), + Mathf.CubicInterpolateInTime(Y, b.Y, preA.Y, postB.Y, weight, t, preAT, postBT) ); } @@ -251,8 +251,8 @@ namespace Godot { return new Vector2 ( - Mathf.BezierInterpolate(x, control1.x, control2.x, end.x, t), - Mathf.BezierInterpolate(y, control1.y, control2.y, end.y, t) + Mathf.BezierInterpolate(X, control1.X, control2.X, end.X, t), + Mathf.BezierInterpolate(Y, control1.Y, control2.Y, end.Y, t) ); } @@ -268,8 +268,8 @@ namespace Godot public readonly Vector2 BezierDerivative(Vector2 control1, Vector2 control2, Vector2 end, real_t t) { return new Vector2( - Mathf.BezierDerivative(x, control1.x, control2.x, end.x, t), - Mathf.BezierDerivative(y, control1.y, control2.y, end.y, t) + Mathf.BezierDerivative(X, control1.X, control2.X, end.X, t), + Mathf.BezierDerivative(Y, control1.Y, control2.Y, end.Y, t) ); } @@ -280,7 +280,7 @@ namespace Godot /// <returns>The direction from this vector to <paramref name="to"/>.</returns> public readonly Vector2 DirectionTo(Vector2 to) { - return new Vector2(to.x - x, to.y - y).Normalized(); + return new Vector2(to.X - X, to.Y - Y).Normalized(); } /// <summary> @@ -292,7 +292,7 @@ namespace Godot /// <returns>The squared distance between the two vectors.</returns> public readonly real_t DistanceSquaredTo(Vector2 to) { - return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); + return (X - to.X) * (X - to.X) + (Y - to.Y) * (Y - to.Y); } /// <summary> @@ -302,7 +302,7 @@ namespace Godot /// <returns>The distance between the two vectors.</returns> public readonly real_t DistanceTo(Vector2 to) { - return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y)); + return Mathf.Sqrt((X - to.X) * (X - to.X) + (Y - to.Y) * (Y - to.Y)); } /// <summary> @@ -312,7 +312,7 @@ namespace Godot /// <returns>The dot product of the two vectors.</returns> public readonly real_t Dot(Vector2 with) { - return (x * with.x) + (y * with.y); + return (X * with.X) + (Y * with.Y); } /// <summary> @@ -321,16 +321,16 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns> public readonly Vector2 Floor() { - return new Vector2(Mathf.Floor(x), Mathf.Floor(y)); + return new Vector2(Mathf.Floor(X), Mathf.Floor(Y)); } /// <summary> - /// Returns the inverse of this vector. This is the same as <c>new Vector2(1 / v.x, 1 / v.y)</c>. + /// Returns the inverse of this vector. This is the same as <c>new Vector2(1 / v.X, 1 / v.Y)</c>. /// </summary> /// <returns>The inverse of this vector.</returns> public readonly Vector2 Inverse() { - return new Vector2(1 / x, 1 / y); + return new Vector2(1 / X, 1 / Y); } /// <summary> @@ -340,7 +340,7 @@ namespace Godot /// <returns>Whether this vector is finite or not.</returns> public readonly bool IsFinite() { - return Mathf.IsFinite(x) && Mathf.IsFinite(y); + return Mathf.IsFinite(X) && Mathf.IsFinite(Y); } /// <summary> @@ -359,7 +359,7 @@ namespace Godot /// <returns>The length of this vector.</returns> public readonly real_t Length() { - return Mathf.Sqrt((x * x) + (y * y)); + return Mathf.Sqrt((X * X) + (Y * Y)); } /// <summary> @@ -370,7 +370,7 @@ namespace Godot /// <returns>The squared length of this vector.</returns> public readonly real_t LengthSquared() { - return (x * x) + (y * y); + return (X * X) + (Y * Y); } /// <summary> @@ -384,8 +384,8 @@ namespace Godot { return new Vector2 ( - Mathf.Lerp(x, to.x, weight), - Mathf.Lerp(y, to.y, weight) + Mathf.Lerp(X, to.X, weight), + Mathf.Lerp(Y, to.Y, weight) ); } @@ -415,7 +415,7 @@ namespace Godot /// <returns>The index of the highest axis.</returns> public readonly Axis MaxAxisIndex() { - return x < y ? Axis.Y : Axis.X; + return X < Y ? Axis.Y : Axis.X; } /// <summary> @@ -425,7 +425,7 @@ namespace Godot /// <returns>The index of the lowest axis.</returns> public readonly Axis MinAxisIndex() { - return x < y ? Axis.X : Axis.Y; + return X < Y ? Axis.X : Axis.Y; } /// <summary> @@ -467,8 +467,8 @@ namespace Godot public readonly Vector2 PosMod(real_t mod) { Vector2 v; - v.x = Mathf.PosMod(x, mod); - v.y = Mathf.PosMod(y, mod); + v.X = Mathf.PosMod(X, mod); + v.Y = Mathf.PosMod(Y, mod); return v; } @@ -483,8 +483,8 @@ namespace Godot public readonly Vector2 PosMod(Vector2 modv) { Vector2 v; - v.x = Mathf.PosMod(x, modv.x); - v.y = Mathf.PosMod(y, modv.y); + v.X = Mathf.PosMod(X, modv.X); + v.Y = Mathf.PosMod(Y, modv.Y); return v; } @@ -524,8 +524,8 @@ namespace Godot (real_t sin, real_t cos) = Mathf.SinCos(angle); return new Vector2 ( - x * cos - y * sin, - x * sin + y * cos + X * cos - Y * sin, + X * sin + Y * cos ); } @@ -536,7 +536,7 @@ namespace Godot /// <returns>The rounded vector.</returns> public readonly Vector2 Round() { - return new Vector2(Mathf.Round(x), Mathf.Round(y)); + return new Vector2(Mathf.Round(X), Mathf.Round(Y)); } /// <summary> @@ -548,8 +548,8 @@ namespace Godot public readonly Vector2 Sign() { Vector2 v; - v.x = Mathf.Sign(x); - v.y = Mathf.Sign(y); + v.X = Mathf.Sign(X); + v.Y = Mathf.Sign(Y); return v; } @@ -597,7 +597,7 @@ namespace Godot /// <returns>The snapped vector.</returns> public readonly Vector2 Snapped(Vector2 step) { - return new Vector2(Mathf.Snapped(x, step.x), Mathf.Snapped(y, step.y)); + return new Vector2(Mathf.Snapped(X, step.X), Mathf.Snapped(Y, step.Y)); } /// <summary> @@ -607,7 +607,7 @@ namespace Godot /// <returns>The perpendicular vector.</returns> public readonly Vector2 Orthogonal() { - return new Vector2(y, -x); + return new Vector2(Y, -X); } // Constants @@ -664,8 +664,8 @@ namespace Godot /// <param name="y">The vector's Y component.</param> public Vector2(real_t x, real_t y) { - this.x = x; - this.y = y; + X = x; + Y = y; } /// <summary> @@ -689,8 +689,8 @@ namespace Godot /// <returns>The added vector.</returns> public static Vector2 operator +(Vector2 left, Vector2 right) { - left.x += right.x; - left.y += right.y; + left.X += right.X; + left.Y += right.Y; return left; } @@ -703,14 +703,14 @@ namespace Godot /// <returns>The subtracted vector.</returns> public static Vector2 operator -(Vector2 left, Vector2 right) { - left.x -= right.x; - left.y -= right.y; + left.X -= right.X; + left.Y -= right.Y; return left; } /// <summary> /// Returns the negative value of the <see cref="Vector2"/>. - /// This is the same as writing <c>new Vector2(-v.x, -v.y)</c>. + /// This is the same as writing <c>new Vector2(-v.X, -v.Y)</c>. /// This operation flips the direction of the vector while /// keeping the same magnitude. /// With floats, the number zero can be either positive or negative. @@ -719,8 +719,8 @@ namespace Godot /// <returns>The negated/flipped vector.</returns> public static Vector2 operator -(Vector2 vec) { - vec.x = -vec.x; - vec.y = -vec.y; + vec.X = -vec.X; + vec.Y = -vec.Y; return vec; } @@ -733,8 +733,8 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector2 operator *(Vector2 vec, real_t scale) { - vec.x *= scale; - vec.y *= scale; + vec.X *= scale; + vec.Y *= scale; return vec; } @@ -747,8 +747,8 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector2 operator *(real_t scale, Vector2 vec) { - vec.x *= scale; - vec.y *= scale; + vec.X *= scale; + vec.Y *= scale; return vec; } @@ -761,8 +761,8 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector2 operator *(Vector2 left, Vector2 right) { - left.x *= right.x; - left.y *= right.y; + left.X *= right.X; + left.Y *= right.Y; return left; } @@ -775,8 +775,8 @@ namespace Godot /// <returns>The divided vector.</returns> public static Vector2 operator /(Vector2 vec, real_t divisor) { - vec.x /= divisor; - vec.y /= divisor; + vec.X /= divisor; + vec.Y /= divisor; return vec; } @@ -789,8 +789,8 @@ namespace Godot /// <returns>The divided vector.</returns> public static Vector2 operator /(Vector2 vec, Vector2 divisorv) { - vec.x /= divisorv.x; - vec.y /= divisorv.y; + vec.X /= divisorv.X; + vec.Y /= divisorv.Y; return vec; } @@ -812,8 +812,8 @@ namespace Godot /// <returns>The remainder vector.</returns> public static Vector2 operator %(Vector2 vec, real_t divisor) { - vec.x %= divisor; - vec.y %= divisor; + vec.X %= divisor; + vec.Y %= divisor; return vec; } @@ -835,8 +835,8 @@ namespace Godot /// <returns>The remainder vector.</returns> public static Vector2 operator %(Vector2 vec, Vector2 divisorv) { - vec.x %= divisorv.x; - vec.y %= divisorv.y; + vec.X %= divisorv.X; + vec.Y %= divisorv.Y; return vec; } @@ -879,11 +879,11 @@ namespace Godot /// <returns>Whether or not the left is less than the right.</returns> public static bool operator <(Vector2 left, Vector2 right) { - if (left.x == right.x) + if (left.X == right.X) { - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> @@ -899,11 +899,11 @@ namespace Godot /// <returns>Whether or not the left is greater than the right.</returns> public static bool operator >(Vector2 left, Vector2 right) { - if (left.x == right.x) + if (left.X == right.X) { - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> @@ -919,11 +919,11 @@ namespace Godot /// <returns>Whether or not the left is less than or equal to the right.</returns> public static bool operator <=(Vector2 left, Vector2 right) { - if (left.x == right.x) + if (left.X == right.X) { - return left.y <= right.y; + return left.Y <= right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> @@ -939,11 +939,11 @@ namespace Godot /// <returns>Whether or not the left is greater than or equal to the right.</returns> public static bool operator >=(Vector2 left, Vector2 right) { - if (left.x == right.x) + if (left.X == right.X) { - return left.y >= right.y; + return left.Y >= right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> @@ -968,7 +968,7 @@ namespace Godot /// <returns>Whether or not the vectors are exactly equal.</returns> public readonly bool Equals(Vector2 other) { - return x == other.x && y == other.y; + return X == other.X && Y == other.Y; } /// <summary> @@ -979,7 +979,7 @@ namespace Godot /// <returns>Whether or not the vectors are approximately equal.</returns> public readonly bool IsEqualApprox(Vector2 other) { - return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y); + return Mathf.IsEqualApprox(X, other.X) && Mathf.IsEqualApprox(Y, other.Y); } /// <summary> @@ -991,7 +991,7 @@ namespace Godot /// <returns>Whether or not the vector is approximately zero.</returns> public readonly bool IsZeroApprox() { - return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y); + return Mathf.IsZeroApprox(X) && Mathf.IsZeroApprox(Y); } /// <summary> @@ -1000,7 +1000,7 @@ namespace Godot /// <returns>A hash code for this vector.</returns> public override readonly int GetHashCode() { - return y.GetHashCode() ^ x.GetHashCode(); + return Y.GetHashCode() ^ X.GetHashCode(); } /// <summary> @@ -1009,7 +1009,7 @@ namespace Godot /// <returns>A string representation of this vector.</returns> public override readonly string ToString() { - return $"({x}, {y})"; + return $"({X}, {Y})"; } /// <summary> @@ -1018,7 +1018,7 @@ namespace Godot /// <returns>A string representation of this vector.</returns> public readonly string ToString(string format) { - return $"({x.ToString(format)}, {y.ToString(format)})"; + return $"({X.ToString(format)}, {Y.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs index 740fedec66..e849939ebb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs @@ -8,7 +8,7 @@ namespace Godot /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential)] - public struct Vector2i : IEquatable<Vector2i> + public struct Vector2I : IEquatable<Vector2I> { /// <summary> /// Enumerated index values for the axes. @@ -29,12 +29,12 @@ namespace Godot /// <summary> /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> - public int x; + public int X; /// <summary> /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> - public int y; + public int Y; /// <summary> /// Access vector components using their index. @@ -43,8 +43,8 @@ namespace Godot /// <paramref name="index"/> is not 0 or 1. /// </exception> /// <value> - /// <c>[0]</c> is equivalent to <see cref="x"/>, - /// <c>[1]</c> is equivalent to <see cref="y"/>. + /// <c>[0]</c> is equivalent to <see cref="X"/>, + /// <c>[1]</c> is equivalent to <see cref="Y"/>. /// </value> public int this[int index] { @@ -53,9 +53,9 @@ namespace Godot switch (index) { case 0: - return x; + return X; case 1: - return y; + return Y; default: throw new ArgumentOutOfRangeException(nameof(index)); } @@ -65,10 +65,10 @@ namespace Godot switch (index) { case 0: - x = value; + X = value; return; case 1: - y = value; + Y = value; return; default: throw new ArgumentOutOfRangeException(nameof(index)); @@ -81,26 +81,26 @@ namespace Godot /// </summary> public readonly void Deconstruct(out int x, out int y) { - x = this.x; - y = this.y; + x = X; + y = Y; } /// <summary> /// Returns a new vector with all components in absolute values (i.e. positive). /// </summary> /// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns> - public readonly Vector2i Abs() + public readonly Vector2I Abs() { - return new Vector2i(Mathf.Abs(x), Mathf.Abs(y)); + return new Vector2I(Mathf.Abs(X), Mathf.Abs(Y)); } /// <summary> - /// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>. + /// Returns the aspect ratio of this vector, the ratio of <see cref="X"/> to <see cref="Y"/>. /// </summary> - /// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns> + /// <returns>The <see cref="X"/> component divided by the <see cref="Y"/> component.</returns> public readonly real_t Aspect() { - return x / (real_t)y; + return X / (real_t)Y; } /// <summary> @@ -111,12 +111,12 @@ namespace Godot /// <param name="min">The vector with minimum allowed values.</param> /// <param name="max">The vector with maximum allowed values.</param> /// <returns>The vector with all components clamped.</returns> - public readonly Vector2i Clamp(Vector2i min, Vector2i max) + public readonly Vector2I Clamp(Vector2I min, Vector2I max) { - return new Vector2i + return new Vector2I ( - Mathf.Clamp(x, min.x, max.x), - Mathf.Clamp(y, min.y, max.y) + Mathf.Clamp(X, min.X, max.X), + Mathf.Clamp(Y, min.Y, max.Y) ); } @@ -127,8 +127,8 @@ namespace Godot /// <returns>The length of this vector.</returns> public readonly real_t Length() { - int x2 = x * x; - int y2 = y * y; + int x2 = X * X; + int y2 = Y * Y; return Mathf.Sqrt(x2 + y2); } @@ -141,8 +141,8 @@ namespace Godot /// <returns>The squared length of this vector.</returns> public readonly int LengthSquared() { - int x2 = x * x; - int y2 = y * y; + int x2 = X * X; + int y2 = Y * Y; return x2 + y2; } @@ -154,7 +154,7 @@ namespace Godot /// <returns>The index of the highest axis.</returns> public readonly Axis MaxAxisIndex() { - return x < y ? Axis.Y : Axis.X; + return X < Y ? Axis.Y : Axis.X; } /// <summary> @@ -164,7 +164,7 @@ namespace Godot /// <returns>The index of the lowest axis.</returns> public readonly Axis MinAxisIndex() { - return x < y ? Axis.X : Axis.Y; + return X < Y ? Axis.X : Axis.Y; } /// <summary> @@ -173,181 +173,181 @@ namespace Godot /// by calling <see cref="Mathf.Sign(int)"/> on each component. /// </summary> /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> - public readonly Vector2i Sign() + public readonly Vector2I Sign() { - Vector2i v = this; - v.x = Mathf.Sign(v.x); - v.y = Mathf.Sign(v.y); + Vector2I v = this; + v.X = Mathf.Sign(v.X); + v.Y = Mathf.Sign(v.Y); return v; } // Constants - private static readonly Vector2i _zero = new Vector2i(0, 0); - private static readonly Vector2i _one = new Vector2i(1, 1); + private static readonly Vector2I _zero = new Vector2I(0, 0); + private static readonly Vector2I _one = new Vector2I(1, 1); - private static readonly Vector2i _up = new Vector2i(0, -1); - private static readonly Vector2i _down = new Vector2i(0, 1); - private static readonly Vector2i _right = new Vector2i(1, 0); - private static readonly Vector2i _left = new Vector2i(-1, 0); + private static readonly Vector2I _up = new Vector2I(0, -1); + private static readonly Vector2I _down = new Vector2I(0, 1); + private static readonly Vector2I _right = new Vector2I(1, 0); + private static readonly Vector2I _left = new Vector2I(-1, 0); /// <summary> /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> - /// <value>Equivalent to <c>new Vector2i(0, 0)</c>.</value> - public static Vector2i Zero { get { return _zero; } } + /// <value>Equivalent to <c>new Vector2I(0, 0)</c>.</value> + public static Vector2I Zero { get { return _zero; } } /// <summary> /// One vector, a vector with all components set to <c>1</c>. /// </summary> - /// <value>Equivalent to <c>new Vector2i(1, 1)</c>.</value> - public static Vector2i One { get { return _one; } } + /// <value>Equivalent to <c>new Vector2I(1, 1)</c>.</value> + public static Vector2I One { get { return _one; } } /// <summary> /// Up unit vector. Y is down in 2D, so this vector points -Y. /// </summary> - /// <value>Equivalent to <c>new Vector2i(0, -1)</c>.</value> - public static Vector2i Up { get { return _up; } } + /// <value>Equivalent to <c>new Vector2I(0, -1)</c>.</value> + public static Vector2I Up { get { return _up; } } /// <summary> /// Down unit vector. Y is down in 2D, so this vector points +Y. /// </summary> - /// <value>Equivalent to <c>new Vector2i(0, 1)</c>.</value> - public static Vector2i Down { get { return _down; } } + /// <value>Equivalent to <c>new Vector2I(0, 1)</c>.</value> + public static Vector2I Down { get { return _down; } } /// <summary> /// Right unit vector. Represents the direction of right. /// </summary> - /// <value>Equivalent to <c>new Vector2i(1, 0)</c>.</value> - public static Vector2i Right { get { return _right; } } + /// <value>Equivalent to <c>new Vector2I(1, 0)</c>.</value> + public static Vector2I Right { get { return _right; } } /// <summary> /// Left unit vector. Represents the direction of left. /// </summary> - /// <value>Equivalent to <c>new Vector2i(-1, 0)</c>.</value> - public static Vector2i Left { get { return _left; } } + /// <value>Equivalent to <c>new Vector2I(-1, 0)</c>.</value> + public static Vector2I Left { get { return _left; } } /// <summary> - /// Constructs a new <see cref="Vector2i"/> with the given components. + /// Constructs a new <see cref="Vector2I"/> with the given components. /// </summary> /// <param name="x">The vector's X component.</param> /// <param name="y">The vector's Y component.</param> - public Vector2i(int x, int y) + public Vector2I(int x, int y) { - this.x = x; - this.y = y; + X = x; + Y = y; } /// <summary> - /// Adds each component of the <see cref="Vector2i"/> - /// with the components of the given <see cref="Vector2i"/>. + /// Adds each component of the <see cref="Vector2I"/> + /// with the components of the given <see cref="Vector2I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The added vector.</returns> - public static Vector2i operator +(Vector2i left, Vector2i right) + public static Vector2I operator +(Vector2I left, Vector2I right) { - left.x += right.x; - left.y += right.y; + left.X += right.X; + left.Y += right.Y; return left; } /// <summary> - /// Subtracts each component of the <see cref="Vector2i"/> - /// by the components of the given <see cref="Vector2i"/>. + /// Subtracts each component of the <see cref="Vector2I"/> + /// by the components of the given <see cref="Vector2I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The subtracted vector.</returns> - public static Vector2i operator -(Vector2i left, Vector2i right) + public static Vector2I operator -(Vector2I left, Vector2I right) { - left.x -= right.x; - left.y -= right.y; + left.X -= right.X; + left.Y -= right.Y; return left; } /// <summary> - /// Returns the negative value of the <see cref="Vector2i"/>. - /// This is the same as writing <c>new Vector2i(-v.x, -v.y)</c>. + /// Returns the negative value of the <see cref="Vector2I"/>. + /// This is the same as writing <c>new Vector2I(-v.X, -v.Y)</c>. /// This operation flips the direction of the vector while /// keeping the same magnitude. /// </summary> /// <param name="vec">The vector to negate/flip.</param> /// <returns>The negated/flipped vector.</returns> - public static Vector2i operator -(Vector2i vec) + public static Vector2I operator -(Vector2I vec) { - vec.x = -vec.x; - vec.y = -vec.y; + vec.X = -vec.X; + vec.Y = -vec.Y; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector2i"/> + /// Multiplies each component of the <see cref="Vector2I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="vec">The vector to multiply.</param> /// <param name="scale">The scale to multiply by.</param> /// <returns>The multiplied vector.</returns> - public static Vector2i operator *(Vector2i vec, int scale) + public static Vector2I operator *(Vector2I vec, int scale) { - vec.x *= scale; - vec.y *= scale; + vec.X *= scale; + vec.Y *= scale; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector2i"/> + /// Multiplies each component of the <see cref="Vector2I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="scale">The scale to multiply by.</param> /// <param name="vec">The vector to multiply.</param> /// <returns>The multiplied vector.</returns> - public static Vector2i operator *(int scale, Vector2i vec) + public static Vector2I operator *(int scale, Vector2I vec) { - vec.x *= scale; - vec.y *= scale; + vec.X *= scale; + vec.Y *= scale; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector2i"/> - /// by the components of the given <see cref="Vector2i"/>. + /// Multiplies each component of the <see cref="Vector2I"/> + /// by the components of the given <see cref="Vector2I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The multiplied vector.</returns> - public static Vector2i operator *(Vector2i left, Vector2i right) + public static Vector2I operator *(Vector2I left, Vector2I right) { - left.x *= right.x; - left.y *= right.y; + left.X *= right.X; + left.Y *= right.Y; return left; } /// <summary> - /// Divides each component of the <see cref="Vector2i"/> + /// Divides each component of the <see cref="Vector2I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="vec">The dividend vector.</param> /// <param name="divisor">The divisor value.</param> /// <returns>The divided vector.</returns> - public static Vector2i operator /(Vector2i vec, int divisor) + public static Vector2I operator /(Vector2I vec, int divisor) { - vec.x /= divisor; - vec.y /= divisor; + vec.X /= divisor; + vec.Y /= divisor; return vec; } /// <summary> - /// Divides each component of the <see cref="Vector2i"/> - /// by the components of the given <see cref="Vector2i"/>. + /// Divides each component of the <see cref="Vector2I"/> + /// by the components of the given <see cref="Vector2I"/>. /// </summary> /// <param name="vec">The dividend vector.</param> /// <param name="divisorv">The divisor vector.</param> /// <returns>The divided vector.</returns> - public static Vector2i operator /(Vector2i vec, Vector2i divisorv) + public static Vector2I operator /(Vector2I vec, Vector2I divisorv) { - vec.x /= divisorv.x; - vec.y /= divisorv.y; + vec.X /= divisorv.X; + vec.Y /= divisorv.Y; return vec; } /// <summary> - /// Gets the remainder of each component of the <see cref="Vector2i"/> + /// Gets the remainder of each component of the <see cref="Vector2I"/> /// with the components of the given <see langword="int"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. @@ -356,22 +356,22 @@ namespace Godot /// </summary> /// <example> /// <code> - /// GD.Print(new Vector2i(10, -20) % 7); // Prints "(3, -6)" + /// GD.Print(new Vector2I(10, -20) % 7); // Prints "(3, -6)" /// </code> /// </example> /// <param name="vec">The dividend vector.</param> /// <param name="divisor">The divisor value.</param> /// <returns>The remainder vector.</returns> - public static Vector2i operator %(Vector2i vec, int divisor) + public static Vector2I operator %(Vector2I vec, int divisor) { - vec.x %= divisor; - vec.y %= divisor; + vec.X %= divisor; + vec.Y %= divisor; return vec; } /// <summary> - /// Gets the remainder of each component of the <see cref="Vector2i"/> - /// with the components of the given <see cref="Vector2i"/>. + /// Gets the remainder of each component of the <see cref="Vector2I"/> + /// with the components of the given <see cref="Vector2I"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead @@ -379,16 +379,16 @@ namespace Godot /// </summary> /// <example> /// <code> - /// GD.Print(new Vector2i(10, -20) % new Vector2i(7, 8)); // Prints "(3, -4)" + /// GD.Print(new Vector2I(10, -20) % new Vector2I(7, 8)); // Prints "(3, -4)" /// </code> /// </example> /// <param name="vec">The dividend vector.</param> /// <param name="divisorv">The divisor vector.</param> /// <returns>The remainder vector.</returns> - public static Vector2i operator %(Vector2i vec, Vector2i divisorv) + public static Vector2I operator %(Vector2I vec, Vector2I divisorv) { - vec.x %= divisorv.x; - vec.y %= divisorv.y; + vec.X %= divisorv.X; + vec.Y %= divisorv.Y; return vec; } @@ -398,7 +398,7 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the vectors are equal.</returns> - public static bool operator ==(Vector2i left, Vector2i right) + public static bool operator ==(Vector2I left, Vector2I right) { return left.Equals(right); } @@ -409,13 +409,13 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the vectors are not equal.</returns> - public static bool operator !=(Vector2i left, Vector2i right) + public static bool operator !=(Vector2I left, Vector2I right) { return !left.Equals(right); } /// <summary> - /// Compares two <see cref="Vector2i"/> vectors by first checking if + /// Compares two <see cref="Vector2I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is less than /// the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -425,17 +425,17 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is less than the right.</returns> - public static bool operator <(Vector2i left, Vector2i right) + public static bool operator <(Vector2I left, Vector2I right) { - if (left.x == right.x) + if (left.X == right.X) { - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> - /// Compares two <see cref="Vector2i"/> vectors by first checking if + /// Compares two <see cref="Vector2I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is greater than /// the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -445,17 +445,17 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is greater than the right.</returns> - public static bool operator >(Vector2i left, Vector2i right) + public static bool operator >(Vector2I left, Vector2I right) { - if (left.x == right.x) + if (left.X == right.X) { - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> - /// Compares two <see cref="Vector2i"/> vectors by first checking if + /// Compares two <see cref="Vector2I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is less than /// or equal to the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -465,17 +465,17 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is less than or equal to the right.</returns> - public static bool operator <=(Vector2i left, Vector2i right) + public static bool operator <=(Vector2I left, Vector2I right) { - if (left.x == right.x) + if (left.X == right.X) { - return left.y <= right.y; + return left.Y <= right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> - /// Compares two <see cref="Vector2i"/> vectors by first checking if + /// Compares two <see cref="Vector2I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is greater than /// or equal to the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -485,33 +485,33 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is greater than or equal to the right.</returns> - public static bool operator >=(Vector2i left, Vector2i right) + public static bool operator >=(Vector2I left, Vector2I right) { - if (left.x == right.x) + if (left.X == right.X) { - return left.y >= right.y; + return left.Y >= right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> - /// Converts this <see cref="Vector2i"/> to a <see cref="Vector2"/>. + /// Converts this <see cref="Vector2I"/> to a <see cref="Vector2"/>. /// </summary> /// <param name="value">The vector to convert.</param> - public static implicit operator Vector2(Vector2i value) + public static implicit operator Vector2(Vector2I value) { - return new Vector2(value.x, value.y); + return new Vector2(value.X, value.Y); } /// <summary> - /// Converts a <see cref="Vector2"/> to a <see cref="Vector2i"/>. + /// Converts a <see cref="Vector2"/> to a <see cref="Vector2I"/>. /// </summary> /// <param name="value">The vector to convert.</param> - public static explicit operator Vector2i(Vector2 value) + public static explicit operator Vector2I(Vector2 value) { - return new Vector2i( - Mathf.RoundToInt(value.x), - Mathf.RoundToInt(value.y) + return new Vector2I( + Mathf.RoundToInt(value.X), + Mathf.RoundToInt(value.Y) ); } @@ -523,7 +523,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override readonly bool Equals(object obj) { - return obj is Vector2i other && Equals(other); + return obj is Vector2I other && Equals(other); } /// <summary> @@ -531,36 +531,36 @@ namespace Godot /// </summary> /// <param name="other">The other vector.</param> /// <returns>Whether or not the vectors are equal.</returns> - public readonly bool Equals(Vector2i other) + public readonly bool Equals(Vector2I other) { - return x == other.x && y == other.y; + return X == other.X && Y == other.Y; } /// <summary> - /// Serves as the hash function for <see cref="Vector2i"/>. + /// Serves as the hash function for <see cref="Vector2I"/>. /// </summary> /// <returns>A hash code for this vector.</returns> public override readonly int GetHashCode() { - return y.GetHashCode() ^ x.GetHashCode(); + return Y.GetHashCode() ^ X.GetHashCode(); } /// <summary> - /// Converts this <see cref="Vector2i"/> to a string. + /// Converts this <see cref="Vector2I"/> to a string. /// </summary> /// <returns>A string representation of this vector.</returns> public override readonly string ToString() { - return $"({x}, {y})"; + return $"({X}, {Y})"; } /// <summary> - /// Converts this <see cref="Vector2i"/> to a string with the given <paramref name="format"/>. + /// Converts this <see cref="Vector2I"/> to a string with the given <paramref name="format"/>. /// </summary> /// <returns>A string representation of this vector.</returns> public readonly string ToString(string format) { - return $"({x.ToString(format)}, {y.ToString(format)})"; + return $"({X.ToString(format)}, {Y.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index b017ba5853..c773c0fda6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -33,17 +33,17 @@ namespace Godot /// <summary> /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> - public real_t x; + public real_t X; /// <summary> /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> - public real_t y; + public real_t Y; /// <summary> /// The vector's Z component. Also accessible by using the index position <c>[2]</c>. /// </summary> - public real_t z; + public real_t Z; /// <summary> /// Access vector components using their index. @@ -52,9 +52,9 @@ namespace Godot /// <paramref name="index"/> is not 0, 1 or 2. /// </exception> /// <value> - /// <c>[0]</c> is equivalent to <see cref="x"/>, - /// <c>[1]</c> is equivalent to <see cref="y"/>, - /// <c>[2]</c> is equivalent to <see cref="z"/>. + /// <c>[0]</c> is equivalent to <see cref="X"/>, + /// <c>[1]</c> is equivalent to <see cref="Y"/>, + /// <c>[2]</c> is equivalent to <see cref="Z"/>. /// </value> public real_t this[int index] { @@ -63,11 +63,11 @@ namespace Godot switch (index) { case 0: - return x; + return X; case 1: - return y; + return Y; case 2: - return z; + return Z; default: throw new ArgumentOutOfRangeException(nameof(index)); } @@ -77,13 +77,13 @@ namespace Godot switch (index) { case 0: - x = value; + X = value; return; case 1: - y = value; + Y = value; return; case 2: - z = value; + Z = value; return; default: throw new ArgumentOutOfRangeException(nameof(index)); @@ -96,9 +96,9 @@ namespace Godot /// </summary> public readonly void Deconstruct(out real_t x, out real_t y, out real_t z) { - x = this.x; - y = this.y; - z = this.z; + x = X; + y = Y; + z = Z; } internal void Normalize() @@ -107,14 +107,14 @@ namespace Godot if (lengthsq == 0) { - x = y = z = 0f; + X = Y = Z = 0f; } else { real_t length = Mathf.Sqrt(lengthsq); - x /= length; - y /= length; - z /= length; + X /= length; + Y /= length; + Z /= length; } } @@ -124,7 +124,7 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns> public readonly Vector3 Abs() { - return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z)); + return new Vector3(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z)); } /// <summary> @@ -153,7 +153,7 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns> public readonly Vector3 Ceil() { - return new Vector3(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z)); + return new Vector3(Mathf.Ceil(X), Mathf.Ceil(Y), Mathf.Ceil(Z)); } /// <summary> @@ -168,9 +168,9 @@ namespace Godot { return new Vector3 ( - Mathf.Clamp(x, min.x, max.x), - Mathf.Clamp(y, min.y, max.y), - Mathf.Clamp(z, min.z, max.z) + Mathf.Clamp(X, min.X, max.X), + Mathf.Clamp(Y, min.Y, max.Y), + Mathf.Clamp(Z, min.Z, max.Z) ); } @@ -183,9 +183,9 @@ namespace Godot { return new Vector3 ( - (y * with.z) - (z * with.y), - (z * with.x) - (x * with.z), - (x * with.y) - (y * with.x) + (Y * with.Z) - (Z * with.Y), + (Z * with.X) - (X * with.Z), + (X * with.Y) - (Y * with.X) ); } @@ -202,9 +202,9 @@ namespace Godot { return new Vector3 ( - Mathf.CubicInterpolate(x, b.x, preA.x, postB.x, weight), - Mathf.CubicInterpolate(y, b.y, preA.y, postB.y, weight), - Mathf.CubicInterpolate(z, b.z, preA.z, postB.z, weight) + Mathf.CubicInterpolate(X, b.X, preA.X, postB.X, weight), + Mathf.CubicInterpolate(Y, b.Y, preA.Y, postB.Y, weight), + Mathf.CubicInterpolate(Z, b.Z, preA.Z, postB.Z, weight) ); } @@ -226,9 +226,9 @@ namespace Godot { return new Vector3 ( - Mathf.CubicInterpolateInTime(x, b.x, preA.x, postB.x, weight, t, preAT, postBT), - Mathf.CubicInterpolateInTime(y, b.y, preA.y, postB.y, weight, t, preAT, postBT), - Mathf.CubicInterpolateInTime(z, b.z, preA.z, postB.z, weight, t, preAT, postBT) + Mathf.CubicInterpolateInTime(X, b.X, preA.X, postB.X, weight, t, preAT, postBT), + Mathf.CubicInterpolateInTime(Y, b.Y, preA.Y, postB.Y, weight, t, preAT, postBT), + Mathf.CubicInterpolateInTime(Z, b.Z, preA.Z, postB.Z, weight, t, preAT, postBT) ); } @@ -245,9 +245,9 @@ namespace Godot { return new Vector3 ( - Mathf.BezierInterpolate(x, control1.x, control2.x, end.x, t), - Mathf.BezierInterpolate(y, control1.y, control2.y, end.y, t), - Mathf.BezierInterpolate(z, control1.z, control2.z, end.z, t) + Mathf.BezierInterpolate(X, control1.X, control2.X, end.X, t), + Mathf.BezierInterpolate(Y, control1.Y, control2.Y, end.Y, t), + Mathf.BezierInterpolate(Z, control1.Z, control2.Z, end.Z, t) ); } @@ -263,9 +263,9 @@ namespace Godot public readonly Vector3 BezierDerivative(Vector3 control1, Vector3 control2, Vector3 end, real_t t) { return new Vector3( - Mathf.BezierDerivative(x, control1.x, control2.x, end.x, t), - Mathf.BezierDerivative(y, control1.y, control2.y, end.y, t), - Mathf.BezierDerivative(z, control1.z, control2.z, end.y, t) + Mathf.BezierDerivative(X, control1.X, control2.X, end.X, t), + Mathf.BezierDerivative(Y, control1.Y, control2.Y, end.Y, t), + Mathf.BezierDerivative(Z, control1.Z, control2.Z, end.Y, t) ); } @@ -276,7 +276,7 @@ namespace Godot /// <returns>The direction from this vector to <paramref name="to"/>.</returns> public readonly Vector3 DirectionTo(Vector3 to) { - return new Vector3(to.x - x, to.y - y, to.z - z).Normalized(); + return new Vector3(to.X - X, to.Y - Y, to.Z - Z).Normalized(); } /// <summary> @@ -309,7 +309,7 @@ namespace Godot /// <returns>The dot product of the two vectors.</returns> public readonly real_t Dot(Vector3 with) { - return (x * with.x) + (y * with.y) + (z * with.z); + return (X * with.X) + (Y * with.Y) + (Z * with.Z); } /// <summary> @@ -318,16 +318,16 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns> public readonly Vector3 Floor() { - return new Vector3(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z)); + return new Vector3(Mathf.Floor(X), Mathf.Floor(Y), Mathf.Floor(Z)); } /// <summary> - /// Returns the inverse of this vector. This is the same as <c>new Vector3(1 / v.x, 1 / v.y, 1 / v.z)</c>. + /// Returns the inverse of this vector. This is the same as <c>new Vector3(1 / v.X, 1 / v.Y, 1 / v.Z)</c>. /// </summary> /// <returns>The inverse of this vector.</returns> public readonly Vector3 Inverse() { - return new Vector3(1 / x, 1 / y, 1 / z); + return new Vector3(1 / X, 1 / Y, 1 / Z); } /// <summary> @@ -337,7 +337,7 @@ namespace Godot /// <returns>Whether this vector is finite or not.</returns> public readonly bool IsFinite() { - return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z); + return Mathf.IsFinite(X) && Mathf.IsFinite(Y) && Mathf.IsFinite(Z); } /// <summary> @@ -356,9 +356,9 @@ namespace Godot /// <returns>The length of this vector.</returns> public readonly real_t Length() { - real_t x2 = x * x; - real_t y2 = y * y; - real_t z2 = z * z; + real_t x2 = X * X; + real_t y2 = Y * Y; + real_t z2 = Z * Z; return Mathf.Sqrt(x2 + y2 + z2); } @@ -371,9 +371,9 @@ namespace Godot /// <returns>The squared length of this vector.</returns> public readonly real_t LengthSquared() { - real_t x2 = x * x; - real_t y2 = y * y; - real_t z2 = z * z; + real_t x2 = X * X; + real_t y2 = Y * Y; + real_t z2 = Z * Z; return x2 + y2 + z2; } @@ -389,9 +389,9 @@ namespace Godot { return new Vector3 ( - Mathf.Lerp(x, to.x, weight), - Mathf.Lerp(y, to.y, weight), - Mathf.Lerp(z, to.z, weight) + Mathf.Lerp(X, to.X, weight), + Mathf.Lerp(Y, to.Y, weight), + Mathf.Lerp(Z, to.Z, weight) ); } @@ -421,7 +421,7 @@ namespace Godot /// <returns>The index of the highest axis.</returns> public readonly Axis MaxAxisIndex() { - return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); + return X < Y ? (Y < Z ? Axis.Z : Axis.Y) : (X < Z ? Axis.Z : Axis.X); } /// <summary> @@ -431,7 +431,7 @@ namespace Godot /// <returns>The index of the lowest axis.</returns> public readonly Axis MinAxisIndex() { - return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z); + return X < Y ? (X < Z ? Axis.X : Axis.Z) : (Y < Z ? Axis.Y : Axis.Z); } /// <summary> @@ -470,9 +470,9 @@ namespace Godot public readonly Basis Outer(Vector3 with) { return new Basis( - x * with.x, x * with.y, x * with.z, - y * with.x, y * with.y, y * with.z, - z * with.x, z * with.y, z * with.z + X * with.X, X * with.Y, X * with.Z, + Y * with.X, Y * with.Y, Y * with.Z, + Z * with.X, Z * with.Y, Z * with.Z ); } @@ -487,9 +487,9 @@ namespace Godot public readonly Vector3 PosMod(real_t mod) { Vector3 v; - v.x = Mathf.PosMod(x, mod); - v.y = Mathf.PosMod(y, mod); - v.z = Mathf.PosMod(z, mod); + v.X = Mathf.PosMod(X, mod); + v.Y = Mathf.PosMod(Y, mod); + v.Z = Mathf.PosMod(Z, mod); return v; } @@ -504,9 +504,9 @@ namespace Godot public readonly Vector3 PosMod(Vector3 modv) { Vector3 v; - v.x = Mathf.PosMod(x, modv.x); - v.y = Mathf.PosMod(y, modv.y); - v.z = Mathf.PosMod(z, modv.z); + v.X = Mathf.PosMod(X, modv.X); + v.Y = Mathf.PosMod(Y, modv.Y); + v.Z = Mathf.PosMod(Z, modv.Z); return v; } @@ -561,7 +561,7 @@ namespace Godot /// <returns>The rounded vector.</returns> public readonly Vector3 Round() { - return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z)); + return new Vector3(Mathf.Round(X), Mathf.Round(Y), Mathf.Round(Z)); } /// <summary> @@ -573,9 +573,9 @@ namespace Godot public readonly Vector3 Sign() { Vector3 v; - v.x = Mathf.Sign(x); - v.y = Mathf.Sign(y); - v.z = Mathf.Sign(z); + v.X = Mathf.Sign(X); + v.Y = Mathf.Sign(Y); + v.Z = Mathf.Sign(Z); return v; } @@ -642,9 +642,9 @@ namespace Godot { return new Vector3 ( - Mathf.Snapped(x, step.x), - Mathf.Snapped(y, step.y), - Mathf.Snapped(z, step.z) + Mathf.Snapped(X, step.X), + Mathf.Snapped(Y, step.Y), + Mathf.Snapped(Z, step.Z) ); } @@ -719,9 +719,9 @@ namespace Godot /// <param name="z">The vector's Z component.</param> public Vector3(real_t x, real_t y, real_t z) { - this.x = x; - this.y = y; - this.z = z; + X = x; + Y = y; + Z = z; } /// <summary> @@ -733,9 +733,9 @@ namespace Godot /// <returns>The added vector.</returns> public static Vector3 operator +(Vector3 left, Vector3 right) { - left.x += right.x; - left.y += right.y; - left.z += right.z; + left.X += right.X; + left.Y += right.Y; + left.Z += right.Z; return left; } @@ -748,15 +748,15 @@ namespace Godot /// <returns>The subtracted vector.</returns> public static Vector3 operator -(Vector3 left, Vector3 right) { - left.x -= right.x; - left.y -= right.y; - left.z -= right.z; + left.X -= right.X; + left.Y -= right.Y; + left.Z -= right.Z; return left; } /// <summary> /// Returns the negative value of the <see cref="Vector3"/>. - /// This is the same as writing <c>new Vector3(-v.x, -v.y, -v.z)</c>. + /// This is the same as writing <c>new Vector3(-v.X, -v.Y, -v.Z)</c>. /// This operation flips the direction of the vector while /// keeping the same magnitude. /// With floats, the number zero can be either positive or negative. @@ -765,9 +765,9 @@ namespace Godot /// <returns>The negated/flipped vector.</returns> public static Vector3 operator -(Vector3 vec) { - vec.x = -vec.x; - vec.y = -vec.y; - vec.z = -vec.z; + vec.X = -vec.X; + vec.Y = -vec.Y; + vec.Z = -vec.Z; return vec; } @@ -780,9 +780,9 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector3 operator *(Vector3 vec, real_t scale) { - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; + vec.X *= scale; + vec.Y *= scale; + vec.Z *= scale; return vec; } @@ -795,9 +795,9 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector3 operator *(real_t scale, Vector3 vec) { - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; + vec.X *= scale; + vec.Y *= scale; + vec.Z *= scale; return vec; } @@ -810,9 +810,9 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector3 operator *(Vector3 left, Vector3 right) { - left.x *= right.x; - left.y *= right.y; - left.z *= right.z; + left.X *= right.X; + left.Y *= right.Y; + left.Z *= right.Z; return left; } @@ -825,9 +825,9 @@ namespace Godot /// <returns>The divided vector.</returns> public static Vector3 operator /(Vector3 vec, real_t divisor) { - vec.x /= divisor; - vec.y /= divisor; - vec.z /= divisor; + vec.X /= divisor; + vec.Y /= divisor; + vec.Z /= divisor; return vec; } @@ -840,9 +840,9 @@ namespace Godot /// <returns>The divided vector.</returns> public static Vector3 operator /(Vector3 vec, Vector3 divisorv) { - vec.x /= divisorv.x; - vec.y /= divisorv.y; - vec.z /= divisorv.z; + vec.X /= divisorv.X; + vec.Y /= divisorv.Y; + vec.Z /= divisorv.Z; return vec; } @@ -864,9 +864,9 @@ namespace Godot /// <returns>The remainder vector.</returns> public static Vector3 operator %(Vector3 vec, real_t divisor) { - vec.x %= divisor; - vec.y %= divisor; - vec.z %= divisor; + vec.X %= divisor; + vec.Y %= divisor; + vec.Z %= divisor; return vec; } @@ -888,9 +888,9 @@ namespace Godot /// <returns>The remainder vector.</returns> public static Vector3 operator %(Vector3 vec, Vector3 divisorv) { - vec.x %= divisorv.x; - vec.y %= divisorv.y; - vec.z %= divisorv.z; + vec.X %= divisorv.X; + vec.Y %= divisorv.Y; + vec.Z %= divisorv.Z; return vec; } @@ -933,15 +933,15 @@ namespace Godot /// <returns>Whether or not the left is less than the right.</returns> public static bool operator <(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - return left.z < right.z; + return left.Z < right.Z; } - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> @@ -957,15 +957,15 @@ namespace Godot /// <returns>Whether or not the left is greater than the right.</returns> public static bool operator >(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - return left.z > right.z; + return left.Z > right.Z; } - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> @@ -981,15 +981,15 @@ namespace Godot /// <returns>Whether or not the left is less than or equal to the right.</returns> public static bool operator <=(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - return left.z <= right.z; + return left.Z <= right.Z; } - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> @@ -1005,15 +1005,15 @@ namespace Godot /// <returns>Whether or not the left is greater than or equal to the right.</returns> public static bool operator >=(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - return left.z >= right.z; + return left.Z >= right.Z; } - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> @@ -1038,7 +1038,7 @@ namespace Godot /// <returns>Whether or not the vectors are exactly equal.</returns> public readonly bool Equals(Vector3 other) { - return x == other.x && y == other.y && z == other.z; + return X == other.X && Y == other.Y && Z == other.Z; } /// <summary> @@ -1049,7 +1049,7 @@ namespace Godot /// <returns>Whether or not the vectors are approximately equal.</returns> public readonly bool IsEqualApprox(Vector3 other) { - return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z); + return Mathf.IsEqualApprox(X, other.X) && Mathf.IsEqualApprox(Y, other.Y) && Mathf.IsEqualApprox(Z, other.Z); } /// <summary> @@ -1061,7 +1061,7 @@ namespace Godot /// <returns>Whether or not the vector is approximately zero.</returns> public readonly bool IsZeroApprox() { - return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z); + return Mathf.IsZeroApprox(X) && Mathf.IsZeroApprox(Y) && Mathf.IsZeroApprox(Z); } /// <summary> @@ -1070,7 +1070,7 @@ namespace Godot /// <returns>A hash code for this vector.</returns> public override readonly int GetHashCode() { - return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode(); + return Y.GetHashCode() ^ X.GetHashCode() ^ Z.GetHashCode(); } /// <summary> @@ -1079,7 +1079,7 @@ namespace Godot /// <returns>A string representation of this vector.</returns> public override readonly string ToString() { - return $"({x}, {y}, {z})"; + return $"({X}, {Y}, {Z})"; } /// <summary> @@ -1088,7 +1088,7 @@ namespace Godot /// <returns>A string representation of this vector.</returns> public readonly string ToString(string format) { - return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})"; + return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs index de0c6d27e7..fe899527ef 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs @@ -8,7 +8,7 @@ namespace Godot /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential)] - public struct Vector3i : IEquatable<Vector3i> + public struct Vector3I : IEquatable<Vector3I> { /// <summary> /// Enumerated index values for the axes. @@ -33,17 +33,17 @@ namespace Godot /// <summary> /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> - public int x; + public int X; /// <summary> /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> - public int y; + public int Y; /// <summary> /// The vector's Z component. Also accessible by using the index position <c>[2]</c>. /// </summary> - public int z; + public int Z; /// <summary> /// Access vector components using their <paramref name="index"/>. @@ -52,9 +52,9 @@ namespace Godot /// <paramref name="index"/> is not 0, 1 or 2. /// </exception> /// <value> - /// <c>[0]</c> is equivalent to <see cref="x"/>, - /// <c>[1]</c> is equivalent to <see cref="y"/>, - /// <c>[2]</c> is equivalent to <see cref="z"/>. + /// <c>[0]</c> is equivalent to <see cref="X"/>, + /// <c>[1]</c> is equivalent to <see cref="Y"/>, + /// <c>[2]</c> is equivalent to <see cref="Z"/>. /// </value> public int this[int index] { @@ -63,11 +63,11 @@ namespace Godot switch (index) { case 0: - return x; + return X; case 1: - return y; + return Y; case 2: - return z; + return Z; default: throw new ArgumentOutOfRangeException(nameof(index)); } @@ -77,13 +77,13 @@ namespace Godot switch (index) { case 0: - x = value; + X = value; return; case 1: - y = value; + Y = value; return; case 2: - z = value; + Z = value; return; default: throw new ArgumentOutOfRangeException(nameof(index)); @@ -96,18 +96,18 @@ namespace Godot /// </summary> public readonly void Deconstruct(out int x, out int y, out int z) { - x = this.x; - y = this.y; - z = this.z; + x = X; + y = Y; + z = Z; } /// <summary> /// Returns a new vector with all components in absolute values (i.e. positive). /// </summary> /// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns> - public readonly Vector3i Abs() + public readonly Vector3I Abs() { - return new Vector3i(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z)); + return new Vector3I(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z)); } /// <summary> @@ -118,13 +118,13 @@ namespace Godot /// <param name="min">The vector with minimum allowed values.</param> /// <param name="max">The vector with maximum allowed values.</param> /// <returns>The vector with all components clamped.</returns> - public readonly Vector3i Clamp(Vector3i min, Vector3i max) + public readonly Vector3I Clamp(Vector3I min, Vector3I max) { - return new Vector3i + return new Vector3I ( - Mathf.Clamp(x, min.x, max.x), - Mathf.Clamp(y, min.y, max.y), - Mathf.Clamp(z, min.z, max.z) + Mathf.Clamp(X, min.X, max.X), + Mathf.Clamp(Y, min.Y, max.Y), + Mathf.Clamp(Z, min.Z, max.Z) ); } @@ -135,9 +135,9 @@ namespace Godot /// <returns>The length of this vector.</returns> public readonly real_t Length() { - int x2 = x * x; - int y2 = y * y; - int z2 = z * z; + int x2 = X * X; + int y2 = Y * Y; + int z2 = Z * Z; return Mathf.Sqrt(x2 + y2 + z2); } @@ -150,9 +150,9 @@ namespace Godot /// <returns>The squared length of this vector.</returns> public readonly int LengthSquared() { - int x2 = x * x; - int y2 = y * y; - int z2 = z * z; + int x2 = X * X; + int y2 = Y * Y; + int z2 = Z * Z; return x2 + y2 + z2; } @@ -164,7 +164,7 @@ namespace Godot /// <returns>The index of the highest axis.</returns> public readonly Axis MaxAxisIndex() { - return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); + return X < Y ? (Y < Z ? Axis.Z : Axis.Y) : (X < Z ? Axis.Z : Axis.X); } /// <summary> @@ -174,7 +174,7 @@ namespace Godot /// <returns>The index of the lowest axis.</returns> public readonly Axis MinAxisIndex() { - return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z); + return X < Y ? (X < Z ? Axis.X : Axis.Z) : (Y < Z ? Axis.Y : Axis.Z); } /// <summary> @@ -183,208 +183,208 @@ namespace Godot /// by calling <see cref="Mathf.Sign(int)"/> on each component. /// </summary> /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> - public readonly Vector3i Sign() + public readonly Vector3I Sign() { - Vector3i v = this; - v.x = Mathf.Sign(v.x); - v.y = Mathf.Sign(v.y); - v.z = Mathf.Sign(v.z); + Vector3I v = this; + v.X = Mathf.Sign(v.X); + v.Y = Mathf.Sign(v.Y); + v.Z = Mathf.Sign(v.Z); return v; } // Constants - private static readonly Vector3i _zero = new Vector3i(0, 0, 0); - private static readonly Vector3i _one = new Vector3i(1, 1, 1); + private static readonly Vector3I _zero = new Vector3I(0, 0, 0); + private static readonly Vector3I _one = new Vector3I(1, 1, 1); - private static readonly Vector3i _up = new Vector3i(0, 1, 0); - private static readonly Vector3i _down = new Vector3i(0, -1, 0); - private static readonly Vector3i _right = new Vector3i(1, 0, 0); - private static readonly Vector3i _left = new Vector3i(-1, 0, 0); - private static readonly Vector3i _forward = new Vector3i(0, 0, -1); - private static readonly Vector3i _back = new Vector3i(0, 0, 1); + private static readonly Vector3I _up = new Vector3I(0, 1, 0); + private static readonly Vector3I _down = new Vector3I(0, -1, 0); + private static readonly Vector3I _right = new Vector3I(1, 0, 0); + private static readonly Vector3I _left = new Vector3I(-1, 0, 0); + private static readonly Vector3I _forward = new Vector3I(0, 0, -1); + private static readonly Vector3I _back = new Vector3I(0, 0, 1); /// <summary> /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> - /// <value>Equivalent to <c>new Vector3i(0, 0, 0)</c>.</value> - public static Vector3i Zero { get { return _zero; } } + /// <value>Equivalent to <c>new Vector3I(0, 0, 0)</c>.</value> + public static Vector3I Zero { get { return _zero; } } /// <summary> /// One vector, a vector with all components set to <c>1</c>. /// </summary> - /// <value>Equivalent to <c>new Vector3i(1, 1, 1)</c>.</value> - public static Vector3i One { get { return _one; } } + /// <value>Equivalent to <c>new Vector3I(1, 1, 1)</c>.</value> + public static Vector3I One { get { return _one; } } /// <summary> /// Up unit vector. /// </summary> - /// <value>Equivalent to <c>new Vector3i(0, 1, 0)</c>.</value> - public static Vector3i Up { get { return _up; } } + /// <value>Equivalent to <c>new Vector3I(0, 1, 0)</c>.</value> + public static Vector3I Up { get { return _up; } } /// <summary> /// Down unit vector. /// </summary> - /// <value>Equivalent to <c>new Vector3i(0, -1, 0)</c>.</value> - public static Vector3i Down { get { return _down; } } + /// <value>Equivalent to <c>new Vector3I(0, -1, 0)</c>.</value> + public static Vector3I Down { get { return _down; } } /// <summary> /// Right unit vector. Represents the local direction of right, /// and the global direction of east. /// </summary> - /// <value>Equivalent to <c>new Vector3i(1, 0, 0)</c>.</value> - public static Vector3i Right { get { return _right; } } + /// <value>Equivalent to <c>new Vector3I(1, 0, 0)</c>.</value> + public static Vector3I Right { get { return _right; } } /// <summary> /// Left unit vector. Represents the local direction of left, /// and the global direction of west. /// </summary> - /// <value>Equivalent to <c>new Vector3i(-1, 0, 0)</c>.</value> - public static Vector3i Left { get { return _left; } } + /// <value>Equivalent to <c>new Vector3I(-1, 0, 0)</c>.</value> + public static Vector3I Left { get { return _left; } } /// <summary> /// Forward unit vector. Represents the local direction of forward, /// and the global direction of north. /// </summary> - /// <value>Equivalent to <c>new Vector3i(0, 0, -1)</c>.</value> - public static Vector3i Forward { get { return _forward; } } + /// <value>Equivalent to <c>new Vector3I(0, 0, -1)</c>.</value> + public static Vector3I Forward { get { return _forward; } } /// <summary> /// Back unit vector. Represents the local direction of back, /// and the global direction of south. /// </summary> - /// <value>Equivalent to <c>new Vector3i(0, 0, 1)</c>.</value> - public static Vector3i Back { get { return _back; } } + /// <value>Equivalent to <c>new Vector3I(0, 0, 1)</c>.</value> + public static Vector3I Back { get { return _back; } } /// <summary> - /// Constructs a new <see cref="Vector3i"/> with the given components. + /// Constructs a new <see cref="Vector3I"/> with the given components. /// </summary> /// <param name="x">The vector's X component.</param> /// <param name="y">The vector's Y component.</param> /// <param name="z">The vector's Z component.</param> - public Vector3i(int x, int y, int z) + public Vector3I(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; + X = x; + Y = y; + Z = z; } /// <summary> - /// Adds each component of the <see cref="Vector3i"/> - /// with the components of the given <see cref="Vector3i"/>. + /// Adds each component of the <see cref="Vector3I"/> + /// with the components of the given <see cref="Vector3I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The added vector.</returns> - public static Vector3i operator +(Vector3i left, Vector3i right) + public static Vector3I operator +(Vector3I left, Vector3I right) { - left.x += right.x; - left.y += right.y; - left.z += right.z; + left.X += right.X; + left.Y += right.Y; + left.Z += right.Z; return left; } /// <summary> - /// Subtracts each component of the <see cref="Vector3i"/> - /// by the components of the given <see cref="Vector3i"/>. + /// Subtracts each component of the <see cref="Vector3I"/> + /// by the components of the given <see cref="Vector3I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The subtracted vector.</returns> - public static Vector3i operator -(Vector3i left, Vector3i right) + public static Vector3I operator -(Vector3I left, Vector3I right) { - left.x -= right.x; - left.y -= right.y; - left.z -= right.z; + left.X -= right.X; + left.Y -= right.Y; + left.Z -= right.Z; return left; } /// <summary> - /// Returns the negative value of the <see cref="Vector3i"/>. - /// This is the same as writing <c>new Vector3i(-v.x, -v.y, -v.z)</c>. + /// Returns the negative value of the <see cref="Vector3I"/>. + /// This is the same as writing <c>new Vector3I(-v.X, -v.Y, -v.Z)</c>. /// This operation flips the direction of the vector while /// keeping the same magnitude. /// </summary> /// <param name="vec">The vector to negate/flip.</param> /// <returns>The negated/flipped vector.</returns> - public static Vector3i operator -(Vector3i vec) + public static Vector3I operator -(Vector3I vec) { - vec.x = -vec.x; - vec.y = -vec.y; - vec.z = -vec.z; + vec.X = -vec.X; + vec.Y = -vec.Y; + vec.Z = -vec.Z; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector3i"/> + /// Multiplies each component of the <see cref="Vector3I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="vec">The vector to multiply.</param> /// <param name="scale">The scale to multiply by.</param> /// <returns>The multiplied vector.</returns> - public static Vector3i operator *(Vector3i vec, int scale) + public static Vector3I operator *(Vector3I vec, int scale) { - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; + vec.X *= scale; + vec.Y *= scale; + vec.Z *= scale; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector3i"/> + /// Multiplies each component of the <see cref="Vector3I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="scale">The scale to multiply by.</param> /// <param name="vec">The vector to multiply.</param> /// <returns>The multiplied vector.</returns> - public static Vector3i operator *(int scale, Vector3i vec) + public static Vector3I operator *(int scale, Vector3I vec) { - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; + vec.X *= scale; + vec.Y *= scale; + vec.Z *= scale; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector3i"/> - /// by the components of the given <see cref="Vector3i"/>. + /// Multiplies each component of the <see cref="Vector3I"/> + /// by the components of the given <see cref="Vector3I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The multiplied vector.</returns> - public static Vector3i operator *(Vector3i left, Vector3i right) + public static Vector3I operator *(Vector3I left, Vector3I right) { - left.x *= right.x; - left.y *= right.y; - left.z *= right.z; + left.X *= right.X; + left.Y *= right.Y; + left.Z *= right.Z; return left; } /// <summary> - /// Divides each component of the <see cref="Vector3i"/> + /// Divides each component of the <see cref="Vector3I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="vec">The dividend vector.</param> /// <param name="divisor">The divisor value.</param> /// <returns>The divided vector.</returns> - public static Vector3i operator /(Vector3i vec, int divisor) + public static Vector3I operator /(Vector3I vec, int divisor) { - vec.x /= divisor; - vec.y /= divisor; - vec.z /= divisor; + vec.X /= divisor; + vec.Y /= divisor; + vec.Z /= divisor; return vec; } /// <summary> - /// Divides each component of the <see cref="Vector3i"/> - /// by the components of the given <see cref="Vector3i"/>. + /// Divides each component of the <see cref="Vector3I"/> + /// by the components of the given <see cref="Vector3I"/>. /// </summary> /// <param name="vec">The dividend vector.</param> /// <param name="divisorv">The divisor vector.</param> /// <returns>The divided vector.</returns> - public static Vector3i operator /(Vector3i vec, Vector3i divisorv) + public static Vector3I operator /(Vector3I vec, Vector3I divisorv) { - vec.x /= divisorv.x; - vec.y /= divisorv.y; - vec.z /= divisorv.z; + vec.X /= divisorv.X; + vec.Y /= divisorv.Y; + vec.Z /= divisorv.Z; return vec; } /// <summary> - /// Gets the remainder of each component of the <see cref="Vector3i"/> + /// Gets the remainder of each component of the <see cref="Vector3I"/> /// with the components of the given <see langword="int"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. @@ -393,23 +393,23 @@ namespace Godot /// </summary> /// <example> /// <code> - /// GD.Print(new Vector3i(10, -20, 30) % 7); // Prints "(3, -6, 2)" + /// GD.Print(new Vector3I(10, -20, 30) % 7); // Prints "(3, -6, 2)" /// </code> /// </example> /// <param name="vec">The dividend vector.</param> /// <param name="divisor">The divisor value.</param> /// <returns>The remainder vector.</returns> - public static Vector3i operator %(Vector3i vec, int divisor) + public static Vector3I operator %(Vector3I vec, int divisor) { - vec.x %= divisor; - vec.y %= divisor; - vec.z %= divisor; + vec.X %= divisor; + vec.Y %= divisor; + vec.Z %= divisor; return vec; } /// <summary> - /// Gets the remainder of each component of the <see cref="Vector3i"/> - /// with the components of the given <see cref="Vector3i"/>. + /// Gets the remainder of each component of the <see cref="Vector3I"/> + /// with the components of the given <see cref="Vector3I"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead @@ -417,17 +417,17 @@ namespace Godot /// </summary> /// <example> /// <code> - /// GD.Print(new Vector3i(10, -20, 30) % new Vector3i(7, 8, 9)); // Prints "(3, -4, 3)" + /// GD.Print(new Vector3I(10, -20, 30) % new Vector3I(7, 8, 9)); // Prints "(3, -4, 3)" /// </code> /// </example> /// <param name="vec">The dividend vector.</param> /// <param name="divisorv">The divisor vector.</param> /// <returns>The remainder vector.</returns> - public static Vector3i operator %(Vector3i vec, Vector3i divisorv) + public static Vector3I operator %(Vector3I vec, Vector3I divisorv) { - vec.x %= divisorv.x; - vec.y %= divisorv.y; - vec.z %= divisorv.z; + vec.X %= divisorv.X; + vec.Y %= divisorv.Y; + vec.Z %= divisorv.Z; return vec; } @@ -437,7 +437,7 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the vectors are equal.</returns> - public static bool operator ==(Vector3i left, Vector3i right) + public static bool operator ==(Vector3I left, Vector3I right) { return left.Equals(right); } @@ -448,13 +448,13 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the vectors are not equal.</returns> - public static bool operator !=(Vector3i left, Vector3i right) + public static bool operator !=(Vector3I left, Vector3I right) { return !left.Equals(right); } /// <summary> - /// Compares two <see cref="Vector3i"/> vectors by first checking if + /// Compares two <see cref="Vector3I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is less than /// the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -464,21 +464,21 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is less than the right.</returns> - public static bool operator <(Vector3i left, Vector3i right) + public static bool operator <(Vector3I left, Vector3I right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - return left.z < right.z; + return left.Z < right.Z; } - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> - /// Compares two <see cref="Vector3i"/> vectors by first checking if + /// Compares two <see cref="Vector3I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is greater than /// the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -488,21 +488,21 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is greater than the right.</returns> - public static bool operator >(Vector3i left, Vector3i right) + public static bool operator >(Vector3I left, Vector3I right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - return left.z > right.z; + return left.Z > right.Z; } - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> - /// Compares two <see cref="Vector3i"/> vectors by first checking if + /// Compares two <see cref="Vector3I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is less than /// or equal to the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -512,21 +512,21 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is less than or equal to the right.</returns> - public static bool operator <=(Vector3i left, Vector3i right) + public static bool operator <=(Vector3I left, Vector3I right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - return left.z <= right.z; + return left.Z <= right.Z; } - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> - /// Compares two <see cref="Vector3i"/> vectors by first checking if + /// Compares two <see cref="Vector3I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is greater than /// or equal to the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -536,38 +536,38 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is greater than or equal to the right.</returns> - public static bool operator >=(Vector3i left, Vector3i right) + public static bool operator >=(Vector3I left, Vector3I right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - return left.z >= right.z; + return left.Z >= right.Z; } - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> - /// Converts this <see cref="Vector3i"/> to a <see cref="Vector3"/>. + /// Converts this <see cref="Vector3I"/> to a <see cref="Vector3"/>. /// </summary> /// <param name="value">The vector to convert.</param> - public static implicit operator Vector3(Vector3i value) + public static implicit operator Vector3(Vector3I value) { - return new Vector3(value.x, value.y, value.z); + return new Vector3(value.X, value.Y, value.Z); } /// <summary> - /// Converts a <see cref="Vector3"/> to a <see cref="Vector3i"/>. + /// Converts a <see cref="Vector3"/> to a <see cref="Vector3I"/>. /// </summary> /// <param name="value">The vector to convert.</param> - public static explicit operator Vector3i(Vector3 value) + public static explicit operator Vector3I(Vector3 value) { - return new Vector3i( - Mathf.RoundToInt(value.x), - Mathf.RoundToInt(value.y), - Mathf.RoundToInt(value.z) + return new Vector3I( + Mathf.RoundToInt(value.X), + Mathf.RoundToInt(value.Y), + Mathf.RoundToInt(value.Z) ); } @@ -579,7 +579,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override readonly bool Equals(object obj) { - return obj is Vector3i other && Equals(other); + return obj is Vector3I other && Equals(other); } /// <summary> @@ -587,36 +587,36 @@ namespace Godot /// </summary> /// <param name="other">The other vector.</param> /// <returns>Whether or not the vectors are equal.</returns> - public readonly bool Equals(Vector3i other) + public readonly bool Equals(Vector3I other) { - return x == other.x && y == other.y && z == other.z; + return X == other.X && Y == other.Y && Z == other.Z; } /// <summary> - /// Serves as the hash function for <see cref="Vector3i"/>. + /// Serves as the hash function for <see cref="Vector3I"/>. /// </summary> /// <returns>A hash code for this vector.</returns> public override readonly int GetHashCode() { - return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode(); + return Y.GetHashCode() ^ X.GetHashCode() ^ Z.GetHashCode(); } /// <summary> - /// Converts this <see cref="Vector3i"/> to a string. + /// Converts this <see cref="Vector3I"/> to a string. /// </summary> /// <returns>A string representation of this vector.</returns> public override readonly string ToString() { - return $"({x}, {y}, {z})"; + return $"({X}, {Y}, {Z})"; } /// <summary> - /// Converts this <see cref="Vector3i"/> to a string with the given <paramref name="format"/>. + /// Converts this <see cref="Vector3I"/> to a string with the given <paramref name="format"/>. /// </summary> /// <returns>A string representation of this vector.</returns> public readonly string ToString(string format) { - return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})"; + return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs index 0f4528bb40..1fd39632b0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs @@ -37,22 +37,22 @@ namespace Godot /// <summary> /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> - public real_t x; + public real_t X; /// <summary> /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> - public real_t y; + public real_t Y; /// <summary> /// The vector's Z component. Also accessible by using the index position <c>[2]</c>. /// </summary> - public real_t z; + public real_t Z; /// <summary> /// The vector's W component. Also accessible by using the index position <c>[3]</c>. /// </summary> - public real_t w; + public real_t W; /// <summary> /// Access vector components using their index. @@ -61,10 +61,10 @@ namespace Godot /// <paramref name="index"/> is not 0, 1, 2 or 3. /// </exception> /// <value> - /// <c>[0]</c> is equivalent to <see cref="x"/>, - /// <c>[1]</c> is equivalent to <see cref="y"/>, - /// <c>[2]</c> is equivalent to <see cref="z"/>. - /// <c>[3]</c> is equivalent to <see cref="w"/>. + /// <c>[0]</c> is equivalent to <see cref="X"/>, + /// <c>[1]</c> is equivalent to <see cref="Y"/>, + /// <c>[2]</c> is equivalent to <see cref="Z"/>. + /// <c>[3]</c> is equivalent to <see cref="W"/>. /// </value> public real_t this[int index] { @@ -73,13 +73,13 @@ namespace Godot switch (index) { case 0: - return x; + return X; case 1: - return y; + return Y; case 2: - return z; + return Z; case 3: - return w; + return W; default: throw new ArgumentOutOfRangeException(nameof(index)); } @@ -89,16 +89,16 @@ namespace Godot switch (index) { case 0: - x = value; + X = value; return; case 1: - y = value; + Y = value; return; case 2: - z = value; + Z = value; return; case 3: - w = value; + W = value; return; default: throw new ArgumentOutOfRangeException(nameof(index)); @@ -111,10 +111,10 @@ namespace Godot /// </summary> public readonly void Deconstruct(out real_t x, out real_t y, out real_t z, out real_t w) { - x = this.x; - y = this.y; - z = this.z; - w = this.w; + x = X; + y = Y; + z = Z; + w = W; } internal void Normalize() @@ -123,15 +123,15 @@ namespace Godot if (lengthsq == 0) { - x = y = z = w = 0f; + X = Y = Z = W = 0f; } else { real_t length = Mathf.Sqrt(lengthsq); - x /= length; - y /= length; - z /= length; - w /= length; + X /= length; + Y /= length; + Z /= length; + W /= length; } } @@ -141,7 +141,7 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns> public readonly Vector4 Abs() { - return new Vector4(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z), Mathf.Abs(w)); + return new Vector4(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z), Mathf.Abs(W)); } /// <summary> @@ -150,7 +150,7 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns> public readonly Vector4 Ceil() { - return new Vector4(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z), Mathf.Ceil(w)); + return new Vector4(Mathf.Ceil(X), Mathf.Ceil(Y), Mathf.Ceil(Z), Mathf.Ceil(W)); } /// <summary> @@ -165,10 +165,10 @@ namespace Godot { return new Vector4 ( - Mathf.Clamp(x, min.x, max.x), - Mathf.Clamp(y, min.y, max.y), - Mathf.Clamp(z, min.z, max.z), - Mathf.Clamp(w, min.w, max.w) + Mathf.Clamp(X, min.X, max.X), + Mathf.Clamp(Y, min.Y, max.Y), + Mathf.Clamp(Z, min.Z, max.Z), + Mathf.Clamp(W, min.W, max.W) ); } @@ -185,10 +185,10 @@ namespace Godot { return new Vector4 ( - Mathf.CubicInterpolate(x, b.x, preA.x, postB.x, weight), - Mathf.CubicInterpolate(y, b.y, preA.y, postB.y, weight), - Mathf.CubicInterpolate(z, b.z, preA.z, postB.z, weight), - Mathf.CubicInterpolate(w, b.w, preA.w, postB.w, weight) + Mathf.CubicInterpolate(X, b.X, preA.X, postB.X, weight), + Mathf.CubicInterpolate(Y, b.Y, preA.Y, postB.Y, weight), + Mathf.CubicInterpolate(Z, b.Z, preA.Z, postB.Z, weight), + Mathf.CubicInterpolate(W, b.W, preA.W, postB.W, weight) ); } @@ -210,10 +210,10 @@ namespace Godot { return new Vector4 ( - Mathf.CubicInterpolateInTime(x, b.x, preA.x, postB.x, weight, t, preAT, postBT), - Mathf.CubicInterpolateInTime(y, b.y, preA.y, postB.y, weight, t, preAT, postBT), - Mathf.CubicInterpolateInTime(z, b.z, preA.z, postB.z, weight, t, preAT, postBT), - Mathf.CubicInterpolateInTime(w, b.w, preA.w, postB.w, weight, t, preAT, postBT) + Mathf.CubicInterpolateInTime(X, b.X, preA.X, postB.X, weight, t, preAT, postBT), + Mathf.CubicInterpolateInTime(Y, b.Y, preA.Y, postB.Y, weight, t, preAT, postBT), + Mathf.CubicInterpolateInTime(Z, b.Z, preA.Z, postB.Z, weight, t, preAT, postBT), + Mathf.CubicInterpolateInTime(W, b.W, preA.W, postB.W, weight, t, preAT, postBT) ); } @@ -224,7 +224,7 @@ namespace Godot /// <returns>The direction from this vector to <paramref name="to"/>.</returns> public readonly Vector4 DirectionTo(Vector4 to) { - Vector4 ret = new Vector4(to.x - x, to.y - y, to.z - z, to.w - w); + Vector4 ret = new Vector4(to.X - X, to.Y - Y, to.Z - Z, to.W - W); ret.Normalize(); return ret; } @@ -258,7 +258,7 @@ namespace Godot /// <returns>The dot product of the two vectors.</returns> public readonly real_t Dot(Vector4 with) { - return (x * with.x) + (y * with.y) + (z * with.z) + (w * with.w); + return (X * with.X) + (Y * with.Y) + (Z * with.Z) + (W * with.W); } /// <summary> @@ -267,16 +267,16 @@ namespace Godot /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns> public readonly Vector4 Floor() { - return new Vector4(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z), Mathf.Floor(w)); + return new Vector4(Mathf.Floor(X), Mathf.Floor(Y), Mathf.Floor(Z), Mathf.Floor(W)); } /// <summary> - /// Returns the inverse of this vector. This is the same as <c>new Vector4(1 / v.x, 1 / v.y, 1 / v.z, 1 / v.w)</c>. + /// Returns the inverse of this vector. This is the same as <c>new Vector4(1 / v.X, 1 / v.Y, 1 / v.Z, 1 / v.W)</c>. /// </summary> /// <returns>The inverse of this vector.</returns> public readonly Vector4 Inverse() { - return new Vector4(1 / x, 1 / y, 1 / z, 1 / w); + return new Vector4(1 / X, 1 / Y, 1 / Z, 1 / W); } /// <summary> @@ -286,7 +286,7 @@ namespace Godot /// <returns>Whether this vector is finite or not.</returns> public readonly bool IsFinite() { - return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w); + return Mathf.IsFinite(X) && Mathf.IsFinite(Y) && Mathf.IsFinite(Z) && Mathf.IsFinite(W); } /// <summary> @@ -305,10 +305,10 @@ namespace Godot /// <returns>The length of this vector.</returns> public readonly real_t Length() { - real_t x2 = x * x; - real_t y2 = y * y; - real_t z2 = z * z; - real_t w2 = w * w; + real_t x2 = X * X; + real_t y2 = Y * Y; + real_t z2 = Z * Z; + real_t w2 = W * W; return Mathf.Sqrt(x2 + y2 + z2 + w2); } @@ -321,10 +321,10 @@ namespace Godot /// <returns>The squared length of this vector.</returns> public readonly real_t LengthSquared() { - real_t x2 = x * x; - real_t y2 = y * y; - real_t z2 = z * z; - real_t w2 = w * w; + real_t x2 = X * X; + real_t y2 = Y * Y; + real_t z2 = Z * Z; + real_t w2 = W * W; return x2 + y2 + z2 + w2; } @@ -340,10 +340,10 @@ namespace Godot { return new Vector4 ( - Mathf.Lerp(x, to.x, weight), - Mathf.Lerp(y, to.y, weight), - Mathf.Lerp(z, to.z, weight), - Mathf.Lerp(w, to.w, weight) + Mathf.Lerp(X, to.X, weight), + Mathf.Lerp(Y, to.Y, weight), + Mathf.Lerp(Z, to.Z, weight), + Mathf.Lerp(W, to.W, weight) ); } @@ -355,7 +355,7 @@ namespace Godot public readonly Axis MaxAxisIndex() { int max_index = 0; - real_t max_value = x; + real_t max_value = X; for (int i = 1; i < 4; i++) { if (this[i] > max_value) @@ -375,7 +375,7 @@ namespace Godot public readonly Axis MinAxisIndex() { int min_index = 0; - real_t min_value = x; + real_t min_value = X; for (int i = 1; i < 4; i++) { if (this[i] <= min_value) @@ -409,10 +409,10 @@ namespace Godot public readonly Vector4 PosMod(real_t mod) { return new Vector4( - Mathf.PosMod(x, mod), - Mathf.PosMod(y, mod), - Mathf.PosMod(z, mod), - Mathf.PosMod(w, mod) + Mathf.PosMod(X, mod), + Mathf.PosMod(Y, mod), + Mathf.PosMod(Z, mod), + Mathf.PosMod(W, mod) ); } @@ -427,10 +427,10 @@ namespace Godot public readonly Vector4 PosMod(Vector4 modv) { return new Vector4( - Mathf.PosMod(x, modv.x), - Mathf.PosMod(y, modv.y), - Mathf.PosMod(z, modv.z), - Mathf.PosMod(w, modv.w) + Mathf.PosMod(X, modv.X), + Mathf.PosMod(Y, modv.Y), + Mathf.PosMod(Z, modv.Z), + Mathf.PosMod(W, modv.W) ); } @@ -441,7 +441,7 @@ namespace Godot /// <returns>The rounded vector.</returns> public readonly Vector4 Round() { - return new Vector4(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z), Mathf.Round(w)); + return new Vector4(Mathf.Round(X), Mathf.Round(Y), Mathf.Round(Z), Mathf.Round(W)); } /// <summary> @@ -453,10 +453,10 @@ namespace Godot public readonly Vector4 Sign() { Vector4 v; - v.x = Mathf.Sign(x); - v.y = Mathf.Sign(y); - v.z = Mathf.Sign(z); - v.w = Mathf.Sign(w); + v.X = Mathf.Sign(X); + v.Y = Mathf.Sign(Y); + v.Z = Mathf.Sign(Z); + v.W = Mathf.Sign(W); return v; } @@ -469,10 +469,10 @@ namespace Godot public readonly Vector4 Snapped(Vector4 step) { return new Vector4( - Mathf.Snapped(x, step.x), - Mathf.Snapped(y, step.y), - Mathf.Snapped(z, step.z), - Mathf.Snapped(w, step.w) + Mathf.Snapped(X, step.X), + Mathf.Snapped(Y, step.Y), + Mathf.Snapped(Z, step.Z), + Mathf.Snapped(W, step.W) ); } @@ -506,10 +506,10 @@ namespace Godot /// <param name="w">The vector's W component.</param> public Vector4(real_t x, real_t y, real_t z, real_t w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; + X = x; + Y = y; + Z = z; + W = w; } /// <summary> @@ -521,10 +521,10 @@ namespace Godot /// <returns>The added vector.</returns> public static Vector4 operator +(Vector4 left, Vector4 right) { - left.x += right.x; - left.y += right.y; - left.z += right.z; - left.w += right.w; + left.X += right.X; + left.Y += right.Y; + left.Z += right.Z; + left.W += right.W; return left; } @@ -537,16 +537,16 @@ namespace Godot /// <returns>The subtracted vector.</returns> public static Vector4 operator -(Vector4 left, Vector4 right) { - left.x -= right.x; - left.y -= right.y; - left.z -= right.z; - left.w -= right.w; + left.X -= right.X; + left.Y -= right.Y; + left.Z -= right.Z; + left.W -= right.W; return left; } /// <summary> /// Returns the negative value of the <see cref="Vector4"/>. - /// This is the same as writing <c>new Vector4(-v.x, -v.y, -v.z, -v.w)</c>. + /// This is the same as writing <c>new Vector4(-v.X, -v.Y, -v.Z, -v.W)</c>. /// This operation flips the direction of the vector while /// keeping the same magnitude. /// With floats, the number zero can be either positive or negative. @@ -555,10 +555,10 @@ namespace Godot /// <returns>The negated/flipped vector.</returns> public static Vector4 operator -(Vector4 vec) { - vec.x = -vec.x; - vec.y = -vec.y; - vec.z = -vec.z; - vec.w = -vec.w; + vec.X = -vec.X; + vec.Y = -vec.Y; + vec.Z = -vec.Z; + vec.W = -vec.W; return vec; } @@ -571,10 +571,10 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector4 operator *(Vector4 vec, real_t scale) { - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; - vec.w *= scale; + vec.X *= scale; + vec.Y *= scale; + vec.Z *= scale; + vec.W *= scale; return vec; } @@ -587,10 +587,10 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector4 operator *(real_t scale, Vector4 vec) { - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; - vec.w *= scale; + vec.X *= scale; + vec.Y *= scale; + vec.Z *= scale; + vec.W *= scale; return vec; } @@ -603,10 +603,10 @@ namespace Godot /// <returns>The multiplied vector.</returns> public static Vector4 operator *(Vector4 left, Vector4 right) { - left.x *= right.x; - left.y *= right.y; - left.z *= right.z; - left.w *= right.w; + left.X *= right.X; + left.Y *= right.Y; + left.Z *= right.Z; + left.W *= right.W; return left; } @@ -619,10 +619,10 @@ namespace Godot /// <returns>The divided vector.</returns> public static Vector4 operator /(Vector4 vec, real_t divisor) { - vec.x /= divisor; - vec.y /= divisor; - vec.z /= divisor; - vec.w /= divisor; + vec.X /= divisor; + vec.Y /= divisor; + vec.Z /= divisor; + vec.W /= divisor; return vec; } @@ -635,10 +635,10 @@ namespace Godot /// <returns>The divided vector.</returns> public static Vector4 operator /(Vector4 vec, Vector4 divisorv) { - vec.x /= divisorv.x; - vec.y /= divisorv.y; - vec.z /= divisorv.z; - vec.w /= divisorv.w; + vec.X /= divisorv.X; + vec.Y /= divisorv.Y; + vec.Z /= divisorv.Z; + vec.W /= divisorv.W; return vec; } @@ -660,10 +660,10 @@ namespace Godot /// <returns>The remainder vector.</returns> public static Vector4 operator %(Vector4 vec, real_t divisor) { - vec.x %= divisor; - vec.y %= divisor; - vec.z %= divisor; - vec.w %= divisor; + vec.X %= divisor; + vec.Y %= divisor; + vec.Z %= divisor; + vec.W %= divisor; return vec; } @@ -685,10 +685,10 @@ namespace Godot /// <returns>The remainder vector.</returns> public static Vector4 operator %(Vector4 vec, Vector4 divisorv) { - vec.x %= divisorv.x; - vec.y %= divisorv.y; - vec.z %= divisorv.z; - vec.w %= divisorv.w; + vec.X %= divisorv.X; + vec.Y %= divisorv.Y; + vec.Z %= divisorv.Z; + vec.W %= divisorv.W; return vec; } @@ -731,19 +731,19 @@ namespace Godot /// <returns>Whether or not the left is less than the right.</returns> public static bool operator <(Vector4 left, Vector4 right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - if (left.z == right.z) + if (left.Z == right.Z) { - return left.w < right.w; + return left.W < right.W; } - return left.z < right.z; + return left.Z < right.Z; } - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> @@ -759,19 +759,19 @@ namespace Godot /// <returns>Whether or not the left is greater than the right.</returns> public static bool operator >(Vector4 left, Vector4 right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - if (left.z == right.z) + if (left.Z == right.Z) { - return left.w > right.w; + return left.W > right.W; } - return left.z > right.z; + return left.Z > right.Z; } - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> @@ -787,19 +787,19 @@ namespace Godot /// <returns>Whether or not the left is less than or equal to the right.</returns> public static bool operator <=(Vector4 left, Vector4 right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - if (left.z == right.z) + if (left.Z == right.Z) { - return left.w <= right.w; + return left.W <= right.W; } - return left.z < right.z; + return left.Z < right.Z; } - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> @@ -815,19 +815,19 @@ namespace Godot /// <returns>Whether or not the left is greater than or equal to the right.</returns> public static bool operator >=(Vector4 left, Vector4 right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - if (left.z == right.z) + if (left.Z == right.Z) { - return left.w >= right.w; + return left.W >= right.W; } - return left.z > right.z; + return left.Z > right.Z; } - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> @@ -852,7 +852,7 @@ namespace Godot /// <returns>Whether or not the vectors are exactly equal.</returns> public readonly bool Equals(Vector4 other) { - return x == other.x && y == other.y && z == other.z && w == other.w; + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// <summary> @@ -863,7 +863,7 @@ namespace Godot /// <returns>Whether or not the vectors are approximately equal.</returns> public readonly bool IsEqualApprox(Vector4 other) { - return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); + return Mathf.IsEqualApprox(X, other.X) && Mathf.IsEqualApprox(Y, other.Y) && Mathf.IsEqualApprox(Z, other.Z) && Mathf.IsEqualApprox(W, other.W); } /// <summary> @@ -875,7 +875,7 @@ namespace Godot /// <returns>Whether or not the vector is approximately zero.</returns> public readonly bool IsZeroApprox() { - return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z) && Mathf.IsZeroApprox(w); + return Mathf.IsZeroApprox(X) && Mathf.IsZeroApprox(Y) && Mathf.IsZeroApprox(Z) && Mathf.IsZeroApprox(W); } /// <summary> @@ -884,7 +884,7 @@ namespace Godot /// <returns>A hash code for this vector.</returns> public override readonly int GetHashCode() { - return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + return Y.GetHashCode() ^ X.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode(); } /// <summary> @@ -893,7 +893,7 @@ namespace Godot /// <returns>A string representation of this vector.</returns> public override string ToString() { - return $"({x}, {y}, {z}, {w})"; + return $"({X}, {Y}, {Z}, {W})"; } /// <summary> @@ -902,7 +902,7 @@ namespace Godot /// <returns>A string representation of this vector.</returns> public readonly string ToString(string format) { - return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})"; + return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}, {W.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs index 00ecc64856..f065327066 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs @@ -8,7 +8,7 @@ namespace Godot /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential)] - public struct Vector4i : IEquatable<Vector4i> + public struct Vector4I : IEquatable<Vector4I> { /// <summary> /// Enumerated index values for the axes. @@ -37,22 +37,22 @@ namespace Godot /// <summary> /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> - public int x; + public int X; /// <summary> /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> - public int y; + public int Y; /// <summary> /// The vector's Z component. Also accessible by using the index position <c>[2]</c>. /// </summary> - public int z; + public int Z; /// <summary> /// The vector's W component. Also accessible by using the index position <c>[3]</c>. /// </summary> - public int w; + public int W; /// <summary> /// Access vector components using their <paramref name="index"/>. @@ -61,10 +61,10 @@ namespace Godot /// <paramref name="index"/> is not 0, 1, 2 or 3. /// </exception> /// <value> - /// <c>[0]</c> is equivalent to <see cref="x"/>, - /// <c>[1]</c> is equivalent to <see cref="y"/>, - /// <c>[2]</c> is equivalent to <see cref="z"/>. - /// <c>[3]</c> is equivalent to <see cref="w"/>. + /// <c>[0]</c> is equivalent to <see cref="X"/>, + /// <c>[1]</c> is equivalent to <see cref="Y"/>, + /// <c>[2]</c> is equivalent to <see cref="Z"/>. + /// <c>[3]</c> is equivalent to <see cref="W"/>. /// </value> public int this[int index] { @@ -73,13 +73,13 @@ namespace Godot switch (index) { case 0: - return x; + return X; case 1: - return y; + return Y; case 2: - return z; + return Z; case 3: - return w; + return W; default: throw new ArgumentOutOfRangeException(nameof(index)); } @@ -89,16 +89,16 @@ namespace Godot switch (index) { case 0: - x = value; + X = value; return; case 1: - y = value; + Y = value; return; case 2: - z = value; + Z = value; return; case 3: - w = value; + W = value; return; default: throw new ArgumentOutOfRangeException(nameof(index)); @@ -111,19 +111,19 @@ namespace Godot /// </summary> public readonly void Deconstruct(out int x, out int y, out int z, out int w) { - x = this.x; - y = this.y; - z = this.z; - w = this.w; + x = X; + y = Y; + z = Z; + w = W; } /// <summary> /// Returns a new vector with all components in absolute values (i.e. positive). /// </summary> /// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns> - public readonly Vector4i Abs() + public readonly Vector4I Abs() { - return new Vector4i(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z), Mathf.Abs(w)); + return new Vector4I(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z), Mathf.Abs(W)); } /// <summary> @@ -134,14 +134,14 @@ namespace Godot /// <param name="min">The vector with minimum allowed values.</param> /// <param name="max">The vector with maximum allowed values.</param> /// <returns>The vector with all components clamped.</returns> - public readonly Vector4i Clamp(Vector4i min, Vector4i max) + public readonly Vector4I Clamp(Vector4I min, Vector4I max) { - return new Vector4i + return new Vector4I ( - Mathf.Clamp(x, min.x, max.x), - Mathf.Clamp(y, min.y, max.y), - Mathf.Clamp(z, min.z, max.z), - Mathf.Clamp(w, min.w, max.w) + Mathf.Clamp(X, min.X, max.X), + Mathf.Clamp(Y, min.Y, max.Y), + Mathf.Clamp(Z, min.Z, max.Z), + Mathf.Clamp(W, min.W, max.W) ); } @@ -152,10 +152,10 @@ namespace Godot /// <returns>The length of this vector.</returns> public readonly real_t Length() { - int x2 = x * x; - int y2 = y * y; - int z2 = z * z; - int w2 = w * w; + int x2 = X * X; + int y2 = Y * Y; + int z2 = Z * Z; + int w2 = W * W; return Mathf.Sqrt(x2 + y2 + z2 + w2); } @@ -168,10 +168,10 @@ namespace Godot /// <returns>The squared length of this vector.</returns> public readonly int LengthSquared() { - int x2 = x * x; - int y2 = y * y; - int z2 = z * z; - int w2 = w * w; + int x2 = X * X; + int y2 = Y * Y; + int z2 = Z * Z; + int w2 = W * W; return x2 + y2 + z2 + w2; } @@ -184,7 +184,7 @@ namespace Godot public readonly Axis MaxAxisIndex() { int max_index = 0; - int max_value = x; + int max_value = X; for (int i = 1; i < 4; i++) { if (this[i] > max_value) @@ -204,7 +204,7 @@ namespace Godot public readonly Axis MinAxisIndex() { int min_index = 0; - int min_value = x; + int min_value = X; for (int i = 1; i < 4; i++) { if (this[i] <= min_value) @@ -222,213 +222,217 @@ namespace Godot /// by calling <see cref="Mathf.Sign(int)"/> on each component. /// </summary> /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> - public readonly Vector4i Sign() + public readonly Vector4I Sign() { - return new Vector4i(Mathf.Sign(x), Mathf.Sign(y), Mathf.Sign(z), Mathf.Sign(w)); + return new Vector4I(Mathf.Sign(X), Mathf.Sign(Y), Mathf.Sign(Z), Mathf.Sign(W)); } // Constants - private static readonly Vector4i _zero = new Vector4i(0, 0, 0, 0); - private static readonly Vector4i _one = new Vector4i(1, 1, 1, 1); + private static readonly Vector4I _zero = new Vector4I(0, 0, 0, 0); + private static readonly Vector4I _one = new Vector4I(1, 1, 1, 1); /// <summary> /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> - /// <value>Equivalent to <c>new Vector4i(0, 0, 0, 0)</c>.</value> - public static Vector4i Zero { get { return _zero; } } + /// <value>Equivalent to <c>new Vector4I(0, 0, 0, 0)</c>.</value> + public static Vector4I Zero { get { return _zero; } } /// <summary> /// One vector, a vector with all components set to <c>1</c>. /// </summary> - /// <value>Equivalent to <c>new Vector4i(1, 1, 1, 1)</c>.</value> - public static Vector4i One { get { return _one; } } + /// <value>Equivalent to <c>new Vector4I(1, 1, 1, 1)</c>.</value> + public static Vector4I One { get { return _one; } } /// <summary> - /// Constructs a new <see cref="Vector4i"/> with the given components. + /// Constructs a new <see cref="Vector4I"/> with the given components. /// </summary> /// <param name="x">The vector's X component.</param> /// <param name="y">The vector's Y component.</param> /// <param name="z">The vector's Z component.</param> /// <param name="w">The vector's W component.</param> - public Vector4i(int x, int y, int z, int w) + public Vector4I(int x, int y, int z, int w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; + X = x; + Y = y; + Z = z; + W = w; } /// <summary> - /// Adds each component of the <see cref="Vector4i"/> - /// with the components of the given <see cref="Vector4i"/>. + /// Adds each component of the <see cref="Vector4I"/> + /// with the components of the given <see cref="Vector4I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The added vector.</returns> - public static Vector4i operator +(Vector4i left, Vector4i right) + public static Vector4I operator +(Vector4I left, Vector4I right) { - left.x += right.x; - left.y += right.y; - left.z += right.z; - left.w += right.w; + left.X += right.X; + left.Y += right.Y; + left.Z += right.Z; + left.W += right.W; return left; } /// <summary> - /// Subtracts each component of the <see cref="Vector4i"/> - /// by the components of the given <see cref="Vector4i"/>. + /// Subtracts each component of the <see cref="Vector4I"/> + /// by the components of the given <see cref="Vector4I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The subtracted vector.</returns> - public static Vector4i operator -(Vector4i left, Vector4i right) + public static Vector4I operator -(Vector4I left, Vector4I right) { - left.x -= right.x; - left.y -= right.y; - left.z -= right.z; - left.w -= right.w; + left.X -= right.X; + left.Y -= right.Y; + left.Z -= right.Z; + left.W -= right.W; return left; } /// <summary> - /// Returns the negative value of the <see cref="Vector4i"/>. - /// This is the same as writing <c>new Vector4i(-v.x, -v.y, -v.z, -v.w)</c>. + /// Returns the negative value of the <see cref="Vector4I"/>. + /// This is the same as writing <c>new Vector4I(-v.X, -v.Y, -v.Z, -v.W)</c>. /// This operation flips the direction of the vector while /// keeping the same magnitude. /// </summary> /// <param name="vec">The vector to negate/flip.</param> /// <returns>The negated/flipped vector.</returns> - public static Vector4i operator -(Vector4i vec) + public static Vector4I operator -(Vector4I vec) { - vec.x = -vec.x; - vec.y = -vec.y; - vec.z = -vec.z; - vec.w = -vec.w; + vec.X = -vec.X; + vec.Y = -vec.Y; + vec.Z = -vec.Z; + vec.W = -vec.W; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector4i"/> + /// Multiplies each component of the <see cref="Vector4I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="vec">The vector to multiply.</param> /// <param name="scale">The scale to multiply by.</param> /// <returns>The multiplied vector.</returns> - public static Vector4i operator *(Vector4i vec, int scale) + public static Vector4I operator *(Vector4I vec, int scale) { - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; - vec.w *= scale; + vec.X *= scale; + vec.Y *= scale; + vec.Z *= scale; + vec.W *= scale; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector4i"/> + /// Multiplies each component of the <see cref="Vector4I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="scale">The scale to multiply by.</param> /// <param name="vec">The vector to multiply.</param> /// <returns>The multiplied vector.</returns> - public static Vector4i operator *(int scale, Vector4i vec) + public static Vector4I operator *(int scale, Vector4I vec) { - vec.x *= scale; - vec.y *= scale; - vec.z *= scale; - vec.w *= scale; + vec.X *= scale; + vec.Y *= scale; + vec.Z *= scale; + vec.W *= scale; return vec; } /// <summary> - /// Multiplies each component of the <see cref="Vector4i"/> - /// by the components of the given <see cref="Vector4i"/>. + /// Multiplies each component of the <see cref="Vector4I"/> + /// by the components of the given <see cref="Vector4I"/>. /// </summary> /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>The multiplied vector.</returns> - public static Vector4i operator *(Vector4i left, Vector4i right) + public static Vector4I operator *(Vector4I left, Vector4I right) { - left.x *= right.x; - left.y *= right.y; - left.z *= right.z; - left.w *= right.w; + left.X *= right.X; + left.Y *= right.Y; + left.Z *= right.Z; + left.W *= right.W; return left; } /// <summary> - /// Divides each component of the <see cref="Vector4i"/> + /// Divides each component of the <see cref="Vector4I"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="vec">The dividend vector.</param> /// <param name="divisor">The divisor value.</param> /// <returns>The divided vector.</returns> - public static Vector4i operator /(Vector4i vec, int divisor) + public static Vector4I operator /(Vector4I vec, int divisor) { - vec.x /= divisor; - vec.y /= divisor; - vec.z /= divisor; - vec.w /= divisor; + vec.X /= divisor; + vec.Y /= divisor; + vec.Z /= divisor; + vec.W /= divisor; return vec; } /// <summary> - /// Divides each component of the <see cref="Vector4i"/> - /// by the components of the given <see cref="Vector4i"/>. + /// Divides each component of the <see cref="Vector4I"/> + /// by the components of the given <see cref="Vector4I"/>. /// </summary> /// <param name="vec">The dividend vector.</param> /// <param name="divisorv">The divisor vector.</param> /// <returns>The divided vector.</returns> - public static Vector4i operator /(Vector4i vec, Vector4i divisorv) + public static Vector4I operator /(Vector4I vec, Vector4I divisorv) { - vec.x /= divisorv.x; - vec.y /= divisorv.y; - vec.z /= divisorv.z; - vec.w /= divisorv.w; + vec.X /= divisorv.X; + vec.Y /= divisorv.Y; + vec.Z /= divisorv.Z; + vec.W /= divisorv.W; return vec; } /// <summary> - /// Gets the remainder of each component of the <see cref="Vector4i"/> + /// Gets the remainder of each component of the <see cref="Vector4I"/> /// with the components of the given <see langword="int"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead + /// if you want to handle negative numbers. /// </summary> /// <example> /// <code> - /// GD.Print(new Vecto43i(10, -20, 30, -40) % 7); // Prints "(3, -6, 2, -5)" + /// GD.Print(new Vector4I(10, -20, 30, -40) % 7); // Prints "(3, -6, 2, -5)" /// </code> /// </example> /// <param name="vec">The dividend vector.</param> /// <param name="divisor">The divisor value.</param> /// <returns>The remainder vector.</returns> - public static Vector4i operator %(Vector4i vec, int divisor) + public static Vector4I operator %(Vector4I vec, int divisor) { - vec.x %= divisor; - vec.y %= divisor; - vec.z %= divisor; - vec.w %= divisor; + vec.X %= divisor; + vec.Y %= divisor; + vec.Z %= divisor; + vec.W %= divisor; return vec; } /// <summary> - /// Gets the remainder of each component of the <see cref="Vector4i"/> - /// with the components of the given <see cref="Vector4i"/>. + /// Gets the remainder of each component of the <see cref="Vector4I"/> + /// with the components of the given <see cref="Vector4I"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead + /// if you want to handle negative numbers. /// </summary> /// <example> /// <code> - /// GD.Print(new Vector4i(10, -20, 30, -40) % new Vector4i(6, 7, 8, 9)); // Prints "(4, -6, 6, -4)" + /// GD.Print(new Vector4I(10, -20, 30, -40) % new Vector4I(6, 7, 8, 9)); // Prints "(4, -6, 6, -4)" /// </code> /// </example> /// <param name="vec">The dividend vector.</param> /// <param name="divisorv">The divisor vector.</param> /// <returns>The remainder vector.</returns> - public static Vector4i operator %(Vector4i vec, Vector4i divisorv) + public static Vector4I operator %(Vector4I vec, Vector4I divisorv) { - vec.x %= divisorv.x; - vec.y %= divisorv.y; - vec.z %= divisorv.z; - vec.w %= divisorv.w; + vec.X %= divisorv.X; + vec.Y %= divisorv.Y; + vec.Z %= divisorv.Z; + vec.W %= divisorv.W; return vec; } @@ -438,7 +442,7 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the vectors are equal.</returns> - public static bool operator ==(Vector4i left, Vector4i right) + public static bool operator ==(Vector4I left, Vector4I right) { return left.Equals(right); } @@ -449,13 +453,13 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the vectors are not equal.</returns> - public static bool operator !=(Vector4i left, Vector4i right) + public static bool operator !=(Vector4I left, Vector4I right) { return !left.Equals(right); } /// <summary> - /// Compares two <see cref="Vector4i"/> vectors by first checking if + /// Compares two <see cref="Vector4I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is less than /// the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -465,25 +469,25 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is less than the right.</returns> - public static bool operator <(Vector4i left, Vector4i right) + public static bool operator <(Vector4I left, Vector4I right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - if (left.z == right.z) + if (left.Z == right.Z) { - return left.w < right.w; + return left.W < right.W; } - return left.z < right.z; + return left.Z < right.Z; } - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> - /// Compares two <see cref="Vector4i"/> vectors by first checking if + /// Compares two <see cref="Vector4I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is greater than /// the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -493,25 +497,25 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is greater than the right.</returns> - public static bool operator >(Vector4i left, Vector4i right) + public static bool operator >(Vector4I left, Vector4I right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - if (left.z == right.z) + if (left.Z == right.Z) { - return left.w > right.w; + return left.W > right.W; } - return left.z > right.z; + return left.Z > right.Z; } - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> - /// Compares two <see cref="Vector4i"/> vectors by first checking if + /// Compares two <see cref="Vector4I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is less than /// or equal to the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -521,25 +525,25 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is less than or equal to the right.</returns> - public static bool operator <=(Vector4i left, Vector4i right) + public static bool operator <=(Vector4I left, Vector4I right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - if (left.z == right.z) + if (left.Z == right.Z) { - return left.w <= right.w; + return left.W <= right.W; } - return left.z < right.z; + return left.Z < right.Z; } - return left.y < right.y; + return left.Y < right.Y; } - return left.x < right.x; + return left.X < right.X; } /// <summary> - /// Compares two <see cref="Vector4i"/> vectors by first checking if + /// Compares two <see cref="Vector4I"/> vectors by first checking if /// the X value of the <paramref name="left"/> vector is greater than /// or equal to the X value of the <paramref name="right"/> vector. /// If the X values are exactly equal, then it repeats this check @@ -549,43 +553,43 @@ namespace Godot /// <param name="left">The left vector.</param> /// <param name="right">The right vector.</param> /// <returns>Whether or not the left is greater than or equal to the right.</returns> - public static bool operator >=(Vector4i left, Vector4i right) + public static bool operator >=(Vector4I left, Vector4I right) { - if (left.x == right.x) + if (left.X == right.X) { - if (left.y == right.y) + if (left.Y == right.Y) { - if (left.z == right.z) + if (left.Z == right.Z) { - return left.w >= right.w; + return left.W >= right.W; } - return left.z > right.z; + return left.Z > right.Z; } - return left.y > right.y; + return left.Y > right.Y; } - return left.x > right.x; + return left.X > right.X; } /// <summary> - /// Converts this <see cref="Vector4i"/> to a <see cref="Vector4"/>. + /// Converts this <see cref="Vector4I"/> to a <see cref="Vector4"/>. /// </summary> /// <param name="value">The vector to convert.</param> - public static implicit operator Vector4(Vector4i value) + public static implicit operator Vector4(Vector4I value) { - return new Vector4(value.x, value.y, value.z, value.w); + return new Vector4(value.X, value.Y, value.Z, value.W); } /// <summary> - /// Converts a <see cref="Vector4"/> to a <see cref="Vector4i"/>. + /// Converts a <see cref="Vector4"/> to a <see cref="Vector4I"/>. /// </summary> /// <param name="value">The vector to convert.</param> - public static explicit operator Vector4i(Vector4 value) + public static explicit operator Vector4I(Vector4 value) { - return new Vector4i( - Mathf.RoundToInt(value.x), - Mathf.RoundToInt(value.y), - Mathf.RoundToInt(value.z), - Mathf.RoundToInt(value.w) + return new Vector4I( + Mathf.RoundToInt(value.X), + Mathf.RoundToInt(value.Y), + Mathf.RoundToInt(value.Z), + Mathf.RoundToInt(value.W) ); } @@ -597,7 +601,7 @@ namespace Godot /// <returns>Whether or not the vector and the object are equal.</returns> public override readonly bool Equals(object obj) { - return obj is Vector4i other && Equals(other); + return obj is Vector4I other && Equals(other); } /// <summary> @@ -605,36 +609,36 @@ namespace Godot /// </summary> /// <param name="other">The other vector.</param> /// <returns>Whether or not the vectors are equal.</returns> - public readonly bool Equals(Vector4i other) + public readonly bool Equals(Vector4I other) { - return x == other.x && y == other.y && z == other.z && w == other.w; + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// <summary> - /// Serves as the hash function for <see cref="Vector4i"/>. + /// Serves as the hash function for <see cref="Vector4I"/>. /// </summary> /// <returns>A hash code for this vector.</returns> public override readonly int GetHashCode() { - return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + return Y.GetHashCode() ^ X.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode(); } /// <summary> - /// Converts this <see cref="Vector4i"/> to a string. + /// Converts this <see cref="Vector4I"/> to a string. /// </summary> /// <returns>A string representation of this vector.</returns> public override readonly string ToString() { - return $"({x}, {y}, {z}, {w})"; + return $"({X}, {Y}, {Z}, {W})"; } /// <summary> - /// Converts this <see cref="Vector4i"/> to a string with the given <paramref name="format"/>. + /// Converts this <see cref="Vector4I"/> to a string with the given <paramref name="format"/>. /// </summary> /// <returns>A string representation of this vector.</returns> public readonly string ToString(string format) { - return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}), {w.ToString(format)})"; + return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}), {W.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 644212c74d..7aa2f7e959 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -49,7 +49,7 @@ </ItemGroup> <!-- Sources --> <ItemGroup> - <Compile Include="Core\AABB.cs" /> + <Compile Include="Core\Aabb.cs" /> <Compile Include="Core\Bridge\GodotSerializationInfo.cs" /> <Compile Include="Core\Bridge\MethodInfo.cs" /> <Compile Include="Core\Callable.generics.cs" /> @@ -60,8 +60,9 @@ <Compile Include="Core\Attributes\ExportCategoryAttribute.cs" /> <Compile Include="Core\Attributes\ExportGroupAttribute.cs" /> <Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" /> + <Compile Include="Core\Attributes\GodotClassNameAttribute.cs" /> <Compile Include="Core\Attributes\MustBeVariantAttribute.cs" /> - <Compile Include="Core\Attributes\RPCAttribute.cs" /> + <Compile Include="Core\Attributes\RpcAttribute.cs" /> <Compile Include="Core\Attributes\ScriptPathAttribute.cs" /> <Compile Include="Core\Attributes\SignalAttribute.cs" /> <Compile Include="Core\Attributes\ToolAttribute.cs" /> @@ -80,11 +81,13 @@ <Compile Include="Core\DelegateUtils.cs" /> <Compile Include="Core\Dictionary.cs" /> <Compile Include="Core\Dispatcher.cs" /> + <Compile Include="Core\Extensions\GodotObjectExtensions.cs" /> <Compile Include="Core\Extensions\NodeExtensions.cs" /> - <Compile Include="Core\Extensions\ObjectExtensions.cs" /> <Compile Include="Core\Extensions\PackedSceneExtensions.cs" /> <Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" /> <Compile Include="Core\GD.cs" /> + <Compile Include="Core\GodotObject.base.cs" /> + <Compile Include="Core\GodotObject.exceptions.cs" /> <Compile Include="Core\GodotSynchronizationContext.cs" /> <Compile Include="Core\GodotTaskScheduler.cs" /> <Compile Include="Core\GodotTraceListener.cs" /> @@ -104,15 +107,13 @@ <Compile Include="Core\NativeInterop\VariantUtils.cs" /> <Compile Include="Core\NativeInterop\VariantUtils.generic.cs" /> <Compile Include="Core\NodePath.cs" /> - <Compile Include="Core\Object.base.cs" /> - <Compile Include="Core\Object.exceptions.cs" /> <Compile Include="Core\Plane.cs" /> <Compile Include="Core\Projection.cs" /> <Compile Include="Core\Quaternion.cs" /> <Compile Include="Core\Rect2.cs" /> - <Compile Include="Core\Rect2i.cs" /> + <Compile Include="Core\Rect2I.cs" /> <Compile Include="Core\ReflectionUtils.cs" /> - <Compile Include="Core\RID.cs" /> + <Compile Include="Core\Rid.cs" /> <Compile Include="Core\NativeInterop\NativeFuncs.cs" /> <Compile Include="Core\NativeInterop\InteropStructs.cs" /> <Compile Include="Core\NativeInterop\Marshaling.cs" /> @@ -124,11 +125,11 @@ <Compile Include="Core\Transform3D.cs" /> <Compile Include="Core\Variant.cs" /> <Compile Include="Core\Vector2.cs" /> - <Compile Include="Core\Vector2i.cs" /> + <Compile Include="Core\Vector2I.cs" /> <Compile Include="Core\Vector3.cs" /> - <Compile Include="Core\Vector3i.cs" /> + <Compile Include="Core\Vector3I.cs" /> <Compile Include="Core\Vector4.cs" /> - <Compile Include="Core\Vector4i.cs" /> + <Compile Include="Core\Vector4I.cs" /> <Compile Include="GlobalUsings.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index f0ea0313ea..d17fe3e75f 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -31,6 +31,7 @@ #include "runtime_interop.h" #include "core/config/engine.h" +#include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" #include "core/io/marshalls.h" @@ -45,6 +46,7 @@ #include "modules/mono/managed_callable.h" #include "modules/mono/mono_gd/gd_mono_cache.h" #include "modules/mono/signal_awaiter_utils.h" +#include "modules/mono/utils/path_utils.h" #ifdef __cplusplus extern "C" { @@ -84,7 +86,8 @@ void godotsharp_stack_info_vector_destroy( void godotsharp_internal_script_debugger_send_error(const String *p_func, const String *p_file, int32_t p_line, const String *p_err, const String *p_descr, bool p_warning, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) { - EngineDebugger::get_script_debugger()->send_error(*p_func, *p_file, p_line, *p_err, *p_descr, + const String file = ProjectSettings::get_singleton()->localize_path(p_file->simplify_path()); + EngineDebugger::get_script_debugger()->send_error(*p_func, file, p_line, *p_err, *p_descr, true, p_warning ? ERR_HANDLER_WARNING : ERR_HANDLER_ERROR, *p_stack_info_vector); } @@ -1009,6 +1012,10 @@ int32_t godotsharp_array_resize(Array *p_self, int32_t p_new_size) { return (int32_t)p_self->resize(p_new_size); } +void godotsharp_array_make_read_only(Array *p_self) { + p_self->make_read_only(); +} + void godotsharp_array_shuffle(Array *p_self) { p_self->shuffle(); } @@ -1066,10 +1073,22 @@ void godotsharp_dictionary_duplicate(const Dictionary *p_self, bool p_deep, Dict memnew_placement(r_dest, Dictionary(p_self->duplicate(p_deep))); } +void godotsharp_dictionary_merge(Dictionary *p_self, const Dictionary *p_dictionary, bool p_overwrite) { + p_self->merge(*p_dictionary, p_overwrite); +} + +bool godotsharp_dictionary_recursive_equal(const Dictionary *p_self, const Dictionary *p_other) { + return p_self->recursive_equal(*p_other, 0); +} + bool godotsharp_dictionary_remove_key(Dictionary *p_self, const Variant *p_key) { return p_self->erase(*p_key); } +void godotsharp_dictionary_make_read_only(Dictionary *p_self) { + p_self->make_read_only(); +} + void godotsharp_dictionary_to_string(const Dictionary *p_self, String *r_str) { *r_str = Variant(*p_self).operator String(); } @@ -1180,21 +1199,6 @@ void godotsharp_weakref(Object *p_ptr, Ref<RefCounted> *r_weak_ref) { memnew_placement(r_weak_ref, Ref<RefCounted>(wref)); } -void godotsharp_str(const godot_array *p_what, godot_string *r_ret) { - String &str = *memnew_placement(r_ret, String); - const Array &what = *reinterpret_cast<const Array *>(p_what); - - for (int i = 0; i < what.size(); i++) { - String os = what[i].operator String(); - - if (i == 0) { - str = os; - } else { - str += os; - } - } -} - void godotsharp_print(const godot_string *p_what) { print_line(*reinterpret_cast<const String *>(p_what)); } @@ -1443,6 +1447,7 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_array_insert, (void *)godotsharp_array_remove_at, (void *)godotsharp_array_resize, + (void *)godotsharp_array_make_read_only, (void *)godotsharp_array_shuffle, (void *)godotsharp_array_to_string, (void *)godotsharp_dictionary_try_get_value, @@ -1455,7 +1460,10 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_dictionary_clear, (void *)godotsharp_dictionary_contains_key, (void *)godotsharp_dictionary_duplicate, + (void *)godotsharp_dictionary_merge, + (void *)godotsharp_dictionary_recursive_equal, (void *)godotsharp_dictionary_remove_key, + (void *)godotsharp_dictionary_make_read_only, (void *)godotsharp_dictionary_to_string, (void *)godotsharp_string_simplify_path, (void *)godotsharp_string_to_camel_case, @@ -1488,7 +1496,6 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_rand_from_seed, (void *)godotsharp_seed, (void *)godotsharp_weakref, - (void *)godotsharp_str, (void *)godotsharp_str_to_var, (void *)godotsharp_var_to_bytes, (void *)godotsharp_var_to_str, diff --git a/modules/mono/utils/naming_utils.cpp b/modules/mono/utils/naming_utils.cpp new file mode 100644 index 0000000000..62fbf815f8 --- /dev/null +++ b/modules/mono/utils/naming_utils.cpp @@ -0,0 +1,293 @@ +/**************************************************************************/ +/* naming_utils.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "naming_utils.h" + +#include "core/string/ucaps.h" +#include "core/templates/hash_map.h" + +HashMap<String, String> _create_hashmap_from_vector(Vector<Pair<String, String>> vector) { + HashMap<String, String> hashmap = HashMap<String, String>(vector.size()); + for (const Pair<String, String> &pair : vector) { + hashmap.insert(pair.first, pair.second); + } + return hashmap; +} + +// Hardcoded collection of PascalCase name conversions. +const HashMap<String, String> pascal_case_name_overrides = _create_hashmap_from_vector({ + { "BitMap", "Bitmap" }, + { "JSONRPC", "JsonRpc" }, + { "Object", "GodotObject" }, + { "OpenXRIPBinding", "OpenXRIPBinding" }, + { "SkeletonModification2DCCDIK", "SkeletonModification2DCcdik" }, + { "SkeletonModification2DFABRIK", "SkeletonModification2DFabrik" }, + { "SkeletonModification3DCCDIK", "SkeletonModification3DCcdik" }, + { "SkeletonModification3DFABRIK", "SkeletonModification3DFabrik" }, + { "System", "System_" }, + { "Thread", "GodotThread" }, +}); + +// Hardcoded collection of PascalCase part conversions. +const HashMap<String, String> pascal_case_part_overrides = _create_hashmap_from_vector({ + { "AA", "AA" }, // Anti Aliasing + { "AO", "AO" }, // Ambient Occlusion + { "FILENAME", "FileName" }, + { "FADEIN", "FadeIn" }, + { "FADEOUT", "FadeOut" }, + { "FX", "FX" }, + { "GI", "GI" }, // Global Illumination + { "GZIP", "GZip" }, + { "HBOX", "HBox" }, // Horizontal Box + { "ID", "Id" }, + { "IO", "IO" }, // Input/Output + { "IP", "IP" }, // Internet Protocol + { "IV", "IV" }, // Initialization Vector + { "MACOS", "MacOS" }, + { "NODEPATH", "NodePath" }, + { "SPIRV", "SpirV" }, + { "STDIN", "StdIn" }, + { "STDOUT", "StdOut" }, + { "USERNAME", "UserName" }, + { "UV", "UV" }, + { "UV2", "UV2" }, + { "VBOX", "VBox" }, // Vertical Box + { "WHITESPACE", "WhiteSpace" }, + { "WM", "WM" }, + { "XR", "XR" }, + { "XRAPI", "XRApi" }, +}); + +String _get_pascal_case_part_override(String p_part, bool p_input_is_upper = true) { + if (!p_input_is_upper) { + for (int i = 0; i < p_part.length(); i++) { + p_part[i] = _find_upper(p_part[i]); + } + } + + if (pascal_case_part_overrides.has(p_part)) { + return pascal_case_part_overrides.get(p_part); + } + + return String(); +} + +Vector<String> _split_pascal_case(const String &p_identifier) { + Vector<String> parts; + int current_part_start = 0; + bool prev_was_upper = is_ascii_upper_case(p_identifier[0]); + for (int i = 1; i < p_identifier.length(); i++) { + if (prev_was_upper) { + if (is_digit(p_identifier[i]) || is_ascii_lower_case(p_identifier[i])) { + if (!is_digit(p_identifier[i])) { + // These conditions only apply when the separator is not a digit. + if (i - current_part_start == 1) { + // Upper character was only the beginning of a word. + prev_was_upper = false; + continue; + } + if (i != p_identifier.length()) { + // If this is not the last character, the last uppercase + // character is the start of the next word. + i--; + } + } + if (i - current_part_start > 0) { + parts.append(p_identifier.substr(current_part_start, i - current_part_start)); + current_part_start = i; + prev_was_upper = false; + } + } + } else { + if (is_digit(p_identifier[i]) || is_ascii_upper_case(p_identifier[i])) { + parts.append(p_identifier.substr(current_part_start, i - current_part_start)); + current_part_start = i; + prev_was_upper = true; + } + } + } + + // Add the rest of the identifier as the last part. + if (current_part_start != p_identifier.length()) { + parts.append(p_identifier.substr(current_part_start)); + } + + return parts; +} + +String pascal_to_pascal_case(const String &p_identifier) { + if (p_identifier.length() == 0) { + return p_identifier; + } + + if (p_identifier.length() <= 2) { + return p_identifier.to_upper(); + } + + if (pascal_case_name_overrides.has(p_identifier)) { + // Use hardcoded value for the identifier. + return pascal_case_name_overrides.get(p_identifier); + } + + Vector<String> parts = _split_pascal_case(p_identifier); + + String ret; + + for (String &part : parts) { + String part_override = _get_pascal_case_part_override(part); + if (!part_override.is_empty()) { + // Use hardcoded value for part. + ret += part_override; + continue; + } + + if (part.length() <= 2 && part.to_upper() == part) { + // Acronym of length 1 or 2. + for (int j = 0; j < part.length(); j++) { + part[j] = _find_upper(part[j]); + } + ret += part; + continue; + } + + part[0] = _find_upper(part[0]); + for (int i = 1; i < part.length(); i++) { + if (is_digit(part[i - 1])) { + // Use uppercase after digits. + part[i] = _find_upper(part[i]); + continue; + } + + part[i] = _find_lower(part[i]); + } + ret += part; + } + + return ret; +} + +String snake_to_pascal_case(const String &p_identifier, bool p_input_is_upper) { + String ret; + Vector<String> parts = p_identifier.split("_", true); + + for (int i = 0; i < parts.size(); i++) { + String part = parts[i]; + + String part_override = _get_pascal_case_part_override(part, p_input_is_upper); + if (!part_override.is_empty()) { + // Use hardcoded value for part. + ret += part_override; + continue; + } + + if (!part.is_empty()) { + part[0] = _find_upper(part[0]); + for (int j = 1; j < part.length(); j++) { + if (is_digit(part[j - 1])) { + // Use uppercase after digits. + part[j] = _find_upper(part[j]); + continue; + } + + if (p_input_is_upper) { + part[j] = _find_lower(part[j]); + } + } + ret += part; + } else { + if (i == 0 || i == (parts.size() - 1)) { + // Preserve underscores at the beginning and end + ret += "_"; + } else { + // Preserve contiguous underscores + if (parts[i - 1].length()) { + ret += "__"; + } else { + ret += "_"; + } + } + } + } + + return ret; +} + +String snake_to_camel_case(const String &p_identifier, bool p_input_is_upper) { + String ret; + Vector<String> parts = p_identifier.split("_", true); + + for (int i = 0; i < parts.size(); i++) { + String part = parts[i]; + + String part_override = _get_pascal_case_part_override(part, p_input_is_upper); + if (!part_override.is_empty()) { + // Use hardcoded value for part. + if (i == 0) { + part_override[0] = _find_lower(part_override[0]); + } + ret += part_override; + continue; + } + + if (!part.is_empty()) { + if (i == 0) { + part[0] = _find_lower(part[0]); + } else { + part[0] = _find_upper(part[0]); + } + for (int j = 1; j < part.length(); j++) { + if (is_digit(part[j - 1])) { + // Use uppercase after digits. + part[j] = _find_upper(part[j]); + continue; + } + + if (p_input_is_upper) { + part[j] = _find_lower(part[j]); + } + } + ret += part; + } else { + if (i == 0 || i == (parts.size() - 1)) { + // Preserve underscores at the beginning and end + ret += "_"; + } else { + // Preserve contiguous underscores + if (parts[i - 1].length()) { + ret += "__"; + } else { + ret += "_"; + } + } + } + } + + return ret; +} diff --git a/modules/mono/utils/naming_utils.h b/modules/mono/utils/naming_utils.h new file mode 100644 index 0000000000..ac64a5c114 --- /dev/null +++ b/modules/mono/utils/naming_utils.h @@ -0,0 +1,42 @@ +/**************************************************************************/ +/* naming_utils.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 MONO_NAMING_UTILS_H +#define MONO_NAMING_UTILS_H + +#include "core/string/ustring.h" + +String pascal_to_pascal_case(const String &p_identifier); + +String snake_to_pascal_case(const String &p_identifier, bool p_input_is_upper = false); + +String snake_to_camel_case(const String &p_identifier, bool p_input_is_upper = false); + +#endif // MONO_NAMING_UTILS_H diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index 2b5db6462c..d546c5d3ba 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -81,36 +81,6 @@ using namespace NavigationUtilities; } \ void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1) -#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \ - struct MERGE(F_NAME, _command) : public SetCommand { \ - T_0 d_0; \ - T_1 d_1; \ - T_2 d_2; \ - T_3 d_3; \ - MERGE(F_NAME, _command) \ - ( \ - T_0 p_d_0, \ - T_1 p_d_1, \ - T_2 p_d_2, \ - T_3 p_d_3) : \ - d_0(p_d_0), \ - d_1(p_d_1), \ - d_2(p_d_2), \ - d_3(p_d_3) {} \ - virtual void exec(GodotNavigationServer *server) override { \ - server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \ - } \ - }; \ - void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) { \ - auto cmd = memnew(MERGE(F_NAME, _command)( \ - D_0, \ - D_1, \ - D_2, \ - D_3)); \ - add_command(cmd); \ - } \ - void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) - GodotNavigationServer::GodotNavigationServer() {} GodotNavigationServer::~GodotNavigationServer() { @@ -535,32 +505,32 @@ uint32_t GodotNavigationServer::link_get_navigation_layers(const RID p_link) con return link->get_navigation_layers(); } -COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location) { +COMMAND_2(link_set_start_position, RID, p_link, Vector3, p_position) { NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND(link == nullptr); - link->set_start_location(p_location); + link->set_start_position(p_position); } -Vector3 GodotNavigationServer::link_get_start_location(RID p_link) const { +Vector3 GodotNavigationServer::link_get_start_position(RID p_link) const { const NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND_V(link == nullptr, Vector3()); - return link->get_start_location(); + return link->get_start_position(); } -COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location) { +COMMAND_2(link_set_end_position, RID, p_link, Vector3, p_position) { NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND(link == nullptr); - link->set_end_location(p_location); + link->set_end_position(p_position); } -Vector3 GodotNavigationServer::link_get_end_location(RID p_link) const { +Vector3 GodotNavigationServer::link_get_end_position(RID p_link) const { const NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND_V(link == nullptr, Vector3()); - return link->get_end_location(); + return link->get_end_position(); } COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost) { @@ -711,17 +681,17 @@ bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const { return agent->is_map_changed(); } -COMMAND_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata) { +COMMAND_2(agent_set_callback, RID, p_agent, Callable, p_callback) { RvoAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); - agent->set_callback(p_object_id, p_method, p_udata); + agent->set_callback(p_callback); if (agent->get_map()) { - if (p_object_id == ObjectID()) { - agent->get_map()->remove_agent_as_controlled(agent); - } else { + if (p_callback.is_valid()) { agent->get_map()->set_agent_as_controlled(agent); + } else { + agent->get_map()->remove_agent_as_controlled(agent); } } } @@ -946,4 +916,3 @@ int GodotNavigationServer::get_process_info(ProcessInfo p_info) const { #undef COMMAND_1 #undef COMMAND_2 -#undef COMMAND_4 diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index a87a88d3bc..eea5713c40 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -54,10 +54,6 @@ virtual void F_NAME(T_0 D_0, T_1 D_1) override; \ void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1) -#define COMMAND_4_DEF(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, D_3_DEF) \ - virtual void F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3 = D_3_DEF) override; \ - void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) - class GodotNavigationServer; struct SetCommand { @@ -158,10 +154,10 @@ public: virtual bool link_is_bidirectional(RID p_link) const override; COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers); virtual uint32_t link_get_navigation_layers(RID p_link) const override; - COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location); - virtual Vector3 link_get_start_location(RID p_link) const override; - COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location); - virtual Vector3 link_get_end_location(RID p_link) const override; + COMMAND_2(link_set_start_position, RID, p_link, Vector3, p_position); + virtual Vector3 link_get_start_position(RID p_link) const override; + COMMAND_2(link_set_end_position, RID, p_link, Vector3, p_position); + virtual Vector3 link_get_end_position(RID p_link) const override; COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost); virtual real_t link_get_enter_cost(RID p_link) const override; COMMAND_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost); @@ -182,7 +178,7 @@ public: COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position); COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore); virtual bool agent_is_map_changed(RID p_agent) const override; - COMMAND_4_DEF(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, Variant()); + COMMAND_2(agent_set_callback, RID, p_agent, Callable, p_callback); COMMAND_1(free, RID, p_object); @@ -198,6 +194,5 @@ public: #undef COMMAND_1 #undef COMMAND_2 -#undef COMMAND_4_DEF #endif // GODOT_NAVIGATION_SERVER_H diff --git a/modules/navigation/nav_link.cpp b/modules/navigation/nav_link.cpp index 05d2b21487..ad87cc0b05 100644 --- a/modules/navigation/nav_link.cpp +++ b/modules/navigation/nav_link.cpp @@ -42,13 +42,13 @@ void NavLink::set_bidirectional(bool p_bidirectional) { link_dirty = true; } -void NavLink::set_start_location(const Vector3 p_location) { - start_location = p_location; +void NavLink::set_start_position(const Vector3 p_position) { + start_position = p_position; link_dirty = true; } -void NavLink::set_end_location(const Vector3 p_location) { - end_location = p_location; +void NavLink::set_end_position(const Vector3 p_position) { + end_position = p_position; link_dirty = true; } diff --git a/modules/navigation/nav_link.h b/modules/navigation/nav_link.h index 47c1211db8..0b8ad4db69 100644 --- a/modules/navigation/nav_link.h +++ b/modules/navigation/nav_link.h @@ -37,8 +37,8 @@ class NavLink : public NavBase { NavMap *map = nullptr; bool bidirectional = true; - Vector3 start_location; - Vector3 end_location; + Vector3 start_position; + Vector3 end_position; bool link_dirty = true; @@ -57,14 +57,14 @@ public: return bidirectional; } - void set_start_location(Vector3 p_location); - Vector3 get_start_location() const { - return start_location; + void set_start_position(Vector3 p_position); + Vector3 get_start_position() const { + return start_position; } - void set_end_location(Vector3 p_location); - Vector3 get_end_location() const { - return end_location; + void set_end_position(Vector3 p_position); + Vector3 get_end_position() const { + return end_position; } bool check_dirty(); diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index fe255c1ce8..d763b1d3bc 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -780,8 +780,8 @@ void NavMap::sync() { // Search for polygons within range of a nav link. for (const NavLink *link : links) { - const Vector3 start = link->get_start_location(); - const Vector3 end = link->get_end_location(); + const Vector3 start = link->get_start_position(); + const Vector3 end = link->get_end_position(); gd::Polygon *closest_start_polygon = nullptr; real_t closest_start_distance = link_connection_radius; diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h index 50437469aa..06a1a1f403 100644 --- a/modules/navigation/nav_utils.h +++ b/modules/navigation/nav_utils.h @@ -125,7 +125,7 @@ struct NavigationPoly { Vector3 back_navigation_edge_pathway_start; Vector3 back_navigation_edge_pathway_end; - /// The entry location of this poly. + /// The entry position of this poly. Vector3 entry; /// The distance to the destination. float traveled_distance = 0.0; diff --git a/modules/navigation/rvo_agent.cpp b/modules/navigation/rvo_agent.cpp index 979ef0d917..40f1e925be 100644 --- a/modules/navigation/rvo_agent.cpp +++ b/modules/navigation/rvo_agent.cpp @@ -32,10 +32,6 @@ #include "nav_map.h" -RvoAgent::RvoAgent() { - callback.id = ObjectID(); -} - void RvoAgent::set_map(NavMap *p_map) { map = p_map; } @@ -50,31 +46,25 @@ bool RvoAgent::is_map_changed() { } } -void RvoAgent::set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata) { - callback.id = p_id; - callback.method = p_method; - callback.udata = p_udata; +void RvoAgent::set_callback(Callable p_callback) { + callback = p_callback; } bool RvoAgent::has_callback() const { - return callback.id.is_valid(); + return callback.is_valid(); } void RvoAgent::dispatch_callback() { - if (callback.id.is_null()) { + if (!callback.is_valid()) { return; } - Object *obj = ObjectDB::get_instance(callback.id); - if (!obj) { - callback.id = ObjectID(); - return; - } - - Callable::CallError responseCallError; - callback.new_velocity = Vector3(agent.newVelocity_.x(), agent.newVelocity_.y(), agent.newVelocity_.z()); + Vector3 new_velocity = Vector3(agent.newVelocity_.x(), agent.newVelocity_.y(), agent.newVelocity_.z()); - const Variant *vp[2] = { &callback.new_velocity, &callback.udata }; - int argc = (callback.udata.get_type() == Variant::NIL) ? 1 : 2; - obj->callp(callback.method, vp, argc, responseCallError); + // Invoke the callback with the new velocity. + Variant args[] = { new_velocity }; + const Variant *args_p[] = { &args[0] }; + Variant return_value; + Callable::CallError call_error; + callback.callp(args_p, 1, return_value, call_error); } diff --git a/modules/navigation/rvo_agent.h b/modules/navigation/rvo_agent.h index 7b19907b2b..5f377b6079 100644 --- a/modules/navigation/rvo_agent.h +++ b/modules/navigation/rvo_agent.h @@ -39,21 +39,12 @@ class NavMap; class RvoAgent : public NavRid { - struct AvoidanceComputedCallback { - ObjectID id; - StringName method; - Variant udata; - Variant new_velocity; - }; - NavMap *map = nullptr; RVO::Agent agent; - AvoidanceComputedCallback callback; + Callable callback = Callable(); uint32_t map_update_id = 0; public: - RvoAgent(); - void set_map(NavMap *p_map); NavMap *get_map() { return map; @@ -65,7 +56,7 @@ public: bool is_map_changed(); - void set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata = Variant()); + void set_callback(Callable p_callback); bool has_callback() const; void dispatch_callback(); diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml index 735ca388de..78d7d6a15b 100644 --- a/modules/noise/doc_classes/Noise.xml +++ b/modules/noise/doc_classes/Noise.xml @@ -17,8 +17,10 @@ <param index="1" name="height" type="int" /> <param index="2" name="invert" type="bool" default="false" /> <param index="3" name="in_3d_space" type="bool" default="false" /> + <param index="4" name="normalize" type="bool" default="true" /> <description> Returns a 2D [Image] noise image. + Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. </description> </method> <method name="get_noise_1d" qualifiers="const"> @@ -66,8 +68,10 @@ <param index="2" name="invert" type="bool" default="false" /> <param index="3" name="in_3d_space" type="bool" default="false" /> <param index="4" name="skirt" type="float" default="0.1" /> + <param index="5" name="normalize" type="bool" default="true" /> <description> Returns a seamless 2D [Image] noise image. + Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. </description> </method> </methods> diff --git a/modules/noise/doc_classes/NoiseTexture2D.xml b/modules/noise/doc_classes/NoiseTexture2D.xml index 0a800a143b..0f10a3f32f 100644 --- a/modules/noise/doc_classes/NoiseTexture2D.xml +++ b/modules/noise/doc_classes/NoiseTexture2D.xml @@ -44,6 +44,10 @@ <member name="noise" type="Noise" setter="set_noise" getter="get_noise"> The instance of the [Noise] object. </member> + <member name="normalize" type="bool" setter="set_normalize" getter="is_normalized" default="true"> + If [code]true[/code], the noise image coming from the noise generator is normalized to the range [code]0.0[/code] to [code]1.0[/code]. + Turning normalization off can affect the contrast and allows you to generate non repeating tileable noise textures. + </member> <member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" /> <member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false"> If [code]true[/code], a seamless texture is requested from the [Noise] resource. diff --git a/modules/noise/noise.cpp b/modules/noise/noise.cpp index 5a901cb6e1..e95788b863 100644 --- a/modules/noise/noise.cpp +++ b/modules/noise/noise.cpp @@ -32,7 +32,7 @@ #include <float.h> -Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt) const { +Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const { ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>()); int skirt_width = MAX(1, p_width * p_blend_skirt); @@ -40,7 +40,7 @@ Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, b int src_width = p_width + skirt_width; int src_height = p_height + skirt_height; - Ref<Image> src = get_image(src_width, src_height, p_invert, p_in_3d_space); + Ref<Image> src = get_image(src_width, src_height, p_invert, p_in_3d_space, p_normalize); bool grayscale = (src->get_format() == Image::FORMAT_L8); if (grayscale) { return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_invert, p_blend_skirt); @@ -58,7 +58,7 @@ uint8_t Noise::_alpha_blend<uint8_t>(uint8_t p_bg, uint8_t p_fg, int p_alpha) co return (uint8_t)((alpha * p_fg + inv_alpha * p_bg) >> 8); } -Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space) const { +Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, bool p_normalize) const { ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>()); Vector<uint8_t> data; @@ -66,38 +66,49 @@ Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_ uint8_t *wd8 = data.ptrw(); - // Get all values and identify min/max values. - Vector<real_t> values; - values.resize(p_width * p_height); - real_t min_val = FLT_MAX; - real_t max_val = -FLT_MAX; - - for (int y = 0, i = 0; y < p_height; y++) { - for (int x = 0; x < p_width; x++, i++) { - values.set(i, p_in_3d_space ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y)); - if (values[i] > max_val) { - max_val = values[i]; - } - if (values[i] < min_val) { - min_val = values[i]; + if (p_normalize) { + // Get all values and identify min/max values. + Vector<real_t> values; + values.resize(p_width * p_height); + real_t min_val = FLT_MAX; + real_t max_val = -FLT_MAX; + for (int y = 0, i = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++, i++) { + values.set(i, p_in_3d_space ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y)); + if (values[i] > max_val) { + max_val = values[i]; + } + if (values[i] < min_val) { + min_val = values[i]; + } } } - } - - // Normalize values and write to texture. - uint8_t value; - for (int i = 0, x = 0; i < p_height; i++) { - for (int j = 0; j < p_width; j++, x++) { - if (max_val == min_val) { - value = 0; - } else { - value = uint8_t(CLAMP((values[x] - min_val) / (max_val - min_val) * 255.f, 0, 255)); + // Normalize values and write to texture. + uint8_t ivalue; + for (int i = 0, x = 0; i < p_height; i++) { + for (int j = 0; j < p_width; j++, x++) { + if (max_val == min_val) { + ivalue = 0; + } else { + ivalue = static_cast<uint8_t>(CLAMP((values[x] - min_val) / (max_val - min_val) * 255.f, 0, 255)); + } + + if (p_invert) { + ivalue = 255 - ivalue; + } + + wd8[x] = ivalue; } - if (p_invert) { - value = 255 - value; + } + } else { + // Without normalization, the expected range of the noise function is [-1, 1]. + uint8_t ivalue; + for (int y = 0, i = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++, i++) { + float value = (p_in_3d_space ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y)); + ivalue = static_cast<uint8_t>(CLAMP(value * 127.5f + 127.5f, 0.0f, 255.0f)); + wd8[i] = p_invert ? (255 - ivalue) : ivalue; } - - wd8[x] = value; } } @@ -113,6 +124,6 @@ void Noise::_bind_methods() { ClassDB::bind_method(D_METHOD("get_noise_3dv", "v"), &Noise::get_noise_3dv); // Textures. - ClassDB::bind_method(D_METHOD("get_image", "width", "height", "invert", "in_3d_space"), &Noise::get_image, DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "invert", "in_3d_space", "skirt"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1)); + ClassDB::bind_method(D_METHOD("get_image", "width", "height", "invert", "in_3d_space", "normalize"), &Noise::get_image, DEFVAL(false), DEFVAL(false), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "invert", "in_3d_space", "skirt", "normalize"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1), DEFVAL(true)); } diff --git a/modules/noise/noise.h b/modules/noise/noise.h index 8f8ecf29a5..f7e615c2aa 100644 --- a/modules/noise/noise.h +++ b/modules/noise/noise.h @@ -233,8 +233,8 @@ public: virtual real_t get_noise_3dv(Vector3 p_v) const = 0; virtual real_t get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const = 0; - virtual Ref<Image> get_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false) const; - virtual Ref<Image> get_seamless_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1) const; + virtual Ref<Image> get_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const; + virtual Ref<Image> get_seamless_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const; }; #endif // NOISE_H diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index 0eedb286bd..0d5e778875 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -76,6 +76,9 @@ void NoiseTexture2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture2D::set_bump_strength); ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture2D::get_bump_strength); + ClassDB::bind_method(D_METHOD("set_normalize", "normalize"), &NoiseTexture2D::set_normalize); + ClassDB::bind_method(D_METHOD("is_normalized"), &NoiseTexture2D::is_normalized); + ClassDB::bind_method(D_METHOD("set_color_ramp", "gradient"), &NoiseTexture2D::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &NoiseTexture2D::get_color_ramp); @@ -91,6 +94,7 @@ void NoiseTexture2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normal_map"), "set_as_normal_map", "is_normal_map"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalize"), "set_normalize", "is_normalized"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise"); } @@ -156,9 +160,9 @@ Ref<Image> NoiseTexture2D::_generate_texture() { Ref<Image> new_image; if (seamless) { - new_image = ref_noise->get_seamless_image(size.x, size.y, invert, in_3d_space, seamless_blend_skirt); + new_image = ref_noise->get_seamless_image(size.x, size.y, invert, in_3d_space, seamless_blend_skirt, normalize); } else { - new_image = ref_noise->get_image(size.x, size.y, invert, in_3d_space); + new_image = ref_noise->get_image(size.x, size.y, invert, in_3d_space, normalize); } if (color_ramp.is_valid()) { new_image = _modulate_with_gradient(new_image, color_ramp); @@ -349,6 +353,18 @@ void NoiseTexture2D::set_color_ramp(const Ref<Gradient> &p_gradient) { _queue_update(); } +void NoiseTexture2D::set_normalize(bool p_normalize) { + if (normalize == p_normalize) { + return; + } + normalize = p_normalize; + _queue_update(); +} + +bool NoiseTexture2D::is_normalized() const { + return normalize; +} + Ref<Gradient> NoiseTexture2D::get_color_ramp() const { return color_ramp; } diff --git a/modules/noise/noise_texture_2d.h b/modules/noise/noise_texture_2d.h index cda14df6c2..f53670b690 100644 --- a/modules/noise/noise_texture_2d.h +++ b/modules/noise/noise_texture_2d.h @@ -59,6 +59,7 @@ private: real_t seamless_blend_skirt = 0.1; bool as_normal_map = false; float bump_strength = 8.0; + bool normalize = true; Ref<Gradient> color_ramp; Ref<Noise> noise; @@ -105,6 +106,9 @@ public: void set_bump_strength(float p_bump_strength); float get_bump_strength(); + void set_normalize(bool p_normalize); + bool is_normalized() const; + void set_color_ramp(const Ref<Gradient> &p_gradient); Ref<Gradient> get_color_ramp() const; diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 7251a4a9bd..f3cc469c9d 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -11,12 +11,33 @@ <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link> </tutorials> <methods> + <method name="get_action_sets" qualifiers="const"> + <return type="Array" /> + <description> + Returns a list of action sets registered with Godot (loaded from the action map at runtime). + </description> + </method> <method name="get_available_display_refresh_rates" qualifiers="const"> <return type="Array" /> <description> Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized. </description> </method> + <method name="is_action_set_active" qualifiers="const"> + <return type="bool" /> + <param index="0" name="name" type="String" /> + <description> + Returns [code]true[/code] if the given action set is active. + </description> + </method> + <method name="set_action_set_active"> + <return type="void" /> + <param index="0" name="name" type="String" /> + <param index="1" name="active" type="bool" /> + <description> + Sets the given action set as active or inactive. + </description> + </method> </methods> <members> <member name="display_refresh_rate" type="float" setter="set_display_refresh_rate" getter="get_display_refresh_rate" default="0.0"> diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index f9afdf2d4a..702e56b410 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -47,6 +47,10 @@ void OpenXRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate"); + ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active); + ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active); + ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets); + ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates); } @@ -621,6 +625,38 @@ Array OpenXRInterface::get_available_display_refresh_rates() const { } } +bool OpenXRInterface::is_action_set_active(const String &p_action_set) const { + for (ActionSet *action_set : action_sets) { + if (action_set->action_set_name == p_action_set) { + return action_set->is_active; + } + } + + WARN_PRINT("OpenXR: Unknown action set " + p_action_set); + return false; +} + +void OpenXRInterface::set_action_set_active(const String &p_action_set, bool p_active) { + for (ActionSet *action_set : action_sets) { + if (action_set->action_set_name == p_action_set) { + action_set->is_active = p_active; + return; + } + } + + WARN_PRINT("OpenXR: Unknown action set " + p_action_set); +} + +Array OpenXRInterface::get_action_sets() const { + Array arr; + + for (ActionSet *action_set : action_sets) { + arr.push_back(action_set->action_set_name); + } + + return arr; +} + Size2 OpenXRInterface::get_render_target_size() { if (openxr_api == nullptr) { return Size2(); diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index 2b43369523..cce329d8e6 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -123,6 +123,10 @@ public: void set_display_refresh_rate(float p_refresh_rate); Array get_available_display_refresh_rates() const; + bool is_action_set_active(const String &p_action_set) const; + void set_action_set_active(const String &p_action_set, bool p_active); + Array get_action_sets() const; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index e53aef965a..9b474bf2ce 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -4068,7 +4068,6 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { RID TextServerAdvanced::_shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const { _THREAD_SAFE_METHOD_ - const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, RID()); @@ -5513,6 +5512,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star } bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { + _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -6569,6 +6569,7 @@ TextServerAdvanced::TextServerAdvanced() { } void TextServerAdvanced::_cleanup() { + _THREAD_SAFE_METHOD_ for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) { const Vector<SystemFontCacheRec> &sysf_cache = E.value.var; for (const SystemFontCacheRec &F : sysf_cache) { diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index 7e896a0ca3..aaeb2025ee 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -13,10 +13,9 @@ <method name="create_client"> <return type="int" enum="Error" /> <param index="0" name="url" type="String" /> - <param index="1" name="verify_tls" type="bool" default="true" /> - <param index="2" name="tls_certificate" type="X509Certificate" default="null" /> + <param index="1" name="tls_client_options" type="TLSOptions" default="null" /> <description> - Starts a new multiplayer client connecting to the given [param url]. If [param verify_tls] is [code]false[/code] certificate validation will be disabled. If specified, the [param tls_certificate] will be used to verify the TLS host. + Starts a new multiplayer client connecting to the given [param url]. TLS certificates will be verified against the hostname when connecting using the [code]wss://[/code] protocol. You can pass the optional [param tls_client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe]. [b]Note[/b]: It is recommended to specify the scheme part of the URL, i.e. the [param url] should start with either [code]ws://[/code] or [code]wss://[/code]. </description> </method> @@ -24,10 +23,9 @@ <return type="int" enum="Error" /> <param index="0" name="port" type="int" /> <param index="1" name="bind_address" type="String" default=""*"" /> - <param index="2" name="tls_key" type="CryptoKey" default="null" /> - <param index="3" name="tls_certificate" type="X509Certificate" default="null" /> + <param index="2" name="tls_server_options" type="TLSOptions" default="null" /> <description> - Starts a new multiplayer server listening on the given [param port]. You can optionally specify a [param bind_address], and provide a [param tls_key] and [param tls_certificate] to use TLS. + Starts a new multiplayer server listening on the given [param port]. You can optionally specify a [param bind_address], and provide valiid [param tls_server_options] to use TLS. See [method TLSOptions.server]. </description> </method> <method name="get_peer" qualifiers="const"> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index 41d166a0f5..0f8c27c4cc 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -58,10 +58,9 @@ <method name="connect_to_url"> <return type="int" enum="Error" /> <param index="0" name="url" type="String" /> - <param index="1" name="verify_tls" type="bool" default="true" /> - <param index="2" name="trusted_tls_certificate" type="X509Certificate" default="null" /> + <param index="1" name="tls_client_options" type="TLSOptions" default="null" /> <description> - Connects to the given URL. If [param verify_tls] is [code]false[/code] certificate validation will be disabled. If specified, the [param trusted_tls_certificate] will be the only one accepted when connecting to a TLS host. + Connects to the given URL. TLS certificates will be verified against the hostname when connecting using the [code]wss://[/code] protocol. You can pass the optional [param tls_client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe]. [b]Note:[/b] To avoid mixed content warnings or errors in Web, you may have to use a [code]url[/code] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's TLS certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the TLS certificate. </description> </method> diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 1ec557427f..7b14a3a61d 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -58,7 +58,8 @@ void EMWSPeer::_esws_on_close(void *p_obj, int p_code, const char *p_reason, int peer->ready_state = STATE_CLOSED; } -Error EMWSPeer::connect_to_url(const String &p_url, bool p_verify_tls, Ref<X509Certificate> p_tls_certificate) { +Error EMWSPeer::connect_to_url(const String &p_url, Ref<TLSOptions> p_tls_options) { + ERR_FAIL_COND_V(p_tls_options.is_valid() && p_tls_options->is_server(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(ready_state != STATE_CLOSED, ERR_ALREADY_IN_USE); _clear(); @@ -85,9 +86,6 @@ Error EMWSPeer::connect_to_url(const String &p_url, bool p_verify_tls, Ref<X509C if (handshake_headers.size()) { WARN_PRINT_ONCE("Custom headers are not supported in Web platform."); } - if (p_tls_certificate.is_valid()) { - WARN_PRINT_ONCE("Custom SSL certificates are not supported in Web platform."); - } requested_url = scheme + host; diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index 66c5283d5c..08b2dad977 100644 --- a/modules/websocket/emws_peer.h +++ b/modules/websocket/emws_peer.h @@ -86,7 +86,7 @@ public: // WebSocketPeer virtual Error send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) override; - virtual Error connect_to_url(const String &p_url, bool p_verify_tls = true, Ref<X509Certificate> p_cert = Ref<X509Certificate>()) override; + virtual Error connect_to_url(const String &p_url, Ref<TLSOptions> p_tls_client_options) override; virtual Error accept_stream(Ref<StreamPeer> p_stream) override; virtual void close(int p_code = 1000, String p_reason = "") override; virtual void poll() override; diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 36b4215f8c..389d8c56ad 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -54,11 +54,9 @@ void WebSocketMultiplayerPeer::_clear() { connection_status = CONNECTION_DISCONNECTED; unique_id = 0; peers_map.clear(); - use_tls = false; tcp_server.unref(); pending_peers.clear(); - tls_certificate.unref(); - tls_key.unref(); + tls_server_options.unref(); if (current_packet.data != nullptr) { memfree(current_packet.data); current_packet.data = nullptr; @@ -73,8 +71,8 @@ void WebSocketMultiplayerPeer::_clear() { } void WebSocketMultiplayerPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("create_client", "url", "verify_tls", "tls_certificate"), &WebSocketMultiplayerPeer::create_client, DEFVAL(true), DEFVAL(Ref<X509Certificate>())); - ClassDB::bind_method(D_METHOD("create_server", "port", "bind_address", "tls_key", "tls_certificate"), &WebSocketMultiplayerPeer::create_server, DEFVAL("*"), DEFVAL(Ref<CryptoKey>()), DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("create_client", "url", "tls_client_options"), &WebSocketMultiplayerPeer::create_client, DEFVAL(Ref<TLSOptions>())); + ClassDB::bind_method(D_METHOD("create_server", "port", "bind_address", "tls_server_options"), &WebSocketMultiplayerPeer::create_server, DEFVAL("*"), DEFVAL(Ref<TLSOptions>())); ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer); ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketMultiplayerPeer::get_peer_address); @@ -179,8 +177,9 @@ int WebSocketMultiplayerPeer::get_max_packet_size() const { return get_outbound_buffer_size() - PROTO_SIZE; } -Error WebSocketMultiplayerPeer::create_server(int p_port, IPAddress p_bind_ip, Ref<CryptoKey> p_tls_key, Ref<X509Certificate> p_tls_certificate) { +Error WebSocketMultiplayerPeer::create_server(int p_port, IPAddress p_bind_ip, Ref<TLSOptions> p_options) { ERR_FAIL_COND_V(get_connection_status() != CONNECTION_DISCONNECTED, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(p_options.is_valid() && !p_options->is_server(), ERR_INVALID_PARAMETER); _clear(); tcp_server.instantiate(); Error err = tcp_server->listen(p_port, p_bind_ip); @@ -190,20 +189,16 @@ Error WebSocketMultiplayerPeer::create_server(int p_port, IPAddress p_bind_ip, R } unique_id = 1; connection_status = CONNECTION_CONNECTED; - // TLS config - tls_key = p_tls_key; - tls_certificate = p_tls_certificate; - if (tls_key.is_valid() && tls_certificate.is_valid()) { - use_tls = true; - } + tls_server_options = p_options; return OK; } -Error WebSocketMultiplayerPeer::create_client(const String &p_url, bool p_verify_tls, Ref<X509Certificate> p_tls_certificate) { +Error WebSocketMultiplayerPeer::create_client(const String &p_url, Ref<TLSOptions> p_options) { ERR_FAIL_COND_V(get_connection_status() != CONNECTION_DISCONNECTED, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(p_options.is_valid() && p_options->is_server(), ERR_INVALID_PARAMETER); _clear(); Ref<WebSocketPeer> peer = _create_peer(); - Error err = peer->connect_to_url(p_url, p_verify_tls, p_tls_certificate); + Error err = peer->connect_to_url(p_url, p_options); if (err != OK) { return err; } @@ -334,14 +329,14 @@ void WebSocketMultiplayerPeer::_poll_server() { to_remove.insert(id); // Error. continue; } - if (!use_tls) { + if (tls_server_options.is_null()) { peer.ws = _create_peer(); peer.ws->accept_stream(peer.tcp); continue; } else { if (peer.connection == peer.tcp) { Ref<StreamPeerTLS> tls = Ref<StreamPeerTLS>(StreamPeerTLS::create()); - Error err = tls->accept_stream(peer.tcp, tls_key, tls_certificate); + Error err = tls->accept_stream(peer.tcp, tls_server_options); if (err != OK) { to_remove.insert(id); continue; diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h index ea10e8799f..22f1bc939b 100644 --- a/modules/websocket/websocket_multiplayer_peer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -71,9 +71,7 @@ protected: Ref<WebSocketPeer> peer_config; HashMap<int, PendingPeer> pending_peers; Ref<TCPServer> tcp_server; - bool use_tls = false; - Ref<X509Certificate> tls_certificate; - Ref<CryptoKey> tls_key; + Ref<TLSOptions> tls_server_options; ConnectionStatus connection_status = CONNECTION_DISCONNECTED; @@ -115,8 +113,8 @@ public: /* WebSocketPeer */ virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const; - Error create_client(const String &p_url, bool p_verify_tls, Ref<X509Certificate> p_tls_certificate); - Error create_server(int p_port, IPAddress p_bind_ip, Ref<CryptoKey> p_tls_key, Ref<X509Certificate> p_tls_certificate); + Error create_client(const String &p_url, Ref<TLSOptions> p_options); + Error create_server(int p_port, IPAddress p_bind_ip, Ref<TLSOptions> p_options); void set_supported_protocols(const Vector<String> &p_protocols); Vector<String> get_supported_protocols() const; diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp index d10315f64c..3c0d316bc9 100644 --- a/modules/websocket/websocket_peer.cpp +++ b/modules/websocket/websocket_peer.cpp @@ -39,7 +39,7 @@ WebSocketPeer::~WebSocketPeer() { } void WebSocketPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_to_url", "url", "verify_tls", "trusted_tls_certificate"), &WebSocketPeer::connect_to_url, DEFVAL(true), DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("connect_to_url", "url", "tls_client_options"), &WebSocketPeer::connect_to_url, DEFVAL(Ref<TLSOptions>())); ClassDB::bind_method(D_METHOD("accept_stream", "stream"), &WebSocketPeer::accept_stream); ClassDB::bind_method(D_METHOD("send", "message", "write_mode"), &WebSocketPeer::_send_bind, DEFVAL(WRITE_MODE_BINARY)); ClassDB::bind_method(D_METHOD("send_text", "message"), &WebSocketPeer::send_text); diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index 3a1527b769..3110e87071 100644 --- a/modules/websocket/websocket_peer.h +++ b/modules/websocket/websocket_peer.h @@ -81,7 +81,7 @@ public: return _create(); } - virtual Error connect_to_url(const String &p_url, bool p_verify_tls = true, Ref<X509Certificate> p_cert = Ref<X509Certificate>()) { return ERR_UNAVAILABLE; }; + virtual Error connect_to_url(const String &p_url, Ref<TLSOptions> p_options = Ref<TLSOptions>()) = 0; virtual Error accept_stream(Ref<StreamPeer> p_stream) = 0; virtual Error send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) = 0; diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 9ba286d5ee..8a150c8561 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -333,8 +333,7 @@ void WSLPeer::_do_client_handshake() { // Start SSL handshake tls = Ref<StreamPeerTLS>(StreamPeerTLS::create()); ERR_FAIL_COND_MSG(tls.is_null(), "SSL is not available in this build."); - tls->set_blocking_handshake_enabled(false); - if (tls->connect_to_stream(tcp, verify_tls, requested_host, tls_cert) != OK) { + if (tls->connect_to_stream(tcp, requested_host, tls_options) != OK) { close(-1); return; // Error. } @@ -476,9 +475,10 @@ bool WSLPeer::_verify_server_response() { return true; } -Error WSLPeer::connect_to_url(const String &p_url, bool p_verify_tls, Ref<X509Certificate> p_cert) { +Error WSLPeer::connect_to_url(const String &p_url, Ref<TLSOptions> p_options) { ERR_FAIL_COND_V(wsl_ctx || tcp.is_valid(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(p_url.is_empty(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_options.is_valid() && p_options->is_server(), ERR_INVALID_PARAMETER); _clear(); @@ -506,8 +506,13 @@ Error WSLPeer::connect_to_url(const String &p_url, bool p_verify_tls, Ref<X509Ce requested_url = p_url; requested_host = host; - verify_tls = p_verify_tls; - tls_cert = p_cert; + + if (p_options.is_valid()) { + tls_options = p_options; + } else { + tls_options = TLSOptions::client(); + } + tcp.instantiate(); resolver.start(host, port); diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h index fc81d39a37..c06e768b6d 100644 --- a/modules/websocket/wsl_peer.h +++ b/modules/websocket/wsl_peer.h @@ -102,8 +102,7 @@ private: // WebSocket configuration. bool use_tls = true; - bool verify_tls = true; - Ref<X509Certificate> tls_cert; + Ref<TLSOptions> tls_options; // Packet buffers. Vector<uint8_t> packet_buffer; @@ -132,7 +131,7 @@ public: // WebSocketPeer virtual Error send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) override; - virtual Error connect_to_url(const String &p_url, bool p_verify_tls = true, Ref<X509Certificate> p_cert = Ref<X509Certificate>()) override; + virtual Error connect_to_url(const String &p_url, Ref<TLSOptions> p_options = Ref<TLSOptions>()) override; virtual Error accept_stream(Ref<StreamPeer> p_stream) override; virtual void close(int p_code = 1000, String p_reason = "") override; virtual void poll() override; diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml index ba1750386f..0c18acbcb1 100644 --- a/modules/webxr/doc_classes/WebXRInterface.xml +++ b/modules/webxr/doc_classes/WebXRInterface.xml @@ -90,7 +90,7 @@ You can use both methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices. </description> <tutorials> - <link title="How to make a VR game for WebXR with Godot">https://www.snopekgames.com/blog/2020/how-make-vr-game-webxr-godot</link> + <link title="How to make a VR game for WebXR with Godot 4">https://www.snopekgames.com/tutorial/2023/how-make-vr-game-webxr-godot-4</link> </tutorials> <methods> <method name="get_input_source_target_ray_mode" qualifiers="const"> diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index 277195a054..17903b3965 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -80,7 +80,20 @@ void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicod ev.instantiate(); Key physical_keycode = godot_code_from_android_code(p_physical_keycode); - Key keycode = fix_keycode(unicode, physical_keycode); + Key keycode = physical_keycode; + if (unicode == '\b') { // 0x08 + keycode = Key::BACKSPACE; + } else if (unicode == '\t') { // 0x09 + keycode = Key::TAB; + } else if (unicode == '\n') { // 0x0A + keycode = Key::ENTER; + } else if (unicode == 0x1B) { + keycode = Key::ESCAPE; + } else if (unicode == 0x1F) { + keycode = Key::KEY_DELETE; + } else { + keycode = fix_keycode(unicode, physical_keycode); + } switch (physical_keycode) { case Key::SHIFT: { diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index 27cb84fb9d..9dad0c9357 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -44,7 +44,7 @@ void AudioDriverOpenSL::_buffer_callback( if (pause) { mix = false; } else { - mix = mutex.try_lock() == OK; + mix = mutex.try_lock(); } if (mix) { diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 587caf81bf..9ebb8aa102 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1076,11 +1076,6 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p Vector<int> feature_versions; if (xr_mode_index == XR_MODE_OPENXR) { - // Set degrees of freedom - feature_names.push_back("android.hardware.vr.headtracking"); - feature_required_list.push_back(true); - feature_versions.push_back(1); - // Check for hand tracking if (hand_tracking_index > XR_HAND_TRACKING_NONE) { feature_names.push_back("oculus.software.handtracking"); @@ -1716,6 +1711,7 @@ Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_enable void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { r_features->push_back("etc2"); + r_features->push_back("astc"); Vector<ABI> abis = get_enabled_abis(p_preset); for (int i = 0; i < abis.size(); ++i) { diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index e450f3edb3..4fdcca68e9 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -259,8 +259,6 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); bool uses_xr = xr_mode_index == XR_MODE_OPENXR; if (uses_xr) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"true\" android:version=\"1\" />\n"; - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required if (hand_tracking_index == XR_HAND_TRACKING_OPTIONAL) { manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; @@ -279,8 +277,6 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { } String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { - int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); - bool uses_xr = xr_mode_index == XR_MODE_OPENXR; String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); String manifest_activity_text = vformat( " <activity android:name=\"com.godot.game.GodotApp\" " @@ -291,11 +287,6 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { bool_to_string(p_preset->get("package/exclude_from_recents")), orientation, bool_to_string(bool(GLOBAL_GET("display/window/size/resizable")))); - if (uses_xr) { - manifest_activity_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"true\" />\n"; - } else { - manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; - } manifest_activity_text += " </activity>\n"; return manifest_activity_text; } @@ -335,8 +326,6 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_ hand_tracking_frequency); manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.version\" android:value=\"V2.0\" />\n"; } - } else { - manifest_application_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.supportedDevices\" />\n"; } manifest_application_text += _get_activity_tag(p_preset); manifest_application_text += " </application>\n"; diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 8c8608cbbb..1969f9c814 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -48,12 +48,6 @@ android:name="xr_hand_tracking_version_name" android:value="xr_hand_tracking_version_value"/> - <!-- Supported Meta devices --> - <!-- This is removed by the exporter if the xr mode is not VR. --> - <meta-data - android:name="com.oculus.supportedDevices" - android:value="all" /> - <activity android:name=".GodotApp" android:label="@string/godot_project_name_string" @@ -66,9 +60,6 @@ android:resizeableActivity="false" tools:ignore="UnusedAttribute" > - <!-- Focus awareness metadata is removed at export time if the xr mode is not VR. --> - <meta-data android:name="com.oculus.vr.focusaware" android:value="true" /> - <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt index f848089aa8..71385315ae 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt @@ -31,12 +31,11 @@ package org.godotengine.editor import android.Manifest +import android.app.ActivityManager +import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.os.Build -import android.os.Bundle -import android.os.Debug -import android.os.Environment +import android.os.* import android.util.Log import android.widget.Toast import androidx.window.layout.WindowMetricsCalculator @@ -64,11 +63,18 @@ open class GodotEditor : FullScreenGodotApp() { private const val COMMAND_LINE_PARAMS = "command_line_params" + private const val EDITOR_ID = 777 private const val EDITOR_ARG = "--editor" private const val EDITOR_ARG_SHORT = "-e" + private const val EDITOR_PROCESS_NAME_SUFFIX = ":GodotEditor" + private const val GAME_ID = 667 + private const val GAME_PROCESS_NAME_SUFFIX = ":GodotGame" + + private const val PROJECT_MANAGER_ID = 555 private const val PROJECT_MANAGER_ARG = "--project-manager" private const val PROJECT_MANAGER_ARG_SHORT = "-p" + private const val PROJECT_MANAGER_PROCESS_NAME_SUFFIX = ":GodotProjectManager" } private val commandLineParams = ArrayList<String>() @@ -102,9 +108,10 @@ open class GodotEditor : FullScreenGodotApp() { override fun getCommandLine() = commandLineParams - override fun onNewGodotInstanceRequested(args: Array<String>) { + override fun onNewGodotInstanceRequested(args: Array<String>): Int { // Parse the arguments to figure out which activity to start. var targetClass: Class<*> = GodotGame::class.java + var instanceId = GAME_ID // Whether we should launch the new godot instance in an adjacent window // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT @@ -115,12 +122,14 @@ open class GodotEditor : FullScreenGodotApp() { if (EDITOR_ARG == arg || EDITOR_ARG_SHORT == arg) { targetClass = GodotEditor::class.java launchAdjacent = false + instanceId = EDITOR_ID break } if (PROJECT_MANAGER_ARG == arg || PROJECT_MANAGER_ARG_SHORT == arg) { targetClass = GodotProjectManager::class.java launchAdjacent = false + instanceId = PROJECT_MANAGER_ID break } } @@ -139,6 +148,37 @@ open class GodotEditor : FullScreenGodotApp() { Log.d(TAG, "Starting $targetClass") startActivity(newInstance) } + return instanceId + } + + override fun onGodotForceQuit(godotInstanceId: Int): Boolean { + val processNameSuffix = when (godotInstanceId) { + GAME_ID -> { + GAME_PROCESS_NAME_SUFFIX + } + EDITOR_ID -> { + EDITOR_PROCESS_NAME_SUFFIX + } + PROJECT_MANAGER_ID -> { + PROJECT_MANAGER_PROCESS_NAME_SUFFIX + } + else -> "" + } + if (processNameSuffix.isBlank()) { + return false + } + + val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val runningProcesses = activityManager.runningAppProcesses + for (runningProcess in runningProcesses) { + if (runningProcess.processName.endsWith(processNameSuffix)) { + Log.v(TAG, "Killing Godot process ${runningProcess.processName}") + Process.killProcess(runningProcess.pid) + return true + } + } + + return false } // Get the screen's density scale diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java index 863e67f1e9..65032d6a68 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java +++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java @@ -74,28 +74,36 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God public void onDestroy() { Log.v(TAG, "Destroying Godot app..."); super.onDestroy(); - onGodotForceQuit(godotFragment); + terminateGodotInstance(godotFragment); } @Override public final void onGodotForceQuit(Godot instance) { + runOnUiThread(() -> { + terminateGodotInstance(instance); + }); + } + + private void terminateGodotInstance(Godot instance) { if (instance == godotFragment) { Log.v(TAG, "Force quitting Godot instance"); - ProcessPhoenix.forceQuit(this); + ProcessPhoenix.forceQuit(FullScreenGodotApp.this); } } @Override public final void onGodotRestartRequested(Godot instance) { - if (instance == godotFragment) { - // It's very hard to properly de-initialize Godot on Android to restart the game - // from scratch. Therefore, we need to kill the whole app process and relaunch it. - // - // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including - // releasing and reloading native libs or resetting their state somehow and clearing statics). - Log.v(TAG, "Restarting Godot instance..."); - ProcessPhoenix.triggerRebirth(this); - } + runOnUiThread(() -> { + if (instance == godotFragment) { + // It's very hard to properly de-initialize Godot on Android to restart the game + // from scratch. Therefore, we need to kill the whole app process and relaunch it. + // + // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including + // releasing and reloading native libs or resetting their state somehow and clearing statics). + Log.v(TAG, "Restarting Godot instance..."); + ProcessPhoenix.triggerRebirth(FullScreenGodotApp.this); + } + }); } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 905db13c85..50263bc392 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -348,11 +348,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } public void restart() { - runOnUiThread(() -> { - if (godotHost != null) { - godotHost.onGodotRestartRequested(this); - } - }); + if (godotHost != null) { + godotHost.onGodotRestartRequested(this); + } } public void alert(final String message, final String title) { @@ -889,11 +887,20 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC private void forceQuit() { // TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each // native Godot components that is started in Godot#onVideoInit. - runOnUiThread(() -> { - if (godotHost != null) { - godotHost.onGodotForceQuit(this); - } - }); + forceQuit(0); + } + + @Keep + private boolean forceQuit(int instanceId) { + if (godotHost == null) { + return false; + } + if (instanceId == 0) { + godotHost.onGodotForceQuit(this); + return true; + } else { + return godotHost.onGodotForceQuit(instanceId); + } } private boolean obbIsCorrupted(String f, String main_pack_md5) { @@ -1052,11 +1059,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } @Keep - private void createNewGodotInstance(String[] args) { - runOnUiThread(() -> { - if (godotHost != null) { - godotHost.onNewGodotInstanceRequested(args); - } - }); + private int createNewGodotInstance(String[] args) { + if (godotHost != null) { + return godotHost.onNewGodotInstanceRequested(args); + } + return 0; } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index 256d04e3a5..7700b9b628 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -55,21 +55,35 @@ public interface GodotHost { default void onGodotMainLoopStarted() {} /** - * Invoked on the UI thread as the last step of the Godot instance clean up phase. + * Invoked on the render thread to terminate the given Godot instance. */ default void onGodotForceQuit(Godot instance) {} /** - * Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host + * Invoked on the render thread to terminate the Godot instance with the given id. + * @param godotInstanceId id of the Godot instance to terminate. See {@code onNewGodotInstanceRequested} + * + * @return true if successful, false otherwise. + */ + default boolean onGodotForceQuit(int godotInstanceId) { + return false; + } + + /** + * Invoked on the render thread when the Godot instance wants to be restarted. It's up to the host * to perform the appropriate action(s). */ default void onGodotRestartRequested(Godot instance) {} /** - * Invoked on the UI thread when a new Godot instance is requested. It's up to the host to + * Invoked on the render thread when a new Godot instance is requested. It's up to the host to * perform the appropriate action(s). * * @param args Arguments used to initialize the new instance. + * + * @return the id of the new instance. See {@code onGodotForceQuit} */ - default void onNewGodotInstanceRequested(String[] args) {} + default int onNewGodotInstanceRequested(String[] args) { + return 0; + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt b/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt index 9649b0aecc..b9b7ebac6e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt @@ -64,7 +64,7 @@ internal class AssetsDirectoryAccess(context: Context) : DirectoryAccessHandler. override fun hasDirId(dirId: Int) = dirs.indexOfKey(dirId) >= 0 override fun dirOpen(path: String): Int { - val assetsPath = getAssetsPath(path) ?: return INVALID_DIR_ID + val assetsPath = getAssetsPath(path) try { val files = assetManager.list(assetsPath) ?: return INVALID_DIR_ID // Empty directories don't get added to the 'assets' directory, so @@ -99,7 +99,7 @@ internal class AssetsDirectoryAccess(context: Context) : DirectoryAccessHandler. } override fun fileExists(path: String): Boolean { - val assetsPath = getAssetsPath(path) ?: return false + val assetsPath = getAssetsPath(path) try { val files = assetManager.list(assetsPath) ?: return false // Empty directories don't get added to the 'assets' directory, so diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 03548d11f6..9d9d087896 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -60,7 +60,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ // get some Godot method pointers... _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()Z"); _restart = p_env->GetMethodID(godot_class, "restart", "()V"); - _finish = p_env->GetMethodID(godot_class, "forceQuit", "()V"); + _finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z"); _set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V"); _alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); _get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I"); @@ -77,7 +77,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;"); _on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V"); _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V"); - _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)V"); + _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I"); _get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;"); // get some Activity method pointers... @@ -179,14 +179,15 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) { } } -void GodotJavaWrapper::force_quit(JNIEnv *p_env) { +bool GodotJavaWrapper::force_quit(JNIEnv *p_env, int p_instance_id) { if (_finish) { if (p_env == nullptr) { p_env = get_jni_env(); } - ERR_FAIL_NULL(p_env); - p_env->CallVoidMethod(godot_instance, _finish); + ERR_FAIL_NULL_V(p_env, false); + return p_env->CallBooleanMethod(godot_instance, _finish, p_instance_id); } + return false; } void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) { @@ -345,14 +346,16 @@ void GodotJavaWrapper::vibrate(int p_duration_ms) { } } -void GodotJavaWrapper::create_new_godot_instance(List<String> args) { +int GodotJavaWrapper::create_new_godot_instance(List<String> args) { if (_create_new_godot_instance) { JNIEnv *env = get_jni_env(); - ERR_FAIL_NULL(env); + ERR_FAIL_NULL_V(env, 0); jobjectArray jargs = env->NewObjectArray(args.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); for (int i = 0; i < args.size(); i++) { env->SetObjectArrayElement(jargs, i, env->NewStringUTF(args[i].utf8().get_data())); } - env->CallVoidMethod(godot_instance, _create_new_godot_instance, jargs); + return env->CallIntMethod(godot_instance, _create_new_godot_instance, jargs); + } else { + return 0; } } diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 5dad2a3eb9..1bd79584d8 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -85,7 +85,7 @@ public: void on_godot_setup_completed(JNIEnv *p_env = nullptr); void on_godot_main_loop_started(JNIEnv *p_env = nullptr); void restart(JNIEnv *p_env = nullptr); - void force_quit(JNIEnv *p_env = nullptr); + bool force_quit(JNIEnv *p_env = nullptr, int p_instance_id = 0); void set_keep_screen_on(bool p_enabled); void alert(const String &p_message, const String &p_title); int get_gles_version_code(); @@ -103,7 +103,7 @@ public: bool is_activity_resumed(); void vibrate(int p_duration_ms); String get_input_fallback_mapping(); - void create_new_godot_instance(List<String> args); + int create_new_godot_instance(List<String> args); }; #endif // JAVA_GODOT_WRAPPER_H diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 29b91983bf..725fea8d54 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -739,9 +739,19 @@ Error OS_Android::create_process(const String &p_path, const List<String> &p_arg } Error OS_Android::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) { - godot_java->create_new_godot_instance(p_arguments); + int instance_id = godot_java->create_new_godot_instance(p_arguments); + if (r_child_id) { + *r_child_id = instance_id; + } return OK; } +Error OS_Android::kill(const ProcessID &p_pid) { + if (godot_java->force_quit(nullptr, p_pid)) { + return OK; + } + return OS_Unix::kill(p_pid); +} + OS_Android::~OS_Android() { } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 9b43797580..53910b1498 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -158,6 +158,7 @@ public: virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override; virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override; virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override; + virtual Error kill(const ProcessID &p_pid) override; virtual bool _check_internal_feature_support(const String &p_feature) override; OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion); diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 87b599bc81..c6f7ec09b1 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -43,6 +43,7 @@ void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { // Vulkan and OpenGL ES 3.0 both mandate ETC2 support. r_features->push_back("etc2"); + r_features->push_back("astc"); Vector<String> architectures = _get_preset_architectures(p_preset); for (int i = 0; i < architectures.size(); ++i) { diff --git a/platform/linuxbsd/dbus-so_wrap.c b/platform/linuxbsd/dbus-so_wrap.c index d03a9166a3..4aec9dc48f 100644 --- a/platform/linuxbsd/dbus-so_wrap.c +++ b/platform/linuxbsd/dbus-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:26:35 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/dbus/dbus.h --sys-include "thirdparty/linuxbsd_headers/dbus/dbus.h" --soname libdbus-1.so.3 --init-name dbus --output-header ./platform/linuxbsd/dbus-so_wrap.h --output-implementation ./platform/linuxbsd/dbus-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:26:35 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/dbus/dbus.h --sys-include "thirdparty/linuxbsd_headers/dbus/dbus.h" --soname libdbus-1.so.3 --init-name dbus --output-header ./platform/linuxbsd/dbus-so_wrap.h --output-implementation ./platform/linuxbsd/dbus-so_wrap.c // #include <stdint.h> diff --git a/platform/linuxbsd/dbus-so_wrap.h b/platform/linuxbsd/dbus-so_wrap.h index 6981912a12..2c63757932 100644 --- a/platform/linuxbsd/dbus-so_wrap.h +++ b/platform/linuxbsd/dbus-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_DBUS // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:26:35 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/dbus/dbus.h --sys-include "thirdparty/linuxbsd_headers/dbus/dbus.h" --soname libdbus-1.so.3 --init-name dbus --output-header ./platform/linuxbsd/dbus-so_wrap.h --output-implementation ./platform/linuxbsd/dbus-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:26:35 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/dbus/dbus.h --sys-include "thirdparty/linuxbsd_headers/dbus/dbus.h" --soname libdbus-1.so.3 --init-name dbus --output-header ./platform/linuxbsd/dbus-so_wrap.h --output-implementation ./platform/linuxbsd/dbus-so_wrap.c // #include <stdint.h> diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index c900cad007..2528bb2b99 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -378,8 +378,8 @@ Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, if (port.is_empty()) { port = "22"; } - Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" "); - Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" "); + Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ", false); + Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ", false); const String basepath = dest.path_join("tmp_linuxbsd_export"); diff --git a/platform/linuxbsd/fontconfig-so_wrap.c b/platform/linuxbsd/fontconfig-so_wrap.c index 6278522b69..86aacbc647 100644 --- a/platform/linuxbsd/fontconfig-so_wrap.c +++ b/platform/linuxbsd/fontconfig-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:15:54 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/fontconfig/fontconfig.h --sys-include "thirdparty/linuxbsd_headers/fontconfig/fontconfig.h" --soname libfontconfig.so.1 --init-name fontconfig --omit-prefix FcCharSetFirst --omit-prefix FcCharSetNext --output-header ./platform/linuxbsd/fontconfig-so_wrap.h --output-implementation ./platform/linuxbsd/fontconfig-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:15:54 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/fontconfig/fontconfig.h --sys-include "thirdparty/linuxbsd_headers/fontconfig/fontconfig.h" --soname libfontconfig.so.1 --init-name fontconfig --omit-prefix FcCharSetFirst --omit-prefix FcCharSetNext --output-header ./platform/linuxbsd/fontconfig-so_wrap.h --output-implementation ./platform/linuxbsd/fontconfig-so_wrap.c // #include <stdint.h> diff --git a/platform/linuxbsd/fontconfig-so_wrap.h b/platform/linuxbsd/fontconfig-so_wrap.h index 021b2148ae..956c094711 100644 --- a/platform/linuxbsd/fontconfig-so_wrap.h +++ b/platform/linuxbsd/fontconfig-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_FONTCONFIG // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:15:54 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/fontconfig/fontconfig.h --sys-include "thirdparty/linuxbsd_headers/fontconfig/fontconfig.h" --soname libfontconfig.so.1 --init-name fontconfig --omit-prefix FcCharSetFirst --omit-prefix FcCharSetNext --output-header ./platform/linuxbsd/fontconfig-so_wrap.h --output-implementation ./platform/linuxbsd/fontconfig-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:15:54 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/fontconfig/fontconfig.h --sys-include "thirdparty/linuxbsd_headers/fontconfig/fontconfig.h" --soname libfontconfig.so.1 --init-name fontconfig --omit-prefix FcCharSetFirst --omit-prefix FcCharSetNext --output-header ./platform/linuxbsd/fontconfig-so_wrap.h --output-implementation ./platform/linuxbsd/fontconfig-so_wrap.c // #include <stdint.h> diff --git a/platform/linuxbsd/libudev-so_wrap.c b/platform/linuxbsd/libudev-so_wrap.c index 9593b5fb0f..5455c1ab4e 100644 --- a/platform/linuxbsd/libudev-so_wrap.c +++ b/platform/linuxbsd/libudev-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:23:01 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/udev/libudev.h --sys-include "thirdparty/linuxbsd_headers/udev/libudev.h" --soname libudev.so.1 --init-name libudev --omit-prefix gnu_ --output-header ./platform/linuxbsd/libudev-so_wrap.h --output-implementation ./platform/linuxbsd/libudev-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:23:01 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/udev/libudev.h --sys-include "thirdparty/linuxbsd_headers/udev/libudev.h" --soname libudev.so.1 --init-name libudev --omit-prefix gnu_ --output-header ./platform/linuxbsd/libudev-so_wrap.h --output-implementation ./platform/linuxbsd/libudev-so_wrap.c // #include <stdint.h> diff --git a/platform/linuxbsd/libudev-so_wrap.h b/platform/linuxbsd/libudev-so_wrap.h index 9b9627877a..174a3663d4 100644 --- a/platform/linuxbsd/libudev-so_wrap.h +++ b/platform/linuxbsd/libudev-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_LIBUDEV // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:23:01 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/udev/libudev.h --sys-include "thirdparty/linuxbsd_headers/udev/libudev.h" --soname libudev.so.1 --init-name libudev --omit-prefix gnu_ --output-header ./platform/linuxbsd/libudev-so_wrap.h --output-implementation ./platform/linuxbsd/libudev-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:23:01 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/udev/libudev.h --sys-include "thirdparty/linuxbsd_headers/udev/libudev.h" --soname libudev.so.1 --init-name libudev --omit-prefix gnu_ --output-header ./platform/linuxbsd/libudev-so_wrap.h --output-implementation ./platform/linuxbsd/libudev-so_wrap.c // #include <stdint.h> diff --git a/platform/linuxbsd/speechd-so_wrap.c b/platform/linuxbsd/speechd-so_wrap.c index c860c686f0..1dc5f08c10 100644 --- a/platform/linuxbsd/speechd-so_wrap.c +++ b/platform/linuxbsd/speechd-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:07:46 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/speechd/libspeechd.h --sys-include "thirdparty/linuxbsd_headers/speechd/libspeechd.h" --soname libspeechd.so.2 --init-name speechd --omit-prefix spd_get_client_list --output-header ./platform/linuxbsd/speechd-so_wrap.h --output-implementation ./platform/linuxbsd/speechd-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:07:46 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/speechd/libspeechd.h --sys-include "thirdparty/linuxbsd_headers/speechd/libspeechd.h" --soname libspeechd.so.2 --init-name speechd --omit-prefix spd_get_client_list --output-header ./platform/linuxbsd/speechd-so_wrap.h --output-implementation ./platform/linuxbsd/speechd-so_wrap.c // #include <stdint.h> diff --git a/platform/linuxbsd/speechd-so_wrap.h b/platform/linuxbsd/speechd-so_wrap.h index 8e0762041a..2967cfa929 100644 --- a/platform/linuxbsd/speechd-so_wrap.h +++ b/platform/linuxbsd/speechd-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_SPEECHD // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-12 10:07:46 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/speechd/libspeechd.h --sys-include "thirdparty/linuxbsd_headers/speechd/libspeechd.h" --soname libspeechd.so.2 --init-name speechd --omit-prefix spd_get_client_list --output-header ./platform/linuxbsd/speechd-so_wrap.h --output-implementation ./platform/linuxbsd/speechd-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-12 10:07:46 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/speechd/libspeechd.h --sys-include "thirdparty/linuxbsd_headers/speechd/libspeechd.h" --soname libspeechd.so.2 --init-name speechd --omit-prefix spd_get_client_list --output-header ./platform/linuxbsd/speechd-so_wrap.h --output-implementation ./platform/linuxbsd/speechd-so_wrap.c // #include <stdint.h> diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index f1159c3981..c09da2f7b3 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1324,14 +1324,20 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) { } #endif - XDestroyWindow(x11_display, wd.x11_xim_window); - - XUnmapWindow(x11_display, wd.x11_window); - XDestroyWindow(x11_display, wd.x11_window); if (wd.xic) { XDestroyIC(wd.xic); wd.xic = nullptr; } + XDestroyWindow(x11_display, wd.x11_xim_window); + if (xkb_loaded) { + if (wd.xkb_state) { + xkb_compose_state_unref(wd.xkb_state); + wd.xkb_state = nullptr; + } + } + + XUnmapWindow(x11_display, wd.x11_window); + XDestroyWindow(x11_display, wd.x11_window); windows.erase(p_id); } @@ -2515,7 +2521,7 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win XSync(x11_display, False); XGetWindowAttributes(x11_display, wd.x11_xim_window, &xwa); if (xwa.map_state == IsViewable) { - XSetInputFocus(x11_display, wd.x11_xim_window, RevertToPointerRoot, CurrentTime); + XSetInputFocus(x11_display, wd.x11_xim_window, RevertToParent, CurrentTime); } XSetICFocus(wd.xic); } else { @@ -2933,9 +2939,10 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, xkeyevent_no_mod.state &= ~ShiftMask; xkeyevent_no_mod.state &= ~ControlMask; XLookupString(xkeyevent, str, 255, &keysym_unicode, nullptr); + XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_keycode, nullptr); String keysym; - if (xkb_keysym_to_utf32 && xkb_keysym_to_upper) { + if (xkb_loaded) { KeySym keysym_unicode_nm = 0; // keysym used to find unicode XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_unicode_nm, nullptr); keysym = String::chr(xkb_keysym_to_utf32(xkb_keysym_to_upper(keysym_unicode_nm))); @@ -3028,6 +3035,64 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, } } while (status == XBufferOverflow); #endif + } else if (xkeyevent->type == KeyPress && wd.xkb_state && xkb_loaded) { + xkb_compose_feed_result res = xkb_compose_state_feed(wd.xkb_state, keysym_unicode); + if (res == XKB_COMPOSE_FEED_ACCEPTED) { + if (xkb_compose_state_get_status(wd.xkb_state) == XKB_COMPOSE_COMPOSED) { + bool keypress = xkeyevent->type == KeyPress; + Key keycode = KeyMappingX11::get_keycode(keysym_keycode); + Key physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode); + + if (keycode >= Key::A + 32 && keycode <= Key::Z + 32) { + keycode -= 'a' - 'A'; + } + + char str_xkb[256] = {}; + int str_xkb_size = xkb_compose_state_get_utf8(wd.xkb_state, str_xkb, 255); + + String tmp; + tmp.parse_utf8(str_xkb, str_xkb_size); + for (int i = 0; i < tmp.length(); i++) { + Ref<InputEventKey> k; + k.instantiate(); + if (physical_keycode == Key::NONE && keycode == Key::NONE && tmp[i] == 0) { + continue; + } + + if (keycode == Key::NONE) { + keycode = (Key)physical_keycode; + } + + _get_key_modifier_state(xkeyevent->state, k); + + k->set_window_id(p_window); + k->set_pressed(keypress); + + k->set_keycode(keycode); + k->set_physical_keycode(physical_keycode); + if (!keysym.is_empty()) { + k->set_key_label(fix_key_label(keysym[0], keycode)); + } else { + k->set_key_label(keycode); + } + if (keypress) { + k->set_unicode(fix_unicode(tmp[i])); + } + + k->set_echo(false); + + if (k->get_keycode() == Key::BACKTAB) { + //make it consistent across platforms. + k->set_keycode(Key::TAB); + k->set_physical_keycode(Key::TAB); + k->set_shift_pressed(true); + } + + Input::get_singleton()->parse_input_event(k); + } + return; + } + } } /* Phase 2, obtain a Godot keycode from the keysym */ @@ -4859,7 +4924,15 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V { wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), win_rect.position.x, win_rect.position.y, win_rect.size.width > 0 ? win_rect.size.width : 1, win_rect.size.height > 0 ? win_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes); - wd.x11_xim_window = XCreateWindow(x11_display, wd.x11_window, 0, 0, 1, 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes); + + XSetWindowAttributes window_attributes_ime = {}; + window_attributes_ime.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask; + + wd.x11_xim_window = XCreateWindow(x11_display, wd.x11_window, 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWEventMask, &window_attributes_ime); + + if (dead_tbl && xkb_loaded) { + wd.xkb_state = xkb_compose_state_new(dead_tbl, XKB_COMPOSE_STATE_NO_FLAGS); + } // Enable receiving notification when the window is initialized (MapNotify) // so the focus can be set at the right time. @@ -5140,7 +5213,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode ERR_FAIL_MSG("Can't load XCursor dynamically."); } - initialize_xkbcommon(dylibloader_verbose); // Optional, used for key_label. + xkb_loaded = (initialize_xkbcommon(dylibloader_verbose) == 0); if (initialize_xext(dylibloader_verbose) != 0) { r_error = ERR_UNAVAILABLE; @@ -5167,6 +5240,23 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode ERR_FAIL_MSG("Can't load Xinput2 dynamically."); } + if (xkb_loaded) { + xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (xkb_ctx) { + const char *locale = getenv("LC_ALL"); + if (!locale || !*locale) { + locale = getenv("LC_CTYPE"); + } + if (!locale || !*locale) { + locale = getenv("LANG"); + } + if (!locale || !*locale) { + locale = "C"; + } + dead_tbl = xkb_compose_table_new_from_locale(xkb_ctx, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + } + } + Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); r_error = OK; @@ -5607,10 +5697,26 @@ DisplayServerX11::~DisplayServerX11() { XDestroyIC(wd.xic); wd.xic = nullptr; } + XDestroyWindow(x11_display, wd.x11_xim_window); + if (xkb_loaded) { + if (wd.xkb_state) { + xkb_compose_state_unref(wd.xkb_state); + wd.xkb_state = nullptr; + } + } XUnmapWindow(x11_display, wd.x11_window); XDestroyWindow(x11_display, wd.x11_window); } + if (xkb_loaded) { + if (dead_tbl) { + xkb_compose_table_unref(dead_tbl); + } + if (xkb_ctx) { + xkb_context_unref(xkb_ctx); + } + } + //destroy drivers #if defined(VULKAN_ENABLED) if (rendering_device_vulkan) { diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index bfb97ae44c..ea54b42262 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -142,6 +142,7 @@ class DisplayServerX11 : public DisplayServer { bool ime_active = false; bool ime_in_progress = false; bool ime_suppress_next_keyup = false; + xkb_compose_state *xkb_state = nullptr; Size2i min_size; Size2i max_size; @@ -185,6 +186,10 @@ class DisplayServerX11 : public DisplayServer { Point2i im_selection; String im_text; + bool xkb_loaded = false; + xkb_context *xkb_ctx = nullptr; + xkb_compose_table *dead_tbl = nullptr; + HashMap<WindowID, WindowData> windows; unsigned int last_mouse_monitor_mask = 0; diff --git a/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c index ecd2c25662..bba21b9cb7 100644 --- a/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:09:53 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/Xcursor/Xcursor.h --sys-include "thirdparty/linuxbsd_headers/X11/Xcursor/Xcursor.h" --soname libXcursor.so.1 --init-name xcursor --output-header ./platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:09:53 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/Xcursor/Xcursor.h --sys-include "thirdparty/linuxbsd_headers/X11/Xcursor/Xcursor.h" --soname libXcursor.so.1 --init-name xcursor --output-header ./platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c // // NOTE: Generated from Xcursor 1.2.0. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h index dc3684ff09..9f8d8bbca2 100644 --- a/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_XCURSOR // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:09:53 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/Xcursor/Xcursor.h --sys-include "thirdparty/linuxbsd_headers/X11/Xcursor/Xcursor.h" --soname libXcursor.so.1 --init-name xcursor --output-header ./platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:09:53 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/Xcursor/Xcursor.h --sys-include "thirdparty/linuxbsd_headers/X11/Xcursor/Xcursor.h" --soname libXcursor.so.1 --init-name xcursor --output-header ./platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xcursor-so_wrap.c // // NOTE: Generated from Xcursor 1.2.0. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c index e9af9033a3..4e3349c574 100644 --- a/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:11:29 -// flags: ../dynload-wrapper/generate-wrapper.py --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xext.h" --include ./thirdparty/linuxbsd_headers/X11/extensions/shape.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/shape.h" --soname libXext.so.6 --init-name xext --output-header ./platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:11:29 +// flags: generate-wrapper.py --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xext.h" --include ./thirdparty/linuxbsd_headers/X11/extensions/shape.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/shape.h" --soname libXext.so.6 --init-name xext --output-header ./platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c // // NOTE: Generated from Xext 1.3.5. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h index bbf23fff34..e535756d82 100644 --- a/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_XEXT // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:11:29 -// flags: ../dynload-wrapper/generate-wrapper.py --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xext.h" --include ./thirdparty/linuxbsd_headers/X11/extensions/shape.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/shape.h" --soname libXext.so.6 --init-name xext --output-header ./platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:11:29 +// flags: generate-wrapper.py --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xext.h" --include ./thirdparty/linuxbsd_headers/X11/extensions/shape.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/shape.h" --soname libXext.so.6 --init-name xext --output-header ./platform/linuxbsd/x11/dynwrappers/xext-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xext-so_wrap.c // // NOTE: Generated from Xext 1.3.5. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c index ab53b232d6..850ed1fc6b 100644 --- a/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:11:35 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xinerama.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xinerama.h" --soname libXinerama.so.1 --init-name xinerama --output-header ./platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:11:35 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xinerama.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xinerama.h" --soname libXinerama.so.1 --init-name xinerama --output-header ./platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c // // NOTE: Generated from Xinerama 1.1.4. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h index 48d5cc44b7..e3cedfc8ad 100644 --- a/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_XINERAMA // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:11:35 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xinerama.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xinerama.h" --soname libXinerama.so.1 --init-name xinerama --output-header ./platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:11:35 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xinerama.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xinerama.h" --soname libXinerama.so.1 --init-name xinerama --output-header ./platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xinerama-so_wrap.c // // NOTE: Generated from Xinerama 1.1.4. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c index d37f2b4d94..fc08b97e3c 100644 --- a/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:12:16 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/XInput2.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/XInput2.h" --soname libXi.so.6 --init-name xinput2 --output-header ./platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:12:16 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/XInput2.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/XInput2.h" --soname libXi.so.6 --init-name xinput2 --output-header ./platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c // // NOTE: Generated from Xi 1.7.10. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h index e39661ffb9..571072c3cd 100644 --- a/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_XINPUT2 // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:12:16 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/XInput2.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/XInput2.h" --soname libXi.so.6 --init-name xinput2 --output-header ./platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:12:16 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/XInput2.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/XInput2.h" --soname libXi.so.6 --init-name xinput2 --output-header ./platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xinput2-so_wrap.c // // NOTE: Generated from Xi 1.7.10. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.c index a746b6c526..d2838569b0 100644 --- a/platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:13:26 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/Xlib.h --include ./thirdparty/linuxbsd_headers/X11/Xutil.h --include ./thirdparty/linuxbsd_headers/X11/XKBlib.h --sys-include "thirdparty/linuxbsd_headers/X11/Xlib.h" --sys-include "thirdparty/linuxbsd_headers/X11/Xutil.h" --sys-include "thirdparty/linuxbsd_headers/X11/XKBlib.h" --soname libX11.so.6 --init-name xlib --omit-prefix XkbGetDeviceIndicatorState --omit-prefix XkbAddSymInterpret --output-header ./platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.c~ +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:13:26 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/Xlib.h --include ./thirdparty/linuxbsd_headers/X11/Xutil.h --include ./thirdparty/linuxbsd_headers/X11/XKBlib.h --sys-include "thirdparty/linuxbsd_headers/X11/Xlib.h" --sys-include "thirdparty/linuxbsd_headers/X11/Xutil.h" --sys-include "thirdparty/linuxbsd_headers/X11/XKBlib.h" --soname libX11.so.6 --init-name xlib --omit-prefix XkbGetDeviceIndicatorState --omit-prefix XkbAddSymInterpret --output-header ./platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.c~ // // NOTE: Generated from Xlib 1.6.9. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.h index b40a25f601..5bad21002d 100644 --- a/platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_XLIB // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:13:26 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/Xlib.h --include ./thirdparty/linuxbsd_headers/X11/Xutil.h --include ./thirdparty/linuxbsd_headers/X11/XKBlib.h --sys-include "thirdparty/linuxbsd_headers/X11/Xlib.h" --sys-include "thirdparty/linuxbsd_headers/X11/Xutil.h" --sys-include "thirdparty/linuxbsd_headers/X11/XKBlib.h" --soname libX11.so.6 --init-name xlib --omit-prefix XkbGetDeviceIndicatorState --omit-prefix XkbAddSymInterpret --output-header ./platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.c~ +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:13:26 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/Xlib.h --include ./thirdparty/linuxbsd_headers/X11/Xutil.h --include ./thirdparty/linuxbsd_headers/X11/XKBlib.h --sys-include "thirdparty/linuxbsd_headers/X11/Xlib.h" --sys-include "thirdparty/linuxbsd_headers/X11/Xutil.h" --sys-include "thirdparty/linuxbsd_headers/X11/XKBlib.h" --soname libX11.so.6 --init-name xlib --omit-prefix XkbGetDeviceIndicatorState --omit-prefix XkbAddSymInterpret --output-header ./platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xlib-so_wrap.c~ // // NOTE: Generated from Xlib 1.6.9. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c index 21e30a03de..05f98d2506 100644 --- a/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:13:54 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xrandr.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xrandr.h" --soname libXrandr.so.2 --init-name xrandr --output-header ./platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:13:54 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xrandr.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xrandr.h" --soname libXrandr.so.2 --init-name xrandr --output-header ./platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c // // NOTE: Generated from Xrandr 1.5.2. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h index f301234b53..db5d44203d 100644 --- a/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_XRANDR // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:13:54 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xrandr.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xrandr.h" --soname libXrandr.so.2 --init-name xrandr --output-header ./platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:13:54 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xrandr.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xrandr.h" --soname libXrandr.so.2 --init-name xrandr --output-header ./platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xrandr-so_wrap.c // // NOTE: Generated from Xrandr 1.5.2. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c b/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c index 5c720bee21..7421f94601 100644 --- a/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c +++ b/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:14:14 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xrender.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xrender.h" --soname libXrender.so.1 --init-name xrender --output-header ./platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c~ +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:14:14 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xrender.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xrender.h" --soname libXrender.so.1 --init-name xrender --output-header ./platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c~ // // NOTE: Generated from Xrender 0.9.10. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h b/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h index 29a8430476..5d3f695959 100644 --- a/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h +++ b/platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_XRENDER // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:14:14 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xrender.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xrender.h" --soname libXrender.so.1 --init-name xrender --output-header ./platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c~ +// generated by generate-wrapper.py 0.3 on 2023-01-23 15:14:14 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/X11/extensions/Xrender.h --sys-include "thirdparty/linuxbsd_headers/X11/extensions/Xrender.h" --soname libXrender.so.1 --init-name xrender --output-header ./platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xrender-so_wrap.c~ // // NOTE: Generated from Xrender 0.9.10. // This has been handpatched to workaround some issues with the generator that diff --git a/platform/linuxbsd/x11/key_mapping_x11.cpp b/platform/linuxbsd/x11/key_mapping_x11.cpp index 506372292d..e5eba6ccad 100644 --- a/platform/linuxbsd/x11/key_mapping_x11.cpp +++ b/platform/linuxbsd/x11/key_mapping_x11.cpp @@ -30,20 +30,6 @@ #include "key_mapping_x11.h" -#include "core/templates/hash_map.h" - -struct HashMapHasherKeys { - static _FORCE_INLINE_ uint32_t hash(const Key p_key) { return hash_fmix32(static_cast<uint32_t>(p_key)); } - static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); } - static _FORCE_INLINE_ uint32_t hash(const unsigned p_key) { return hash_fmix32(p_key); } - static _FORCE_INLINE_ uint32_t hash(const KeySym p_key) { return hash_fmix32(p_key); } -}; - -HashMap<KeySym, Key, HashMapHasherKeys> xkeysym_map; -HashMap<unsigned int, Key, HashMapHasherKeys> scancode_map; -HashMap<Key, unsigned int, HashMapHasherKeys> scancode_map_inv; -HashMap<KeySym, char32_t, HashMapHasherKeys> xkeysym_unicode_map; - void KeyMappingX11::initialize() { // X11 Keysym to Godot Key map. diff --git a/platform/linuxbsd/x11/key_mapping_x11.h b/platform/linuxbsd/x11/key_mapping_x11.h index d4278a563c..48beefff4c 100644 --- a/platform/linuxbsd/x11/key_mapping_x11.h +++ b/platform/linuxbsd/x11/key_mapping_x11.h @@ -39,8 +39,21 @@ #include <X11/keysymdef.h> #include "core/os/keyboard.h" +#include "core/templates/hash_map.h" class KeyMappingX11 { + struct HashMapHasherKeys { + static _FORCE_INLINE_ uint32_t hash(const Key p_key) { return hash_fmix32(static_cast<uint32_t>(p_key)); } + static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); } + static _FORCE_INLINE_ uint32_t hash(const unsigned p_key) { return hash_fmix32(p_key); } + static _FORCE_INLINE_ uint32_t hash(const KeySym p_key) { return hash_fmix32(p_key); } + }; + + static inline HashMap<KeySym, Key, HashMapHasherKeys> xkeysym_map; + static inline HashMap<unsigned int, Key, HashMapHasherKeys> scancode_map; + static inline HashMap<Key, unsigned int, HashMapHasherKeys> scancode_map_inv; + static inline HashMap<KeySym, char32_t, HashMapHasherKeys> xkeysym_unicode_map; + KeyMappingX11() {} public: diff --git a/platform/linuxbsd/xkbcommon-so_wrap.c b/platform/linuxbsd/xkbcommon-so_wrap.c index 601d4c5052..3382e2e553 100644 --- a/platform/linuxbsd/xkbcommon-so_wrap.c +++ b/platform/linuxbsd/xkbcommon-so_wrap.c @@ -1,7 +1,7 @@ // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:14:21 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h --sys-include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h" --soname libxkbcommon.so.0 --init-name xkbcommon --output-header ./platform/linuxbsd/x11/dynwrappers/xkbcommon-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xkbcommon-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-30 10:40:26 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h --include ./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-compose.h --include ./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-keysyms.h --sys-include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h" --sys-include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-compose.h" --sys-include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-keysyms.h" --soname libxkbcommon.so.0 --init-name xkbcommon --output-header ./platform/linuxbsd/xkbcommon-so_wrap.h --output-implementation ./platform/linuxbsd/xkbcommon-so_wrap.c // #include <stdint.h> @@ -81,7 +81,23 @@ #define xkb_state_layout_index_is_active xkb_state_layout_index_is_active_dylibloader_orig_xkbcommon #define xkb_state_led_name_is_active xkb_state_led_name_is_active_dylibloader_orig_xkbcommon #define xkb_state_led_index_is_active xkb_state_led_index_is_active_dylibloader_orig_xkbcommon +#define xkb_compose_table_new_from_locale xkb_compose_table_new_from_locale_dylibloader_orig_xkbcommon +#define xkb_compose_table_new_from_file xkb_compose_table_new_from_file_dylibloader_orig_xkbcommon +#define xkb_compose_table_new_from_buffer xkb_compose_table_new_from_buffer_dylibloader_orig_xkbcommon +#define xkb_compose_table_ref xkb_compose_table_ref_dylibloader_orig_xkbcommon +#define xkb_compose_table_unref xkb_compose_table_unref_dylibloader_orig_xkbcommon +#define xkb_compose_state_new xkb_compose_state_new_dylibloader_orig_xkbcommon +#define xkb_compose_state_ref xkb_compose_state_ref_dylibloader_orig_xkbcommon +#define xkb_compose_state_unref xkb_compose_state_unref_dylibloader_orig_xkbcommon +#define xkb_compose_state_get_compose_table xkb_compose_state_get_compose_table_dylibloader_orig_xkbcommon +#define xkb_compose_state_feed xkb_compose_state_feed_dylibloader_orig_xkbcommon +#define xkb_compose_state_reset xkb_compose_state_reset_dylibloader_orig_xkbcommon +#define xkb_compose_state_get_status xkb_compose_state_get_status_dylibloader_orig_xkbcommon +#define xkb_compose_state_get_utf8 xkb_compose_state_get_utf8_dylibloader_orig_xkbcommon +#define xkb_compose_state_get_one_sym xkb_compose_state_get_one_sym_dylibloader_orig_xkbcommon #include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h" +#include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-compose.h" +#include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-keysyms.h" #undef xkb_keysym_get_name #undef xkb_keysym_from_name #undef xkb_keysym_to_utf8 @@ -158,6 +174,20 @@ #undef xkb_state_layout_index_is_active #undef xkb_state_led_name_is_active #undef xkb_state_led_index_is_active +#undef xkb_compose_table_new_from_locale +#undef xkb_compose_table_new_from_file +#undef xkb_compose_table_new_from_buffer +#undef xkb_compose_table_ref +#undef xkb_compose_table_unref +#undef xkb_compose_state_new +#undef xkb_compose_state_ref +#undef xkb_compose_state_unref +#undef xkb_compose_state_get_compose_table +#undef xkb_compose_state_feed +#undef xkb_compose_state_reset +#undef xkb_compose_state_get_status +#undef xkb_compose_state_get_utf8 +#undef xkb_compose_state_get_one_sym #include <dlfcn.h> #include <stdio.h> int (*xkb_keysym_get_name_dylibloader_wrapper_xkbcommon)( xkb_keysym_t, char*, size_t); @@ -236,6 +266,20 @@ int (*xkb_state_layout_name_is_active_dylibloader_wrapper_xkbcommon)(struct xkb_ int (*xkb_state_layout_index_is_active_dylibloader_wrapper_xkbcommon)(struct xkb_state*, xkb_layout_index_t,enum xkb_state_component); int (*xkb_state_led_name_is_active_dylibloader_wrapper_xkbcommon)(struct xkb_state*,const char*); int (*xkb_state_led_index_is_active_dylibloader_wrapper_xkbcommon)(struct xkb_state*, xkb_led_index_t); +struct xkb_compose_table* (*xkb_compose_table_new_from_locale_dylibloader_wrapper_xkbcommon)(struct xkb_context*,const char*,enum xkb_compose_compile_flags); +struct xkb_compose_table* (*xkb_compose_table_new_from_file_dylibloader_wrapper_xkbcommon)(struct xkb_context*, FILE*,const char*,enum xkb_compose_format,enum xkb_compose_compile_flags); +struct xkb_compose_table* (*xkb_compose_table_new_from_buffer_dylibloader_wrapper_xkbcommon)(struct xkb_context*,const char*, size_t,const char*,enum xkb_compose_format,enum xkb_compose_compile_flags); +struct xkb_compose_table* (*xkb_compose_table_ref_dylibloader_wrapper_xkbcommon)(struct xkb_compose_table*); +void (*xkb_compose_table_unref_dylibloader_wrapper_xkbcommon)(struct xkb_compose_table*); +struct xkb_compose_state* (*xkb_compose_state_new_dylibloader_wrapper_xkbcommon)(struct xkb_compose_table*,enum xkb_compose_state_flags); +struct xkb_compose_state* (*xkb_compose_state_ref_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +void (*xkb_compose_state_unref_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +struct xkb_compose_table* (*xkb_compose_state_get_compose_table_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +enum xkb_compose_feed_result (*xkb_compose_state_feed_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*, xkb_keysym_t); +void (*xkb_compose_state_reset_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +enum xkb_compose_status (*xkb_compose_state_get_status_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +int (*xkb_compose_state_get_utf8_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*, char*, size_t); +xkb_keysym_t (*xkb_compose_state_get_one_sym_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); int initialize_xkbcommon(int verbose) { void *handle; char *error; @@ -855,5 +899,117 @@ int initialize_xkbcommon(int verbose) { fprintf(stderr, "%s\n", error); } } +// xkb_compose_table_new_from_locale + *(void **) (&xkb_compose_table_new_from_locale_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_table_new_from_locale"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_table_new_from_file + *(void **) (&xkb_compose_table_new_from_file_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_table_new_from_file"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_table_new_from_buffer + *(void **) (&xkb_compose_table_new_from_buffer_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_table_new_from_buffer"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_table_ref + *(void **) (&xkb_compose_table_ref_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_table_ref"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_table_unref + *(void **) (&xkb_compose_table_unref_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_table_unref"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_new + *(void **) (&xkb_compose_state_new_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_new"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_ref + *(void **) (&xkb_compose_state_ref_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_ref"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_unref + *(void **) (&xkb_compose_state_unref_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_unref"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_get_compose_table + *(void **) (&xkb_compose_state_get_compose_table_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_get_compose_table"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_feed + *(void **) (&xkb_compose_state_feed_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_feed"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_reset + *(void **) (&xkb_compose_state_reset_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_reset"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_get_status + *(void **) (&xkb_compose_state_get_status_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_get_status"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_get_utf8 + *(void **) (&xkb_compose_state_get_utf8_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_get_utf8"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } +// xkb_compose_state_get_one_sym + *(void **) (&xkb_compose_state_get_one_sym_dylibloader_wrapper_xkbcommon) = dlsym(handle, "xkb_compose_state_get_one_sym"); + if (verbose) { + error = dlerror(); + if (error != NULL) { + fprintf(stderr, "%s\n", error); + } + } return 0; } diff --git a/platform/linuxbsd/xkbcommon-so_wrap.h b/platform/linuxbsd/xkbcommon-so_wrap.h index f7e6f4c4cf..4ae69fdf1f 100644 --- a/platform/linuxbsd/xkbcommon-so_wrap.h +++ b/platform/linuxbsd/xkbcommon-so_wrap.h @@ -2,8 +2,8 @@ #define DYLIBLOAD_WRAPPER_XKBCOMMON // This file is generated. Do not edit! // see https://github.com/hpvb/dynload-wrapper for details -// generated by ../dynload-wrapper/generate-wrapper.py 0.3 on 2023-01-23 15:14:21 -// flags: ../dynload-wrapper/generate-wrapper.py --include ./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h --sys-include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h" --soname libxkbcommon.so.0 --init-name xkbcommon --output-header ./platform/linuxbsd/x11/dynwrappers/xkbcommon-so_wrap.h --output-implementation ./platform/linuxbsd/x11/dynwrappers/xkbcommon-so_wrap.c +// generated by generate-wrapper.py 0.3 on 2023-01-30 10:40:26 +// flags: generate-wrapper.py --include ./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h --include ./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-compose.h --include ./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-keysyms.h --sys-include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h" --sys-include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-compose.h" --sys-include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-keysyms.h" --soname libxkbcommon.so.0 --init-name xkbcommon --output-header ./platform/linuxbsd/xkbcommon-so_wrap.h --output-implementation ./platform/linuxbsd/xkbcommon-so_wrap.c // #include <stdint.h> @@ -83,7 +83,23 @@ #define xkb_state_layout_index_is_active xkb_state_layout_index_is_active_dylibloader_orig_xkbcommon #define xkb_state_led_name_is_active xkb_state_led_name_is_active_dylibloader_orig_xkbcommon #define xkb_state_led_index_is_active xkb_state_led_index_is_active_dylibloader_orig_xkbcommon +#define xkb_compose_table_new_from_locale xkb_compose_table_new_from_locale_dylibloader_orig_xkbcommon +#define xkb_compose_table_new_from_file xkb_compose_table_new_from_file_dylibloader_orig_xkbcommon +#define xkb_compose_table_new_from_buffer xkb_compose_table_new_from_buffer_dylibloader_orig_xkbcommon +#define xkb_compose_table_ref xkb_compose_table_ref_dylibloader_orig_xkbcommon +#define xkb_compose_table_unref xkb_compose_table_unref_dylibloader_orig_xkbcommon +#define xkb_compose_state_new xkb_compose_state_new_dylibloader_orig_xkbcommon +#define xkb_compose_state_ref xkb_compose_state_ref_dylibloader_orig_xkbcommon +#define xkb_compose_state_unref xkb_compose_state_unref_dylibloader_orig_xkbcommon +#define xkb_compose_state_get_compose_table xkb_compose_state_get_compose_table_dylibloader_orig_xkbcommon +#define xkb_compose_state_feed xkb_compose_state_feed_dylibloader_orig_xkbcommon +#define xkb_compose_state_reset xkb_compose_state_reset_dylibloader_orig_xkbcommon +#define xkb_compose_state_get_status xkb_compose_state_get_status_dylibloader_orig_xkbcommon +#define xkb_compose_state_get_utf8 xkb_compose_state_get_utf8_dylibloader_orig_xkbcommon +#define xkb_compose_state_get_one_sym xkb_compose_state_get_one_sym_dylibloader_orig_xkbcommon #include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon.h" +#include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-compose.h" +#include "./thirdparty/linuxbsd_headers/xkbcommon/xkbcommon-keysyms.h" #undef xkb_keysym_get_name #undef xkb_keysym_from_name #undef xkb_keysym_to_utf8 @@ -160,6 +176,20 @@ #undef xkb_state_layout_index_is_active #undef xkb_state_led_name_is_active #undef xkb_state_led_index_is_active +#undef xkb_compose_table_new_from_locale +#undef xkb_compose_table_new_from_file +#undef xkb_compose_table_new_from_buffer +#undef xkb_compose_table_ref +#undef xkb_compose_table_unref +#undef xkb_compose_state_new +#undef xkb_compose_state_ref +#undef xkb_compose_state_unref +#undef xkb_compose_state_get_compose_table +#undef xkb_compose_state_feed +#undef xkb_compose_state_reset +#undef xkb_compose_state_get_status +#undef xkb_compose_state_get_utf8 +#undef xkb_compose_state_get_one_sym #ifdef __cplusplus extern "C" { #endif @@ -239,6 +269,20 @@ extern "C" { #define xkb_state_layout_index_is_active xkb_state_layout_index_is_active_dylibloader_wrapper_xkbcommon #define xkb_state_led_name_is_active xkb_state_led_name_is_active_dylibloader_wrapper_xkbcommon #define xkb_state_led_index_is_active xkb_state_led_index_is_active_dylibloader_wrapper_xkbcommon +#define xkb_compose_table_new_from_locale xkb_compose_table_new_from_locale_dylibloader_wrapper_xkbcommon +#define xkb_compose_table_new_from_file xkb_compose_table_new_from_file_dylibloader_wrapper_xkbcommon +#define xkb_compose_table_new_from_buffer xkb_compose_table_new_from_buffer_dylibloader_wrapper_xkbcommon +#define xkb_compose_table_ref xkb_compose_table_ref_dylibloader_wrapper_xkbcommon +#define xkb_compose_table_unref xkb_compose_table_unref_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_new xkb_compose_state_new_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_ref xkb_compose_state_ref_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_unref xkb_compose_state_unref_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_get_compose_table xkb_compose_state_get_compose_table_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_feed xkb_compose_state_feed_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_reset xkb_compose_state_reset_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_get_status xkb_compose_state_get_status_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_get_utf8 xkb_compose_state_get_utf8_dylibloader_wrapper_xkbcommon +#define xkb_compose_state_get_one_sym xkb_compose_state_get_one_sym_dylibloader_wrapper_xkbcommon extern int (*xkb_keysym_get_name_dylibloader_wrapper_xkbcommon)( xkb_keysym_t, char*, size_t); extern xkb_keysym_t (*xkb_keysym_from_name_dylibloader_wrapper_xkbcommon)(const char*,enum xkb_keysym_flags); extern int (*xkb_keysym_to_utf8_dylibloader_wrapper_xkbcommon)( xkb_keysym_t, char*, size_t); @@ -315,6 +359,20 @@ extern int (*xkb_state_layout_name_is_active_dylibloader_wrapper_xkbcommon)(stru extern int (*xkb_state_layout_index_is_active_dylibloader_wrapper_xkbcommon)(struct xkb_state*, xkb_layout_index_t,enum xkb_state_component); extern int (*xkb_state_led_name_is_active_dylibloader_wrapper_xkbcommon)(struct xkb_state*,const char*); extern int (*xkb_state_led_index_is_active_dylibloader_wrapper_xkbcommon)(struct xkb_state*, xkb_led_index_t); +extern struct xkb_compose_table* (*xkb_compose_table_new_from_locale_dylibloader_wrapper_xkbcommon)(struct xkb_context*,const char*,enum xkb_compose_compile_flags); +extern struct xkb_compose_table* (*xkb_compose_table_new_from_file_dylibloader_wrapper_xkbcommon)(struct xkb_context*, FILE*,const char*,enum xkb_compose_format,enum xkb_compose_compile_flags); +extern struct xkb_compose_table* (*xkb_compose_table_new_from_buffer_dylibloader_wrapper_xkbcommon)(struct xkb_context*,const char*, size_t,const char*,enum xkb_compose_format,enum xkb_compose_compile_flags); +extern struct xkb_compose_table* (*xkb_compose_table_ref_dylibloader_wrapper_xkbcommon)(struct xkb_compose_table*); +extern void (*xkb_compose_table_unref_dylibloader_wrapper_xkbcommon)(struct xkb_compose_table*); +extern struct xkb_compose_state* (*xkb_compose_state_new_dylibloader_wrapper_xkbcommon)(struct xkb_compose_table*,enum xkb_compose_state_flags); +extern struct xkb_compose_state* (*xkb_compose_state_ref_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +extern void (*xkb_compose_state_unref_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +extern struct xkb_compose_table* (*xkb_compose_state_get_compose_table_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +extern enum xkb_compose_feed_result (*xkb_compose_state_feed_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*, xkb_keysym_t); +extern void (*xkb_compose_state_reset_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +extern enum xkb_compose_status (*xkb_compose_state_get_status_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); +extern int (*xkb_compose_state_get_utf8_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*, char*, size_t); +extern xkb_keysym_t (*xkb_compose_state_get_one_sym_dylibloader_wrapper_xkbcommon)(struct xkb_compose_state*); int initialize_xkbcommon(int verbose); #ifdef __cplusplus } diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 73fbd45400..bb96308a75 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -48,16 +48,18 @@ #endif void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { - if (p_preset->get("texture_format/s3tc")) { + r_features->push_back(p_preset->get("binary_format/architecture")); + String architecture = p_preset->get("binary_format/architecture"); + + if (architecture == "universal" || architecture == "x86_64") { r_features->push_back("s3tc"); - } - if (p_preset->get("texture_format/etc")) { - r_features->push_back("etc"); - } - if (p_preset->get("texture_format/etc2")) { + r_features->push_back("bptc"); + } else if (architecture == "arm64") { r_features->push_back("etc2"); + r_features->push_back("astc"); + } else { + ERR_PRINT("Invalid architecture"); } - r_features->push_back(p_preset->get("binary_format/architecture")); } bool EditorExportPlatformMacOS::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const { @@ -210,10 +212,6 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/removable_volumes_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use removable volumes"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/removable_volumes_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); - String run_script = "#!/usr/bin/env bash\n" "unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n" "open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}"; @@ -1766,6 +1764,24 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE } } + String architecture = p_preset->get("binary_format/architecture"); + + if (architecture == "universal" || architecture == "x86_64") { + const String bc_error = test_bc(); + if (!bc_error.is_empty()) { + valid = false; + err += bc_error; + } + } else if (architecture == "arm64") { + const String etc_error = test_etc2(); + if (!etc_error.is_empty()) { + valid = false; + err += etc_error; + } + } else { + ERR_PRINT("Invalid architecture"); + } + // Look for export templates (official templates, check only is custom templates are not set). if (!dvalid || !rvalid) { dvalid = exists_export_template("macos.zip", &err); @@ -2016,8 +2032,8 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in if (port.is_empty()) { port = "22"; } - Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" "); - Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" "); + Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ", false); + Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ", false); const String basepath = dest.path_join("tmp_macos_export"); diff --git a/platform/web/export/editor_http_server.h b/platform/web/export/editor_http_server.h index ce6b0be713..3f87288537 100644 --- a/platform/web/export/editor_http_server.h +++ b/platform/web/export/editor_http_server.h @@ -205,8 +205,7 @@ public: if (tls.is_null()) { tls = Ref<StreamPeerTLS>(StreamPeerTLS::create()); peer = tls; - tls->set_blocking_handshake_enabled(false); - if (tls->accept_stream(tcp, key, cert) != OK) { + if (tls->accept_stream(tcp, TLSOptions::server(key, cert)) != OK) { _clear_client(); return; } diff --git a/platform/web/http_client_web.cpp b/platform/web/http_client_web.cpp index 31f54dad9f..3e4ba5a2ae 100644 --- a/platform/web/http_client_web.cpp +++ b/platform/web/http_client_web.cpp @@ -37,20 +37,20 @@ void HTTPClientWeb::_parse_headers(int p_len, const char **p_headers, void *p_re } } -Error HTTPClientWeb::connect_to_host(const String &p_host, int p_port, bool p_tls, bool p_verify_host) { +Error HTTPClientWeb::connect_to_host(const String &p_host, int p_port, Ref<TLSOptions> p_tls_options) { + ERR_FAIL_COND_V(p_tls_options.is_valid() && p_tls_options->is_server(), ERR_INVALID_PARAMETER); + close(); - if (p_tls && !p_verify_host) { - WARN_PRINT("Disabling HTTPClientWeb's host verification is not supported for the Web platform, host will be verified"); - } port = p_port; - use_tls = p_tls; + use_tls = p_tls_options.is_valid(); host = p_host; String host_lower = host.to_lower(); if (host_lower.begins_with("http://")) { host = host.substr(7, host.length() - 7); + use_tls = false; } else if (host_lower.begins_with("https://")) { use_tls = true; host = host.substr(8, host.length() - 8); diff --git a/platform/web/http_client_web.h b/platform/web/http_client_web.h index 993ec6c0e2..def7837a27 100644 --- a/platform/web/http_client_web.h +++ b/platform/web/http_client_web.h @@ -86,7 +86,7 @@ public: Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override; - Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) override; + Error connect_to_host(const String &p_host, int p_port = -1, Ref<TLSOptions> p_tls_options = Ref<TLSOptions>()) override; void set_connection(const Ref<StreamPeer> &p_connection) override; Ref<StreamPeer> get_connection() const override; void close() override; diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 610fc92265..ebd0733c55 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1626,7 +1626,7 @@ Vector2i DisplayServerWindows::ime_get_selection() const { ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length); int32_t utf32_cursor = 0; - for (int32_t i = 0; i < length / sizeof(wchar_t); i++) { + for (int32_t i = 0; i < length / int32_t(sizeof(wchar_t)); i++) { if ((string[i] & 0xfffffc00) == 0xd800) { i++; } @@ -1671,9 +1671,11 @@ void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p if (p_active) { wd.ime_active = true; ImmAssociateContext(wd.hWnd, wd.im_himc); + CreateCaret(wd.hWnd, NULL, 1, 1); window_set_ime_position(wd.im_position, p_window); } else { ImmAssociateContext(wd.hWnd, (HIMC)0); + DestroyCaret(); wd.ime_active = false; } } @@ -3440,9 +3442,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA [[fallthrough]]; } case WM_CHAR: { - if (windows[window_id].ime_in_progress) { - break; - } ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE); // Make sure we don't include modifiers for the modifier key itself. @@ -3469,15 +3468,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_IME_COMPOSITION: { CANDIDATEFORM cf; cf.dwIndex = 0; - cf.dwStyle = CFS_EXCLUDE; + + cf.dwStyle = CFS_CANDIDATEPOS; cf.ptCurrentPos.x = windows[window_id].im_position.x; cf.ptCurrentPos.y = windows[window_id].im_position.y; + ImmSetCandidateWindow(windows[window_id].im_himc, &cf); + + cf.dwStyle = CFS_EXCLUDE; cf.rcArea.left = windows[window_id].im_position.x; cf.rcArea.right = windows[window_id].im_position.x; cf.rcArea.top = windows[window_id].im_position.y; cf.rcArea.bottom = windows[window_id].im_position.y; ImmSetCandidateWindow(windows[window_id].im_himc, &cf); + if (windows[window_id].ime_active) { + SetCaretPos(windows[window_id].im_position.x, windows[window_id].im_position.y); OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); } } break; diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index bf32b16018..4107a8a17e 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -831,8 +831,8 @@ Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, if (port.is_empty()) { port = "22"; } - Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" "); - Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" "); + Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ", false); + Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ", false); const String basepath = dest.path_join("tmp_windows_export"); diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows.h index b97d0f667c..361c559a5a 100644 --- a/platform/windows/gl_manager_windows.h +++ b/platform/windows/gl_manager_windows.h @@ -74,7 +74,6 @@ private: GLWindow *_current_window = nullptr; PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr; - PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = nullptr; // funcs void _internal_set_current_window(GLWindow *p_win); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 08299d9b98..d384049fb5 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -830,7 +830,7 @@ class FallbackTextAnalysisSource : public IDWriteTextAnalysisSource { IDWriteNumberSubstitution *n_sub = nullptr; public: - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) { + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) override { if (IID_IUnknown == riid) { AddRef(); *ppvInterface = (IUnknown *)this; @@ -844,11 +844,11 @@ public: return S_OK; } - ULONG STDMETHODCALLTYPE AddRef() { + ULONG STDMETHODCALLTYPE AddRef() override { return InterlockedIncrement(&_cRef); } - ULONG STDMETHODCALLTYPE Release() { + ULONG STDMETHODCALLTYPE Release() override { ULONG ulRef = InterlockedDecrement(&_cRef); if (0 == ulRef) { delete this; diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index a4a965aa41..8f7006caca 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -117,7 +117,6 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const { } if (p_property.name == "animation") { - p_property.hint = PROPERTY_HINT_ENUM; List<StringName> names; frames->get_animation_list(&names); names.sort_custom<StringName::AlphCompare>(); @@ -167,6 +166,12 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const { void AnimatedSprite2D::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: { + if (!Engine::get_singleton()->is_editor_hint() && !frames.is_null() && frames->has_animation(autoplay)) { + play(autoplay); + } + } break; + case NOTIFICATION_INTERNAL_PROCESS: { if (frames.is_null() || !frames->has_animation(animation)) { return; @@ -176,7 +181,8 @@ void AnimatedSprite2D::_notification(int p_what) { int i = 0; while (remaining) { // Animation speed may be changed by animation_finished or frame_changed signals. - double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + double speed = frames->get_animation_speed(animation) * speed_scale * custom_speed_scale * frame_speed_scale; + double abs_speed = Math::abs(speed); if (speed == 0) { return; // Do nothing. @@ -185,53 +191,57 @@ void AnimatedSprite2D::_notification(int p_what) { // Frame count may be changed by animation_finished or frame_changed signals. int fc = frames->get_frame_count(animation); - if (timeout <= 0) { - int last_frame = fc - 1; - if (!playing_backwards) { - // Forward. + int last_frame = fc - 1; + if (!signbit(speed)) { + // Forwards. + if (frame_progress >= 1.0) { if (frame >= last_frame) { if (frames->get_animation_loop(animation)) { frame = 0; - emit_signal(SceneStringNames::get_singleton()->animation_finished); + emit_signal("animation_looped"); } else { frame = last_frame; - if (!is_over) { - is_over = true; - emit_signal(SceneStringNames::get_singleton()->animation_finished); - } + pause(); + emit_signal(SceneStringNames::get_singleton()->animation_finished); + return; } } else { frame++; } - } else { - // Reversed. + _calc_frame_speed_scale(); + frame_progress = 0.0; + queue_redraw(); + emit_signal(SceneStringNames::get_singleton()->frame_changed); + } + double to_process = MIN((1.0 - frame_progress) / abs_speed, remaining); + frame_progress += to_process * abs_speed; + remaining -= to_process; + } else { + // Backwards. + if (frame_progress <= 0) { if (frame <= 0) { if (frames->get_animation_loop(animation)) { frame = last_frame; - emit_signal(SceneStringNames::get_singleton()->animation_finished); + emit_signal("animation_looped"); } else { frame = 0; - if (!is_over) { - is_over = true; - emit_signal(SceneStringNames::get_singleton()->animation_finished); - } + pause(); + emit_signal(SceneStringNames::get_singleton()->animation_finished); + return; } } else { frame--; } + _calc_frame_speed_scale(); + frame_progress = 1.0; + queue_redraw(); + emit_signal(SceneStringNames::get_singleton()->frame_changed); } - - timeout = _get_frame_duration(); - - queue_redraw(); - - emit_signal(SceneStringNames::get_singleton()->frame_changed); + double to_process = MIN(frame_progress / abs_speed, remaining); + frame_progress -= to_process * abs_speed; + remaining -= to_process; } - double to_process = MIN(timeout / speed, remaining); - timeout -= to_process * speed; - remaining -= to_process; - i++; if (i > fc) { return; // Prevents freezing if to_process is each time much less than remaining. @@ -275,25 +285,37 @@ void AnimatedSprite2D::_notification(int p_what) { } void AnimatedSprite2D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { + if (frames == p_frames) { + return; + } + if (frames.is_valid()) { frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite2D::_res_changed)); } - + stop(); frames = p_frames; if (frames.is_valid()) { frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite2D::_res_changed)); - } - if (frames.is_null()) { - frame = 0; - } else { - set_frame(frame); + List<StringName> al; + frames->get_animation_list(&al); + if (al.size() == 0) { + set_animation(StringName()); + set_autoplay(String()); + } else { + if (!frames->has_animation(animation)) { + set_animation(al[0]); + } + if (!frames->has_animation(autoplay)) { + set_autoplay(String()); + } + } } notify_property_list_changed(); - _reset_timeout(); queue_redraw(); update_configuration_warnings(); + emit_signal("sprite_frames_changed"); } Ref<SpriteFrames> AnimatedSprite2D::get_sprite_frames() const { @@ -301,44 +323,63 @@ Ref<SpriteFrames> AnimatedSprite2D::get_sprite_frames() const { } void AnimatedSprite2D::set_frame(int p_frame) { + set_frame_and_progress(p_frame, signbit(get_playing_speed()) ? 1.0 : 0.0); +} + +int AnimatedSprite2D::get_frame() const { + return frame; +} + +void AnimatedSprite2D::set_frame_progress(real_t p_progress) { + frame_progress = p_progress; +} + +real_t AnimatedSprite2D::get_frame_progress() const { + return frame_progress; +} + +void AnimatedSprite2D::set_frame_and_progress(int p_frame, real_t p_progress) { if (frames.is_null()) { return; } - if (frames->has_animation(animation)) { - int limit = frames->get_frame_count(animation); - if (p_frame >= limit) { - p_frame = limit - 1; - } - } + bool has_animation = frames->has_animation(animation); + int end_frame = has_animation ? MAX(0, frames->get_frame_count(animation) - 1) : 0; + bool is_changed = frame != p_frame; if (p_frame < 0) { - p_frame = 0; + frame = 0; + } else if (has_animation && p_frame > end_frame) { + frame = end_frame; + } else { + frame = p_frame; } - if (frame == p_frame) { - return; - } + _calc_frame_speed_scale(); + frame_progress = p_progress; - frame = p_frame; - _reset_timeout(); + if (!is_changed) { + return; // No change, don't redraw. + } queue_redraw(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } -int AnimatedSprite2D::get_frame() const { - return frame; -} - void AnimatedSprite2D::set_speed_scale(float p_speed_scale) { speed_scale = p_speed_scale; - playing_backwards = signbit(speed_scale) != backwards; } float AnimatedSprite2D::get_speed_scale() const { return speed_scale; } +float AnimatedSprite2D::get_playing_speed() const { + if (!playing) { + return 0; + } + return speed_scale * custom_speed_scale; +} + void AnimatedSprite2D::set_centered(bool p_center) { centered = p_center; queue_redraw(); @@ -378,69 +419,131 @@ bool AnimatedSprite2D::is_flipped_v() const { } void AnimatedSprite2D::_res_changed() { - set_frame(frame); + set_frame_and_progress(frame, frame_progress); queue_redraw(); notify_property_list_changed(); } -void AnimatedSprite2D::set_playing(bool p_playing) { - if (playing == p_playing) { - return; +bool AnimatedSprite2D::is_playing() const { + return playing; +} + +void AnimatedSprite2D::set_autoplay(const String &p_name) { + if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { + WARN_PRINT("Setting autoplay after the node has been added to the scene has no effect."); } - playing = p_playing; - playing_backwards = signbit(speed_scale) != backwards; - set_process_internal(playing); - notify_property_list_changed(); + + autoplay = p_name; } -bool AnimatedSprite2D::is_playing() const { - return playing; +String AnimatedSprite2D::get_autoplay() const { + return autoplay; } -void AnimatedSprite2D::play(const StringName &p_animation, bool p_backwards) { - backwards = p_backwards; - playing_backwards = signbit(speed_scale) != backwards; +void AnimatedSprite2D::play(const StringName &p_name, float p_custom_scale, bool p_from_end) { + StringName name = p_name; - if (p_animation) { - set_animation(p_animation); - if (frames.is_valid() && playing_backwards && get_frame() == 0) { - set_frame(frames->get_frame_count(p_animation) - 1); + if (name == StringName()) { + name = animation; + } + + ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", name)); + ERR_FAIL_COND_MSG(!frames->get_animation_names().has(name), vformat("There is no animation with name '%s'.", name)); + + if (frames->get_frame_count(name) == 0) { + return; + } + + playing = true; + custom_speed_scale = p_custom_scale; + + int end_frame = MAX(0, frames->get_frame_count(animation) - 1); + if (name != animation) { + animation = name; + if (p_from_end) { + set_frame_and_progress(end_frame, 1.0); + } else { + set_frame_and_progress(0, 0.0); + } + emit_signal("animation_changed"); + } else { + bool is_backward = signbit(speed_scale * custom_speed_scale); + if (p_from_end && is_backward && frame == 0 && frame_progress <= 0.0) { + set_frame_and_progress(end_frame, 1.0); + } else if (!p_from_end && !is_backward && frame == end_frame && frame_progress >= 1.0) { + set_frame_and_progress(0, 0.0); } } - is_over = false; - set_playing(true); + set_process_internal(true); + notify_property_list_changed(); + queue_redraw(); +} + +void AnimatedSprite2D::play_backwards(const StringName &p_name) { + play(p_name, -1, true); +} + +void AnimatedSprite2D::_stop_internal(bool p_reset) { + playing = false; + if (p_reset) { + custom_speed_scale = 1.0; + set_frame_and_progress(0, 0.0); + } + notify_property_list_changed(); + set_process_internal(false); +} + +void AnimatedSprite2D::pause() { + _stop_internal(false); } void AnimatedSprite2D::stop() { - set_playing(false); - backwards = false; - _reset_timeout(); + _stop_internal(true); } double AnimatedSprite2D::_get_frame_duration() { if (frames.is_valid() && frames->has_animation(animation)) { return frames->get_frame_duration(animation, frame); } - return 0.0; + return 1.0; } -void AnimatedSprite2D::_reset_timeout() { - timeout = _get_frame_duration(); - is_over = false; +void AnimatedSprite2D::_calc_frame_speed_scale() { + frame_speed_scale = 1.0 / _get_frame_duration(); } -void AnimatedSprite2D::set_animation(const StringName &p_animation) { - ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation)); - ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation)); +void AnimatedSprite2D::set_animation(const StringName &p_name) { + if (animation == p_name) { + return; + } + + animation = p_name; + + emit_signal("animation_changed"); - if (animation == p_animation) { + if (frames == nullptr) { + animation = StringName(); + stop(); + ERR_FAIL_MSG(vformat("There is no animation with name '%s'.", p_name)); + } + + int frame_count = frames->get_frame_count(animation); + if (animation == StringName() || frame_count == 0) { + stop(); return; + } else if (!frames->get_animation_names().has(animation)) { + animation = StringName(); + stop(); + ERR_FAIL_MSG(vformat("There is no animation with name '%s'.", p_name)); + } + + if (signbit(get_playing_speed())) { + set_frame_and_progress(frame_count - 1, 1.0); + } else { + set_frame_and_progress(0, 0.0); } - animation = p_animation; - set_frame(0); - _reset_timeout(); notify_property_list_changed(); queue_redraw(); } @@ -468,17 +571,30 @@ void AnimatedSprite2D::get_argument_options(const StringName &p_function, int p_ Node::get_argument_options(p_function, p_idx, r_options); } +#ifndef DISABLE_DEPRECATED +bool AnimatedSprite2D::_set(const StringName &p_name, const Variant &p_value) { + if ((p_name == SNAME("frames"))) { + set_sprite_frames(p_value); + return true; + } + return false; +} +#endif void AnimatedSprite2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite2D::set_sprite_frames); ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite2D::get_sprite_frames); - ClassDB::bind_method(D_METHOD("set_animation", "animation"), &AnimatedSprite2D::set_animation); + ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimatedSprite2D::set_animation); ClassDB::bind_method(D_METHOD("get_animation"), &AnimatedSprite2D::get_animation); - ClassDB::bind_method(D_METHOD("set_playing", "playing"), &AnimatedSprite2D::set_playing); + ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimatedSprite2D::set_autoplay); + ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimatedSprite2D::get_autoplay); + ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite2D::is_playing); - ClassDB::bind_method(D_METHOD("play", "anim", "backwards"), &AnimatedSprite2D::play, DEFVAL(StringName()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("play", "name", "custom_speed", "from_end"), &AnimatedSprite2D::play, DEFVAL(StringName()), DEFVAL(1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("play_backwards", "name"), &AnimatedSprite2D::play_backwards, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("pause"), &AnimatedSprite2D::pause); ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite2D::stop); ClassDB::bind_method(D_METHOD("set_centered", "centered"), &AnimatedSprite2D::set_centered); @@ -496,18 +612,28 @@ void AnimatedSprite2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite2D::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite2D::get_frame); + ClassDB::bind_method(D_METHOD("set_frame_progress", "progress"), &AnimatedSprite2D::set_frame_progress); + ClassDB::bind_method(D_METHOD("get_frame_progress"), &AnimatedSprite2D::get_frame_progress); + + ClassDB::bind_method(D_METHOD("set_frame_and_progress", "frame", "progress"), &AnimatedSprite2D::set_frame_and_progress); + ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite2D::set_speed_scale); ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite2D::get_speed_scale); + ClassDB::bind_method(D_METHOD("get_playing_speed"), &AnimatedSprite2D::get_playing_speed); + ADD_SIGNAL(MethodInfo("sprite_frames_changed")); + ADD_SIGNAL(MethodInfo("animation_changed")); ADD_SIGNAL(MethodInfo("frame_changed")); + ADD_SIGNAL(MethodInfo("animation_looped")); ADD_SIGNAL(MethodInfo("animation_finished")); ADD_GROUP("Animation", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sprite_frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation", PROPERTY_HINT_ENUM, ""), "set_animation", "get_animation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_autoplay", "get_autoplay"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frame_progress", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_frame_progress", "get_frame_progress"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale"), "set_speed_scale", "get_speed_scale"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "set_playing", "is_playing"); ADD_GROUP("Offset", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index c1d35f3a2f..ac53bd26ee 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -38,18 +38,19 @@ class AnimatedSprite2D : public Node2D { GDCLASS(AnimatedSprite2D, Node2D); Ref<SpriteFrames> frames; + String autoplay; + bool playing = false; - bool playing_backwards = false; - bool backwards = false; StringName animation = "default"; int frame = 0; float speed_scale = 1.0; + float custom_speed_scale = 1.0; bool centered = true; Point2 offset; - bool is_over = false; - float timeout = 0.0; + real_t frame_speed_scale = 1.0; + real_t frame_progress = 0.0; bool hflip = false; bool vflip = false; @@ -57,10 +58,15 @@ class AnimatedSprite2D : public Node2D { void _res_changed(); double _get_frame_duration(); - void _reset_timeout(); + void _calc_frame_speed_scale(); + void _stop_internal(bool p_reset); + Rect2 _get_rect() const; protected: +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); +#endif static void _bind_methods(); void _notification(int p_what); void _validate_property(PropertyInfo &p_property) const; @@ -82,20 +88,30 @@ public: void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; - void play(const StringName &p_animation = StringName(), bool p_backwards = false); + void play(const StringName &p_name = StringName(), float p_custom_scale = 1.0, bool p_from_end = false); + void play_backwards(const StringName &p_name = StringName()); + void pause(); void stop(); - void set_playing(bool p_playing); bool is_playing() const; - void set_animation(const StringName &p_animation); + void set_animation(const StringName &p_name); StringName get_animation() const; + void set_autoplay(const String &p_name); + String get_autoplay() const; + void set_frame(int p_frame); int get_frame() const; + void set_frame_progress(real_t p_progress); + real_t get_frame_progress() const; + + void set_frame_and_progress(int p_frame, real_t p_progress); + void set_speed_scale(float p_speed_scale); float get_speed_scale() const; + float get_playing_speed() const; void set_centered(bool p_center); bool is_centered() const; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index f80da6e9ab..a37fabf21f 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -51,13 +51,13 @@ bool Area2D::is_gravity_a_point() const { return gravity_is_point; } -void Area2D::set_gravity_point_distance_scale(real_t p_scale) { - gravity_distance_scale = p_scale; - PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_DISTANCE_SCALE, p_scale); +void Area2D::set_gravity_point_unit_distance(real_t p_scale) { + gravity_point_unit_distance = p_scale; + PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE, p_scale); } -real_t Area2D::get_gravity_point_distance_scale() const { - return gravity_distance_scale; +real_t Area2D::get_gravity_point_unit_distance() const { + return gravity_point_unit_distance; } void Area2D::set_gravity_point_center(const Vector2 &p_center) { @@ -557,8 +557,8 @@ void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gravity_is_point", "enable"), &Area2D::set_gravity_is_point); ClassDB::bind_method(D_METHOD("is_gravity_a_point"), &Area2D::is_gravity_a_point); - ClassDB::bind_method(D_METHOD("set_gravity_point_distance_scale", "distance_scale"), &Area2D::set_gravity_point_distance_scale); - ClassDB::bind_method(D_METHOD("get_gravity_point_distance_scale"), &Area2D::get_gravity_point_distance_scale); + ClassDB::bind_method(D_METHOD("set_gravity_point_unit_distance", "distance_scale"), &Area2D::set_gravity_point_unit_distance); + ClassDB::bind_method(D_METHOD("get_gravity_point_unit_distance"), &Area2D::get_gravity_point_unit_distance); ClassDB::bind_method(D_METHOD("set_gravity_point_center", "center"), &Area2D::set_gravity_point_center); ClassDB::bind_method(D_METHOD("get_gravity_point_center"), &Area2D::get_gravity_point_center); @@ -622,7 +622,7 @@ void Area2D::_bind_methods() { ADD_GROUP("Gravity", "gravity_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gravity_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_space_override_mode", "get_gravity_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_unit_distance", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp,suffix:px"), "set_gravity_point_unit_distance", "get_gravity_point_unit_distance"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_point_center", PROPERTY_HINT_NONE, "suffix:px"), "set_gravity_point_center", "get_gravity_point_center"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_direction"), "set_gravity_direction", "get_gravity_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-4096,4096,0.001,or_less,or_greater,suffix:px/s\u00B2"), "set_gravity", "get_gravity"); @@ -652,6 +652,7 @@ Area2D::Area2D() : set_gravity_direction(Vector2(0, 1)); set_monitoring(true); set_monitorable(true); + set_hide_clip_children(true); } Area2D::~Area2D() { diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index aaf7ea28f8..8f4bbe3219 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -51,7 +51,7 @@ private: Vector2 gravity_vec; real_t gravity = 0.0; bool gravity_is_point = false; - real_t gravity_distance_scale = 0.0; + real_t gravity_point_unit_distance = 0.0; SpaceOverride linear_damp_space_override = SPACE_OVERRIDE_DISABLED; SpaceOverride angular_damp_space_override = SPACE_OVERRIDE_DISABLED; @@ -144,8 +144,8 @@ public: void set_gravity_is_point(bool p_enabled); bool is_gravity_a_point() const; - void set_gravity_point_distance_scale(real_t p_scale); - real_t get_gravity_point_distance_scale() const; + void set_gravity_point_unit_distance(real_t p_scale); + real_t get_gravity_point_unit_distance() const; void set_gravity_point_center(const Vector2 &p_center); const Vector2 &get_gravity_point_center() const; diff --git a/scene/2d/audio_listener_2d.cpp b/scene/2d/audio_listener_2d.cpp index 5b8833ce62..b4484694a5 100644 --- a/scene/2d/audio_listener_2d.cpp +++ b/scene/2d/audio_listener_2d.cpp @@ -110,3 +110,7 @@ void AudioListener2D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_current"), &AudioListener2D::clear_current); ClassDB::bind_method(D_METHOD("is_current"), &AudioListener2D::is_current); } + +AudioListener2D::AudioListener2D() { + set_hide_clip_children(true); +} diff --git a/scene/2d/audio_listener_2d.h b/scene/2d/audio_listener_2d.h index 12a44f26ae..abada06971 100644 --- a/scene/2d/audio_listener_2d.h +++ b/scene/2d/audio_listener_2d.h @@ -54,6 +54,8 @@ public: void make_current(); void clear_current(); bool is_current() const; + + AudioListener2D(); }; #endif // AUDIO_LISTENER_2D_H diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 7b681eac7a..902fba38bf 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -72,12 +72,10 @@ void AudioStreamPlayer2D::_notification(int p_what) { _update_panning(); } - if (setplay.get() >= 0 && stream.is_valid()) { + if (setplayback.is_valid() && setplay.get() >= 0) { active.set(); - Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback(); - ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); - AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale); - stream_playbacks.push_back(new_playback); + AudioServer::get_singleton()->start_playback_stream(setplayback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale); + setplayback.unref(); setplay.set(-1); } @@ -255,7 +253,11 @@ void AudioStreamPlayer2D::play(float p_from_pos) { if (stream->is_monophonic() && is_playing()) { stop(); } + Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback(); + ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback."); + stream_playbacks.push_back(stream_playback); + setplayback = stream_playback; setplay.set(p_from_pos); active.set(); set_physics_process_internal(true); @@ -390,6 +392,10 @@ bool AudioStreamPlayer2D::get_stream_paused() const { return false; } +bool AudioStreamPlayer2D::has_stream_playback() { + return !stream_playbacks.is_empty(); +} + Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() { ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback()."); return stream_playbacks[stream_playbacks.size() - 1]; @@ -458,6 +464,7 @@ void AudioStreamPlayer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_panning_strength", "panning_strength"), &AudioStreamPlayer2D::set_panning_strength); ClassDB::bind_method(D_METHOD("get_panning_strength"), &AudioStreamPlayer2D::get_panning_strength); + ClassDB::bind_method(D_METHOD("has_stream_playback"), &AudioStreamPlayer2D::has_stream_playback); ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); @@ -479,6 +486,7 @@ void AudioStreamPlayer2D::_bind_methods() { AudioStreamPlayer2D::AudioStreamPlayer2D() { AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer2D::_bus_layout_changed)); cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength"); + set_hide_clip_children(true); } AudioStreamPlayer2D::~AudioStreamPlayer2D() { diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index a5fd584513..79a026fed2 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -56,6 +56,7 @@ private: SafeFlag active{ false }; SafeNumeric<float> setplay{ -1.0 }; + Ref<AudioStreamPlayback> setplayback; Vector<AudioFrame> volume_vector; @@ -129,6 +130,7 @@ public: void set_panning_strength(float p_panning_strength); float get_panning_strength() const; + bool has_stream_playback(); Ref<AudioStreamPlayback> get_stream_playback(); AudioStreamPlayer2D(); diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp index ab048f0cd7..60b344b002 100644 --- a/scene/2d/back_buffer_copy.cpp +++ b/scene/2d/back_buffer_copy.cpp @@ -101,6 +101,7 @@ void BackBufferCopy::_bind_methods() { BackBufferCopy::BackBufferCopy() { _update_copy_mode(); + set_hide_clip_children(true); } BackBufferCopy::~BackBufferCopy() { diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index c4647ae9ff..71b8fdb539 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -832,4 +832,5 @@ Camera2D::Camera2D() { drag_margin[SIDE_BOTTOM] = 0.2; set_notify_transform(true); + set_hide_clip_children(true); } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index caea753d99..ba3b0cec5c 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -651,6 +651,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { area = p_area; pickable = true; set_notify_transform(true); + set_hide_clip_children(true); total_subshapes = 0; only_update_transform_changes = false; diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 0e18f77b8a..32dea80650 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -131,15 +131,7 @@ void CollisionPolygon2D::_notification(int p_what) { break; } - int polygon_count = polygon.size(); - for (int i = 0; i < polygon_count; i++) { - Vector2 p = polygon[i]; - Vector2 n = polygon[(i + 1) % polygon_count]; - // draw line with width <= 1, so it does not scale with zoom and break pixel exact editing - draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 1); - } - - if (polygon_count > 2) { + if (polygon.size() > 2) { #define DEBUG_DECOMPOSE #if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE) Vector<Vector<Vector2>> decomp = _decompose_in_convex(); @@ -152,6 +144,11 @@ void CollisionPolygon2D::_notification(int p_what) { #else draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color()); #endif + + const Color stroke_color = Color(0.9, 0.2, 0.0); + draw_polyline(polygon, stroke_color); + // Draw the last segment. + draw_line(polygon[polygon.size() - 1], polygon[0], stroke_color); } if (one_way_collision) { @@ -323,4 +320,5 @@ void CollisionPolygon2D::_bind_methods() { CollisionPolygon2D::CollisionPolygon2D() { set_notify_local_transform(true); + set_hide_clip_children(true); } diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 6ff789cad2..5951405bbe 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -288,5 +288,6 @@ void CollisionShape2D::_bind_methods() { CollisionShape2D::CollisionShape2D() { set_notify_local_transform(true); + set_hide_clip_children(true); debug_color = _get_default_debug_color(); } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 9a3b7c8687..00d13c59b9 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -638,7 +638,7 @@ void GPUParticles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); ADD_GROUP("Collision", "collision_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,suffix:px"), "set_collision_base_size", "get_collision_base_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size"); ADD_GROUP("Drawing", ""); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "visibility_rect", PROPERTY_HINT_NONE, "suffix:px"), "set_visibility_rect", "get_visibility_rect"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 47d0ac6e35..ce427d47aa 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -243,6 +243,7 @@ void Joint2D::_bind_methods() { Joint2D::Joint2D() { joint = PhysicsServer2D::get_singleton()->joint_create(); + set_hide_clip_children(true); } Joint2D::~Joint2D() { diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index fb53400fd6..15b638ed92 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -443,6 +443,7 @@ void PointLight2D::_bind_methods() { PointLight2D::PointLight2D() { RS::get_singleton()->canvas_light_set_mode(_get_light(), RS::CANVAS_LIGHT_MODE_POINT); + set_hide_clip_children(true); } ////////// @@ -467,4 +468,5 @@ void DirectionalLight2D::_bind_methods() { DirectionalLight2D::DirectionalLight2D() { RS::get_singleton()->canvas_light_set_mode(_get_light(), RS::CANVAS_LIGHT_MODE_DIRECTIONAL); set_max_distance(max_distance); // Update RenderingServer. + set_hide_clip_children(true); } diff --git a/scene/2d/marker_2d.cpp b/scene/2d/marker_2d.cpp index 512875833c..9595fcfffe 100644 --- a/scene/2d/marker_2d.cpp +++ b/scene/2d/marker_2d.cpp @@ -117,4 +117,5 @@ void Marker2D::_bind_methods() { } Marker2D::Marker2D() { + set_hide_clip_children(true); } diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 6b842e6e6b..e73b6e7e23 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -76,10 +76,10 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent2D::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent2D::get_navigation_map); - ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location); - ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location); + ClassDB::bind_method(D_METHOD("set_target_position", "position"), &NavigationAgent2D::set_target_position); + ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent2D::get_target_position); - ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location); + ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent2D::get_next_path_position); ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target); ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent2D::set_velocity); ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent2D::get_current_navigation_result); @@ -88,12 +88,12 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent2D::is_target_reached); ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent2D::is_target_reachable); ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent2D::is_navigation_finished); - ClassDB::bind_method(D_METHOD("get_final_location"), &NavigationAgent2D::get_final_location); + ClassDB::bind_method(D_METHOD("get_final_position"), &NavigationAgent2D::get_final_position); ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent2D::_avoidance_done); ADD_GROUP("Pathfinding", ""); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_location", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_location", "get_target_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,1000,0.01,suffix:px"), "set_path_desired_distance", "get_path_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,1000,0.01,suffix:px"), "set_target_desired_distance", "get_target_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,1000,1,suffix:px"), "set_path_max_distance", "get_path_max_distance"); @@ -205,9 +205,9 @@ NavigationAgent2D::~NavigationAgent2D() { void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) { avoidance_enabled = p_enabled; if (avoidance_enabled) { - NavigationServer2D::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done"); + NavigationServer2D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done)); } else { - NavigationServer2D::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); + NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable()); } } @@ -217,7 +217,8 @@ bool NavigationAgent2D::get_avoidance_enabled() const { void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) { // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map - NavigationServer2D::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); + NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable()); + if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) { // place agent on navigation map first or else the RVO agent callback creation fails silently later agent_parent = Object::cast_to<Node2D>(p_agent_parent); @@ -226,6 +227,7 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) { } else { NavigationServer2D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->get_navigation_map()); } + // create new avoidance callback if enabled set_avoidance_enabled(avoidance_enabled); } else { @@ -328,17 +330,17 @@ real_t NavigationAgent2D::get_path_max_distance() { return path_max_distance; } -void NavigationAgent2D::set_target_location(Vector2 p_location) { - target_location = p_location; +void NavigationAgent2D::set_target_position(Vector2 p_position) { + target_position = p_position; target_position_submitted = true; _request_repath(); } -Vector2 NavigationAgent2D::get_target_location() const { - return target_location; +Vector2 NavigationAgent2D::get_target_position() const { + return target_position; } -Vector2 NavigationAgent2D::get_next_location() { +Vector2 NavigationAgent2D::get_next_path_position() { update_navigation(); const Vector<Vector2> &navigation_path = navigation_result->get_path(); @@ -352,7 +354,7 @@ Vector2 NavigationAgent2D::get_next_location() { real_t NavigationAgent2D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); - return agent_parent->get_global_position().distance_to(target_location); + return agent_parent->get_global_position().distance_to(target_position); } bool NavigationAgent2D::is_target_reached() const { @@ -360,7 +362,7 @@ bool NavigationAgent2D::is_target_reached() const { } bool NavigationAgent2D::is_target_reachable() { - return target_desired_distance >= get_final_location().distance_to(target_location); + return target_desired_distance >= get_final_position().distance_to(target_position); } bool NavigationAgent2D::is_navigation_finished() { @@ -368,7 +370,7 @@ bool NavigationAgent2D::is_navigation_finished() { return navigation_finished; } -Vector2 NavigationAgent2D::get_final_location() { +Vector2 NavigationAgent2D::get_final_position() { update_navigation(); const Vector<Vector2> &navigation_path = navigation_result->get_path(); @@ -450,7 +452,7 @@ void NavigationAgent2D::update_navigation() { if (reload_path) { navigation_query->set_start_position(origin); - navigation_query->set_target_position(target_location); + navigation_query->set_target_position(target_position); navigation_query->set_navigation_layers(navigation_layers); navigation_query->set_metadata_flags(path_metadata_flags); @@ -472,7 +474,7 @@ void NavigationAgent2D::update_navigation() { // Check if we can advance the navigation path if (navigation_finished == false) { - // Advances to the next far away location. + // Advances to the next far away position. const Vector<Vector2> &navigation_path = navigation_result->get_path(); const Vector<int32_t> &navigation_path_types = navigation_result->get_path_types(); const TypedArray<RID> &navigation_path_rids = navigation_result->get_path_rids(); @@ -482,7 +484,7 @@ void NavigationAgent2D::update_navigation() { Dictionary details; const Vector2 waypoint = navigation_path[navigation_path_index]; - details[SNAME("location")] = waypoint; + details[SNAME("position")] = waypoint; int waypoint_type = -1; if (path_metadata_flags.has_flag(NavigationPathQueryParameters2D::PathMetadataFlags::PATH_METADATA_INCLUDE_TYPES)) { diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 190a2fcbda..9787bb1bdb 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -60,7 +60,7 @@ class NavigationAgent2D : public Node { real_t path_max_distance = 100.0; - Vector2 target_location; + Vector2 target_position; bool target_position_submitted = false; Ref<NavigationPathQueryParameters2D> navigation_query; Ref<NavigationPathQueryResult2D> navigation_result; @@ -143,10 +143,10 @@ public: void set_path_max_distance(real_t p_pmd); real_t get_path_max_distance(); - void set_target_location(Vector2 p_location); - Vector2 get_target_location() const; + void set_target_position(Vector2 p_position); + Vector2 get_target_position() const; - Vector2 get_next_location(); + Vector2 get_next_path_position(); Ref<NavigationPathQueryResult2D> get_current_navigation_result() const { return navigation_result; @@ -162,7 +162,7 @@ public: bool is_target_reached() const; bool is_target_reachable(); bool is_navigation_finished(); - Vector2 get_final_location(); + Vector2 get_final_position(); void set_velocity(Vector2 p_velocity); void _avoidance_done(Vector3 p_new_velocity); diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index 9ef0ba617e..26dca40176 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -48,11 +48,11 @@ void NavigationLink2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink2D::set_navigation_layer_value); ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink2D::get_navigation_layer_value); - ClassDB::bind_method(D_METHOD("set_start_location", "location"), &NavigationLink2D::set_start_location); - ClassDB::bind_method(D_METHOD("get_start_location"), &NavigationLink2D::get_start_location); + ClassDB::bind_method(D_METHOD("set_start_position", "position"), &NavigationLink2D::set_start_position); + ClassDB::bind_method(D_METHOD("get_start_position"), &NavigationLink2D::get_start_position); - ClassDB::bind_method(D_METHOD("set_end_location", "location"), &NavigationLink2D::set_end_location); - ClassDB::bind_method(D_METHOD("get_end_location"), &NavigationLink2D::get_end_location); + ClassDB::bind_method(D_METHOD("set_end_position", "position"), &NavigationLink2D::set_end_position); + ClassDB::bind_method(D_METHOD("get_end_position"), &NavigationLink2D::get_end_position); ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink2D::set_enter_cost); ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink2D::get_enter_cost); @@ -63,12 +63,38 @@ void NavigationLink2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_location"), "set_start_location", "get_start_location"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "end_location"), "set_end_location", "get_end_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_position"), "set_start_position", "get_start_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "end_position"), "set_end_position", "get_end_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); } +#ifndef DISABLE_DEPRECATED +bool NavigationLink2D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "start_location") { + set_start_position(p_value); + return true; + } + if (p_name == "end_location") { + set_end_position(p_value); + return true; + } + return false; +} + +bool NavigationLink2D::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "start_location") { + r_ret = get_start_position(); + return true; + } + if (p_name == "end_location") { + r_ret = get_end_position(); + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + void NavigationLink2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -77,15 +103,15 @@ void NavigationLink2D::_notification(int p_what) { // Update global positions for the link. Transform2D gt = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); - NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); + NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); } } break; case NOTIFICATION_TRANSFORM_CHANGED: { // Update global positions for the link. Transform2D gt = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); - NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); + NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); } break; case NOTIFICATION_EXIT_TREE: { NavigationServer2D::get_singleton()->link_set_map(link, RID()); @@ -102,9 +128,9 @@ void NavigationLink2D::_notification(int p_what) { real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); - draw_line(get_start_location(), get_end_location(), color); - draw_arc(get_start_location(), radius, 0, Math_TAU, 10, color); - draw_arc(get_end_location(), radius, 0, Math_TAU, 10, color); + draw_line(get_start_position(), get_end_position(), color); + draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color); + draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color); } #endif // DEBUG_ENABLED } break; @@ -119,14 +145,14 @@ Rect2 NavigationLink2D::_edit_get_rect() const { real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); - Rect2 rect(get_start_location(), Size2()); - rect.expand_to(get_end_location()); + Rect2 rect(get_start_position(), Size2()); + rect.expand_to(get_end_position()); rect.grow_by(radius); return rect; } bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - Point2 segment[2] = { get_start_location(), get_end_location() }; + Point2 segment[2] = { get_start_position(), get_end_position() }; Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, segment); return p_point.distance_to(closest_point) < p_tolerance; @@ -199,19 +225,19 @@ bool NavigationLink2D::get_navigation_layer_value(int p_layer_number) const { return get_navigation_layers() & (1 << (p_layer_number - 1)); } -void NavigationLink2D::set_start_location(Vector2 p_location) { - if (start_location.is_equal_approx(p_location)) { +void NavigationLink2D::set_start_position(Vector2 p_position) { + if (start_position.is_equal_approx(p_position)) { return; } - start_location = p_location; + start_position = p_position; if (!is_inside_tree()) { return; } Transform2D gt = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer2D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); update_configuration_warnings(); @@ -222,19 +248,19 @@ void NavigationLink2D::set_start_location(Vector2 p_location) { #endif // DEBUG_ENABLED } -void NavigationLink2D::set_end_location(Vector2 p_location) { - if (end_location.is_equal_approx(p_location)) { +void NavigationLink2D::set_end_position(Vector2 p_position) { + if (end_position.is_equal_approx(p_position)) { return; } - end_location = p_location; + end_position = p_position; if (!is_inside_tree()) { return; } Transform2D gt = get_global_transform(); - NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer2D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); update_configuration_warnings(); @@ -270,8 +296,8 @@ void NavigationLink2D::set_travel_cost(real_t p_travel_cost) { PackedStringArray NavigationLink2D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); - if (start_location.is_equal_approx(end_location)) { - warnings.push_back(RTR("NavigationLink2D start location should be different than the end location to be useful.")); + if (start_position.is_equal_approx(end_position)) { + warnings.push_back(RTR("NavigationLink2D start position should be different than the end position to be useful.")); } return warnings; @@ -282,6 +308,7 @@ NavigationLink2D::NavigationLink2D() { NavigationServer2D::get_singleton()->link_set_owner_id(link, get_instance_id()); set_notify_transform(true); + set_hide_clip_children(true); } NavigationLink2D::~NavigationLink2D() { diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index e14ee5adb9..5bf2a72358 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -40,8 +40,8 @@ class NavigationLink2D : public Node2D { RID link; bool bidirectional = true; uint32_t navigation_layers = 1; - Vector2 end_location; - Vector2 start_location; + Vector2 end_position; + Vector2 start_position; real_t enter_cost = 0.0; real_t travel_cost = 1.0; @@ -49,6 +49,11 @@ protected: static void _bind_methods(); void _notification(int p_what); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; +#endif // DISABLE_DEPRECATED + public: #ifdef TOOLS_ENABLED virtual Rect2 _edit_get_rect() const override; @@ -67,11 +72,11 @@ public: void set_navigation_layer_value(int p_layer_number, bool p_value); bool get_navigation_layer_value(int p_layer_number) const; - void set_start_location(Vector2 p_location); - Vector2 get_start_location() const { return start_location; } + void set_start_position(Vector2 p_position); + Vector2 get_start_position() const { return start_position; } - void set_end_location(Vector2 p_location); - Vector2 get_end_location() const { return end_location; } + void set_end_position(Vector2 p_position); + Vector2 get_end_position() const { return end_position; } void set_enter_cost(real_t p_enter_cost); real_t get_enter_cost() const { return enter_cost; } diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index fe6af8dad2..3484a9de65 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -330,6 +330,7 @@ bool NavigationRegion2D::_get(const StringName &p_name, Variant &r_ret) const { NavigationRegion2D::NavigationRegion2D() { set_notify_transform(true); + set_hide_clip_children(true); region = NavigationServer2D::get_singleton()->region_create(); NavigationServer2D::get_singleton()->region_set_owner_id(region, get_instance_id()); diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 39f88a0b5e..988ea87054 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -370,4 +370,5 @@ void RayCast2D::_bind_methods() { } RayCast2D::RayCast2D() { + set_hide_clip_children(true); } diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index e9ce9560d3..c6730f7ab2 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -219,4 +219,5 @@ void RemoteTransform2D::_bind_methods() { RemoteTransform2D::RemoteTransform2D() { set_notify_transform(true); + set_hide_clip_children(true); } diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp index 24821858cf..bafb83361a 100644 --- a/scene/2d/shape_cast_2d.cpp +++ b/scene/2d/shape_cast_2d.cpp @@ -472,3 +472,7 @@ void ShapeCast2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled"); } + +ShapeCast2D::ShapeCast2D() { + set_hide_clip_children(true); +} diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h index 182614a721..8a62b799f8 100644 --- a/scene/2d/shape_cast_2d.h +++ b/scene/2d/shape_cast_2d.h @@ -119,6 +119,8 @@ public: void clear_exceptions(); PackedStringArray get_configuration_warnings() const override; + + ShapeCast2D(); }; #endif // SHAPE_CAST_2D_H diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 83cfffc333..96711bbe72 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -519,6 +519,7 @@ Bone2D::Bone2D() { bone_angle = 0; autocalculate_length_and_angle = true; set_notify_local_transform(true); + set_hide_clip_children(true); //this is a clever hack so the bone knows no rest has been set yet, allowing to show an error. for (int i = 0; i < 3; i++) { rest[i] = Vector2(0, 0); @@ -801,6 +802,7 @@ void Skeleton2D::_bind_methods() { Skeleton2D::Skeleton2D() { skeleton = RS::get_singleton()->skeleton_create(); set_notify_transform(true); + set_hide_clip_children(true); } Skeleton2D::~Skeleton2D() { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 8c5aab4c80..95bf67d38d 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1379,7 +1379,7 @@ void TileMap::draw_tile(RID p_canvas_item, const Vector2i &p_position, const Ref Color modulate = tile_data->get_modulate() * p_modulation; // Compute the offset. - Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile); + Vector2i tile_offset = tile_data->get_texture_origin(); // Get destination rect. Rect2 dest_rect; @@ -3969,6 +3969,17 @@ PackedStringArray TileMap::get_configuration_warnings() const { } } + // Check if Y-sort is enabled on a layer but not on the node. + if (!is_y_sort_enabled()) { + for (const TileMapLayer &layer : layers) { + if (layer.y_sort_enabled) { + warnings.push_back(RTR("A TileMap layer is set as Y-sorted, but Y-sort is not enabled on the TileMap node itself.")); + break; + } + } + } + + // Check if we are in isometric mode without Y-sort enabled. if (tile_set.is_valid() && tile_set->get_tile_shape() == TileSet::TILE_SHAPE_ISOMETRIC) { bool warn = !is_y_sort_enabled(); if (!warn) { diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp index 237eb3d987..1177cdb811 100644 --- a/scene/2d/visible_on_screen_notifier_2d.cpp +++ b/scene/2d/visible_on_screen_notifier_2d.cpp @@ -110,6 +110,7 @@ void VisibleOnScreenNotifier2D::_bind_methods() { VisibleOnScreenNotifier2D::VisibleOnScreenNotifier2D() { rect = Rect2(-10, -10, 20, 20); + set_hide_clip_children(true); } ////////////////////////////////////// diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 72f186c676..5901e38bb4 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -51,13 +51,13 @@ bool Area3D::is_gravity_a_point() const { return gravity_is_point; } -void Area3D::set_gravity_point_distance_scale(real_t p_scale) { - gravity_distance_scale = p_scale; - PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_DISTANCE_SCALE, p_scale); +void Area3D::set_gravity_point_unit_distance(real_t p_scale) { + gravity_point_unit_distance = p_scale; + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE, p_scale); } -real_t Area3D::get_gravity_point_distance_scale() const { - return gravity_distance_scale; +real_t Area3D::get_gravity_point_unit_distance() const { + return gravity_point_unit_distance; } void Area3D::set_gravity_point_center(const Vector3 &p_center) { @@ -655,8 +655,8 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gravity_is_point", "enable"), &Area3D::set_gravity_is_point); ClassDB::bind_method(D_METHOD("is_gravity_a_point"), &Area3D::is_gravity_a_point); - ClassDB::bind_method(D_METHOD("set_gravity_point_distance_scale", "distance_scale"), &Area3D::set_gravity_point_distance_scale); - ClassDB::bind_method(D_METHOD("get_gravity_point_distance_scale"), &Area3D::get_gravity_point_distance_scale); + ClassDB::bind_method(D_METHOD("set_gravity_point_unit_distance", "distance_scale"), &Area3D::set_gravity_point_unit_distance); + ClassDB::bind_method(D_METHOD("get_gravity_point_unit_distance"), &Area3D::get_gravity_point_unit_distance); ClassDB::bind_method(D_METHOD("set_gravity_point_center", "center"), &Area3D::set_gravity_point_center); ClassDB::bind_method(D_METHOD("get_gravity_point_center"), &Area3D::get_gravity_point_center); @@ -741,7 +741,7 @@ void Area3D::_bind_methods() { ADD_GROUP("Gravity", "gravity_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gravity_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_space_override_mode", "get_gravity_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_unit_distance", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp,suffix:m"), "set_gravity_point_unit_distance", "get_gravity_point_unit_distance"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_point_center", PROPERTY_HINT_NONE, "suffix:m"), "set_gravity_point_center", "get_gravity_point_center"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_direction"), "set_gravity_direction", "get_gravity_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-32,32,0.001,or_less,or_greater,suffix:m/s\u00B2"), "set_gravity", "get_gravity"); diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index 91b91f741a..607e0d2af8 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -51,7 +51,7 @@ private: Vector3 gravity_vec; real_t gravity = 0.0; bool gravity_is_point = false; - real_t gravity_distance_scale = 0.0; + real_t gravity_point_unit_distance = 0.0; SpaceOverride linear_damp_space_override = SPACE_OVERRIDE_DISABLED; SpaceOverride angular_damp_space_override = SPACE_OVERRIDE_DISABLED; @@ -155,8 +155,8 @@ public: void set_gravity_is_point(bool p_enabled); bool is_gravity_a_point() const; - void set_gravity_point_distance_scale(real_t p_scale); - real_t get_gravity_point_distance_scale() const; + void set_gravity_point_unit_distance(real_t p_scale); + real_t get_gravity_point_unit_distance() const; void set_gravity_point_center(const Vector3 &p_center); const Vector3 &get_gravity_point_center() const; diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 7ed18d2d41..436b936586 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -284,14 +284,12 @@ void AudioStreamPlayer3D::_notification(int p_what) { volume_vector = _update_panning(); } - if (setplay.get() >= 0 && stream.is_valid()) { + if (setplayback.is_valid() && setplay.get() >= 0) { active.set(); - Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback(); - ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); HashMap<StringName, Vector<AudioFrame>> bus_map; bus_map[_get_actual_bus()] = volume_vector; - AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz); - stream_playbacks.push_back(new_playback); + AudioServer::get_singleton()->start_playback_stream(setplayback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz); + setplayback.unref(); setplay.set(-1); } @@ -580,14 +578,21 @@ void AudioStreamPlayer3D::play(float p_from_pos) { if (stream->is_monophonic() && is_playing()) { stop(); } + Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback(); + ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback."); + + stream_playbacks.push_back(stream_playback); + setplayback = stream_playback; setplay.set(p_from_pos); active.set(); set_physics_process_internal(true); } void AudioStreamPlayer3D::seek(float p_seconds) { - stop(); - play(p_seconds); + if (is_playing()) { + stop(); + play(p_seconds); + } } void AudioStreamPlayer3D::stop() { @@ -783,6 +788,10 @@ bool AudioStreamPlayer3D::get_stream_paused() const { return false; } +bool AudioStreamPlayer3D::has_stream_playback() { + return !stream_playbacks.is_empty(); +} + Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() { ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback()."); return stream_playbacks[stream_playbacks.size() - 1]; @@ -875,6 +884,7 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_panning_strength", "panning_strength"), &AudioStreamPlayer3D::set_panning_strength); ClassDB::bind_method(D_METHOD("get_panning_strength"), &AudioStreamPlayer3D::get_panning_strength); + ClassDB::bind_method(D_METHOD("has_stream_playback"), &AudioStreamPlayer3D::has_stream_playback); ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 67cec91896..bba8f10761 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -69,6 +69,7 @@ private: SafeFlag active{ false }; SafeNumeric<float> setplay{ -1.0 }; + Ref<AudioStreamPlayback> setplayback; AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE; float volume_db = 0.0; @@ -188,6 +189,7 @@ public: void set_panning_strength(float p_panning_strength); float get_panning_strength() const; + bool has_stream_playback(); Ref<AudioStreamPlayback> get_stream_playback(); AudioStreamPlayer3D(); diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index d0f71768d2..b39ca43d2e 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -109,6 +109,15 @@ void Label3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold", "threshold"), &Label3D::set_alpha_scissor_threshold); ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold"), &Label3D::get_alpha_scissor_threshold); + ClassDB::bind_method(D_METHOD("set_alpha_hash_scale", "threshold"), &Label3D::set_alpha_hash_scale); + ClassDB::bind_method(D_METHOD("get_alpha_hash_scale"), &Label3D::get_alpha_hash_scale); + + ClassDB::bind_method(D_METHOD("set_alpha_antialiasing", "alpha_aa"), &Label3D::set_alpha_antialiasing); + ClassDB::bind_method(D_METHOD("get_alpha_antialiasing"), &Label3D::get_alpha_antialiasing); + + ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge", "edge"), &Label3D::set_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge"), &Label3D::get_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &Label3D::set_texture_filter); ClassDB::bind_method(D_METHOD("get_texture_filter"), &Label3D::get_texture_filter); @@ -127,8 +136,11 @@ void Label3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE); - ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass,Alpha Hash"), "set_alpha_cut_mode", "get_alpha_cut_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_outline_render_priority", "get_outline_render_priority"); @@ -162,6 +174,7 @@ void Label3D::_bind_methods() { BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED); BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD); BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS); + BIND_ENUM_CONSTANT(ALPHA_CUT_HASH); } void Label3D::_validate_property(PropertyInfo &p_property) const { @@ -350,13 +363,24 @@ void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, RS::get_singleton()->material_set_param(surf.material, "uv2_offset", Vector3(0, 0, 0)); RS::get_singleton()->material_set_param(surf.material, "uv2_scale", Vector3(1, 1, 1)); RS::get_singleton()->material_set_param(surf.material, "alpha_scissor_threshold", alpha_scissor_threshold); + RS::get_singleton()->material_set_param(surf.material, "alpha_hash_scale", alpha_hash_scale); + RS::get_singleton()->material_set_param(surf.material, "alpha_antialiasing_edge", alpha_antialiasing_edge); if (msdf) { RS::get_singleton()->material_set_param(surf.material, "msdf_pixel_range", TS->font_get_msdf_pixel_range(p_glyph.font_rid)); RS::get_singleton()->material_set_param(surf.material, "msdf_outline_size", p_outline_size); } + BaseMaterial3D::Transparency mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA; + if (get_alpha_cut_mode() == ALPHA_CUT_DISCARD) { + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_SCISSOR; + } else if (get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS) { + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS; + } else if (get_alpha_cut_mode() == ALPHA_CUT_HASH) { + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_HASH; + } + RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), true, get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, msdf, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), texture_filter, &shader_rid); + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, msdf, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), texture_filter, alpha_antialiasing_mode, &shader_rid); RS::get_singleton()->material_set_shader(surf.material, shader_rid); RS::get_singleton()->material_set_param(surf.material, "texture_albedo", tex); @@ -906,7 +930,7 @@ StandardMaterial3D::BillboardMode Label3D::get_billboard_mode() const { } void Label3D::set_alpha_cut_mode(AlphaCutMode p_mode) { - ERR_FAIL_INDEX(p_mode, 3); + ERR_FAIL_INDEX(p_mode, ALPHA_CUT_MAX); if (alpha_cut != p_mode) { alpha_cut = p_mode; _queue_update(); @@ -929,6 +953,17 @@ Label3D::AlphaCutMode Label3D::get_alpha_cut_mode() const { return alpha_cut; } +void Label3D::set_alpha_hash_scale(float p_hash_scale) { + if (alpha_hash_scale != p_hash_scale) { + alpha_hash_scale = p_hash_scale; + _queue_update(); + } +} + +float Label3D::get_alpha_hash_scale() const { + return alpha_hash_scale; +} + void Label3D::set_alpha_scissor_threshold(float p_threshold) { if (alpha_scissor_threshold != p_threshold) { alpha_scissor_threshold = p_threshold; @@ -940,6 +975,28 @@ float Label3D::get_alpha_scissor_threshold() const { return alpha_scissor_threshold; } +void Label3D::set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa) { + if (alpha_antialiasing_mode != p_alpha_aa) { + alpha_antialiasing_mode = p_alpha_aa; + _queue_update(); + } +} + +BaseMaterial3D::AlphaAntiAliasing Label3D::get_alpha_antialiasing() const { + return alpha_antialiasing_mode; +} + +void Label3D::set_alpha_antialiasing_edge(float p_edge) { + if (alpha_antialiasing_edge != p_edge) { + alpha_antialiasing_edge = p_edge; + _queue_update(); + } +} + +float Label3D::get_alpha_antialiasing_edge() const { + return alpha_antialiasing_edge; +} + Label3D::Label3D() { for (int i = 0; i < FLAG_MAX; i++) { flags[i] = (i == FLAG_DOUBLE_SIDED); diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h index 96cc941209..912f485354 100644 --- a/scene/3d/label_3d.h +++ b/scene/3d/label_3d.h @@ -51,7 +51,9 @@ public: enum AlphaCutMode { ALPHA_CUT_DISABLED, ALPHA_CUT_DISCARD, - ALPHA_CUT_OPAQUE_PREPASS + ALPHA_CUT_OPAQUE_PREPASS, + ALPHA_CUT_HASH, + ALPHA_CUT_MAX }; private: @@ -59,6 +61,9 @@ private: bool flags[FLAG_MAX] = {}; AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; float alpha_scissor_threshold = 0.5; + float alpha_hash_scale = 1.0; + StandardMaterial3D::AlphaAntiAliasing alpha_antialiasing_mode = StandardMaterial3D::ALPHA_ANTIALIASING_OFF; + float alpha_antialiasing_edge = 0.0f; AABB aabb; @@ -228,6 +233,15 @@ public: void set_alpha_scissor_threshold(float p_threshold); float get_alpha_scissor_threshold() const; + void set_alpha_hash_scale(float p_hash_scale); + float get_alpha_hash_scale() const; + + void set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa); + BaseMaterial3D::AlphaAntiAliasing get_alpha_antialiasing() const; + + void set_alpha_antialiasing_edge(float p_edge); + float get_alpha_antialiasing_edge() const; + void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode); StandardMaterial3D::BillboardMode get_billboard_mode() const; diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 0034bf78b9..4aa6e61ec5 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -80,10 +80,10 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent3D::set_navigation_map); ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent3D::get_navigation_map); - ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent3D::set_target_location); - ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent3D::get_target_location); + ClassDB::bind_method(D_METHOD("set_target_position", "position"), &NavigationAgent3D::set_target_position); + ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent3D::get_target_position); - ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent3D::get_next_location); + ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent3D::get_next_path_position); ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent3D::distance_to_target); ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent3D::set_velocity); ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent3D::get_current_navigation_result); @@ -92,12 +92,12 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent3D::is_target_reached); ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent3D::is_target_reachable); ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent3D::is_navigation_finished); - ClassDB::bind_method(D_METHOD("get_final_location"), &NavigationAgent3D::get_final_location); + ClassDB::bind_method(D_METHOD("get_final_position"), &NavigationAgent3D::get_final_position); ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent3D::_avoidance_done); ADD_GROUP("Pathfinding", ""); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_location", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_location", "get_target_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_target_position", "get_target_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_path_desired_distance", "get_path_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_target_desired_distance", "get_target_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,suffix:m"), "set_agent_height_offset", "get_agent_height_offset"); @@ -212,9 +212,9 @@ NavigationAgent3D::~NavigationAgent3D() { void NavigationAgent3D::set_avoidance_enabled(bool p_enabled) { avoidance_enabled = p_enabled; if (avoidance_enabled) { - NavigationServer3D::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done"); + NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done)); } else { - NavigationServer3D::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); + NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable()); } } @@ -224,7 +224,8 @@ bool NavigationAgent3D::get_avoidance_enabled() const { void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) { // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map - NavigationServer3D::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); + NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable()); + if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) { // place agent on navigation map first or else the RVO agent callback creation fails silently later agent_parent = Object::cast_to<Node3D>(p_agent_parent); @@ -233,6 +234,7 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) { } else { NavigationServer3D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_3d()->get_navigation_map()); } + // create new avoidance callback if enabled set_avoidance_enabled(avoidance_enabled); } else { @@ -344,17 +346,17 @@ real_t NavigationAgent3D::get_path_max_distance() { return path_max_distance; } -void NavigationAgent3D::set_target_location(Vector3 p_location) { - target_location = p_location; +void NavigationAgent3D::set_target_position(Vector3 p_position) { + target_position = p_position; target_position_submitted = true; _request_repath(); } -Vector3 NavigationAgent3D::get_target_location() const { - return target_location; +Vector3 NavigationAgent3D::get_target_position() const { + return target_position; } -Vector3 NavigationAgent3D::get_next_location() { +Vector3 NavigationAgent3D::get_next_path_position() { update_navigation(); const Vector<Vector3> &navigation_path = navigation_result->get_path(); @@ -368,7 +370,7 @@ Vector3 NavigationAgent3D::get_next_location() { real_t NavigationAgent3D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); - return agent_parent->get_global_transform().origin.distance_to(target_location); + return agent_parent->get_global_transform().origin.distance_to(target_position); } bool NavigationAgent3D::is_target_reached() const { @@ -376,7 +378,7 @@ bool NavigationAgent3D::is_target_reached() const { } bool NavigationAgent3D::is_target_reachable() { - return target_desired_distance >= get_final_location().distance_to(target_location); + return target_desired_distance >= get_final_position().distance_to(target_position); } bool NavigationAgent3D::is_navigation_finished() { @@ -384,7 +386,7 @@ bool NavigationAgent3D::is_navigation_finished() { return navigation_finished; } -Vector3 NavigationAgent3D::get_final_location() { +Vector3 NavigationAgent3D::get_final_position() { update_navigation(); const Vector<Vector3> &navigation_path = navigation_result->get_path(); @@ -467,7 +469,7 @@ void NavigationAgent3D::update_navigation() { if (reload_path) { navigation_query->set_start_position(origin); - navigation_query->set_target_position(target_location); + navigation_query->set_target_position(target_position); navigation_query->set_navigation_layers(navigation_layers); navigation_query->set_metadata_flags(path_metadata_flags); @@ -489,7 +491,7 @@ void NavigationAgent3D::update_navigation() { // Check if we can advance the navigation path if (navigation_finished == false) { - // Advances to the next far away location. + // Advances to the next far away position. const Vector<Vector3> &navigation_path = navigation_result->get_path(); const Vector<int32_t> &navigation_path_types = navigation_result->get_path_types(); const TypedArray<RID> &navigation_path_rids = navigation_result->get_path_rids(); @@ -499,7 +501,7 @@ void NavigationAgent3D::update_navigation() { Dictionary details; const Vector3 waypoint = navigation_path[navigation_path_index]; - details[SNAME("location")] = waypoint; + details[SNAME("position")] = waypoint; int waypoint_type = -1; if (path_metadata_flags.has_flag(NavigationPathQueryParameters3D::PathMetadataFlags::PATH_METADATA_INCLUDE_TYPES)) { diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 91be068392..12f83ce6a8 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -62,7 +62,7 @@ class NavigationAgent3D : public Node { real_t path_max_distance = 3.0; - Vector3 target_location; + Vector3 target_position; bool target_position_submitted = false; Ref<NavigationPathQueryParameters3D> navigation_query; Ref<NavigationPathQueryResult3D> navigation_result; @@ -155,10 +155,10 @@ public: void set_path_max_distance(real_t p_pmd); real_t get_path_max_distance(); - void set_target_location(Vector3 p_location); - Vector3 get_target_location() const; + void set_target_position(Vector3 p_position); + Vector3 get_target_position() const; - Vector3 get_next_location(); + Vector3 get_next_path_position(); Ref<NavigationPathQueryResult3D> get_current_navigation_result() const { return navigation_result; @@ -174,7 +174,7 @@ public: bool is_target_reached() const; bool is_target_reachable(); bool is_navigation_finished(); - Vector3 get_final_location(); + Vector3 get_final_position(); void set_velocity(Vector3 p_velocity); void _avoidance_done(Vector3 p_new_velocity); diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index e058ef62d0..f47fcfaf51 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -70,10 +70,10 @@ void NavigationLink3D::_update_debug_mesh() { Vector<Vector3> lines; // Draw line between the points. - lines.push_back(start_location); - lines.push_back(end_location); + lines.push_back(start_position); + lines.push_back(end_position); - // Draw start location search radius + // Draw start position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -84,21 +84,21 @@ void NavigationLink3D::_update_debug_mesh() { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(start_location + Vector3(0, a.x, a.y)); - lines.append(start_location + Vector3(0, b.x, b.y)); + lines.append(start_position + Vector3(0, a.x, a.y)); + lines.append(start_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(start_location + Vector3(a.x, 0, a.y)); - lines.append(start_location + Vector3(b.x, 0, b.y)); + lines.append(start_position + Vector3(a.x, 0, a.y)); + lines.append(start_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(start_location + Vector3(a.x, a.y, 0)); - lines.append(start_location + Vector3(b.x, b.y, 0)); + lines.append(start_position + Vector3(a.x, a.y, 0)); + lines.append(start_position + Vector3(b.x, b.y, 0)); break; } } - // Draw end location search radius + // Draw end position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -109,16 +109,16 @@ void NavigationLink3D::_update_debug_mesh() { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(end_location + Vector3(0, a.x, a.y)); - lines.append(end_location + Vector3(0, b.x, b.y)); + lines.append(end_position + Vector3(0, a.x, a.y)); + lines.append(end_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(end_location + Vector3(a.x, 0, a.y)); - lines.append(end_location + Vector3(b.x, 0, b.y)); + lines.append(end_position + Vector3(a.x, 0, a.y)); + lines.append(end_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(end_location + Vector3(a.x, a.y, 0)); - lines.append(end_location + Vector3(b.x, b.y, 0)); + lines.append(end_position + Vector3(a.x, a.y, 0)); + lines.append(end_position + Vector3(b.x, b.y, 0)); break; } } @@ -157,11 +157,11 @@ void NavigationLink3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink3D::set_navigation_layer_value); ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink3D::get_navigation_layer_value); - ClassDB::bind_method(D_METHOD("set_start_location", "location"), &NavigationLink3D::set_start_location); - ClassDB::bind_method(D_METHOD("get_start_location"), &NavigationLink3D::get_start_location); + ClassDB::bind_method(D_METHOD("set_start_position", "position"), &NavigationLink3D::set_start_position); + ClassDB::bind_method(D_METHOD("get_start_position"), &NavigationLink3D::get_start_position); - ClassDB::bind_method(D_METHOD("set_end_location", "location"), &NavigationLink3D::set_end_location); - ClassDB::bind_method(D_METHOD("get_end_location"), &NavigationLink3D::get_end_location); + ClassDB::bind_method(D_METHOD("set_end_position", "position"), &NavigationLink3D::set_end_position); + ClassDB::bind_method(D_METHOD("get_end_position"), &NavigationLink3D::get_end_position); ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink3D::set_enter_cost); ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink3D::get_enter_cost); @@ -172,12 +172,38 @@ void NavigationLink3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_location"), "set_start_location", "get_start_location"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "end_location"), "set_end_location", "get_end_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_position"), "set_start_position", "get_start_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "end_position"), "set_end_position", "get_end_position"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); } +#ifndef DISABLE_DEPRECATED +bool NavigationLink3D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "start_location") { + set_start_position(p_value); + return true; + } + if (p_name == "end_location") { + set_end_position(p_value); + return true; + } + return false; +} + +bool NavigationLink3D::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "start_location") { + r_ret = get_start_position(); + return true; + } + if (p_name == "end_location") { + r_ret = get_end_position(); + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + void NavigationLink3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -186,8 +212,8 @@ void NavigationLink3D::_notification(int p_what) { // Update global positions for the link. Transform3D gt = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); - NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); } #ifdef DEBUG_ENABLED @@ -197,8 +223,8 @@ void NavigationLink3D::_notification(int p_what) { case NOTIFICATION_TRANSFORM_CHANGED: { // Update global positions for the link. Transform3D gt = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); - NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); + NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); #ifdef DEBUG_ENABLED if (is_inside_tree() && debug_instance.is_valid()) { @@ -316,19 +342,19 @@ bool NavigationLink3D::get_navigation_layer_value(int p_layer_number) const { return get_navigation_layers() & (1 << (p_layer_number - 1)); } -void NavigationLink3D::set_start_location(Vector3 p_location) { - if (start_location.is_equal_approx(p_location)) { +void NavigationLink3D::set_start_position(Vector3 p_position) { + if (start_position.is_equal_approx(p_position)) { return; } - start_location = p_location; + start_position = p_position; if (!is_inside_tree()) { return; } Transform3D gt = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer3D::get_singleton()->link_set_start_position(link, gt.xform(start_position)); #ifdef DEBUG_ENABLED _update_debug_mesh(); @@ -338,19 +364,19 @@ void NavigationLink3D::set_start_location(Vector3 p_location) { update_configuration_warnings(); } -void NavigationLink3D::set_end_location(Vector3 p_location) { - if (end_location.is_equal_approx(p_location)) { +void NavigationLink3D::set_end_position(Vector3 p_position) { + if (end_position.is_equal_approx(p_position)) { return; } - end_location = p_location; + end_position = p_position; if (!is_inside_tree()) { return; } Transform3D gt = get_global_transform(); - NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + NavigationServer3D::get_singleton()->link_set_end_position(link, gt.xform(end_position)); #ifdef DEBUG_ENABLED _update_debug_mesh(); @@ -385,8 +411,8 @@ void NavigationLink3D::set_travel_cost(real_t p_travel_cost) { PackedStringArray NavigationLink3D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); - if (start_location.is_equal_approx(end_location)) { - warnings.push_back(RTR("NavigationLink3D start location should be different than the end location to be useful.")); + if (start_position.is_equal_approx(end_position)) { + warnings.push_back(RTR("NavigationLink3D start position should be different than the end position to be useful.")); } return warnings; diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index 175c5cdd5d..5c9ec36189 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -40,8 +40,8 @@ class NavigationLink3D : public Node3D { RID link; bool bidirectional = true; uint32_t navigation_layers = 1; - Vector3 end_location; - Vector3 start_location; + Vector3 end_position; + Vector3 start_position; real_t enter_cost = 0.0; real_t travel_cost = 1.0; @@ -56,6 +56,11 @@ protected: static void _bind_methods(); void _notification(int p_what); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; +#endif // DISABLE_DEPRECATED + public: NavigationLink3D(); ~NavigationLink3D(); @@ -72,11 +77,11 @@ public: void set_navigation_layer_value(int p_layer_number, bool p_value); bool get_navigation_layer_value(int p_layer_number) const; - void set_start_location(Vector3 p_location); - Vector3 get_start_location() const { return start_location; } + void set_start_position(Vector3 p_position); + Vector3 get_start_position() const { return start_position; } - void set_end_location(Vector3 p_location); - Vector3 get_end_location() const { return end_location; } + void set_end_position(Vector3 p_position); + Vector3 get_end_position() const { return end_position; } void set_enter_cost(real_t p_enter_cost); real_t get_enter_cost() const { return enter_cost; } diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 71d76c0d1c..59e4a0b718 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -245,8 +245,26 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, RS::get_singleton()->mesh_set_custom_aabb(mesh_new, aabb_new); set_aabb(aabb_new); + RS::get_singleton()->material_set_param(get_material(), "alpha_scissor_threshold", alpha_scissor_threshold); + RS::get_singleton()->material_set_param(get_material(), "alpha_hash_scale", alpha_hash_scale); + RS::get_singleton()->material_set_param(get_material(), "alpha_antialiasing_edge", alpha_antialiasing_edge); + + BaseMaterial3D::Transparency mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_DISABLED; + if (get_draw_flag(FLAG_TRANSPARENT)) { + if (get_alpha_cut_mode() == ALPHA_CUT_DISCARD) { + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_SCISSOR; + } else if (get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS) { + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS; + } else if (get_alpha_cut_mode() == ALPHA_CUT_HASH) { + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_HASH; + } else { + mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA; + } + } + RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), alpha_antialiasing_mode, &shader_rid); + if (last_shader != shader_rid) { RS::get_singleton()->material_set_shader(get_material(), shader_rid); last_shader = shader_rid; @@ -433,7 +451,7 @@ bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const { } void SpriteBase3D::set_alpha_cut_mode(AlphaCutMode p_mode) { - ERR_FAIL_INDEX(p_mode, 3); + ERR_FAIL_INDEX(p_mode, ALPHA_CUT_MAX); alpha_cut = p_mode; _queue_redraw(); } @@ -442,6 +460,50 @@ SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const { return alpha_cut; } +void SpriteBase3D::set_alpha_hash_scale(float p_hash_scale) { + if (alpha_hash_scale != p_hash_scale) { + alpha_hash_scale = p_hash_scale; + _queue_redraw(); + } +} + +float SpriteBase3D::get_alpha_hash_scale() const { + return alpha_hash_scale; +} + +void SpriteBase3D::set_alpha_scissor_threshold(float p_threshold) { + if (alpha_scissor_threshold != p_threshold) { + alpha_scissor_threshold = p_threshold; + _queue_redraw(); + } +} + +float SpriteBase3D::get_alpha_scissor_threshold() const { + return alpha_scissor_threshold; +} + +void SpriteBase3D::set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa) { + if (alpha_antialiasing_mode != p_alpha_aa) { + alpha_antialiasing_mode = p_alpha_aa; + _queue_redraw(); + } +} + +BaseMaterial3D::AlphaAntiAliasing SpriteBase3D::get_alpha_antialiasing() const { + return alpha_antialiasing_mode; +} + +void SpriteBase3D::set_alpha_antialiasing_edge(float p_edge) { + if (alpha_antialiasing_edge != p_edge) { + alpha_antialiasing_edge = p_edge; + _queue_redraw(); + } +} + +float SpriteBase3D::get_alpha_antialiasing_edge() const { + return alpha_antialiasing_edge; +} + void SpriteBase3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { ERR_FAIL_INDEX(p_mode, 3); // Cannot use BILLBOARD_PARTICLES. billboard_mode = p_mode; @@ -494,6 +556,18 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alpha_cut_mode", "mode"), &SpriteBase3D::set_alpha_cut_mode); ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &SpriteBase3D::get_alpha_cut_mode); + ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold", "threshold"), &SpriteBase3D::set_alpha_scissor_threshold); + ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold"), &SpriteBase3D::get_alpha_scissor_threshold); + + ClassDB::bind_method(D_METHOD("set_alpha_hash_scale", "threshold"), &SpriteBase3D::set_alpha_hash_scale); + ClassDB::bind_method(D_METHOD("get_alpha_hash_scale"), &SpriteBase3D::get_alpha_hash_scale); + + ClassDB::bind_method(D_METHOD("set_alpha_antialiasing", "alpha_aa"), &SpriteBase3D::set_alpha_antialiasing); + ClassDB::bind_method(D_METHOD("get_alpha_antialiasing"), &SpriteBase3D::get_alpha_antialiasing); + + ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge", "edge"), &SpriteBase3D::set_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge"), &SpriteBase3D::get_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &SpriteBase3D::set_billboard_mode); ClassDB::bind_method(D_METHOD("get_billboard_mode"), &SpriteBase3D::get_billboard_mode); @@ -519,7 +593,11 @@ void SpriteBase3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE); - ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass,Alpha Hash"), "set_alpha_cut_mode", "get_alpha_cut_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); @@ -533,6 +611,7 @@ void SpriteBase3D::_bind_methods() { BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED); BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD); BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS); + BIND_ENUM_CONSTANT(ALPHA_CUT_HASH); } SpriteBase3D::SpriteBase3D() { @@ -550,7 +629,6 @@ SpriteBase3D::SpriteBase3D() { RS::get_singleton()->material_set_param(material, "uv1_scale", Vector3(1, 1, 1)); RS::get_singleton()->material_set_param(material, "uv2_offset", Vector3(0, 0, 0)); RS::get_singleton()->material_set_param(material, "uv2_scale", Vector3(1, 1, 1)); - RS::get_singleton()->material_set_param(material, "alpha_scissor_threshold", 0.5); mesh = RenderingServer::get_singleton()->mesh_create(); @@ -866,7 +944,6 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &p_property) const { } if (p_property.name == "animation") { - p_property.hint = PROPERTY_HINT_ENUM; List<StringName> names; frames->get_animation_list(&names); names.sort_custom<StringName::AlphCompare>(); @@ -916,6 +993,12 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &p_property) const { void AnimatedSprite3D::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: { + if (!Engine::get_singleton()->is_editor_hint() && !frames.is_null() && frames->has_animation(autoplay)) { + play(autoplay); + } + } break; + case NOTIFICATION_INTERNAL_PROCESS: { if (frames.is_null() || !frames->has_animation(animation)) { return; @@ -925,7 +1008,8 @@ void AnimatedSprite3D::_notification(int p_what) { int i = 0; while (remaining) { // Animation speed may be changed by animation_finished or frame_changed signals. - double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + double speed = frames->get_animation_speed(animation) * speed_scale * custom_speed_scale * frame_speed_scale; + double abs_speed = Math::abs(speed); if (speed == 0) { return; // Do nothing. @@ -934,53 +1018,57 @@ void AnimatedSprite3D::_notification(int p_what) { // Frame count may be changed by animation_finished or frame_changed signals. int fc = frames->get_frame_count(animation); - if (timeout <= 0) { - int last_frame = fc - 1; - if (!playing_backwards) { - // Forward. + int last_frame = fc - 1; + if (!signbit(speed)) { + // Forwards. + if (frame_progress >= 1.0) { if (frame >= last_frame) { if (frames->get_animation_loop(animation)) { frame = 0; - emit_signal(SceneStringNames::get_singleton()->animation_finished); + emit_signal("animation_looped"); } else { frame = last_frame; - if (!is_over) { - is_over = true; - emit_signal(SceneStringNames::get_singleton()->animation_finished); - } + pause(); + emit_signal(SceneStringNames::get_singleton()->animation_finished); + return; } } else { frame++; } - } else { - // Reversed. + _calc_frame_speed_scale(); + frame_progress = 0.0; + _queue_redraw(); + emit_signal(SceneStringNames::get_singleton()->frame_changed); + } + double to_process = MIN((1.0 - frame_progress) / abs_speed, remaining); + frame_progress += to_process * abs_speed; + remaining -= to_process; + } else { + // Backwards. + if (frame_progress <= 0) { if (frame <= 0) { if (frames->get_animation_loop(animation)) { frame = last_frame; - emit_signal(SceneStringNames::get_singleton()->animation_finished); + emit_signal("animation_looped"); } else { frame = 0; - if (!is_over) { - is_over = true; - emit_signal(SceneStringNames::get_singleton()->animation_finished); - } + pause(); + emit_signal(SceneStringNames::get_singleton()->animation_finished); + return; } } else { frame--; } + _calc_frame_speed_scale(); + frame_progress = 1.0; + _queue_redraw(); + emit_signal(SceneStringNames::get_singleton()->frame_changed); } - - timeout = _get_frame_duration(); - - _queue_redraw(); - - emit_signal(SceneStringNames::get_singleton()->frame_changed); + double to_process = MIN(frame_progress / abs_speed, remaining); + frame_progress -= to_process * abs_speed; + remaining -= to_process; } - double to_process = MIN(timeout / speed, remaining); - timeout -= to_process * speed; - remaining -= to_process; - i++; if (i > fc) { return; // Prevents freezing if to_process is each time much less than remaining. @@ -991,25 +1079,37 @@ void AnimatedSprite3D::_notification(int p_what) { } void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { + if (frames == p_frames) { + return; + } + if (frames.is_valid()) { frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed)); } - + stop(); frames = p_frames; if (frames.is_valid()) { frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed)); - } - if (frames.is_null()) { - frame = 0; - } else { - set_frame(frame); + List<StringName> al; + frames->get_animation_list(&al); + if (al.size() == 0) { + set_animation(StringName()); + set_autoplay(String()); + } else { + if (!frames->has_animation(animation)) { + set_animation(al[0]); + } + if (!frames->has_animation(autoplay)) { + set_autoplay(String()); + } + } } notify_property_list_changed(); - _reset_timeout(); _queue_redraw(); update_configuration_warnings(); + emit_signal("sprite_frames_changed"); } Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const { @@ -1017,44 +1117,63 @@ Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const { } void AnimatedSprite3D::set_frame(int p_frame) { + set_frame_and_progress(p_frame, signbit(get_playing_speed()) ? 1.0 : 0.0); +} + +int AnimatedSprite3D::get_frame() const { + return frame; +} + +void AnimatedSprite3D::set_frame_progress(real_t p_progress) { + frame_progress = p_progress; +} + +real_t AnimatedSprite3D::get_frame_progress() const { + return frame_progress; +} + +void AnimatedSprite3D::set_frame_and_progress(int p_frame, real_t p_progress) { if (frames.is_null()) { return; } - if (frames->has_animation(animation)) { - int limit = frames->get_frame_count(animation); - if (p_frame >= limit) { - p_frame = limit - 1; - } - } + bool has_animation = frames->has_animation(animation); + int end_frame = has_animation ? MAX(0, frames->get_frame_count(animation) - 1) : 0; + bool is_changed = frame != p_frame; if (p_frame < 0) { - p_frame = 0; + frame = 0; + } else if (has_animation && p_frame > end_frame) { + frame = end_frame; + } else { + frame = p_frame; } - if (frame == p_frame) { - return; - } + _calc_frame_speed_scale(); + frame_progress = p_progress; - frame = p_frame; - _reset_timeout(); + if (!is_changed) { + return; // No change, don't redraw. + } _queue_redraw(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } -int AnimatedSprite3D::get_frame() const { - return frame; -} - void AnimatedSprite3D::set_speed_scale(float p_speed_scale) { speed_scale = p_speed_scale; - playing_backwards = signbit(speed_scale) != backwards; } float AnimatedSprite3D::get_speed_scale() const { return speed_scale; } +float AnimatedSprite3D::get_playing_speed() const { + if (!playing) { + return 0; + } + return speed_scale * custom_speed_scale; +} + Rect2 AnimatedSprite3D::get_item_rect() const { if (frames.is_null() || !frames->has_animation(animation)) { return Rect2(0, 0, 1, 1); @@ -1085,69 +1204,131 @@ Rect2 AnimatedSprite3D::get_item_rect() const { } void AnimatedSprite3D::_res_changed() { - set_frame(frame); + set_frame_and_progress(frame, frame_progress); _queue_redraw(); notify_property_list_changed(); } -void AnimatedSprite3D::set_playing(bool p_playing) { - if (playing == p_playing) { - return; +bool AnimatedSprite3D::is_playing() const { + return playing; +} + +void AnimatedSprite3D::set_autoplay(const String &p_name) { + if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { + WARN_PRINT("Setting autoplay after the node has been added to the scene has no effect."); } - playing = p_playing; - playing_backwards = signbit(speed_scale) != backwards; - set_process_internal(playing); - notify_property_list_changed(); + + autoplay = p_name; } -bool AnimatedSprite3D::is_playing() const { - return playing; +String AnimatedSprite3D::get_autoplay() const { + return autoplay; } -void AnimatedSprite3D::play(const StringName &p_animation, bool p_backwards) { - backwards = p_backwards; - playing_backwards = signbit(speed_scale) != backwards; +void AnimatedSprite3D::play(const StringName &p_name, float p_custom_scale, bool p_from_end) { + StringName name = p_name; + + if (name == StringName()) { + name = animation; + } + + ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", name)); + ERR_FAIL_COND_MSG(!frames->get_animation_names().has(name), vformat("There is no animation with name '%s'.", name)); - if (p_animation) { - set_animation(p_animation); - if (frames.is_valid() && playing_backwards && get_frame() == 0) { - set_frame(frames->get_frame_count(p_animation) - 1); + if (frames->get_frame_count(name) == 0) { + return; + } + + playing = true; + custom_speed_scale = p_custom_scale; + + int end_frame = MAX(0, frames->get_frame_count(animation) - 1); + if (name != animation) { + animation = name; + if (p_from_end) { + set_frame_and_progress(end_frame, 1.0); + } else { + set_frame_and_progress(0, 0.0); + } + emit_signal("animation_changed"); + } else { + bool is_backward = signbit(speed_scale * custom_speed_scale); + if (p_from_end && is_backward && frame == 0 && frame_progress <= 0.0) { + set_frame_and_progress(end_frame, 1.0); + } else if (!p_from_end && !is_backward && frame == end_frame && frame_progress >= 1.0) { + set_frame_and_progress(0, 0.0); } } - is_over = false; - set_playing(true); + set_process_internal(true); + notify_property_list_changed(); + _queue_redraw(); +} + +void AnimatedSprite3D::play_backwards(const StringName &p_name) { + play(p_name, -1, true); +} + +void AnimatedSprite3D::_stop_internal(bool p_reset) { + playing = false; + if (p_reset) { + custom_speed_scale = 1.0; + set_frame_and_progress(0, 0.0); + } + notify_property_list_changed(); + set_process_internal(false); +} + +void AnimatedSprite3D::pause() { + _stop_internal(false); } void AnimatedSprite3D::stop() { - set_playing(false); - backwards = false; - _reset_timeout(); + _stop_internal(true); } double AnimatedSprite3D::_get_frame_duration() { if (frames.is_valid() && frames->has_animation(animation)) { return frames->get_frame_duration(animation, frame); } - return 0.0; + return 1.0; } -void AnimatedSprite3D::_reset_timeout() { - timeout = _get_frame_duration(); - is_over = false; +void AnimatedSprite3D::_calc_frame_speed_scale() { + frame_speed_scale = 1.0 / _get_frame_duration(); } -void AnimatedSprite3D::set_animation(const StringName &p_animation) { - ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation)); - ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation)); +void AnimatedSprite3D::set_animation(const StringName &p_name) { + if (animation == p_name) { + return; + } + + animation = p_name; - if (animation == p_animation) { + emit_signal("animation_changed"); + + if (frames == nullptr) { + animation = StringName(); + stop(); + ERR_FAIL_MSG(vformat("There is no animation with name '%s'.", p_name)); + } + + int frame_count = frames->get_frame_count(animation); + if (animation == StringName() || frame_count == 0) { + stop(); return; + } else if (!frames->get_animation_names().has(animation)) { + animation = StringName(); + stop(); + ERR_FAIL_MSG(vformat("There is no animation with name '%s'.", p_name)); + } + + if (signbit(get_playing_speed())) { + set_frame_and_progress(frame_count - 1, 1.0); + } else { + set_frame_and_progress(0, 0.0); } - animation = p_animation; - set_frame(0); - _reset_timeout(); notify_property_list_changed(); _queue_redraw(); } @@ -1175,35 +1356,58 @@ void AnimatedSprite3D::get_argument_options(const StringName &p_function, int p_ Node::get_argument_options(p_function, p_idx, r_options); } +#ifndef DISABLE_DEPRECATED +bool AnimatedSprite3D::_set(const StringName &p_name, const Variant &p_value) { + if ((p_name == SNAME("frames"))) { + set_sprite_frames(p_value); + return true; + } + return false; +} +#endif void AnimatedSprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite3D::set_sprite_frames); ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite3D::get_sprite_frames); - ClassDB::bind_method(D_METHOD("set_animation", "animation"), &AnimatedSprite3D::set_animation); + ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimatedSprite3D::set_animation); ClassDB::bind_method(D_METHOD("get_animation"), &AnimatedSprite3D::get_animation); - ClassDB::bind_method(D_METHOD("set_playing", "playing"), &AnimatedSprite3D::set_playing); + ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimatedSprite3D::set_autoplay); + ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimatedSprite3D::get_autoplay); + ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite3D::is_playing); - ClassDB::bind_method(D_METHOD("play", "anim", "backwards"), &AnimatedSprite3D::play, DEFVAL(StringName()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("play", "name", "custom_speed", "from_end"), &AnimatedSprite3D::play, DEFVAL(StringName()), DEFVAL(1.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("play_backwards", "name"), &AnimatedSprite3D::play_backwards, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("pause"), &AnimatedSprite3D::pause); ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite3D::stop); ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite3D::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite3D::get_frame); + ClassDB::bind_method(D_METHOD("set_frame_progress", "progress"), &AnimatedSprite3D::set_frame_progress); + ClassDB::bind_method(D_METHOD("get_frame_progress"), &AnimatedSprite3D::get_frame_progress); + + ClassDB::bind_method(D_METHOD("set_frame_and_progress", "frame", "progress"), &AnimatedSprite3D::set_frame_and_progress); + ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite3D::set_speed_scale); ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite3D::get_speed_scale); + ClassDB::bind_method(D_METHOD("get_playing_speed"), &AnimatedSprite3D::get_playing_speed); ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite3D::_res_changed); + ADD_SIGNAL(MethodInfo("sprite_frames_changed")); + ADD_SIGNAL(MethodInfo("animation_changed")); ADD_SIGNAL(MethodInfo("frame_changed")); + ADD_SIGNAL(MethodInfo("animation_looped")); ADD_SIGNAL(MethodInfo("animation_finished")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sprite_frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation", PROPERTY_HINT_ENUM, ""), "set_animation", "get_animation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_autoplay", "get_autoplay"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frame_progress", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_frame_progress", "get_frame_progress"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale"), "set_speed_scale", "get_speed_scale"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "set_playing", "is_playing"); } AnimatedSprite3D::AnimatedSprite3D() { diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index c5509aa723..1eb1211951 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -53,7 +53,9 @@ public: enum AlphaCutMode { ALPHA_CUT_DISABLED, ALPHA_CUT_DISCARD, - ALPHA_CUT_OPAQUE_PREPASS + ALPHA_CUT_OPAQUE_PREPASS, + ALPHA_CUT_HASH, + ALPHA_CUT_MAX }; private: @@ -85,6 +87,10 @@ private: bool flags[FLAG_MAX] = {}; AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; + float alpha_scissor_threshold = 0.5; + float alpha_hash_scale = 1.0; + StandardMaterial3D::AlphaAntiAliasing alpha_antialiasing_mode = StandardMaterial3D::ALPHA_ANTIALIASING_OFF; + float alpha_antialiasing_edge = 0.0f; StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; StandardMaterial3D::TextureFilter texture_filter = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; bool pending_update = false; @@ -143,6 +149,18 @@ public: void set_alpha_cut_mode(AlphaCutMode p_mode); AlphaCutMode get_alpha_cut_mode() const; + void set_alpha_scissor_threshold(float p_threshold); + float get_alpha_scissor_threshold() const; + + void set_alpha_hash_scale(float p_hash_scale); + float get_alpha_hash_scale() const; + + void set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa); + BaseMaterial3D::AlphaAntiAliasing get_alpha_antialiasing() const; + + void set_alpha_antialiasing_edge(float p_edge); + float get_alpha_antialiasing_edge() const; + void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode); StandardMaterial3D::BillboardMode get_billboard_mode() const; @@ -209,24 +227,29 @@ class AnimatedSprite3D : public SpriteBase3D { GDCLASS(AnimatedSprite3D, SpriteBase3D); Ref<SpriteFrames> frames; + String autoplay; + bool playing = false; - bool playing_backwards = false; - bool backwards = false; StringName animation = "default"; int frame = 0; float speed_scale = 1.0; + float custom_speed_scale = 1.0; bool centered = false; - bool is_over = false; - double timeout = 0.0; + real_t frame_speed_scale = 1.0; + real_t frame_progress = 0.0; void _res_changed(); double _get_frame_duration(); - void _reset_timeout(); + void _calc_frame_speed_scale(); + void _stop_internal(bool p_reset); protected: +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); +#endif virtual void _draw() override; static void _bind_methods(); void _notification(int p_what); @@ -236,20 +259,30 @@ public: void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; - void play(const StringName &p_animation = StringName(), bool p_backwards = false); + void play(const StringName &p_name = StringName(), float p_custom_scale = 1.0, bool p_from_end = false); + void play_backwards(const StringName &p_name = StringName()); + void pause(); void stop(); - void set_playing(bool p_playing); bool is_playing() const; - void set_animation(const StringName &p_animation); + void set_animation(const StringName &p_name); StringName get_animation() const; + void set_autoplay(const String &p_name); + String get_autoplay() const; + void set_frame(int p_frame); int get_frame() const; + void set_frame_progress(real_t p_progress); + real_t get_frame_progress() const; + + void set_frame_and_progress(int p_frame, real_t p_progress); + void set_speed_scale(float p_speed_scale); float get_speed_scale() const; + float get_playing_speed() const; virtual Rect2 get_item_rect() const override; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index a00b1d8ee1..45f4f690b9 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -644,9 +644,66 @@ AnimationNodeTimeSeek::AnimationNodeTimeSeek() { ///////////////////////////////////////////////// +bool AnimationNodeTransition::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (!path.begins_with("input_")) { + return false; + } + + int which = path.get_slicec('/', 0).get_slicec('_', 1).to_int(); + String what = path.get_slicec('/', 1); + + if (which == get_input_count() && what == "name") { + if (add_input(p_value)) { + return true; + } + return false; + } + + ERR_FAIL_INDEX_V(which, get_input_count(), false); + + if (what == "name") { + set_input_name(which, p_value); + } else if (what == "auto_advance") { + set_input_as_auto_advance(which, p_value); + } else if (what == "reset") { + set_input_reset(which, p_value); + } else { + return false; + } + + return true; +} + +bool AnimationNodeTransition::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (!path.begins_with("input_")) { + return false; + } + + int which = path.get_slicec('/', 0).get_slicec('_', 1).to_int(); + String what = path.get_slicec('/', 1); + + ERR_FAIL_INDEX_V(which, get_input_count(), false); + + if (what == "name") { + r_ret = get_input_name(which); + } else if (what == "auto_advance") { + r_ret = is_input_set_as_auto_advance(which); + } else if (what == "reset") { + r_ret = is_input_reset(which); + } else { + return false; + } + + return true; +} + void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) const { String anims; - for (int i = 0; i < enabled_inputs; i++) { + for (int i = 0; i < get_input_count(); i++) { if (i > 0) { anims += ","; } @@ -684,56 +741,47 @@ String AnimationNodeTransition::get_caption() const { return "Transition"; } -void AnimationNodeTransition::_update_inputs() { - while (get_input_count() < enabled_inputs) { - add_input(inputs[get_input_count()].name); +void AnimationNodeTransition::set_input_count(int p_inputs) { + for (int i = get_input_count(); i < p_inputs; i++) { + add_input("state_" + itos(i)); } - - while (get_input_count() > enabled_inputs) { + while (get_input_count() > p_inputs) { remove_input(get_input_count() - 1); } + notify_property_list_changed(); } -void AnimationNodeTransition::set_enabled_inputs(int p_inputs) { - ERR_FAIL_INDEX(p_inputs, MAX_INPUTS); - enabled_inputs = p_inputs; - _update_inputs(); +bool AnimationNodeTransition::add_input(const String &p_name) { + if (AnimationNode::add_input(p_name)) { + input_data.push_back(InputData()); + return true; + } + return false; } -int AnimationNodeTransition::get_enabled_inputs() { - return enabled_inputs; +void AnimationNodeTransition::remove_input(int p_index) { + input_data.remove_at(p_index); + AnimationNode::remove_input(p_index); } void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) { - ERR_FAIL_INDEX(p_input, MAX_INPUTS); - inputs[p_input].auto_advance = p_enable; + ERR_FAIL_INDEX(p_input, get_input_count()); + input_data.write[p_input].auto_advance = p_enable; } bool AnimationNodeTransition::is_input_set_as_auto_advance(int p_input) const { - ERR_FAIL_INDEX_V(p_input, MAX_INPUTS, false); - return inputs[p_input].auto_advance; -} - -void AnimationNodeTransition::set_input_caption(int p_input, const String &p_name) { - ERR_FAIL_INDEX(p_input, MAX_INPUTS); - inputs[p_input].name = p_name; - set_input_name(p_input, p_name); + ERR_FAIL_INDEX_V(p_input, get_input_count(), false); + return input_data[p_input].auto_advance; } -String AnimationNodeTransition::get_input_caption(int p_input) const { - ERR_FAIL_INDEX_V(p_input, MAX_INPUTS, String()); - return inputs[p_input].name; +void AnimationNodeTransition::set_input_reset(int p_input, bool p_enable) { + ERR_FAIL_INDEX(p_input, get_input_count()); + input_data.write[p_input].reset = p_enable; } -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; +bool AnimationNodeTransition::is_input_reset(int p_input) const { + ERR_FAIL_INDEX_V(p_input, get_input_count(), true); + return input_data[p_input].reset; } void AnimationNodeTransition::set_xfade_time(double p_fade) { @@ -752,14 +800,6 @@ Ref<Curve> AnimationNodeTransition::get_xfade_curve() const { return xfade_curve; } -void AnimationNodeTransition::set_reset(bool p_reset) { - reset = p_reset; -} - -bool AnimationNodeTransition::is_reset() const { - return reset; -} - double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_external_seeking) { String cur_transition_request = get_parameter(transition_request); int cur_current_index = get_parameter(current_index); @@ -772,11 +812,11 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex bool restart = false; if (!cur_transition_request.is_empty()) { - int new_idx = find_input_caption(cur_transition_request); + int new_idx = find_input(cur_transition_request); if (new_idx >= 0) { if (cur_current_index == new_idx) { // Transition to same state. - restart = reset; + restart = input_data[cur_current_index].reset; cur_prev_xfading = 0; set_parameter(prev_xfading, 0); cur_prev_index = -1; @@ -807,14 +847,14 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex cur_time = 0; } - if (cur_current_index < 0 || cur_current_index >= enabled_inputs || cur_prev_index >= enabled_inputs) { + if (cur_current_index < 0 || cur_current_index >= get_input_count() || cur_prev_index >= get_input_count()) { return 0; } double rem = 0.0; if (sync) { - for (int i = 0; i < enabled_inputs; i++) { + for (int i = 0; i < get_input_count(); i++) { if (i != cur_current_index && i != cur_prev_index) { blend_input(i, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); } @@ -831,8 +871,8 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex cur_time += p_time; } - if (inputs[cur_current_index].auto_advance && rem <= xfade_time) { - set_parameter(transition_request, get_input_caption((cur_current_index + 1) % enabled_inputs)); + if (input_data[cur_current_index].auto_advance && rem <= xfade_time) { + set_parameter(transition_request, get_input_name((cur_current_index + 1) % get_input_count())); } } else { // cross-fading from prev to current @@ -844,7 +884,7 @@ 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 (reset && !p_seek && switched) { //just switched, seek to start of current + if (input_data[cur_current_index].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_index, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true); @@ -869,28 +909,22 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex return rem; } -void AnimationNodeTransition::_validate_property(PropertyInfo &p_property) const { - if (p_property.name.begins_with("input_")) { - String n = p_property.name.get_slicec('/', 0).get_slicec('_', 1); - if (n != "count") { - int idx = n.to_int(); - if (idx >= enabled_inputs) { - p_property.usage = PROPERTY_USAGE_NONE; - } - } +void AnimationNodeTransition::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < get_input_count(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/auto_advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/reset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL)); } } void AnimationNodeTransition::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_enabled_inputs", "amount"), &AnimationNodeTransition::set_enabled_inputs); - ClassDB::bind_method(D_METHOD("get_enabled_inputs"), &AnimationNodeTransition::get_enabled_inputs); + ClassDB::bind_method(D_METHOD("set_input_count", "input_count"), &AnimationNodeTransition::set_input_count); ClassDB::bind_method(D_METHOD("set_input_as_auto_advance", "input", "enable"), &AnimationNodeTransition::set_input_as_auto_advance); ClassDB::bind_method(D_METHOD("is_input_set_as_auto_advance", "input"), &AnimationNodeTransition::is_input_set_as_auto_advance); - 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_input_reset", "input", "enable"), &AnimationNodeTransition::set_input_reset); + ClassDB::bind_method(D_METHOD("is_input_reset", "input"), &AnimationNodeTransition::is_input_reset); 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); @@ -898,24 +932,12 @@ 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_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,31,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, "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); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/auto_advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_input_as_auto_advance", "is_input_set_as_auto_advance", i); - } + ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED, "Inputs,input_"), "set_input_count", "get_input_count"); } AnimationNodeTransition::AnimationNodeTransition() { - for (int i = 0; i < MAX_INPUTS; i++) { - inputs[i].name = "state " + itos(i); - } } ///////////////////// diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index a1969bb621..3c27edbf70 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -276,16 +276,11 @@ public: class AnimationNodeTransition : public AnimationNodeSync { GDCLASS(AnimationNodeTransition, AnimationNodeSync); - enum { - MAX_INPUTS = 32 - }; struct InputData { - String name; bool auto_advance = false; + bool reset = true; }; - - InputData inputs[MAX_INPUTS]; - int enabled_inputs = 0; + Vector<InputData> input_data; StringName time = "time"; StringName prev_xfading = "prev_xfading"; @@ -299,13 +294,12 @@ class AnimationNodeTransition : public AnimationNodeSync { double xfade_time = 0.0; Ref<Curve> xfade_curve; - bool reset = true; - - void _update_inputs(); protected: + bool _get(const StringName &p_path, Variant &r_ret) const; + bool _set(const StringName &p_path, const Variant &p_value); static void _bind_methods(); - void _validate_property(PropertyInfo &p_property) const; + void _get_property_list(List<PropertyInfo> *p_list) const; public: virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; @@ -314,15 +308,16 @@ public: virtual String get_caption() const override; - void set_enabled_inputs(int p_inputs); - int get_enabled_inputs(); + void set_input_count(int p_inputs); + + virtual bool add_input(const String &p_name) override; + virtual void remove_input(int p_index) override; void set_input_as_auto_advance(int p_input, bool p_enable); bool is_input_set_as_auto_advance(int p_input) const; - 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_input_reset(int p_input, bool p_enable); + bool is_input_reset(int p_input) const; void set_xfade_time(double p_fade); double get_xfade_time() const; @@ -330,9 +325,6 @@ public: void set_xfade_curve(const Ref<Curve> &p_curve); Ref<Curve> get_xfade_curve() 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; AnimationNodeTransition(); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index f8c09b4bde..7fb831b3b2 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -228,6 +228,22 @@ float AnimationNodeStateMachinePlayback::get_current_length() const { return len_current; } +float AnimationNodeStateMachinePlayback::get_fade_from_play_pos() const { + return pos_fade_from; +} + +float AnimationNodeStateMachinePlayback::get_fade_from_length() const { + return len_fade_from; +} + +float AnimationNodeStateMachinePlayback::get_fading_time() const { + return fading_time; +} + +float AnimationNodeStateMachinePlayback::get_fading_pos() const { + return fading_pos; +} + bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel) { ERR_FAIL_COND_V(!playing, false); ERR_FAIL_COND_V(!p_state_machine->states.has(p_travel), false); @@ -466,7 +482,17 @@ double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_ if (fading_from != StringName()) { double fade_blend_inv = 1.0 - fade_blend; - p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + float fading_from_rem = 0.0; + fading_from_rem = p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. + //guess playback position + if (fading_from_rem > len_fade_from) { // weird but ok + len_fade_from = fading_from_rem; + } + + { //advance and loop check + float next_pos = len_fade_from - fading_from_rem; + pos_fade_from = next_pos; //looped + } if (fade_blend >= 1.0) { fading_from = StringName(); } @@ -633,6 +659,8 @@ double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_ } current = next; + pos_fade_from = pos_current; + len_fade_from = len_current; 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. diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 1b4e010a06..cf4d850aa6 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -118,6 +118,9 @@ class AnimationNodeStateMachinePlayback : public Resource { StringName next; }; + double len_fade_from = 0.0; + double pos_fade_from = 0.0; + double len_current = 0.0; double pos_current = 0.0; bool end_loop = false; @@ -164,6 +167,12 @@ public: float get_current_play_pos() const; float get_current_length() const; + float get_fade_from_play_pos() const; + float get_fade_from_length() const; + + float get_fading_time() const; + float get_fading_pos() const; + AnimationNodeStateMachinePlayback(); }; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 047997ca09..2e25d685d6 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -431,6 +431,17 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov } } + if (a->track_get_type(i) == Animation::TYPE_AUDIO) { + if (!node_cache->audio_anim.has(a->track_get_path(i).get_concatenated_names())) { + TrackNodeCache::AudioAnim aa; + aa.object = (Object *)child; + aa.audio_stream.instantiate(); + aa.audio_stream->set_polyphony(audio_max_polyphony); + + node_cache->audio_anim[a->track_get_path(i).get_concatenated_names()] = aa; + } + } + node_cache->last_setup_pass = setup_pass; } } @@ -451,6 +462,15 @@ static void _call_object(Object *p_object, const StringName &p_method, const Vec } } +Variant AnimationPlayer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { + Variant res; + if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, const_cast<Object *>(p_object), p_object_idx, res)) { + return res; + } + + return _post_process_key_value(p_anim, p_track, p_value, p_object, p_object_idx); +} + Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { switch (p_anim->track_get_type(p_track)) { #ifndef _3D_DISABLED @@ -473,7 +493,9 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count()); Animation *a = p_anim->animation.operator->(); +#ifdef TOOLS_ENABLED bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); +#endif // TOOLS_ENABLED bool backward = signbit(p_delta); for (int i = 0; i < a->get_track_count(); i++) { @@ -512,7 +534,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } - loc = _post_process_key_value(a, i, loc, nc->node_3d, nc->bone_idx); + loc = post_process_key_value(a, i, loc, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -540,7 +562,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } - rot = _post_process_key_value(a, i, rot, nc->node_3d, nc->bone_idx); + rot = post_process_key_value(a, i, rot, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -568,7 +590,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } - scale = _post_process_key_value(a, i, scale, nc->node_3d, nc->bone_idx); + scale = post_process_key_value(a, i, scale, nc->node_3d, nc->bone_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -596,7 +618,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (err != OK) { continue; } - blend = _post_process_key_value(a, i, blend, nc->node_blend_shape, nc->blend_shape_idx); + blend = post_process_key_value(a, i, blend, nc->node_blend_shape, nc->blend_shape_idx); if (nc->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); @@ -649,7 +671,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (p_time < first_key_time) { double c = Math::ease(p_time / first_key_time, transition); Variant first_value = a->track_get_key_value(i, first_key); - first_value = _post_process_key_value(a, i, first_value, nc->node); + first_value = post_process_key_value(a, i, first_value, nc->node); Variant interp_value = Animation::interpolate_variant(pa->capture, first_value, c); if (pa->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); @@ -670,7 +692,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (value == Variant()) { continue; } - value = _post_process_key_value(a, i, value, nc->node); + value = post_process_key_value(a, i, value, nc->node); if (pa->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); @@ -701,7 +723,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double for (int &F : indices) { Variant value = a->track_get_key_value(i, F); - value = _post_process_key_value(a, i, value, nc->node); + value = post_process_key_value(a, i, value, nc->node); switch (pa->special) { case SP_NONE: { bool valid; @@ -745,11 +767,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } break; case Animation::TYPE_METHOD: { - if (!nc->node || is_stopping) { +#ifdef TOOLS_ENABLED + if (!can_call) { continue; } - if (!p_is_current) { - break; +#endif // TOOLS_ENABLED + if (!p_is_current || !nc->node || is_stopping) { + continue; } List<int> indices; @@ -772,16 +796,12 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double for (int &E : indices) { StringName method = a->method_track_get_name(i, E); Vector<Variant> params = a->method_track_get_params(i, E); - #ifdef DEBUG_ENABLED if (!nc->node->has_method(method)) { ERR_PRINT("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'."); } #endif - - if (can_call) { - _call_object(nc->node, method, params, method_call_mode == ANIMATION_METHOD_CALL_DEFERRED); - } + _call_object(nc->node, method, params, method_call_mode == ANIMATION_METHOD_CALL_DEFERRED); } } break; @@ -796,7 +816,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double TrackNodeCache::BezierAnim *ba = &E->value; real_t bezier = a->bezier_track_interpolate(i, p_time); - bezier = _post_process_key_value(a, i, bezier, nc->node); + bezier = post_process_key_value(a, i, bezier, nc->node); if (ba->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX); cache_update_bezier[cache_update_bezier_size++] = ba; @@ -811,48 +831,40 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (!nc->node || is_stopping) { continue; } +#ifdef TOOLS_ENABLED + if (p_seeked && !can_call) { + continue; // To avoid spamming the preview in editor. + } +#endif // TOOLS_ENABLED + HashMap<StringName, TrackNodeCache::AudioAnim>::Iterator E = nc->audio_anim.find(a->track_get_path(i).get_concatenated_names()); + ERR_CONTINUE(!E); //should it continue, or create a new one? - if (p_seeked) { - //find whatever should be playing - int idx = a->track_find_key(i, p_time); - if (idx < 0) { - continue; - } - - Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx); - if (!stream.is_valid()) { - nc->node->call(SNAME("stop")); - nc->audio_playing = false; - playing_caches.erase(nc); - } else { - float start_ofs = a->audio_track_get_key_start_offset(i, idx); - start_ofs += p_time - a->track_get_key_time(i, idx); - float end_ofs = a->audio_track_get_key_end_offset(i, idx); - float len = stream->get_length(); - - if (start_ofs > len - end_ofs) { - nc->node->call(SNAME("stop")); - nc->audio_playing = false; - playing_caches.erase(nc); - continue; - } - - nc->node->call(SNAME("set_stream"), stream); - nc->node->call(SNAME("play"), start_ofs); - - nc->audio_playing = true; - playing_caches.insert(nc); - if (len && end_ofs > 0) { //force an end at a time - nc->audio_len = len - start_ofs - end_ofs; - } else { - nc->audio_len = 0; - } + TrackNodeCache::AudioAnim *aa = &E->value; + Node *asp = Object::cast_to<Node>(aa->object); + if (!asp) { + continue; + } + aa->length = a->get_length(); + aa->time = p_time; + aa->loop = a->get_loop_mode() != Animation::LOOP_NONE; + aa->backward = backward; + if (aa->accum_pass != accum_pass) { + ERR_CONTINUE(cache_update_audio_size >= NODE_CACHE_UPDATE_MAX); + cache_update_audio[cache_update_audio_size++] = aa; + aa->accum_pass = accum_pass; + } - nc->audio_start = p_time; + HashMap<int, TrackNodeCache::PlayingAudioStreamInfo> &map = aa->playing_streams; + // Find stream. + int idx = -1; + if (p_seeked) { + idx = a->track_find_key(i, p_time); + // Discard previous stream when seeking. + if (map.has(idx)) { + aa->audio_stream_playback->stop_stream(map[idx].index); + map.erase(idx); } - } else { - //find stuff to play List<int> to_play; if (p_started) { int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT); @@ -862,55 +874,47 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag); if (to_play.size()) { - int idx = to_play.back()->get(); - - Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx); - if (!stream.is_valid()) { - nc->node->call(SNAME("stop")); - nc->audio_playing = false; - playing_caches.erase(nc); - } else { - float start_ofs = a->audio_track_get_key_start_offset(i, idx); - float end_ofs = a->audio_track_get_key_end_offset(i, idx); - float len = stream->get_length(); - - nc->node->call(SNAME("set_stream"), stream); - nc->node->call(SNAME("play"), start_ofs); - - nc->audio_playing = true; - playing_caches.insert(nc); - if (len && end_ofs > 0) { //force an end at a time - nc->audio_len = len - start_ofs - end_ofs; - } else { - nc->audio_len = 0; - } - - nc->audio_start = p_time; - } - } else if (nc->audio_playing) { - bool loop = a->get_loop_mode() != Animation::LOOP_NONE; - - bool stop = false; - - if (!loop) { - if ((p_time < nc->audio_start && !backward) || (p_time > nc->audio_start && backward)) { - stop = true; - } - } else if (nc->audio_len > 0) { - float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start; + idx = to_play.back()->get(); + } + } + if (idx < 0) { + continue; + } - if (len > nc->audio_len) { - stop = true; - } + // Play stream. + Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx); + if (stream.is_valid()) { + double start_ofs = a->audio_track_get_key_start_offset(i, idx); + double end_ofs = a->audio_track_get_key_end_offset(i, idx); + double len = stream->get_length(); + + if (aa->object->call(SNAME("get_stream")) != aa->audio_stream) { + aa->object->call(SNAME("set_stream"), aa->audio_stream); + aa->audio_stream_playback.unref(); + if (!playing_audio_stream_players.has(asp)) { + playing_audio_stream_players.push_back(asp); } + } + if (!aa->object->call(SNAME("is_playing"))) { + aa->object->call(SNAME("play")); + } + if (!aa->object->call(SNAME("has_stream_playback"))) { + aa->audio_stream_playback.unref(); + continue; + } + if (aa->audio_stream_playback.is_null()) { + aa->audio_stream_playback = aa->object->call(SNAME("get_stream_playback")); + } - if (stop) { - //time to stop - nc->node->call(SNAME("stop")); - nc->audio_playing = false; - playing_caches.erase(nc); - } + TrackNodeCache::PlayingAudioStreamInfo pasi; + pasi.index = aa->audio_stream_playback->play_stream(stream, start_ofs); + pasi.start = p_time; + if (len && end_ofs > 0) { // Force an end at a time. + pasi.len = len - start_ofs - end_ofs; + } else { + pasi.len = 0; } + map[idx] = pasi; } } break; @@ -1210,6 +1214,53 @@ void AnimationPlayer::_animation_update_transforms() { ERR_CONTINUE(ba->accum_pass != accum_pass); ba->object->set_indexed(ba->bezier_property, ba->bezier_accum); } + + for (int i = 0; i < cache_update_audio_size; i++) { + TrackNodeCache::AudioAnim *aa = cache_update_audio[i]; + + ERR_CONTINUE(aa->accum_pass != accum_pass); + + // Audio ending process. + LocalVector<int> erase_list; + for (const KeyValue<int, TrackNodeCache::PlayingAudioStreamInfo> &K : aa->playing_streams) { + TrackNodeCache::PlayingAudioStreamInfo pasi = K.value; + + bool stop = false; + if (!aa->audio_stream_playback->is_stream_playing(pasi.index)) { + stop = true; + } + if (!aa->loop) { + if (!aa->backward) { + if (aa->time < pasi.start) { + stop = true; + } + } else if (aa->backward) { + if (aa->time > pasi.start) { + stop = true; + } + } + } + if (pasi.len > 0) { + double len = 0.0; + if (!aa->backward) { + len = pasi.start > aa->time ? (aa->length - pasi.start) + aa->time : aa->time - pasi.start; + } else { + len = pasi.start < aa->time ? (aa->length - aa->time) + pasi.start : pasi.start - aa->time; + } + if (len > pasi.len) { + stop = true; + } + } + if (stop) { + // Time to stop. + aa->audio_stream_playback->stop_stream(pasi.index); + erase_list.push_back(K.key); + } + } + for (uint32_t erase_idx = 0; erase_idx < erase_list.size(); erase_idx++) { + aa->playing_streams.erase(erase_list[erase_idx]); + } + } } void AnimationPlayer::_animation_process(double p_delta) { @@ -1225,6 +1276,7 @@ void AnimationPlayer::_animation_process(double p_delta) { cache_update_size = 0; cache_update_prop_size = 0; cache_update_bezier_size = 0; + cache_update_audio_size = 0; AnimationData *prev_from = playback.current.from; _animation_process2(p_delta, started); @@ -1662,6 +1714,7 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa } if (get_current_animation() != p_name) { + _clear_audio_streams(); _stop_playing_caches(false); } @@ -1709,8 +1762,11 @@ bool AnimationPlayer::is_playing() const { void AnimationPlayer::set_current_animation(const String &p_anim) { if (p_anim == "[stop]" || p_anim.is_empty()) { stop(); - } else if (!is_playing() || playback.assigned != p_anim) { + } else if (!is_playing()) { play(p_anim); + } else if (playback.assigned != p_anim) { + float speed = get_playing_speed(); + play(p_anim, -1.0, speed, signbit(speed)); } else { // Same animation, do not replay from start } @@ -1722,7 +1778,8 @@ String AnimationPlayer::get_current_animation() const { void AnimationPlayer::set_assigned_animation(const String &p_anim) { if (is_playing()) { - play(p_anim); + float speed = get_playing_speed(); + play(p_anim, -1.0, speed, signbit(speed)); } else { ERR_FAIL_COND_MSG(!animation_set.has(p_anim), vformat("Animation not found: %s.", p_anim)); playback.current.pos = 0; @@ -1759,15 +1816,18 @@ float AnimationPlayer::get_playing_speed() const { } void AnimationPlayer::seek(double p_time, bool p_update) { + playback.current.pos = p_time; + if (!playback.current.from) { if (playback.assigned) { ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned)); playback.current.from = &animation_set[playback.assigned]; } - ERR_FAIL_COND(!playback.current.from); + if (!playback.current.from) { + return; // There is no animation. + } } - playback.current.pos = p_time; playback.seeked = true; if (p_update) { _animation_process(0); @@ -1775,20 +1835,22 @@ void AnimationPlayer::seek(double p_time, bool p_update) { } void AnimationPlayer::seek_delta(double p_time, double p_delta) { + playback.current.pos = p_time - p_delta; + if (!playback.current.from) { if (playback.assigned) { ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned)); playback.current.from = &animation_set[playback.assigned]; } - ERR_FAIL_COND(!playback.current.from); + if (!playback.current.from) { + return; // There is no animation. + } } - playback.current.pos = p_time - p_delta; if (speed_scale != 0.0) { p_delta /= speed_scale; } _animation_process(p_delta); - //playback.current.pos=p_time; } bool AnimationPlayer::is_valid() const { @@ -1839,6 +1901,7 @@ void AnimationPlayer::_node_removed(Node *p_node) { } void AnimationPlayer::clear_caches() { + _clear_audio_streams(); _stop_playing_caches(true); node_cache_map.clear(); @@ -1850,10 +1913,19 @@ void AnimationPlayer::clear_caches() { cache_update_size = 0; cache_update_prop_size = 0; cache_update_bezier_size = 0; + cache_update_audio_size = 0; emit_signal(SNAME("caches_cleared")); } +void AnimationPlayer::_clear_audio_streams() { + for (int i = 0; i < playing_audio_stream_players.size(); i++) { + playing_audio_stream_players[i]->call(SNAME("stop")); + playing_audio_stream_players[i]->call(SNAME("set_stream"), Ref<AudioStream>()); + } + playing_audio_stream_players.clear(); +} + void AnimationPlayer::set_active(bool p_active) { if (active == p_active) { return; @@ -1933,6 +2005,15 @@ AnimationPlayer::AnimationMethodCallMode AnimationPlayer::get_method_call_mode() return method_call_mode; } +void AnimationPlayer::set_audio_max_polyphony(int p_audio_max_polyphony) { + ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128); + audio_max_polyphony = p_audio_max_polyphony; +} + +int AnimationPlayer::get_audio_max_polyphony() const { + return audio_max_polyphony; +} + void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) { movie_quit_on_finish = p_enabled; } @@ -1961,6 +2042,7 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) { } void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) { + _clear_audio_streams(); _stop_playing_caches(p_reset); Playback &c = playback; c.blend.clear(); @@ -2181,6 +2263,9 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode); ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode); + ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationPlayer::set_audio_max_polyphony); + ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationPlayer::get_audio_max_polyphony); + ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled", "enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled); ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled); @@ -2190,6 +2275,8 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::seek, DEFVAL(false)); ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationPlayer::advance); + GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object", "object_idx"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_assigned_animation", "get_assigned_animation"); @@ -2202,8 +2289,9 @@ void AnimationPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_callback", "get_process_callback"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:s"), "set_default_blend_time", "get_default_blend_time"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_active", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_active", "is_active"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled"); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index 7e7d12f982..b0975fbead 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -37,6 +37,7 @@ #include "scene/3d/skeleton_3d.h" #include "scene/resources/animation.h" #include "scene/resources/animation_library.h" +#include "scene/resources/audio_stream_polyphonic.h" #ifdef TOOLS_ENABLED class AnimatedValuesBackup : public RefCounted { @@ -147,6 +148,26 @@ private: HashMap<StringName, BezierAnim> bezier_anim; + struct PlayingAudioStreamInfo { + AudioStreamPlaybackPolyphonic::ID index = -1; + double start = 0.0; + double len = 0.0; + }; + + struct AudioAnim { + Ref<AudioStreamPolyphonic> audio_stream; + Ref<AudioStreamPlaybackPolyphonic> audio_stream_playback; + HashMap<int, PlayingAudioStreamInfo> playing_streams; + Object *object = nullptr; + uint64_t accum_pass = 0; + double length = 0.0; + double time = 0.0; + bool loop = false; + bool backward = false; + }; + + HashMap<StringName, AudioAnim> audio_anim; + uint32_t last_setup_pass = 0; TrackNodeCache() {} }; @@ -187,7 +208,10 @@ private: int cache_update_prop_size = 0; TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX]; int cache_update_bezier_size = 0; + TrackNodeCache::AudioAnim *cache_update_audio[NODE_CACHE_UPDATE_MAX]; + int cache_update_audio_size = 0; HashSet<TrackNodeCache *> playing_caches; + Vector<Node *> playing_audio_stream_players; uint64_t accum_pass = 1; float speed_scale = 1.0; @@ -263,6 +287,7 @@ private: bool reset_on_save = true; AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE; AnimationMethodCallMode method_call_mode = ANIMATION_METHOD_CALL_DEFERRED; + int audio_max_polyphony = 32; bool movie_quit_on_finish = false; bool processing = false; bool active = true; @@ -278,6 +303,7 @@ private: void _animation_process(double p_delta); void _node_removed(Node *p_node); + void _clear_audio_streams(); void _stop_playing_caches(bool p_reset); // bind helpers @@ -317,6 +343,8 @@ protected: static void _bind_methods(); + GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, Object *, int); + Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); public: @@ -375,6 +403,9 @@ public: void set_method_call_mode(AnimationMethodCallMode p_mode); AnimationMethodCallMode get_method_call_mode() const; + void set_audio_max_polyphony(int p_audio_max_polyphony); + int get_audio_max_polyphony() const; + void set_movie_quit_on_finish_enabled(bool p_enabled); bool is_movie_quit_on_finish_enabled() const; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 077a5696bb..1c1f94c986 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -303,36 +303,21 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_is_external_seeking, p_connections); } -int AnimationNode::get_input_count() const { - return inputs.size(); -} - -String AnimationNode::get_input_name(int p_input) { - ERR_FAIL_INDEX_V(p_input, inputs.size(), String()); - return inputs[p_input].name; -} - String AnimationNode::get_caption() const { String ret = "Node"; GDVIRTUAL_CALL(_get_caption, ret); return ret; } -void AnimationNode::add_input(const String &p_name) { +bool AnimationNode::add_input(const String &p_name) { //root nodes can't add inputs - ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != nullptr); + ERR_FAIL_COND_V(Object::cast_to<AnimationRootNode>(this) != nullptr, false); Input input; - ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/")); + ERR_FAIL_COND_V(p_name.contains(".") || p_name.contains("/"), false); input.name = p_name; inputs.push_back(input); emit_changed(); -} - -void AnimationNode::set_input_name(int p_input, const String &p_name) { - ERR_FAIL_INDEX(p_input, inputs.size()); - ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/")); - inputs.write[p_input].name = p_name; - emit_changed(); + return true; } void AnimationNode::remove_input(int p_index) { @@ -341,6 +326,34 @@ void AnimationNode::remove_input(int p_index) { emit_changed(); } +bool AnimationNode::set_input_name(int p_input, const String &p_name) { + ERR_FAIL_INDEX_V(p_input, inputs.size(), false); + ERR_FAIL_COND_V(p_name.contains(".") || p_name.contains("/"), false); + inputs.write[p_input].name = p_name; + emit_changed(); + return true; +} + +String AnimationNode::get_input_name(int p_input) const { + ERR_FAIL_INDEX_V(p_input, inputs.size(), String()); + return inputs[p_input].name; +} + +int AnimationNode::get_input_count() const { + return inputs.size(); +} + +int AnimationNode::find_input(const String &p_name) const { + int idx = -1; + for (int i = 0; i < inputs.size(); i++) { + if (inputs[i].name == p_name) { + idx = i; + break; + } + } + return idx; +} + double AnimationNode::process(double p_time, bool p_seek, bool p_is_external_seeking) { double ret = 0; GDVIRTUAL_CALL(_process, p_time, p_seek, p_is_external_seeking, ret); @@ -404,11 +417,12 @@ Ref<AnimationNode> AnimationNode::get_child_by_name(const StringName &p_name) { } void AnimationNode::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_input_count"), &AnimationNode::get_input_count); - ClassDB::bind_method(D_METHOD("get_input_name", "input"), &AnimationNode::get_input_name); - ClassDB::bind_method(D_METHOD("add_input", "name"), &AnimationNode::add_input); ClassDB::bind_method(D_METHOD("remove_input", "index"), &AnimationNode::remove_input); + ClassDB::bind_method(D_METHOD("set_input_name", "input", "name"), &AnimationNode::set_input_name); + ClassDB::bind_method(D_METHOD("get_input_name", "input"), &AnimationNode::get_input_name); + ClassDB::bind_method(D_METHOD("get_input_count"), &AnimationNode::get_input_count); + ClassDB::bind_method(D_METHOD("find_input", "name"), &AnimationNode::find_input); ClassDB::bind_method(D_METHOD("set_filter_path", "path", "enable"), &AnimationNode::set_filter_path); ClassDB::bind_method(D_METHOD("is_path_filtered", "path"), &AnimationNode::is_path_filtered); @@ -486,13 +500,7 @@ void AnimationTree::set_active(bool p_active) { } if (!active && is_inside_tree()) { - for (const TrackCache *E : playing_caches) { - if (ObjectDB::get_instance(E->object_id)) { - E->object->call(SNAME("stop")); - } - } - - playing_caches.clear(); + _clear_caches(); } } @@ -531,6 +539,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { if (!player->has_node(player->get_root())) { ERR_PRINT("AnimationTree: AnimationPlayer root is invalid."); set_active(false); + _clear_caches(); return false; } Node *parent = player->get_node(player->get_root()); @@ -763,6 +772,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_audio->object = child; track_audio->object_id = track_audio->object->get_instance_id(); + track_audio->audio_stream.instantiate(); + track_audio->audio_stream->set_polyphony(audio_max_polyphony); track = track_audio; @@ -860,14 +871,32 @@ void AnimationTree::_animation_player_changed() { } void AnimationTree::_clear_caches() { + _clear_audio_streams(); + _clear_playing_caches(); for (KeyValue<NodePath, TrackCache *> &K : track_cache) { memdelete(K.value); } - playing_caches.clear(); track_cache.clear(); cache_valid = false; } +void AnimationTree::_clear_audio_streams() { + for (int i = 0; i < playing_audio_stream_players.size(); i++) { + playing_audio_stream_players[i]->call(SNAME("stop")); + playing_audio_stream_players[i]->call(SNAME("set_stream"), Ref<AudioStream>()); + } + playing_audio_stream_players.clear(); +} + +void AnimationTree::_clear_playing_caches() { + for (const TrackCache *E : playing_caches) { + if (ObjectDB::get_instance(E->object_id)) { + E->object->call(SNAME("stop")); + } + } + playing_caches.clear(); +} + static void _call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) { // Separate function to use alloca() more efficiently const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * p_params.size()); @@ -1007,6 +1036,13 @@ void AnimationTree::_process_graph(double p_delta) { TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); t->value = t->init_value; } break; + case Animation::TYPE_AUDIO: { + TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); + for (KeyValue<ObjectID, PlayingAudioTrackInfo> &L : t->playing_streams) { + PlayingAudioTrackInfo &track_info = L.value; + track_info.volume = 0.0; + } + } break; default: { } break; } @@ -1015,8 +1051,9 @@ void AnimationTree::_process_graph(double p_delta) { // Apply value/transform/blend/bezier blends to track caches and execute method/audio/animation tracks. { +#ifdef TOOLS_ENABLED bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); - +#endif // TOOLS_ENABLED for (const AnimationNode::AnimationState &as : state.animation_states) { Ref<Animation> a = as.animation; double time = as.time; @@ -1025,8 +1062,8 @@ void AnimationTree::_process_graph(double p_delta) { bool seeked = as.seeked; Animation::LoopedFlag looped_flag = as.looped_flag; bool is_external_seeking = as.is_external_seeking; + bool backward = signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream. #ifndef _3D_DISABLED - bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames. bool calc_root = !seeked || is_external_seeking; #endif // _3D_DISABLED @@ -1045,9 +1082,6 @@ void AnimationTree::_process_graph(double p_delta) { int blend_idx = state.track_map[path]; ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count); real_t blend = (*as.track_blends)[blend_idx] * weight; - if (Math::is_zero_approx(blend)) { - continue; // Nothing to blend. - } Animation::TrackType ttype = a->track_get_type(i); if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) { @@ -1059,6 +1093,9 @@ void AnimationTree::_process_graph(double p_delta) { switch (ttype) { case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (track->root_motion && calc_root) { double prev_time = time - delta; @@ -1104,9 +1141,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, (double)a->get_length(), &loc[1]); - loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = 0; } @@ -1116,9 +1153,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, 0, &loc[1]); - loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = (double)a->get_length(); } @@ -1128,10 +1165,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); a->position_track_interpolate(i, time, &loc[1]); - loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); t->loc += (loc[1] - loc[0]) * blend; prev_time = !backward ? 0 : (double)a->get_length(); @@ -1142,7 +1179,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - loc = _post_process_key_value(a, i, loc, t->object, t->bone_idx); + loc = post_process_key_value(a, i, loc, t->object, t->bone_idx); t->loc += (loc - t->init_loc) * blend; } @@ -1150,6 +1187,9 @@ void AnimationTree::_process_graph(double p_delta) { } break; case Animation::TYPE_ROTATION_3D: { #ifndef _3D_DISABLED + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (track->root_motion && calc_root) { double prev_time = time - delta; @@ -1195,9 +1235,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]); - rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx); + rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = 0; } @@ -1207,7 +1247,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, 0, &rot[1]); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = (double)a->get_length(); @@ -1218,10 +1258,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); a->rotation_track_interpolate(i, time, &rot[1]); - rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx); + rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = !backward ? 0 : (double)a->get_length(); @@ -1232,7 +1272,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - rot = _post_process_key_value(a, i, rot, t->object, t->bone_idx); + rot = post_process_key_value(a, i, rot, t->object, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized(); } @@ -1240,6 +1280,9 @@ void AnimationTree::_process_graph(double p_delta) { } break; case Animation::TYPE_SCALE_3D: { #ifndef _3D_DISABLED + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (track->root_motion && calc_root) { double prev_time = time - delta; @@ -1285,10 +1328,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]); t->scale += (scale[1] - scale[0]) * blend; - scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); prev_time = 0; } } else { @@ -1297,9 +1340,9 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, 0, &scale[1]); - scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); t->scale += (scale[1] - scale[0]) * blend; prev_time = (double)a->get_length(); } @@ -1309,10 +1352,10 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); a->scale_track_interpolate(i, time, &scale[1]); - scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); t->scale += (scale[1] - scale[0]) * blend; prev_time = !backward ? 0 : (double)a->get_length(); @@ -1323,7 +1366,7 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - scale = _post_process_key_value(a, i, scale, t->object, t->bone_idx); + scale = post_process_key_value(a, i, scale, t->object, t->bone_idx); t->scale += (scale - t->init_scale) * blend; } @@ -1331,6 +1374,9 @@ void AnimationTree::_process_graph(double p_delta) { } break; case Animation::TYPE_BLEND_SHAPE: { #ifndef _3D_DISABLED + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); float value; @@ -1341,19 +1387,22 @@ void AnimationTree::_process_graph(double p_delta) { if (err != OK) { continue; } - value = _post_process_key_value(a, i, value, t->object, t->shape_index); + value = post_process_key_value(a, i, value, t->object, t->shape_index); t->value += (value - t->init_value) * blend; #endif // _3D_DISABLED } break; case Animation::TYPE_VALUE: { + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } TrackCacheValue *t = static_cast<TrackCacheValue *>(track); Animation::UpdateMode update_mode = a->value_track_get_update_mode(i); if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { Variant value = a->value_track_interpolate(i, time); - value = _post_process_key_value(a, i, value, t->object); + value = post_process_key_value(a, i, value, t->object); if (value == Variant()) { continue; @@ -1393,14 +1442,14 @@ void AnimationTree::_process_graph(double p_delta) { continue; } Variant value = a->track_get_key_value(i, idx); - value = _post_process_key_value(a, i, value, t->object); + value = post_process_key_value(a, i, value, t->object); t->object->set_indexed(t->subpath, value); } else { List<int> indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { Variant value = a->track_get_key_value(i, F); - value = _post_process_key_value(a, i, value, t->object); + value = post_process_key_value(a, i, value, t->object); t->object->set_indexed(t->subpath, value); } } @@ -1408,6 +1457,14 @@ void AnimationTree::_process_graph(double p_delta) { } break; case Animation::TYPE_METHOD: { +#ifdef TOOLS_ENABLED + if (!can_call) { + continue; + } +#endif // TOOLS_ENABLED + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track); if (seeked) { @@ -1417,137 +1474,112 @@ void AnimationTree::_process_graph(double p_delta) { } StringName method = a->method_track_get_name(i, idx); Vector<Variant> params = a->method_track_get_params(i, idx); - if (can_call) { - _call_object(t->object, method, params, false); - } + _call_object(t->object, method, params, false); } else { List<int> indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { StringName method = a->method_track_get_name(i, F); Vector<Variant> params = a->method_track_get_params(i, F); - if (can_call) { - _call_object(t->object, method, params, true); - } + _call_object(t->object, method, params, true); } } } break; case Animation::TYPE_BEZIER: { + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); real_t bezier = a->bezier_track_interpolate(i, time); - bezier = _post_process_key_value(a, i, bezier, t->object); + bezier = post_process_key_value(a, i, bezier, t->object); t->value += (bezier - t->init_value) * blend; } break; case Animation::TYPE_AUDIO: { TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); - if (seeked) { - //find whatever should be playing - int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT); - if (idx < 0) { - continue; - } - - Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx); - if (!stream.is_valid()) { - t->object->call(SNAME("stop")); - t->playing = false; - playing_caches.erase(t); - } else { - double start_ofs = a->audio_track_get_key_start_offset(i, idx); - start_ofs += time - a->track_get_key_time(i, idx); - double end_ofs = a->audio_track_get_key_end_offset(i, idx); - double len = stream->get_length(); - - if (start_ofs > len - end_ofs) { - t->object->call(SNAME("stop")); - t->playing = false; - playing_caches.erase(t); - continue; - } - - t->object->call(SNAME("set_stream"), stream); - t->object->call(SNAME("play"), start_ofs); - - t->playing = true; - playing_caches.insert(t); - if (len && end_ofs > 0) { //force an end at a time - t->len = len - start_ofs - end_ofs; - } else { - t->len = 0; - } + Node *asp = Object::cast_to<Node>(t->object); + if (!asp) { + t->playing_streams.clear(); + continue; + } - t->start = time; + ObjectID oid = a->get_instance_id(); + if (!t->playing_streams.has(oid)) { + t->playing_streams[oid] = PlayingAudioTrackInfo(); + } + // The end of audio should be observed even if the blend value is 0, build up the information and store to the cache for that. + PlayingAudioTrackInfo &track_info = t->playing_streams[oid]; + track_info.length = a->get_length(); + track_info.time = time; + track_info.volume += blend; + track_info.loop = a->get_loop_mode() != Animation::LOOP_NONE; + track_info.backward = backward; + track_info.use_blend = a->audio_track_is_use_blend(i); + + HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info; + // Find stream. + int idx = -1; + if (seeked) { + idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT); + // Discard previous stream when seeking. + if (map.has(idx)) { + t->audio_stream_playback->stop_stream(map[idx].index); + map.erase(idx); } - } else { - //find stuff to play List<int> to_play; a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag); if (to_play.size()) { - int idx = to_play.back()->get(); - - Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx); - if (!stream.is_valid()) { - t->object->call(SNAME("stop")); - t->playing = false; - playing_caches.erase(t); - } else { - double start_ofs = a->audio_track_get_key_start_offset(i, idx); - double end_ofs = a->audio_track_get_key_end_offset(i, idx); - double len = stream->get_length(); - - t->object->call(SNAME("set_stream"), stream); - t->object->call(SNAME("play"), start_ofs); - - t->playing = true; - playing_caches.insert(t); - if (len && end_ofs > 0) { //force an end at a time - t->len = len - start_ofs - end_ofs; - } else { - t->len = 0; - } - - t->start = time; - } - } else if (t->playing) { - bool loop = a->get_loop_mode() != Animation::LOOP_NONE; - - bool stop = false; - - if (!loop) { - if (delta > 0) { - if (time < t->start) { - stop = true; - } - } else if (delta < 0) { - if (time > t->start) { - stop = true; - } - } - } else if (t->len > 0) { - double len = t->start > time ? (a->get_length() - t->start) + time : time - t->start; + idx = to_play.back()->get(); + } + } + if (idx < 0) { + continue; + } - if (len > t->len) { - stop = true; - } + // Play stream. + Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx); + if (stream.is_valid()) { + double start_ofs = a->audio_track_get_key_start_offset(i, idx); + double end_ofs = a->audio_track_get_key_end_offset(i, idx); + double len = stream->get_length(); + + if (t->object->call(SNAME("get_stream")) != t->audio_stream) { + t->object->call(SNAME("set_stream"), t->audio_stream); + t->audio_stream_playback.unref(); + if (!playing_audio_stream_players.has(asp)) { + playing_audio_stream_players.push_back(asp); } + } + if (!t->object->call(SNAME("is_playing"))) { + t->object->call(SNAME("play")); + } + if (!t->object->call(SNAME("has_stream_playback"))) { + t->audio_stream_playback.unref(); + continue; + } + if (t->audio_stream_playback.is_null()) { + t->audio_stream_playback = t->object->call(SNAME("get_stream_playback")); + } - if (stop) { - //time to stop - t->object->call(SNAME("stop")); - t->playing = false; - playing_caches.erase(t); - } + PlayingAudioStreamInfo pasi; + pasi.index = t->audio_stream_playback->play_stream(stream, start_ofs); + pasi.start = time; + if (len && end_ofs > 0) { // Force an end at a time. + pasi.len = len - start_ofs - end_ofs; + } else { + pasi.len = 0; } + map[idx] = pasi; } - real_t db = Math::linear_to_db(MAX(blend, 0.00001)); - t->object->call(SNAME("set_volume_db"), db); } break; case Animation::TYPE_ANIMATION: { + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track); AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object); @@ -1693,6 +1725,64 @@ void AnimationTree::_process_graph(double p_delta) { t->object->set_indexed(t->subpath, t->value); } break; + case Animation::TYPE_AUDIO: { + TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); + + // Audio ending process. + LocalVector<ObjectID> erase_maps; + for (KeyValue<ObjectID, PlayingAudioTrackInfo> &L : t->playing_streams) { + PlayingAudioTrackInfo &track_info = L.value; + float db = Math::linear_to_db(track_info.use_blend ? track_info.volume : 1.0); + LocalVector<int> erase_streams; + HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info; + for (const KeyValue<int, PlayingAudioStreamInfo> &M : map) { + PlayingAudioStreamInfo pasi = M.value; + + bool stop = false; + if (!t->audio_stream_playback->is_stream_playing(pasi.index)) { + stop = true; + } + if (!track_info.loop) { + if (!track_info.backward) { + if (track_info.time < pasi.start) { + stop = true; + } + } else if (track_info.backward) { + if (track_info.time > pasi.start) { + stop = true; + } + } + } + if (pasi.len > 0) { + double len = 0.0; + if (!track_info.backward) { + len = pasi.start > track_info.time ? (track_info.length - pasi.start) + track_info.time : track_info.time - pasi.start; + } else { + len = pasi.start < track_info.time ? (track_info.length - track_info.time) + pasi.start : pasi.start - track_info.time; + } + if (len > pasi.len) { + stop = true; + } + } + if (stop) { + // Time to stop. + t->audio_stream_playback->stop_stream(pasi.index); + erase_streams.push_back(M.key); + } else { + t->audio_stream_playback->set_stream_volume(pasi.index, db); + } + } + for (uint32_t erase_idx = 0; erase_idx < erase_streams.size(); erase_idx++) { + map.erase(erase_streams[erase_idx]); + } + if (map.size() == 0) { + erase_maps.push_back(L.key); + } + } + for (uint32_t erase_idx = 0; erase_idx < erase_maps.size(); erase_idx++) { + t->playing_streams.erase(erase_maps[erase_idx]); + } + } break; default: { } //the rest don't matter } @@ -1700,6 +1790,15 @@ void AnimationTree::_process_graph(double p_delta) { } } +Variant AnimationTree::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { + Variant res; + if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, const_cast<Object *>(p_object), p_object_idx, res)) { + return res; + } + + return _post_process_key_value(p_anim, p_track, p_value, p_object, p_object_idx); +} + Variant AnimationTree::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { switch (p_anim->track_get_type(p_track)) { #ifndef _3D_DISABLED @@ -1809,6 +1908,15 @@ NodePath AnimationTree::get_advance_expression_base_node() const { return advance_expression_base_node; } +void AnimationTree::set_audio_max_polyphony(int p_audio_max_polyphony) { + ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128); + audio_max_polyphony = p_audio_max_polyphony; +} + +int AnimationTree::get_audio_max_polyphony() const { + return audio_max_polyphony; +} + bool AnimationTree::is_state_invalid() const { return !state.valid; } @@ -2024,6 +2132,9 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationTree::set_root_motion_track); ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationTree::get_root_motion_track); + ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationTree::set_audio_max_polyphony); + ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationTree::get_audio_max_polyphony); + ClassDB::bind_method(D_METHOD("get_root_motion_position"), &AnimationTree::get_root_motion_position); ClassDB::bind_method(D_METHOD("get_root_motion_rotation"), &AnimationTree::get_root_motion_rotation); ClassDB::bind_method(D_METHOD("get_root_motion_scale"), &AnimationTree::get_root_motion_scale); @@ -2034,12 +2145,16 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationTree::advance); + GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object", "object_idx"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode"), "set_tree_root", "get_tree_root"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_callback", "get_process_callback"); + ADD_GROUP("Audio", "audio_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony"); ADD_GROUP("Root Motion", "root_motion_"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track"); diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 52d3e1bd41..a6fb4a8430 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -35,6 +35,7 @@ #include "scene/3d/node_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/resources/animation.h" +#include "scene/resources/audio_stream_polyphonic.h" class AnimationNodeBlendTree; class AnimationNodeStartState; @@ -140,12 +141,12 @@ public: virtual double process(double p_time, bool p_seek, bool p_is_external_seeking); virtual String get_caption() const; + virtual bool add_input(const String &p_name); + virtual void remove_input(int p_index); + virtual bool set_input_name(int p_input, const String &p_name); + virtual String get_input_name(int p_input) const; int get_input_count() const; - String get_input_name(int p_input); - - void add_input(const String &p_name); - void set_input_name(int p_input, const String &p_name); - void remove_input(int p_index); + int find_input(const String &p_name) const; void set_filter_path(const NodePath &p_path, bool p_enable); bool is_path_filtered(const NodePath &p_path) const; @@ -252,10 +253,28 @@ private: } }; - struct TrackCacheAudio : public TrackCache { - bool playing = false; + // Audio stream information for each audio stream placed on the track. + struct PlayingAudioStreamInfo { + AudioStreamPlaybackPolyphonic::ID index = -1; // ID retrieved from AudioStreamPlaybackPolyphonic. double start = 0.0; double len = 0.0; + }; + + // Audio track information for mixng and ending. + struct PlayingAudioTrackInfo { + HashMap<int, PlayingAudioStreamInfo> stream_info; + double length = 0.0; + double time = 0.0; + real_t volume = 0.0; + bool loop = false; + bool backward = false; + bool use_blend = false; + }; + + struct TrackCacheAudio : public TrackCache { + Ref<AudioStreamPolyphonic> audio_stream; + Ref<AudioStreamPlaybackPolyphonic> audio_stream_playback; + HashMap<ObjectID, PlayingAudioTrackInfo> playing_streams; // Key is Animation resource ObjectID. TrackCacheAudio() { type = Animation::TYPE_AUDIO; @@ -272,6 +291,7 @@ private: HashMap<NodePath, TrackCache *> track_cache; HashSet<TrackCache *> playing_caches; + Vector<Node *> playing_audio_stream_players; Ref<AnimationNode> root; NodePath advance_expression_base_node = NodePath(String(".")); @@ -279,6 +299,7 @@ private: AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE; bool active = false; NodePath animation_player; + int audio_max_polyphony = 32; AnimationNode::State state; bool cache_valid = false; @@ -287,6 +308,8 @@ private: void _setup_animation_player(); void _animation_player_changed(); void _clear_caches(); + void _clear_playing_caches(); + void _clear_audio_streams(); bool _update_caches(AnimationPlayer *player); void _process_graph(double p_delta); @@ -328,6 +351,8 @@ protected: void _notification(int p_what); static void _bind_methods(); + GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, Object *, int); + Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); public: @@ -346,6 +371,9 @@ public: void set_advance_expression_base_node(const NodePath &p_advance_expression_base_node); NodePath get_advance_expression_base_node() const; + void set_audio_max_polyphony(int p_audio_max_polyphony); + int get_audio_max_polyphony() const; + PackedStringArray get_configuration_warnings() const override; bool is_state_invalid() const; diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index e6b258df3e..3d8d451c70 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -80,7 +80,8 @@ bool RootMotionView::get_zero_y() const { void RootMotionView::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - immediate_material = StandardMaterial3D::get_material_for_2d(false, true, false, false, false); + immediate_material = StandardMaterial3D::get_material_for_2d(false, BaseMaterial3D::TRANSPARENCY_ALPHA, false); + first = true; } break; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 39d1793368..abc7814877 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -594,7 +594,7 @@ PropertyTweener::PropertyTweener(Object *p_target, NodePath p_property, Variant } PropertyTweener::PropertyTweener() { - ERR_FAIL_MSG("Can't create empty PropertyTweener. Use get_tree().tween_property() or tween_property() instead."); + ERR_FAIL_MSG("PropertyTweener can't be created directly. Use the tween_property() method in Tween."); } void IntervalTweener::start() { @@ -625,7 +625,7 @@ IntervalTweener::IntervalTweener(double p_time) { } IntervalTweener::IntervalTweener() { - ERR_FAIL_MSG("Can't create empty IntervalTweener. Use get_tree().tween_interval() instead."); + ERR_FAIL_MSG("IntervalTweener can't be created directly. Use the tween_interval() method in Tween."); } Ref<CallbackTweener> CallbackTweener::set_delay(double p_delay) { @@ -676,7 +676,7 @@ CallbackTweener::CallbackTweener(Callable p_callback) { } CallbackTweener::CallbackTweener() { - ERR_FAIL_MSG("Can't create empty CallbackTweener. Use get_tree().tween_callback() instead."); + ERR_FAIL_MSG("CallbackTweener can't be created directly. Use the tween_callback() method in Tween."); } Ref<MethodTweener> MethodTweener::set_delay(double p_delay) { @@ -769,5 +769,5 @@ MethodTweener::MethodTweener(Callable p_callback, Variant p_from, Variant p_to, } MethodTweener::MethodTweener() { - ERR_FAIL_MSG("Can't create empty MethodTweener. Use get_tree().tween_method() instead."); + ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween."); } diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index d40fc10441..7533a56b59 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -307,6 +307,10 @@ void AudioStreamPlayer::_bus_layout_changed() { notify_property_list_changed(); } +bool AudioStreamPlayer::has_stream_playback() { + return !stream_playbacks.is_empty(); +} + Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() { ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback()."); return stream_playbacks[stream_playbacks.size() - 1]; @@ -347,6 +351,7 @@ void AudioStreamPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer::set_max_polyphony); ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer::get_max_polyphony); + ClassDB::bind_method(D_METHOD("has_stream_playback"), &AudioStreamPlayer::has_stream_playback); ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index 5368391073..d1f6fca2ee 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -107,6 +107,7 @@ public: void set_stream_paused(bool p_pause); bool get_stream_paused() const; + bool has_stream_playback(); Ref<AudioStreamPlayback> get_stream_playback(); AudioStreamPlayer(); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 472299b135..c26a00221a 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -479,14 +479,16 @@ void BaseButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "button_pressed"), "set_pressed", "is_pressed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); + + ADD_GROUP("Shortcut", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_feedback"), "set_shortcut_feedback", "is_shortcut_feedback"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled"); BIND_ENUM_CONSTANT(DRAW_NORMAL); BIND_ENUM_CONSTANT(DRAW_PRESSED); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 1e07a53642..2a8b1cd8c4 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -575,9 +575,13 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); + + ADD_GROUP("Text Behavior", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); + + ADD_GROUP("Icon Behavior", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index c977d9d2fb..b084cb5bea 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -3088,6 +3088,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } code_completion_options.append_array(completion_options_casei); + code_completion_options.append_array(completion_options_substr); + code_completion_options.append_array(completion_options_substr_casei); code_completion_options.append_array(completion_options_subseq); code_completion_options.append_array(completion_options_subseq_casei); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index fead0878fb..f7c056316d 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1387,13 +1387,7 @@ Point2 Control::get_global_position() const { Point2 Control::get_screen_position() const { ERR_FAIL_COND_V(!is_inside_tree(), Point2()); - Point2 global_pos = get_global_transform_with_canvas().get_origin(); - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - global_pos += w->get_position(); - } - - return global_pos; + return get_screen_transform().get_origin(); } void Control::_set_size(const Size2 &p_size) { @@ -1444,24 +1438,20 @@ void Control::set_rect(const Rect2 &p_rect) { } Rect2 Control::get_rect() const { - return Rect2(get_position(), get_size()); + Transform2D xform = get_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_global_rect() const { - return Rect2(get_global_position(), get_size()); + Transform2D xform = get_global_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_screen_rect() const { ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); - Rect2 r(get_global_position(), get_size()); - - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - r.position += w->get_position(); - } - - return r; + Transform2D xform = get_screen_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_anchorable_rect() const { diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 6c495ab2c9..dc23bcb14a 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -920,7 +920,8 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from scaled_points.push_back(points[i] * p_zoom); } - p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * get_theme_default_base_scale()), lines_antialiased); + // Thickness below 0.5 doesn't look good on the graph or its minimap. + p_where->draw_polyline_colors(scaled_points, colors, MAX(0.5, Math::floor(p_width * get_theme_default_base_scale())), lines_antialiased); } void GraphEdit::_connections_layer_draw() { @@ -1088,7 +1089,7 @@ void GraphEdit::_minimap_draw() { from_color = from_color.lerp(activity_color, E.activity); to_color = to_color.lerp(activity_color, E.activity); } - _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.1, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length()); + _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.5, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length()); } // Draw the "camera" viewport. @@ -1380,34 +1381,15 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { accept_event(); } } - - Ref<InputEventMagnifyGesture> magnify_gesture = p_ev; - if (magnify_gesture.is_valid()) { - set_zoom_custom(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); - } - - Ref<InputEventPanGesture> pan_gesture = p_ev; - if (pan_gesture.is_valid()) { - h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); - v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); - } -} - -void GraphEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - if (p_scroll_vec.x != 0) { - h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * Math::abs(p_scroll_vec.x) / 8) * SIGN(p_scroll_vec.x)); - } else { - v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * Math::abs(p_scroll_vec.y) / 8) * SIGN(p_scroll_vec.y)); - } } -void GraphEdit::_pan_callback(Vector2 p_scroll_vec) { +void GraphEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { h_scroll->set_value(h_scroll->get_value() - p_scroll_vec.x); v_scroll->set_value(v_scroll->get_value() - p_scroll_vec.y); } -void GraphEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - set_zoom_custom(p_scroll_vec.y < 0 ? zoom * zoom_step : zoom / zoom_step, p_origin); +void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + set_zoom_custom(zoom * p_zoom_factor, p_origin); } void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) { @@ -1502,6 +1484,7 @@ void GraphEdit::set_zoom_step(float p_zoom_step) { } zoom_step = p_zoom_step; + panner->set_scroll_zoom_factor(zoom_step); } float GraphEdit::get_zoom_step() const { @@ -2421,7 +2404,7 @@ GraphEdit::GraphEdit() { zoom_max = (1 * Math::pow(zoom_step, 4)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &GraphEdit::_scroll_callback), callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback)); top_layer = memnew(GraphEditFilter(this)); add_child(top_layer, false, INTERNAL_MODE_BACK); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 030f40e370..dfe6b94906 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -129,9 +129,8 @@ private: Ref<ViewPanner> panner; bool warped_panning = true; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); bool arrange_nodes_button_hidden = false; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index e2a5b0c8a3..dba08e16cb 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -247,7 +247,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { return; } if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { - _ensure_menu(); + _update_context_menu(); menu->set_position(get_screen_position() + get_local_mouse_position()); menu->reset_size(); menu->popup(); @@ -452,7 +452,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { - _ensure_menu(); + _update_context_menu(); Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); menu->set_position(get_screen_position() + pos); menu->reset_size(); @@ -1724,6 +1724,10 @@ void LineEdit::insert_text_at_caret(String p_text) { input_direction = (TextDirection)dir; } set_caret_column(caret_column + p_text.length()); + + if (!ime_text.is_empty()) { + _shape(); + } } void LineEdit::clear_internal() { @@ -2073,7 +2077,9 @@ bool LineEdit::is_menu_visible() const { } PopupMenu *LineEdit::get_menu() const { - const_cast<LineEdit *>(this)->_ensure_menu(); + if (!menu) { + const_cast<LineEdit *>(this)->_generate_context_menu(); + } return menu; } @@ -2331,6 +2337,115 @@ Key LineEdit::_get_menu_action_accelerator(const String &p_action) { } } +void LineEdit::_generate_context_menu() { + menu = memnew(PopupMenu); + add_child(menu, false, INTERNAL_MODE_FRONT); + + menu_dir = memnew(PopupMenu); + menu_dir->set_name("DirMenu"); + menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); + menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); + menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); + menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); + menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); + + menu_ctl = memnew(PopupMenu); + menu_ctl->set_name("CTLMenu"); + menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); + menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); + menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); + menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); + menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); + menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); + menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); + menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); + menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); + menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); + menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); + menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); + menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); + menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); + menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); + + menu->add_item(RTR("Cut"), MENU_CUT); + menu->add_item(RTR("Copy"), MENU_COPY); + menu->add_item(RTR("Paste"), MENU_PASTE); + menu->add_separator(); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL); + menu->add_item(RTR("Clear"), MENU_CLEAR); + menu->add_separator(); + menu->add_item(RTR("Undo"), MENU_UNDO); + menu->add_item(RTR("Redo"), MENU_REDO); + menu->add_separator(); + menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu", MENU_SUBMENU_TEXT_DIR); + menu->add_separator(); + menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); + menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu", MENU_SUBMENU_INSERT_UCC); + + menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + menu_ctl->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + + menu->connect(SNAME("focus_entered"), callable_mp(this, &LineEdit::_validate_caret_can_draw)); + menu->connect(SNAME("focus_exited"), callable_mp(this, &LineEdit::_validate_caret_can_draw)); +} + +void LineEdit::_update_context_menu() { + if (!menu) { + _generate_context_menu(); + } + + int idx = -1; + +#define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + m_menu->set_item_disabled(idx, m_disabled); \ + } + +#define MENU_ITEM_ACTION(m_menu, m_id, m_action) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + } + +#define MENU_ITEM_DISABLED(m_menu, m_id, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_disabled(idx, m_disabled); \ + } + +#define MENU_ITEM_CHECKED(m_menu, m_id, m_checked) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_checked(idx, m_checked); \ + } + + MENU_ITEM_ACTION_DISABLED(menu, MENU_CUT, "ui_cut", !editable) + MENU_ITEM_ACTION(menu, MENU_COPY, "ui_copy") + MENU_ITEM_ACTION_DISABLED(menu, MENU_PASTE, "ui_paste", !editable) + MENU_ITEM_ACTION_DISABLED(menu, MENU_SELECT_ALL, "ui_text_select_all", !selecting_enabled) + MENU_ITEM_DISABLED(menu, MENU_CLEAR, !editable) + MENU_ITEM_ACTION_DISABLED(menu, MENU_UNDO, "ui_undo", !editable || !has_undo()) + MENU_ITEM_ACTION_DISABLED(menu, MENU_REDO, "ui_redo", !editable || !has_redo()) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_INHERITED, text_direction == TEXT_DIRECTION_INHERITED) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_AUTO, text_direction == TEXT_DIRECTION_AUTO) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_LTR, text_direction == TEXT_DIRECTION_LTR) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_RTL, text_direction == TEXT_DIRECTION_RTL) + MENU_ITEM_CHECKED(menu, MENU_DISPLAY_UCC, draw_control_chars) + MENU_ITEM_DISABLED(menu, MENU_SUBMENU_INSERT_UCC, !editable) + +#undef MENU_ITEM_ACTION_DISABLED +#undef MENU_ITEM_ACTION +#undef MENU_ITEM_DISABLED +#undef MENU_ITEM_CHECKED +} + void LineEdit::_validate_property(PropertyInfo &p_property) const { if (!caret_blink_enabled && p_property.name == "caret_blink_interval") { p_property.usage = PROPERTY_USAGE_NO_EDITOR; @@ -2425,11 +2540,13 @@ void LineEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_UNDO); BIND_ENUM_CONSTANT(MENU_REDO); + BIND_ENUM_CONSTANT(MENU_SUBMENU_TEXT_DIR); BIND_ENUM_CONSTANT(MENU_DIR_INHERITED); BIND_ENUM_CONSTANT(MENU_DIR_AUTO); BIND_ENUM_CONSTANT(MENU_DIR_LTR); BIND_ENUM_CONSTANT(MENU_DIR_RTL); BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC); + BIND_ENUM_CONSTANT(MENU_SUBMENU_INSERT_UCC); BIND_ENUM_CONSTANT(MENU_INSERT_LRM); BIND_ENUM_CONSTANT(MENU_INSERT_RLM); BIND_ENUM_CONSTANT(MENU_INSERT_LRE); @@ -2492,86 +2609,6 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } -void LineEdit::_ensure_menu() { - if (!menu) { - menu = memnew(PopupMenu); - add_child(menu, false, INTERNAL_MODE_FRONT); - - menu_dir = memnew(PopupMenu); - menu_dir->set_name("DirMenu"); - menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); - menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); - menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); - menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); - menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); - - menu_ctl = memnew(PopupMenu); - menu_ctl->set_name("CTLMenu"); - menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); - menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); - menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); - menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); - menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); - menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); - menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); - menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); - menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); - menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); - menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); - menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); - menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); - menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); - menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); - menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); - menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); - - menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - menu->connect(SNAME("focus_entered"), callable_mp(this, &LineEdit::_validate_caret_can_draw)); - menu->connect(SNAME("focus_exited"), callable_mp(this, &LineEdit::_validate_caret_can_draw)); - menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - menu_ctl->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - } - - // Reorganize context menu. - menu->clear(); - if (editable) { - menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : Key::NONE); - } - menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE); - if (editable) { - menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE); - } - menu->add_separator(); - if (is_selecting_enabled()) { - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE); - } - if (editable) { - menu->add_item(RTR("Clear"), MENU_CLEAR); - menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : Key::NONE); - menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : Key::NONE); - } - menu->add_separator(); - menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu"); - menu->add_separator(); - menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); - menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); - if (editable) { - menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu"); - } - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); - - if (editable) { - menu->set_item_disabled(menu->get_item_index(MENU_UNDO), !has_undo()); - menu->set_item_disabled(menu->get_item_index(MENU_REDO), !has_redo()); - } -} - LineEdit::LineEdit(const String &p_placeholder) { text_rid = TS->create_shaped_text(); _create_undo_state(); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 5107845a5e..81c506069a 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -46,11 +46,13 @@ public: MENU_SELECT_ALL, MENU_UNDO, MENU_REDO, + MENU_SUBMENU_TEXT_DIR, MENU_DIR_INHERITED, MENU_DIR_AUTO, MENU_DIR_LTR, MENU_DIR_RTL, MENU_DISPLAY_UCC, + MENU_SUBMENU_INSERT_UCC, MENU_INSERT_LRM, MENU_INSERT_RLM, MENU_INSERT_LRE, @@ -207,6 +209,8 @@ private: void _create_undo_state(); Key _get_menu_action_accelerator(const String &p_action); + void _generate_context_menu(); + void _update_context_menu(); void _shape(); void _fit_to_width(); @@ -239,8 +243,6 @@ private: void _backspace(bool p_word = false, bool p_all_to_left = false); void _delete(bool p_word = false, bool p_all_to_right = false); - void _ensure_menu(); - protected: bool _is_over_clear_button(const Point2 &p_pos) const; virtual void _update_theme_item_cache() override; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index a68a7ddfb8..3051502dd0 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1797,7 +1797,9 @@ void RichTextLabel::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - queue_redraw(); + if (is_visible_in_tree()) { + queue_redraw(); + } } break; case NOTIFICATION_DRAW: { @@ -2665,19 +2667,26 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { return false; } -void RichTextLabel::_thread_function(void *self) { - RichTextLabel *rtl = reinterpret_cast<RichTextLabel *>(self); - rtl->set_physics_process_internal(true); - rtl->_process_line_caches(); - rtl->set_physics_process_internal(false); - rtl->updating.store(false); - rtl->call_deferred(SNAME("queue_redraw")); +void RichTextLabel::_thread_function(void *p_userdata) { + _process_line_caches(); + updating.store(false); + call_deferred(SNAME("thread_end")); +} + +void RichTextLabel::_thread_end() { + set_physics_process_internal(false); + if (is_visible_in_tree()) { + queue_redraw(); + } } void RichTextLabel::_stop_thread() { if (threaded) { stop_thread.store(true); - thread.wait_to_finish(); + if (task != WorkerThreadPool::INVALID_TASK_ID) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(task); + task = WorkerThreadPool::INVALID_TASK_ID; + } } } @@ -2787,7 +2796,8 @@ bool RichTextLabel::_validate_line_caches() { if (threaded) { updating.store(true); loaded.store(true); - thread.start(RichTextLabel::_thread_function, reinterpret_cast<void *>(this)); + task = WorkerThreadPool::get_singleton()->add_template_task(this, &RichTextLabel::_thread_function, nullptr, true, vformat("RichTextLabelShape:%x", (int64_t)get_instance_id())); + set_physics_process_internal(true); loading_started = OS::get_singleton()->get_ticks_msec(); return false; } else { @@ -2808,7 +2818,7 @@ void RichTextLabel::_process_line_caches() { int ctrl_height = get_size().height; int fi = main->first_invalid_line.load(); - int total_chars = (fi == 0) ? 0 : (main->lines[fi].char_offset + main->lines[fi].char_count); + int total_chars = main->lines[fi].char_offset; float total_height = (fi == 0) ? 0 : _calculate_line_vertical_offset(main->lines[fi - 1]); for (int i = fi; i < (int)main->lines.size(); i++) { @@ -3435,6 +3445,8 @@ void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionar item->custom_effect = p_custom_effect; item->char_fx_transform->environment = p_environment; _add_item(item, true); + + set_process_internal(true); } void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) { @@ -4552,7 +4564,6 @@ void RichTextLabel::append_text(const String &p_bbcode) { push_customfx(effect, properties); pos = brk_end + 1; tag_stack.push_front(identifier); - set_process_internal(true); } else { add_text("["); //ignore pos = brk_pos + 1; @@ -5346,6 +5357,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell); ClassDB::bind_method(D_METHOD("push_fgcolor", "fgcolor"), &RichTextLabel::push_fgcolor); ClassDB::bind_method(D_METHOD("push_bgcolor", "bgcolor"), &RichTextLabel::push_bgcolor); + ClassDB::bind_method(D_METHOD("push_customfx", "effect", "env"), &RichTextLabel::push_customfx); ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop); ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear); @@ -5455,6 +5467,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_menu"), &RichTextLabel::get_menu); ClassDB::bind_method(D_METHOD("is_menu_visible"), &RichTextLabel::is_menu_visible); + ClassDB::bind_method(D_METHOD("_thread_end"), &RichTextLabel::_thread_end); + // Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 58b82d4672..fcbb91f67e 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -31,6 +31,7 @@ #ifndef RICH_TEXT_LABEL_H #define RICH_TEXT_LABEL_H +#include "core/object/worker_thread_pool.h" #include "rich_text_effect.h" #include "scene/gui/popup_menu.h" #include "scene/gui/scroll_bar.h" @@ -369,7 +370,7 @@ private: Item *current = nullptr; ItemFrame *current_frame = nullptr; - Thread thread; + WorkerThreadPool::TaskID task = WorkerThreadPool::INVALID_TASK_ID; Mutex data_mutex; bool threaded = false; std::atomic<bool> stop_thread; @@ -409,7 +410,8 @@ private: void _invalidate_current_line(ItemFrame *p_frame); - static void _thread_function(void *self); + void _thread_function(void *p_userdata); + void _thread_end(); void _stop_thread(); bool _validate_line_caches(); void _process_line_caches(); diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index dfc03c666b..f99b2edd54 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -368,7 +368,7 @@ void SpinBox::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "update_on_text_changed"), "set_update_on_text_changed", "get_update_on_text_changed"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_arrow_step"), "set_custom_arrow_step", "get_custom_arrow_step"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_arrow_step", PROPERTY_HINT_RANGE, "0,10000,0.0001,or_greater"), "set_custom_arrow_step", "get_custom_arrow_step"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus"); } diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 440597c24a..7c1d2f95a9 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -180,24 +180,51 @@ void SubViewportContainer::input(const Ref<InputEvent> &p_event) { return; } - Transform2D xform = get_global_transform_with_canvas(); + if (_is_propagated_in_gui_input(p_event)) { + return; + } - if (stretch) { - Transform2D scale_xf; - scale_xf.scale(Vector2(shrink, shrink)); - xform *= scale_xf; + _send_event_to_viewports(p_event); +} + +void SubViewportContainer::gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + if (Engine::get_singleton()->is_editor_hint()) { + return; } - Ref<InputEvent> ev = p_event->xformed_by(xform.affine_inverse()); + if (!_is_propagated_in_gui_input(p_event)) { + return; + } + if (stretch && shrink > 1) { + Transform2D xform; + xform.scale(Vector2(1, 1) / shrink); + _send_event_to_viewports(p_event->xformed_by(xform)); + } else { + _send_event_to_viewports(p_event); + } +} + +void SubViewportContainer::_send_event_to_viewports(const Ref<InputEvent> &p_event) { for (int i = 0; i < get_child_count(); i++) { SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); if (!c || c->is_input_disabled()) { continue; } - c->push_input(ev); + c->push_input(p_event); + } +} + +bool SubViewportContainer::_is_propagated_in_gui_input(const Ref<InputEvent> &p_event) { + // Propagation of events with a position property happen in gui_input + // Propagation of other events happen in input + if (Object::cast_to<InputEventMouse>(*p_event) || Object::cast_to<InputEventScreenDrag>(*p_event) || Object::cast_to<InputEventScreenTouch>(*p_event) || Object::cast_to<InputEventGesture>(*p_event)) { + return true; } + return false; } void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) { diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index d918c4a615..d3236b0c4e 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -39,6 +39,8 @@ class SubViewportContainer : public Container { bool stretch = false; int shrink = 1; void _notify_viewports(int p_notification); + bool _is_propagated_in_gui_input(const Ref<InputEvent> &p_event); + void _send_event_to_viewports(const Ref<InputEvent> &p_event); protected: void _notification(int p_what); @@ -52,6 +54,7 @@ public: bool is_stretch_enabled() const; virtual void input(const Ref<InputEvent> &p_event) override; + virtual void gui_input(const Ref<InputEvent> &p_event) override; virtual void unhandled_input(const Ref<InputEvent> &p_event) override; void set_stretch_shrink(int p_shrink); int get_stretch_shrink() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 77d5dda4f2..d785280701 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1861,7 +1861,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (context_menu_enabled) { - _generate_context_menu(); + _update_context_menu(); menu->set_position(get_screen_position() + mpos); menu->reset_size(); menu->popup(); @@ -2141,7 +2141,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { // MISC. if (k->is_action("ui_menu", true)) { if (context_menu_enabled) { - _generate_context_menu(); + _update_context_menu(); adjust_viewport_to_caret(); menu->set_position(get_screen_position() + get_caret_draw_pos()); menu->reset_size(); @@ -3544,6 +3544,19 @@ void TextEdit::insert_text_at_caret(const String &p_text, int p_caret) { adjust_carets_after_edit(i, new_line, new_column, from_line, from_col); } + + if (!ime_text.is_empty()) { + for (int i = 0; i < carets.size(); i++) { + String t; + if (get_caret_column(i) >= 0) { + t = text[get_caret_line(i)].substr(0, get_caret_column(i)) + ime_text + text[get_caret_line(i)].substr(get_caret_column(i), text[get_caret_line(i)].length()); + } else { + t = ime_text; + } + text.invalidate_cache(get_caret_line(i), get_caret_column(i), true, t, structured_text_parser(st_parser, st_args, t)); + } + } + end_complex_operation(); queue_redraw(); } @@ -3713,7 +3726,9 @@ void TextEdit::paste_primary_clipboard(int p_caret) { // Context menu. PopupMenu *TextEdit::get_menu() const { - const_cast<TextEdit *>(this)->_generate_context_menu(); + if (!menu) { + const_cast<TextEdit *>(this)->_generate_context_menu(); + } return menu; } @@ -6062,11 +6077,13 @@ void TextEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_UNDO); BIND_ENUM_CONSTANT(MENU_REDO); + BIND_ENUM_CONSTANT(MENU_SUBMENU_TEXT_DIR); BIND_ENUM_CONSTANT(MENU_DIR_INHERITED); BIND_ENUM_CONSTANT(MENU_DIR_AUTO); BIND_ENUM_CONSTANT(MENU_DIR_LTR); BIND_ENUM_CONSTANT(MENU_DIR_RTL); BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC); + BIND_ENUM_CONSTANT(MENU_SUBMENU_INSERT_UCC); BIND_ENUM_CONSTANT(MENU_INSERT_LRM); BIND_ENUM_CONSTANT(MENU_INSERT_RLM); BIND_ENUM_CONSTANT(MENU_INSERT_LRE); @@ -6717,87 +6734,7 @@ void TextEdit::_paste_primary_clipboard_internal(int p_caret) { grab_focus(); } -/* Text. */ // Context menu. -void TextEdit::_generate_context_menu() { - if (!menu) { - menu = memnew(PopupMenu); - add_child(menu, false, INTERNAL_MODE_FRONT); - - menu_dir = memnew(PopupMenu); - menu_dir->set_name("DirMenu"); - menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); - menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); - menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); - menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); - menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); - - menu_ctl = memnew(PopupMenu); - menu_ctl->set_name("CTLMenu"); - menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); - menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); - menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); - menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); - menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); - menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); - menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); - menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); - menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); - menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); - menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); - menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); - menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); - menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); - menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); - menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); - menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); - - menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); - menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); - menu_ctl->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); - } - - // Reorganize context menu. - menu->clear(); - if (editable) { - menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : Key::NONE); - } - menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE); - if (editable) { - menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE); - } - if (selecting_enabled || editable) { - menu->add_separator(); - } - if (selecting_enabled) { - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE); - } - if (editable) { - menu->add_item(RTR("Clear"), MENU_CLEAR); - menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : Key::NONE); - menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : Key::NONE); - } - menu->add_separator(); - menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu"); - menu->add_separator(); - menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); - menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); - if (editable) { - menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu"); - } - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); - - if (editable) { - menu->set_item_disabled(menu->get_item_index(MENU_UNDO), !has_undo()); - menu->set_item_disabled(menu->get_item_index(MENU_REDO), !has_redo()); - } -} Key TextEdit::_get_menu_action_accelerator(const String &p_action) { const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action); @@ -6824,6 +6761,112 @@ Key TextEdit::_get_menu_action_accelerator(const String &p_action) { } } +void TextEdit::_generate_context_menu() { + menu = memnew(PopupMenu); + add_child(menu, false, INTERNAL_MODE_FRONT); + + menu_dir = memnew(PopupMenu); + menu_dir->set_name("DirMenu"); + menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); + menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); + menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); + menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); + menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); + + menu_ctl = memnew(PopupMenu); + menu_ctl->set_name("CTLMenu"); + menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); + menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); + menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); + menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); + menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); + menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); + menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); + menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); + menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); + menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); + menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); + menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); + menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); + menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); + menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); + + menu->add_item(RTR("Cut"), MENU_CUT); + menu->add_item(RTR("Copy"), MENU_COPY); + menu->add_item(RTR("Paste"), MENU_PASTE); + menu->add_separator(); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL); + menu->add_item(RTR("Clear"), MENU_CLEAR); + menu->add_separator(); + menu->add_item(RTR("Undo"), MENU_UNDO); + menu->add_item(RTR("Redo"), MENU_REDO); + menu->add_separator(); + menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu", MENU_SUBMENU_TEXT_DIR); + menu->add_separator(); + menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); + menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu", MENU_SUBMENU_INSERT_UCC); + + menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); + menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); + menu_ctl->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); +} + +void TextEdit::_update_context_menu() { + if (!menu) { + _generate_context_menu(); + } + + int idx = -1; + +#define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + m_menu->set_item_disabled(idx, m_disabled); \ + } + +#define MENU_ITEM_ACTION(m_menu, m_id, m_action) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + } + +#define MENU_ITEM_DISABLED(m_menu, m_id, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_disabled(idx, m_disabled); \ + } + +#define MENU_ITEM_CHECKED(m_menu, m_id, m_checked) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_checked(idx, m_checked); \ + } + + MENU_ITEM_ACTION_DISABLED(menu, MENU_CUT, "ui_cut", !editable) + MENU_ITEM_ACTION(menu, MENU_COPY, "ui_copy") + MENU_ITEM_ACTION_DISABLED(menu, MENU_PASTE, "ui_paste", !editable) + MENU_ITEM_ACTION_DISABLED(menu, MENU_SELECT_ALL, "ui_text_select_all", !selecting_enabled) + MENU_ITEM_DISABLED(menu, MENU_CLEAR, !editable) + MENU_ITEM_ACTION_DISABLED(menu, MENU_UNDO, "ui_undo", !editable || !has_undo()) + MENU_ITEM_ACTION_DISABLED(menu, MENU_REDO, "ui_redo", !editable || !has_redo()) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_INHERITED, text_direction == TEXT_DIRECTION_INHERITED) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_AUTO, text_direction == TEXT_DIRECTION_AUTO) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_LTR, text_direction == TEXT_DIRECTION_LTR) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_RTL, text_direction == TEXT_DIRECTION_RTL) + MENU_ITEM_CHECKED(menu, MENU_DISPLAY_UCC, draw_control_chars) + MENU_ITEM_DISABLED(menu, MENU_SUBMENU_INSERT_UCC, !editable) + +#undef MENU_ITEM_ACTION_DISABLED +#undef MENU_ITEM_ACTION +#undef MENU_ITEM_DISABLED +#undef MENU_ITEM_CHECKED +} + /* Versioning */ void TextEdit::_push_current_op() { if (pending_action_end) { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 2ec2f39409..a084fa3833 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -87,11 +87,13 @@ public: MENU_SELECT_ALL, MENU_UNDO, MENU_REDO, + MENU_SUBMENU_TEXT_DIR, MENU_DIR_INHERITED, MENU_DIR_AUTO, MENU_DIR_LTR, MENU_DIR_RTL, MENU_DISPLAY_UCC, + MENU_SUBMENU_INSERT_UCC, MENU_INSERT_LRM, MENU_INSERT_RLM, MENU_INSERT_LRE, @@ -303,8 +305,9 @@ private: PopupMenu *menu_dir = nullptr; PopupMenu *menu_ctl = nullptr; - void _generate_context_menu(); Key _get_menu_action_accelerator(const String &p_action); + void _generate_context_menu(); + void _update_context_menu(); /* Versioning */ struct Caret; diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index e8d54e6937..145497fa61 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -43,36 +43,42 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) if (scroll_vec != Vector2() && mb->is_pressed()) { if (control_scheme == SCROLL_PANS) { if (mb->is_ctrl_pressed()) { - scroll_vec.y *= mb->get_factor(); - callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_pressed())); + // Compute the zoom factor. + float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); + zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; + float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; + callback_helper(zoom_callback, varray(zoom, mb->get_position(), p_event)); return true; } else { - Vector2 panning; - if (mb->is_shift_pressed()) { - panning.x += mb->get_factor() * scroll_vec.y; - panning.y += mb->get_factor() * scroll_vec.x; - } else { - panning.y += mb->get_factor() * scroll_vec.y; - panning.x += mb->get_factor() * scroll_vec.x; + Vector2 panning = scroll_vec * mb->get_factor(); + if (pan_axis == PAN_AXIS_HORIZONTAL) { + panning = Vector2(panning.x + panning.y, 0); + } else if (pan_axis == PAN_AXIS_VERTICAL) { + panning = Vector2(0, panning.x + panning.y); + } else if (mb->is_shift_pressed()) { + panning = Vector2(panning.y, panning.x); } - callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed())); + callback_helper(pan_callback, varray(-panning * scroll_speed, p_event)); return true; } } else { if (mb->is_ctrl_pressed()) { - Vector2 panning; - if (mb->is_shift_pressed()) { - panning.x += mb->get_factor() * scroll_vec.y; - panning.y += mb->get_factor() * scroll_vec.x; - } else { - panning.y += mb->get_factor() * scroll_vec.y; - panning.x += mb->get_factor() * scroll_vec.x; + Vector2 panning = scroll_vec * mb->get_factor(); + if (pan_axis == PAN_AXIS_HORIZONTAL) { + panning = Vector2(panning.x + panning.y, 0); + } else if (pan_axis == PAN_AXIS_VERTICAL) { + panning = Vector2(0, panning.x + panning.y); + } else if (mb->is_shift_pressed()) { + panning = Vector2(panning.y, panning.x); } - callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed())); + callback_helper(pan_callback, varray(-panning * scroll_speed, p_event)); return true; } else if (!mb->is_shift_pressed()) { - scroll_vec.y *= mb->get_factor(); - callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_pressed())); + // Compute the zoom factor. + float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); + zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; + float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; + callback_helper(zoom_callback, varray(zoom, mb->get_position(), p_event)); return true; } } @@ -102,14 +108,31 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) if (mm.is_valid()) { if (is_dragging) { if (p_canvas_rect != Rect2()) { - callback_helper(pan_callback, varray(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect))); + callback_helper(pan_callback, varray(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect), p_event)); } else { - callback_helper(pan_callback, varray(mm->get_relative())); + callback_helper(pan_callback, varray(mm->get_relative(), p_event)); } return true; } } + Ref<InputEventMagnifyGesture> magnify_gesture = p_event; + if (magnify_gesture.is_valid()) { + // Zoom gesture + callback_helper(zoom_callback, varray(magnify_gesture->get_factor(), magnify_gesture->get_position(), p_event)); + return true; + } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + callback_helper(pan_callback, varray(-pan_gesture->get_delta(), p_event)); + } + + Ref<InputEventScreenDrag> screen_drag = p_event; + if (screen_drag.is_valid()) { + callback_helper(pan_callback, varray(screen_drag->get_relative(), p_event)); + } + Ref<InputEventKey> k = p_event; if (k.is_valid()) { if (pan_view_shortcut.is_valid() && pan_view_shortcut->matches_event(k)) { @@ -140,8 +163,7 @@ void ViewPanner::callback_helper(Callable p_callback, Vector<Variant> p_args) { p_callback.callp(argptr, p_args.size(), result, ce); } -void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) { - scroll_callback = p_scroll_callback; +void ViewPanner::set_callbacks(Callable p_pan_callback, Callable p_zoom_callback) { pan_callback = p_pan_callback; zoom_callback = p_zoom_callback; } @@ -163,6 +185,20 @@ void ViewPanner::set_simple_panning_enabled(bool p_enabled) { simple_panning_enabled = p_enabled; } +void ViewPanner::set_scroll_speed(int p_scroll_speed) { + ERR_FAIL_COND(p_scroll_speed <= 0); + scroll_speed = p_scroll_speed; +} + +void ViewPanner::set_scroll_zoom_factor(float p_scroll_zoom_factor) { + ERR_FAIL_COND(p_scroll_zoom_factor <= 1.0); + scroll_zoom_factor = p_scroll_zoom_factor; +} + +void ViewPanner::set_pan_axis(PanAxis p_pan_axis) { + pan_axis = p_pan_axis; +} + void ViewPanner::setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning) { set_control_scheme(p_scheme); set_pan_shortcut(p_shortcut); diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h index 861574a80c..60d36ca04c 100644 --- a/scene/gui/view_panner.h +++ b/scene/gui/view_panner.h @@ -45,7 +45,17 @@ public: SCROLL_PANS, }; + enum PanAxis { + PAN_AXIS_BOTH, + PAN_AXIS_HORIZONTAL, + PAN_AXIS_VERTICAL, + }; + private: + int scroll_speed = 32; + float scroll_zoom_factor = 1.1; + PanAxis pan_axis = PAN_AXIS_BOTH; + bool is_dragging = false; bool pan_key_pressed = false; bool force_drag = false; @@ -55,7 +65,6 @@ private: Ref<Shortcut> pan_view_shortcut; - Callable scroll_callback; Callable pan_callback; Callable zoom_callback; @@ -63,11 +72,14 @@ private: ControlScheme control_scheme = SCROLL_ZOOMS; public: - void set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback); + void set_callbacks(Callable p_pan_callback, Callable p_zoom_callback); void set_control_scheme(ControlScheme p_scheme); void set_enable_rmb(bool p_enable); void set_pan_shortcut(Ref<Shortcut> p_shortcut); void set_simple_panning_enabled(bool p_enabled); + void set_scroll_speed(int p_scroll_speed); + void set_scroll_zoom_factor(float p_scroll_zoom_factor); + void set_pan_axis(PanAxis p_pan_axis); void setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 7b0554442c..35176f0edd 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -154,17 +154,7 @@ Transform2D CanvasItem::get_global_transform_with_canvas() const { Transform2D CanvasItem::get_screen_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); - Transform2D xform = get_global_transform_with_canvas(); - - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - Transform2D s; - s.set_origin(w->get_position()); - - xform = s * xform; - } - - return xform; + return get_viewport()->get_popup_base_transform() * get_global_transform_with_canvas(); } Transform2D CanvasItem::get_global_transform() const { @@ -927,6 +917,12 @@ void CanvasItem::force_update_transform() { notification(NOTIFICATION_TRANSFORM_CHANGED); } +void CanvasItem::_validate_property(PropertyInfo &p_property) const { + if (hide_clip_children && p_property.name == "clip_children") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("_top_level_raise_self"), &CanvasItem::_top_level_raise_self); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 644fe856ec..1c84ea338a 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -106,6 +106,7 @@ private: bool use_parent_material = false; bool notify_local_transform = false; bool notify_transform = false; + bool hide_clip_children = false; ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED; @@ -155,6 +156,9 @@ protected: void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; + + _FORCE_INLINE_ void set_hide_clip_children(bool p_value) { hide_clip_children = p_value; } GDVIRTUAL0(_draw) public: diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 46ba7e67eb..0d53f740db 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -33,7 +33,7 @@ #include "scene/main/timer.h" Error HTTPRequest::_request() { - return client->connect_to_host(url, port, use_tls, validate_tls); + return client->connect_to_host(url, port, use_tls ? tls_options : nullptr); } Error HTTPRequest::_parse_url(const String &p_url) { @@ -96,7 +96,7 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S return value; } -Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_tls_validate_domain, HTTPClient::Method p_method, const String &p_request_data) { +Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, HTTPClient::Method p_method, const String &p_request_data) { // Copy the string into a raw buffer. Vector<uint8_t> raw_data; @@ -108,10 +108,10 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h memcpy(w, charstr.ptr(), len); } - return request_raw(p_url, p_custom_headers, p_tls_validate_domain, p_method, raw_data); + return request_raw(p_url, p_custom_headers, p_method, raw_data); } -Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, bool p_tls_validate_domain, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) { +Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) { ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(requesting, ERR_BUSY, "HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one."); @@ -127,8 +127,6 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust return err; } - validate_tls = p_tls_validate_domain; - headers = p_custom_headers; if (accept_gzip) { @@ -590,10 +588,16 @@ void HTTPRequest::_timeout() { _defer_done(RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray()); } +void HTTPRequest::set_tls_options(const Ref<TLSOptions> &p_options) { + ERR_FAIL_COND(p_options.is_null() || p_options->is_server()); + tls_options = p_options; +} + void HTTPRequest::_bind_methods() { - ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "tls_validate_domain", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String())); - ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "tls_validate_domain", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray())); ClassDB::bind_method(D_METHOD("cancel_request"), &HTTPRequest::cancel_request); + ClassDB::bind_method(D_METHOD("set_tls_options", "client_options"), &HTTPRequest::set_tls_options); ClassDB::bind_method(D_METHOD("get_http_client_status"), &HTTPRequest::get_http_client_status); @@ -654,6 +658,7 @@ void HTTPRequest::_bind_methods() { HTTPRequest::HTTPRequest() { client = Ref<HTTPClient>(HTTPClient::create()); + tls_options = TLSOptions::client(); timer = memnew(Timer); timer->set_one_shot(true); timer->connect("timeout", callable_mp(this, &HTTPRequest::_timeout)); diff --git a/scene/main/http_request.h b/scene/main/http_request.h index add4e9538d..9a91171eaf 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -68,8 +68,8 @@ private: String url; int port = 80; Vector<String> headers; - bool validate_tls = false; bool use_tls = false; + Ref<TLSOptions> tls_options; HTTPClient::Method method; Vector<uint8_t> request_data; @@ -125,8 +125,8 @@ protected: static void _bind_methods(); public: - Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_tls_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request - Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_tls_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request + Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request + Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request void cancel_request(); HTTPClient::Status get_http_client_status() const; @@ -161,6 +161,8 @@ public: void set_http_proxy(const String &p_host, int p_port); void set_https_proxy(const String &p_host, int p_port); + void set_tls_options(const Ref<TLSOptions> &p_options); + HTTPRequest(); }; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index de486094fe..ba75c92c85 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1987,7 +1987,16 @@ String Node::get_scene_file_path() const { } void Node::set_editor_description(const String &p_editor_description) { + if (data.editor_description == p_editor_description) { + return; + } + data.editor_description = p_editor_description; + + if (Engine::get_singleton()->is_editor_hint() && is_inside_tree()) { + // Update tree so the tooltip in the Scene tree dock is also updated in the editor. + get_tree()->tree_changed(); + } } String Node::get_editor_description() const { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 07bcf45899..48cff5aa8e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -704,25 +704,15 @@ void Viewport::_process_picking() { } #ifndef _3D_DISABLED - bool captured = false; - + CollisionObject3D *capture_object = nullptr; if (physics_object_capture.is_valid()) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture)); - if (co && camera_3d) { - _collision_object_3d_input_event(co, camera_3d, ev, Vector3(), Vector3(), 0); - captured = true; - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) { - physics_object_capture = ObjectID(); - } - - } else { + capture_object = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture)); + if (!capture_object || !camera_3d || (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed())) { physics_object_capture = ObjectID(); } } - if (captured) { - // None. - } else if (pos == last_pos) { + if (pos == last_pos) { if (last_id.is_valid()) { if (ObjectDB::get_instance(last_id) && last_object) { // Good, exists. @@ -748,13 +738,12 @@ void Viewport::_process_picking() { bool col = space->intersect_ray(ray_params, result); ObjectID new_collider; - if (col) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(result.collider); - if (co && co->can_process()) { - _collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape); + CollisionObject3D *co = col ? Object::cast_to<CollisionObject3D>(result.collider) : nullptr; + if (co && co->can_process()) { + new_collider = result.collider_id; + if (!capture_object) { last_object = co; last_id = result.collider_id; - new_collider = last_id; if (co->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { physics_object_capture = last_id; } @@ -763,21 +752,24 @@ void Viewport::_process_picking() { if (is_mouse && new_collider != physics_object_over) { if (physics_object_over.is_valid()) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over)); - if (co) { - co->_mouse_exit(); + CollisionObject3D *previous_co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over)); + if (previous_co) { + previous_co->_mouse_exit(); } } if (new_collider.is_valid()) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(new_collider)); - if (co) { - co->_mouse_enter(); - } + DEV_ASSERT(co); + co->_mouse_enter(); } physics_object_over = new_collider; } + if (capture_object) { + _collision_object_3d_input_event(capture_object, camera_3d, ev, result.position, result.normal, result.shape); + } else if (new_collider.is_valid()) { + _collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape); + } } last_pos = pos; @@ -1050,7 +1042,7 @@ Camera2D *Viewport::get_camera_2d() const { } Transform2D Viewport::get_final_transform() const { - return stretch_transform * global_canvas_transform; + return _get_input_pre_xform().affine_inverse() * stretch_transform * global_canvas_transform; } void Viewport::_update_canvas_items(Node *p_node) { @@ -1133,7 +1125,7 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { return ev; // No transformation defined for null event } - Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); + Transform2D ai = get_final_transform().affine_inverse(); return ev->xformed_by(ai); } @@ -1795,9 +1787,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (w->is_embedded()) { embedder = w->_get_embedder(); - Transform2D ai = (get_final_transform().affine_inverse() * _get_input_pre_xform()).affine_inverse(); - - viewport_pos = ai.xform(mpos) + w->get_position(); // To parent coords. + viewport_pos = get_final_transform().xform(mpos) + w->get_position(); // To parent coords. } } } @@ -1847,7 +1837,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (viewport_under) { if (viewport_under != this) { - Transform2D ai = (viewport_under->get_final_transform().affine_inverse() * viewport_under->_get_input_pre_xform()); + Transform2D ai = viewport_under->get_final_transform().affine_inverse(); viewport_pos = ai.xform(viewport_pos); } // Find control under at position. @@ -2853,7 +2843,7 @@ bool Viewport::get_physics_object_picking() { } Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const { - Transform2D xf = get_final_transform(); + Transform2D xf = stretch_transform * global_canvas_transform; return xf.xform(p_viewport_coords); } @@ -3245,7 +3235,7 @@ Viewport::SDFScale Viewport::get_sdf_scale() const { } Transform2D Viewport::get_screen_transform() const { - return _get_input_pre_xform().affine_inverse() * get_final_transform(); + return get_final_transform(); } void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) { @@ -4188,6 +4178,21 @@ Transform2D SubViewport::get_screen_transform() const { return container_transform * Viewport::get_screen_transform(); } +Transform2D SubViewport::get_popup_base_transform() const { + if (is_embedding_subwindows()) { + return Transform2D(); + } + SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent()); + if (!c) { + return Viewport::get_screen_transform(); + } + Transform2D container_transform; + if (c->is_stretch_enabled()) { + container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink())); + } + return c->get_screen_transform() * container_transform * Viewport::get_screen_transform(); +} + void SubViewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 603e92b071..d5d5201e9a 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -649,6 +649,7 @@ public: bool get_canvas_cull_mask_bit(uint32_t p_layer) const; virtual Transform2D get_screen_transform() const; + virtual Transform2D get_popup_base_transform() const { return Transform2D(); } #ifndef _3D_DISABLED bool use_xr = false; @@ -775,6 +776,7 @@ public: ClearMode get_clear_mode() const; virtual Transform2D get_screen_transform() const override; + virtual Transform2D get_popup_base_transform() const override; SubViewport(); ~SubViewport(); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 2c6599d849..869d12b4df 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -663,18 +663,18 @@ void Window::set_visible(bool p_visible) { return; } - visible = p_visible; - if (!is_inside_tree()) { + visible = p_visible; return; } - if (updating_child_controls) { - _update_child_controls(); - } - ERR_FAIL_COND_MSG(get_parent() == nullptr, "Can't change visibility of main window."); + visible = p_visible; + + // Stop any queued resizing, as the window will be resized right now. + updating_child_controls = false; + Viewport *embedder_vp = _get_embedder(); if (!embedder_vp) { @@ -833,7 +833,7 @@ bool Window::is_visible() const { } void Window::_update_window_size() { - // Force window to respect size limitations of rendering server + // Force window to respect size limitations of rendering server. RenderingServer *rendering_server = RenderingServer::get_singleton(); if (rendering_server) { Size2i max_window_size = rendering_server->get_maximum_viewport_size(); @@ -1130,6 +1130,7 @@ void Window::_notification(int p_what) { case NOTIFICATION_READY: { if (wrap_controls) { + // Finish any resizing immediately so it doesn't interfere on stuff overriding _ready(). _update_child_controls(); } } break; @@ -1138,9 +1139,7 @@ void Window::_notification(int p_what) { _invalidate_theme_cache(); _update_theme_item_cache(); - if (embedder) { - embedder->_sub_window_update(this); - } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) { String tr_title = atr(title); #ifdef DEBUG_ENABLED if (window_id == DisplayServer::MAIN_WINDOW_ID) { @@ -1152,8 +1151,6 @@ void Window::_notification(int p_what) { #endif DisplayServer::get_singleton()->window_set_title(tr_title, window_id); } - - child_controls_changed(); } break; case NOTIFICATION_EXIT_TREE: { @@ -1254,8 +1251,13 @@ Vector<Vector2> Window::get_mouse_passthrough_polygon() const { void Window::set_wrap_controls(bool p_enable) { wrap_controls = p_enable; - if (wrap_controls) { - child_controls_changed(); + + if (!is_inside_tree()) { + return; + } + + if (updating_child_controls) { + _update_child_controls(); } else { _update_window_size(); } @@ -1282,23 +1284,23 @@ Size2 Window::_get_contents_minimum_size() const { return max; } -void Window::_update_child_controls() { - if (!updating_child_controls) { +void Window::child_controls_changed() { + if (!is_inside_tree() || !visible || updating_child_controls) { return; } - _update_window_size(); - - updating_child_controls = false; + updating_child_controls = true; + call_deferred(SNAME("_update_child_controls")); } -void Window::child_controls_changed() { - if (!is_inside_tree() || !visible || updating_child_controls) { +void Window::_update_child_controls() { + if (!updating_child_controls) { return; } - updating_child_controls = true; - call_deferred(SNAME("_update_child_controls")); + _update_window_size(); + + updating_child_controls = false; } bool Window::_can_consume_input_events() const { @@ -1647,7 +1649,24 @@ void Window::_invalidate_theme_cache() { void Window::_update_theme_item_cache() { // Request an update on the next frame to reflect theme changes. // Updating without a delay can cause a lot of lag. - child_controls_changed(); + if (!wrap_controls) { + updating_embedded_window = true; + call_deferred(SNAME("_update_embedded_window")); + } else { + child_controls_changed(); + } +} + +void Window::_update_embedded_window() { + if (!updating_embedded_window) { + return; + } + + if (embedder) { + embedder->_sub_window_update(this); + }; + + updating_embedded_window = false; } void Window::set_theme_type_variation(const StringName &p_theme_type) { @@ -2104,6 +2123,19 @@ Transform2D Window::get_screen_transform() const { return embedder_transform * Viewport::get_screen_transform(); } +Transform2D Window::get_popup_base_transform() const { + if (is_embedding_subwindows()) { + return Transform2D(); + } + Transform2D window_transform; + window_transform.set_origin(get_position()); + window_transform *= Viewport::get_screen_transform(); + if (_get_embedder()) { + return _get_embedder()->get_popup_base_transform() * window_transform; + } + return window_transform; +} + void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title); ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title); @@ -2188,6 +2220,7 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("child_controls_changed"), &Window::child_controls_changed); ClassDB::bind_method(D_METHOD("_update_child_controls"), &Window::_update_child_controls); + ClassDB::bind_method(D_METHOD("_update_embedded_window"), &Window::_update_embedded_window); ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Window::set_theme); ClassDB::bind_method(D_METHOD("get_theme"), &Window::get_theme); diff --git a/scene/main/window.h b/scene/main/window.h index e9c217f973..182caf5f0c 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -116,6 +116,7 @@ private: bool exclusive = false; bool wrap_controls = false; bool updating_child_controls = false; + bool updating_embedded_window = false; bool clamp_to_embedder = false; LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; @@ -123,6 +124,7 @@ private: bool auto_translate = true; void _update_child_controls(); + void _update_embedded_window(); Size2i content_scale_size; ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED; @@ -373,6 +375,7 @@ public: // virtual Transform2D get_screen_transform() const override; + virtual Transform2D get_popup_base_transform() const override; Rect2i get_parent_rect() const; virtual DisplayServer::WindowID get_window_id() const override; diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 50f3015814..bfbc92a8d4 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -127,6 +127,10 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { } } return true; + } else if (what == "use_blend") { + if (track_get_type(track) == TYPE_AUDIO) { + audio_track_set_use_blend(track, p_value); + } } else if (what == "interp") { track_set_interpolation_type(track, InterpolationType(p_value.operator int())); } else if (what == "loop_wrap") { @@ -528,7 +532,10 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { } return true; - + } else if (what == "use_blend") { + if (track_get_type(track) == TYPE_AUDIO) { + r_ret = audio_track_is_use_blend(track); + } } else if (what == "interp") { r_ret = track_get_interpolation_type(track); } else if (what == "loop_wrap") { @@ -834,6 +841,9 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } + if (track_get_type(i) == TYPE_AUDIO) { + p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/use_blend", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + } } } @@ -3581,6 +3591,27 @@ real_t Animation::audio_track_get_key_end_offset(int p_track, int p_key) const { return at->values[p_key].value.end_offset; } +void Animation::audio_track_set_use_blend(int p_track, bool p_enable) { + ERR_FAIL_INDEX(p_track, tracks.size()); + Track *t = tracks[p_track]; + ERR_FAIL_COND(t->type != TYPE_AUDIO); + + AudioTrack *at = static_cast<AudioTrack *>(t); + + at->use_blend = p_enable; + emit_changed(); +} + +bool Animation::audio_track_is_use_blend(int p_track) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), false); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_AUDIO, false); + + AudioTrack *at = static_cast<AudioTrack *>(t); + + return at->use_blend; +} + // int Animation::animation_track_insert_key(int p_track, double p_time, const StringName &p_animation) { @@ -3813,6 +3844,8 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("audio_track_get_key_stream", "track_idx", "key_idx"), &Animation::audio_track_get_key_stream); ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_start_offset); ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_end_offset); + ClassDB::bind_method(D_METHOD("audio_track_set_use_blend", "track_idx", "enable"), &Animation::audio_track_set_use_blend); + ClassDB::bind_method(D_METHOD("audio_track_is_use_blend", "track_idx"), &Animation::audio_track_is_use_blend); ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track_idx", "time", "animation"), &Animation::animation_track_insert_key); ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation); @@ -4691,6 +4724,7 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol data_tracks.resize(tracks_to_compress.size()); time_tracks.resize(tracks_to_compress.size()); + uint32_t needed_min_page_size = base_page_size; for (uint32_t i = 0; i < data_tracks.size(); i++) { data_tracks[i].split_tolerance = p_split_tolerance; if (track_get_type(tracks_to_compress[i]) == TYPE_BLEND_SHAPE) { @@ -4698,7 +4732,12 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol } else { data_tracks[i].components = 3; } + needed_min_page_size += data_tracks[i].data.size() + data_tracks[i].get_temp_packet_size(); + } + for (uint32_t i = 0; i < time_tracks.size(); i++) { + needed_min_page_size += time_tracks[i].packets.size() * 4; // time packet is 32 bits } + ERR_FAIL_COND_MSG(p_page_size < needed_min_page_size, "Cannot compress with the given page size"); while (true) { // Begin by finding the keyframe in all tracks with the time closest to the current time diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 00a0761e0a..2c2ddb7095 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -213,6 +213,7 @@ private: struct AudioTrack : public Track { Vector<TKey<AudioKey>> values; + bool use_blend = true; AudioTrack() { type = TYPE_AUDIO; @@ -447,6 +448,8 @@ public: Ref<Resource> audio_track_get_key_stream(int p_track, int p_key) const; real_t audio_track_get_key_start_offset(int p_track, int p_key) const; real_t audio_track_get_key_end_offset(int p_track, int p_key) const; + void audio_track_set_use_blend(int p_track, bool p_enable); + bool audio_track_is_use_blend(int p_track) const; int animation_track_insert_key(int p_track, double p_time, const StringName &p_animation); void animation_track_set_key_animation(int p_track, int p_key, const StringName &p_animation); diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index 5dc4e64c2e..5309e54846 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -88,8 +88,10 @@ void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector<Vector2> points = _get_points(); Vector<Color> col = { p_color }; RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); + if (is_collision_outline_enabled()) { points.push_back(points[0]); + col = { Color(p_color, 1.0) }; RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); } } diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index a15dfc0a54..0b207c33ca 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -84,6 +84,7 @@ void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) { if (is_collision_outline_enabled()) { points.push_back(points[0]); + col = { Color(p_color, 1.0) }; RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); } } diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index e51b48a4b1..7f19dd63e6 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -76,13 +76,14 @@ void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) { return; } - Vector<Color> col; - col.push_back(p_color); + Vector<Color> col = { p_color }; RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); + if (is_collision_outline_enabled()) { + col = { Color(p_color, 1.0) }; RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); - // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. - RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color, 1.0); + // Draw the last segment. + RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], Color(p_color, 1.0)); } } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index deee187e8e..e5a1adff20 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -2868,6 +2868,12 @@ void SystemFont::_bind_methods() { ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &SystemFont::set_multichannel_signed_distance_field); ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &SystemFont::is_multichannel_signed_distance_field); + ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &SystemFont::set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &SystemFont::get_msdf_pixel_range); + + ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &SystemFont::set_msdf_size); + ClassDB::bind_method(D_METHOD("get_msdf_size"), &SystemFont::get_msdf_size); + ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &SystemFont::set_oversampling); ClassDB::bind_method(D_METHOD("get_oversampling"), &SystemFont::get_oversampling); @@ -2890,6 +2896,8 @@ void SystemFont::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), "set_subpixel_positioning", "get_subpixel_positioning"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field"), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range"), "set_msdf_pixel_range", "get_msdf_pixel_range"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size"), "set_msdf_size", "get_msdf_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); } @@ -2987,6 +2995,8 @@ void SystemFont::_update_base_font() { file->set_hinting(hinting); file->set_subpixel_positioning(subpixel_positioning); file->set_multichannel_signed_distance_field(msdf); + file->set_msdf_pixel_range(msdf_pixel_range); + file->set_msdf_size(msdf_size); file->set_oversampling(oversampling); base_font = file; @@ -3186,6 +3196,34 @@ bool SystemFont::is_multichannel_signed_distance_field() const { return msdf; } +void SystemFont::set_msdf_pixel_range(int p_msdf_pixel_range) { + if (msdf_pixel_range != p_msdf_pixel_range) { + msdf_pixel_range = p_msdf_pixel_range; + if (base_font.is_valid()) { + base_font->set_msdf_pixel_range(msdf_pixel_range); + } + emit_changed(); + } +} + +int SystemFont::get_msdf_pixel_range() const { + return msdf_pixel_range; +} + +void SystemFont::set_msdf_size(int p_msdf_size) { + if (msdf_size != p_msdf_size) { + msdf_size = p_msdf_size; + if (base_font.is_valid()) { + base_font->set_msdf_size(msdf_size); + } + emit_changed(); + } +} + +int SystemFont::get_msdf_size() const { + return msdf_size; +} + void SystemFont::set_oversampling(real_t p_oversampling) { if (oversampling != p_oversampling) { oversampling = p_oversampling; diff --git a/scene/resources/font.h b/scene/resources/font.h index 4d468a8841..ef79f8bd02 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -452,6 +452,8 @@ class SystemFont : public Font { TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; real_t oversampling = 0.f; bool msdf = false; + int msdf_pixel_range = 16; + int msdf_size = 48; protected: static void _bind_methods(); @@ -488,6 +490,12 @@ public: virtual void set_multichannel_signed_distance_field(bool p_msdf); virtual bool is_multichannel_signed_distance_field() const; + virtual void set_msdf_pixel_range(int p_msdf_pixel_range); + virtual int get_msdf_pixel_range() const; + + virtual void set_msdf_size(int p_msdf_size); + virtual int get_msdf_size() const; + virtual void set_font_names(const PackedStringArray &p_names); virtual PackedStringArray get_font_names() const; diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 55b633a40c..742ac2bbd9 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -1058,6 +1058,8 @@ struct EditorSceneFormatImporterMeshLightmapSurface { String name; }; +static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; + Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); @@ -1178,9 +1180,6 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, return ERR_CANT_CREATE; } - //remove surfaces - clear(); - //create surfacetools for each surface.. LocalVector<Ref<SurfaceTool>> surfaces_tools; @@ -1190,9 +1189,16 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, st->begin(Mesh::PRIMITIVE_TRIANGLES); st->set_material(lightmap_surfaces[i].material); st->set_meta("name", lightmap_surfaces[i].name); + + for (int custom_i = 0; custom_i < RS::ARRAY_CUSTOM_COUNT; custom_i++) { + st->set_custom_format(custom_i, (SurfaceTool::CustomFormat)((lightmap_surfaces[i].format >> custom_shift[custom_i]) & RS::ARRAY_FORMAT_CUSTOM_MASK)); + } surfaces_tools.push_back(st); //stay there } + //remove surfaces + clear(); + print_verbose("Mesh: Gen indices: " + itos(gen_index_count)); //go through all indices @@ -1229,6 +1235,11 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) { surfaces_tools[surface]->set_weights(v.weights); } + for (int custom_i = 0; custom_i < RS::ARRAY_CUSTOM_COUNT; custom_i++) { + if ((lightmap_surfaces[surface].format >> custom_shift[custom_i]) & RS::ARRAY_FORMAT_CUSTOM_MASK) { + surfaces_tools[surface]->set_custom(custom_i, v.custom[custom_i]); + } + } Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); surfaces_tools[surface]->set_uv2(uv2); @@ -1238,10 +1249,11 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, } //generate surfaces - for (Ref<SurfaceTool> &tool : surfaces_tools) { + for (int i = 0; i < lightmap_surfaces.size(); i++) { + Ref<SurfaceTool> &tool = surfaces_tools[i]; tool->index(); Array arrays = tool->commit_to_arrays(); - add_surface(tool->get_primitive_type(), arrays, Array(), Dictionary(), tool->get_material(), tool->get_meta("name")); + add_surface(tool->get_primitive_type(), arrays, Array(), Dictionary(), tool->get_material(), tool->get_meta("name"), lightmap_surfaces[i].format); } set_lightmap_size_hint(Size2(size_x, size_y)); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index f4aa0637f7..7e84814ab3 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -2316,71 +2316,51 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel() return refraction_texture_channel; } -Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y, bool p_msdf, bool p_no_depth, bool p_fixed_size, TextureFilter p_filter, RID *r_shader_rid) { - int64_t hash = 0; - if (p_shaded) { - hash |= 1 << 0; - } - if (p_transparent) { - hash |= 1 << 1; - } - if (p_cut_alpha) { - hash |= 1 << 2; - } - if (p_opaque_prepass) { - hash |= 1 << 3; - } - if (p_double_sided) { - hash |= 1 << 4; - } - if (p_billboard) { - hash |= 1 << 5; - } - if (p_billboard_y) { - hash |= 1 << 6; - } - if (p_msdf) { - hash |= 1 << 7; - } - if (p_no_depth) { - hash |= 1 << 8; - } - if (p_fixed_size) { - hash |= 1 << 9; - } - hash = hash_murmur3_one_64(p_filter, hash); - - if (materials_for_2d.has(hash)) { +Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, Transparency p_transparency, bool p_double_sided, bool p_billboard, bool p_billboard_y, bool p_msdf, bool p_no_depth, bool p_fixed_size, TextureFilter p_filter, AlphaAntiAliasing p_alpha_antialiasing_mode, RID *r_shader_rid) { + uint64_t key = 0; + key |= ((int8_t)p_shaded & 0x01) << 0; + key |= ((int8_t)p_transparency & 0x07) << 1; // Bits 1-3. + key |= ((int8_t)p_double_sided & 0x01) << 4; + key |= ((int8_t)p_billboard & 0x01) << 5; + key |= ((int8_t)p_billboard_y & 0x01) << 6; + key |= ((int8_t)p_msdf & 0x01) << 7; + key |= ((int8_t)p_no_depth & 0x01) << 8; + key |= ((int8_t)p_fixed_size & 0x01) << 9; + key |= ((int8_t)p_filter & 0x07) << 10; // Bits 10-12. + key |= ((int8_t)p_alpha_antialiasing_mode & 0x07) << 13; // Bits 13-15. + + if (materials_for_2d.has(key)) { if (r_shader_rid) { - *r_shader_rid = materials_for_2d[hash]->get_shader_rid(); + *r_shader_rid = materials_for_2d[key]->get_shader_rid(); } - return materials_for_2d[hash]; + return materials_for_2d[key]; } Ref<StandardMaterial3D> material; material.instantiate(); material->set_shading_mode(p_shaded ? SHADING_MODE_PER_PIXEL : SHADING_MODE_UNSHADED); - material->set_transparency(p_transparent ? (p_opaque_prepass ? TRANSPARENCY_ALPHA_DEPTH_PRE_PASS : (p_cut_alpha ? TRANSPARENCY_ALPHA_SCISSOR : TRANSPARENCY_ALPHA)) : TRANSPARENCY_DISABLED); + material->set_transparency(p_transparency); material->set_cull_mode(p_double_sided ? CULL_DISABLED : CULL_BACK); material->set_flag(FLAG_SRGB_VERTEX_COLOR, true); material->set_flag(FLAG_ALBEDO_FROM_VERTEX_COLOR, true); material->set_flag(FLAG_ALBEDO_TEXTURE_MSDF, p_msdf); material->set_flag(FLAG_DISABLE_DEPTH_TEST, p_no_depth); material->set_flag(FLAG_FIXED_SIZE, p_fixed_size); + material->set_alpha_antialiasing(p_alpha_antialiasing_mode); material->set_texture_filter(p_filter); if (p_billboard || p_billboard_y) { material->set_flag(FLAG_BILLBOARD_KEEP_SCALE, true); material->set_billboard_mode(p_billboard_y ? BILLBOARD_FIXED_Y : BILLBOARD_ENABLED); } - materials_for_2d[hash] = material; + materials_for_2d[key] = material; if (r_shader_rid) { - *r_shader_rid = materials_for_2d[hash]->get_shader_rid(); + *r_shader_rid = materials_for_2d[key]->get_shader_rid(); } - return materials_for_2d[hash]; + return materials_for_2d[key]; } void BaseMaterial3D::set_on_top_of_alpha() { diff --git a/scene/resources/material.h b/scene/resources/material.h index fe1448cd4c..5ea9a807d4 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -760,7 +760,7 @@ public: static void finish_shaders(); static void flush_changes(); - static Ref<Material> get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RID *r_shader_rid = nullptr); + static Ref<Material> get_material_for_2d(bool p_shaded, Transparency p_transparency, bool p_double_sided, bool p_billboard = false, bool p_billboard_y = false, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, AlphaAntiAliasing p_alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF, RID *r_shader_rid = nullptr); virtual RID get_shader_rid() const override; diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index 84c147b4b4..65b1653293 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -79,11 +79,7 @@ void RectangleShape2D::draw(const RID &p_to_rid, const Color &p_color) { stroke_points.write[3] = Vector2(-size.x, size.y) * 0.5; stroke_points.write[4] = -size * 0.5; - Vector<Color> stroke_colors; - stroke_colors.resize(5); - for (int i = 0; i < 5; i++) { - stroke_colors.write[i] = (p_color); - } + Vector<Color> stroke_colors = { Color(p_color, 1.0) }; RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, stroke_points, stroke_colors); } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index a09dbd50cf..fd2be9ba22 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -55,6 +55,12 @@ void Shader::set_path(const String &p_path, bool p_take_over) { RS::get_singleton()->shader_set_path_hint(shader, p_path); } +void Shader::set_include_path(const String &p_path) { + // Used only if the shader does not have a resource path set, + // for example during loading stage or when created by code. + include_path = p_path; +} + void Shader::set_code(const String &p_code) { for (Ref<ShaderInclude> E : include_dependencies) { E->disconnect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); @@ -80,11 +86,15 @@ void Shader::set_code(const String &p_code) { HashSet<Ref<ShaderInclude>> new_include_dependencies; { + String path = get_path(); + if (path.is_empty()) { + path = include_path; + } // Preprocessor must run here and not in the server because: // 1) Need to keep track of include dependencies at resource level // 2) Server does not do interaction with Resource filetypes, this is a scene level feature. ShaderPreprocessor preprocessor; - preprocessor.preprocess(p_code, "", pp_code, nullptr, nullptr, nullptr, &new_include_dependencies); + preprocessor.preprocess(p_code, path, pp_code, nullptr, nullptr, nullptr, &new_include_dependencies); } // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) @@ -231,6 +241,7 @@ Ref<Resource> ResourceFormatLoaderShader::load(const String &p_path, const Strin String str; str.parse_utf8((const char *)buffer.ptr(), buffer.size()); + shader->set_include_path(p_path); shader->set_code(str); if (r_error) { diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 55608b6c11..ca889940ef 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -56,6 +56,7 @@ private: Mode mode = MODE_SPATIAL; HashSet<Ref<ShaderInclude>> include_dependencies; String code; + String include_path; HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures; @@ -72,6 +73,7 @@ public: virtual Mode get_mode() const; virtual void set_path(const String &p_path, bool p_take_over = false) override; + void set_include_path(const String &p_path); void set_code(const String &p_code); String get_code() const; diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp index cd5e9861f7..68137cbec0 100644 --- a/scene/resources/shader_include.cpp +++ b/scene/resources/shader_include.cpp @@ -45,9 +45,14 @@ void ShaderInclude::set_code(const String &p_code) { } { + String path = get_path(); + if (path.is_empty()) { + path = include_path; + } + String pp_code; ShaderPreprocessor preprocessor; - preprocessor.preprocess(p_code, "", pp_code, nullptr, nullptr, nullptr, &new_dependencies); + preprocessor.preprocess(p_code, path, pp_code, nullptr, nullptr, nullptr, &new_dependencies); } // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) @@ -64,6 +69,10 @@ String ShaderInclude::get_code() const { return code; } +void ShaderInclude::set_include_path(const String &p_path) { + include_path = p_path; +} + void ShaderInclude::_bind_methods() { ClassDB::bind_method(D_METHOD("set_code", "code"), &ShaderInclude::set_code); ClassDB::bind_method(D_METHOD("get_code"), &ShaderInclude::get_code); @@ -86,6 +95,7 @@ Ref<Resource> ResourceFormatLoaderShaderInclude::load(const String &p_path, cons String str; str.parse_utf8((const char *)buffer.ptr(), buffer.size()); + shader_inc->set_include_path(p_path); shader_inc->set_code(str); if (r_error) { diff --git a/scene/resources/shader_include.h b/scene/resources/shader_include.h index 04f4f5cf84..a8949b327e 100644 --- a/scene/resources/shader_include.h +++ b/scene/resources/shader_include.h @@ -42,6 +42,7 @@ class ShaderInclude : public Resource { private: String code; + String include_path; HashSet<Ref<ShaderInclude>> dependencies; void _dependency_changed(); @@ -51,6 +52,8 @@ protected: public: void set_code(const String &p_text); String get_code() const; + + void set_include_path(const String &p_path); }; class ResourceFormatLoaderShaderInclude : public ResourceFormatLoader { diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index c101974248..818be38681 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -36,7 +36,7 @@ void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_t HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - p_duration = MAX(0.0, p_duration); + p_duration = MAX(SPRITE_FRAME_MINIMUM_DURATION, p_duration); Frame frame = { p_texture, p_duration }; @@ -57,7 +57,7 @@ void SpriteFrames::set_frame(const StringName &p_anim, int p_idx, const Ref<Text return; } - p_duration = MAX(0.0, p_duration); + p_duration = MAX(SPRITE_FRAME_MINIMUM_DURATION, p_duration); Frame frame = { p_texture, p_duration }; @@ -214,7 +214,7 @@ void SpriteFrames::_set_animations(const Array &p_animations) { ERR_CONTINUE(!f.has("texture")); ERR_CONTINUE(!f.has("duration")); - Frame frame = { f["texture"], f["duration"] }; + Frame frame = { f["texture"], MAX(SPRITE_FRAME_MINIMUM_DURATION, (float)f["duration"]) }; anim.frames.push_back(frame); } diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h index 61bead6948..6de2b4a37b 100644 --- a/scene/resources/sprite_frames.h +++ b/scene/resources/sprite_frames.h @@ -33,6 +33,8 @@ #include "scene/resources/texture.h" +static const float SPRITE_FRAME_MINIMUM_DURATION = 0.01; + class SpriteFrames : public Resource { GDCLASS(SpriteFrames, Resource); @@ -89,10 +91,10 @@ public: _FORCE_INLINE_ float get_frame_duration(const StringName &p_anim, int p_idx) const { HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); - ERR_FAIL_COND_V_MSG(!E, 0.0, "Animation '" + String(p_anim) + "' doesn't exist."); - ERR_FAIL_COND_V(p_idx < 0, 0.0); + ERR_FAIL_COND_V_MSG(!E, 1.0, "Animation '" + String(p_anim) + "' doesn't exist."); + ERR_FAIL_COND_V(p_idx < 0, 1.0); if (p_idx >= E->value.frames.size()) { - return 0.0; + return 1.0; } return E->value.frames[p_idx].duration; diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 4f16986392..9e0b856ecd 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -151,11 +151,6 @@ void StyleBoxTexture::set_texture(Ref<Texture2D> p_texture) { return; } texture = p_texture; - if (p_texture.is_null()) { - region_rect = Rect2(0, 0, 0, 0); - } else { - region_rect = Rect2(Point2(), texture->get_size()); - } emit_changed(); } @@ -231,22 +226,6 @@ bool StyleBoxTexture::is_draw_center_enabled() const { return draw_center; } -Size2 StyleBoxTexture::get_minimum_size() const { - Size2 min_size = StyleBox::get_minimum_size(); - - // Make sure that the min size is no smaller than the used texture region. - if (texture.is_valid()) { - if (min_size.x < region_rect.size.x) { - min_size.x = region_rect.size.x; - } - if (min_size.y < region_rect.size.y) { - min_size.y = region_rect.size.y; - } - } - - return min_size; -} - void StyleBoxTexture::set_expand_margin(Side p_side, float p_size) { ERR_FAIL_INDEX((int)p_side, 4); expand_margin[p_side] = p_size; @@ -1028,7 +1007,7 @@ void StyleBoxLine::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_begin", "get_grow_begin"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_end", "get_grow_end"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,10,suffix:px"), "set_thickness", "get_thickness"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,100,suffix:px"), "set_thickness", "get_thickness"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index 91033617ab..17acfd773e 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -107,8 +107,6 @@ protected: static void _bind_methods(); public: - virtual Size2 get_minimum_size() const override; - void set_expand_margin(Side p_expand_side, float p_size); void set_expand_margin_all(float p_expand_margin_size); void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 17e92ddfca..5a2b917b9a 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -146,6 +146,25 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) { return h; } +bool SurfaceTool::SmoothGroupVertex::operator==(const SmoothGroupVertex &p_vertex) const { + if (vertex != p_vertex.vertex) { + return false; + } + + if (smooth_group != p_vertex.smooth_group) { + return false; + } + + return true; +} + +uint32_t SurfaceTool::SmoothGroupVertexHasher::hash(const SmoothGroupVertex &p_vtx) { + uint32_t h = hash_djb2_buffer((const uint8_t *)&p_vtx.vertex, sizeof(real_t) * 3); + h = hash_murmur3_one_32(p_vtx.smooth_group, h); + h = hash_fmix32(h); + return h; +} + uint32_t SurfaceTool::TriangleHasher::hash(const int *p_triangle) { int t0 = p_triangle[0]; int t1 = p_triangle[1]; @@ -1152,7 +1171,7 @@ void SurfaceTool::generate_normals(bool p_flip) { ERR_FAIL_COND((vertex_array.size() % 3) != 0); - HashMap<Vertex, Vector3, VertexHasher> vertex_hash; + HashMap<SmoothGroupVertex, Vector3, SmoothGroupVertexHasher> smooth_hash; for (uint32_t vi = 0; vi < vertex_array.size(); vi += 3) { Vertex *v = &vertex_array[vi]; @@ -1165,21 +1184,28 @@ void SurfaceTool::generate_normals(bool p_flip) { } for (int i = 0; i < 3; i++) { - Vector3 *lv = vertex_hash.getptr(v[i]); - if (!lv) { - vertex_hash.insert(v[i], normal); + // Add face normal to smooth vertex influence if vertex is member of a smoothing group + if (v[i].smooth_group != UINT32_MAX) { + Vector3 *lv = smooth_hash.getptr(v[i]); + if (!lv) { + smooth_hash.insert(v[i], normal); + } else { + (*lv) += normal; + } } else { - (*lv) += normal; + v[i].normal = normal; } } } for (Vertex &vertex : vertex_array) { - Vector3 *lv = vertex_hash.getptr(vertex); - if (!lv) { - vertex.normal = Vector3(); - } else { - vertex.normal = lv->normalized(); + if (vertex.smooth_group != UINT32_MAX) { + Vector3 *lv = smooth_hash.getptr(vertex); + if (!lv) { + vertex.normal = Vector3(); + } else { + vertex.normal = lv->normalized(); + } } } diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index 25e078d2ff..00438c4a53 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -97,6 +97,21 @@ private: static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx); }; + struct SmoothGroupVertex { + Vector3 vertex; + uint32_t smooth_group = 0; + bool operator==(const SmoothGroupVertex &p_vertex) const; + + SmoothGroupVertex(const Vertex &p_vertex) { + vertex = p_vertex.vertex; + smooth_group = p_vertex.smooth_group; + }; + }; + + struct SmoothGroupVertexHasher { + static _FORCE_INLINE_ uint32_t hash(const SmoothGroupVertex &p_vtx); + }; + struct TriangleHasher { static _FORCE_INLINE_ uint32_t hash(const int *p_triangle); static _FORCE_INLINE_ bool compare(const int *p_lhs, const int *p_rhs); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 85e21d6056..7e3156d2ff 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -653,7 +653,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si uint32_t mipmaps = f->get_32(); Image::Format format = Image::Format(f->get_32()); - if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP || data_format == DATA_FORMAT_BASIS_UNIVERSAL) { + if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP) { //look for a PNG or WebP file inside int sw = w; @@ -684,9 +684,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si } Ref<Image> img; - if (data_format == DATA_FORMAT_BASIS_UNIVERSAL && Image::basis_universal_unpacker) { - img = Image::basis_universal_unpacker(pv); - } else if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) { + if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) { img = Image::png_unpacker(pv); } else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) { img = Image::webp_unpacker(pv); @@ -745,6 +743,32 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si return image; } + } else if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) { + int sw = w; + int sh = h; + uint32_t size = f->get_32(); + if (p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) { + //can't load this due to size limit + sw = MAX(sw >> 1, 1); + sh = MAX(sh >> 1, 1); + f->seek(f->get_position() + size); + return Ref<Image>(); + } + Vector<uint8_t> pv; + pv.resize(size); + { + uint8_t *wr = pv.ptrw(); + f->get_buffer(wr, size); + } + Ref<Image> img; + img = Image::basis_universal_unpacker(pv); + if (img.is_null() || img->is_empty()) { + ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>()); + } + format = img->get_format(); + sw = MAX(sw >> 1, 1); + sh = MAX(sh >> 1, 1); + return img; } else if (data_format == DATA_FORMAT_IMAGE) { int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index b5a68ef14b..58a638804d 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -991,6 +991,28 @@ uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const { return navigation_layers[p_layer_index].layers; } +void TileSet::set_navigation_layer_layer_value(int p_layer_index, int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + + uint32_t _navigation_layers = get_navigation_layer_layers(p_layer_index); + + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + + set_navigation_layer_layers(p_layer_index, _navigation_layers); +} + +bool TileSet::get_navigation_layer_layer_value(int p_layer_index, int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + + return get_navigation_layer_layers(p_layer_index) & (1 << (p_layer_number - 1)); +} + // Custom data. int TileSet::get_custom_data_layers_count() const { return custom_data_layers.size(); @@ -2533,6 +2555,11 @@ void TileSet::_compatibility_conversion() { bool flip_v = flags & 2; bool transpose = flags & 4; + Transform2D xform; + xform = flip_h ? xform.scaled(Size2(-1, 1)) : xform; + xform = flip_v ? xform.scaled(Size2(1, -1)) : xform; + xform = transpose ? xform.rotated(Math_PI).scaled(Size2(-1, -1)) : xform; + int alternative_tile = 0; if (!atlas_source->has_tile(coords)) { atlas_source->create_tile(coords); @@ -2569,14 +2596,26 @@ void TileSet::_compatibility_conversion() { if (ctd->occluder.is_valid()) { if (get_occlusion_layers_count() < 1) { add_occlusion_layer(); + }; + Ref<OccluderPolygon2D> occluder = ctd->occluder->duplicate(); + Vector<Vector2> polygon = ctd->occluder->get_polygon(); + for (int index = 0; index < polygon.size(); index++) { + polygon.write[index] = xform.xform(polygon[index] - ctd->region.get_size() / 2.0); } - tile_data->set_occluder(0, ctd->occluder); + occluder->set_polygon(polygon); + tile_data->set_occluder(0, occluder); } if (ctd->navigation.is_valid()) { if (get_navigation_layers_count() < 1) { add_navigation_layer(); } - tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); + Ref<NavigationPolygon> navigation = ctd->navigation->duplicate(); + Vector<Vector2> vertices = navigation->get_vertices(); + for (int index = 0; index < vertices.size(); index++) { + vertices.write[index] = xform.xform(vertices[index] - ctd->region.get_size() / 2.0); + } + navigation->set_vertices(vertices); + tile_data->set_navigation_polygon(0, navigation); } tile_data->set_z_index(ctd->z_index); @@ -2594,7 +2633,7 @@ void TileSet::_compatibility_conversion() { if (convex_shape.is_valid()) { Vector<Vector2> polygon = convex_shape->get_points(); for (int point_index = 0; point_index < polygon.size(); point_index++) { - polygon.write[point_index] = csd.transform.xform(polygon[point_index]); + polygon.write[point_index] = xform.xform(csd.transform.xform(polygon[point_index]) - ctd->region.get_size() / 2.0); } tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1); int index = tile_data->get_collision_polygons_count(0) - 1; @@ -2605,9 +2644,15 @@ void TileSet::_compatibility_conversion() { } } } + // Update the size count. + if (!compatibility_size_count.has(ctd->region.get_size())) { + compatibility_size_count[ctd->region.get_size()] = 0; + } + compatibility_size_count[ctd->region.get_size()]++; } break; case COMPATIBILITY_TILE_MODE_AUTO_TILE: { // Not supported. It would need manual conversion. + WARN_PRINT_ONCE("Could not convert 3.x autotiles to 4.x. This operation cannot be done automatically, autotiles must be re-created using the terrain system."); } break; case COMPATIBILITY_TILE_MODE_ATLAS_TILE: { atlas_source->set_margins(ctd->region.get_position()); @@ -2624,6 +2669,11 @@ void TileSet::_compatibility_conversion() { bool flip_v = flags & 2; bool transpose = flags & 4; + Transform2D xform; + xform = flip_h ? xform.scaled(Size2(-1, 1)) : xform; + xform = flip_v ? xform.scaled(Size2(1, -1)) : xform; + xform = transpose ? xform.rotated(Math_PI).scaled(Size2(-1, -1)) : xform; + int alternative_tile = 0; if (!atlas_source->has_tile(coords)) { atlas_source->create_tile(coords); @@ -2661,13 +2711,25 @@ void TileSet::_compatibility_conversion() { if (get_occlusion_layers_count() < 1) { add_occlusion_layer(); } - tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]); + Ref<OccluderPolygon2D> occluder = ctd->autotile_occluder_map[coords]->duplicate(); + Vector<Vector2> polygon = ctd->occluder->get_polygon(); + for (int index = 0; index < polygon.size(); index++) { + polygon.write[index] = xform.xform(polygon[index] - ctd->region.get_size() / 2.0); + } + occluder->set_polygon(polygon); + tile_data->set_occluder(0, occluder); } if (ctd->autotile_navpoly_map.has(coords)) { if (get_navigation_layers_count() < 1) { add_navigation_layer(); } - tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); + Ref<NavigationPolygon> navigation = ctd->autotile_navpoly_map[coords]->duplicate(); + Vector<Vector2> vertices = navigation->get_vertices(); + for (int index = 0; index < vertices.size(); index++) { + vertices.write[index] = xform.xform(vertices[index] - ctd->region.get_size() / 2.0); + } + navigation->set_vertices(vertices); + tile_data->set_navigation_polygon(0, navigation); } if (ctd->autotile_priority_map.has(coords)) { tile_data->set_probability(ctd->autotile_priority_map[coords]); @@ -2689,7 +2751,7 @@ void TileSet::_compatibility_conversion() { if (convex_shape.is_valid()) { Vector<Vector2> polygon = convex_shape->get_points(); for (int point_index = 0; point_index < polygon.size(); point_index++) { - polygon.write[point_index] = csd.transform.xform(polygon[point_index]); + polygon.write[point_index] = xform.xform(csd.transform.xform(polygon[point_index]) - ctd->autotile_tile_size / 2.0); } tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1); int index = tile_data->get_collision_polygons_count(0) - 1; @@ -2712,6 +2774,12 @@ void TileSet::_compatibility_conversion() { } } } + + // Update the size count. + if (!compatibility_size_count.has(ctd->region.get_size())) { + compatibility_size_count[ctd->autotile_tile_size] = 0; + } + compatibility_size_count[ctd->autotile_tile_size] += atlas_size.x * atlas_size.y; } break; } @@ -2728,7 +2796,18 @@ void TileSet::_compatibility_conversion() { } } - // Reset compatibility data + // Update the TileSet tile_size according to the most common size found. + Vector2i max_size = get_tile_size(); + int max_count = 0; + for (KeyValue<Vector2i, int> kv : compatibility_size_count) { + if (kv.value > max_count) { + max_size = kv.key; + max_count = kv.value; + } + } + set_tile_size(max_size); + + // Reset compatibility data (besides the histogram counts) for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { memdelete(E.value); } @@ -2909,6 +2988,10 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { } ctd->shapes.push_back(csd); } + } else if (what == "occluder") { + ctd->occluder = p_value; + } else if (what == "navigation") { + ctd->navigation = p_value; /* // IGNORED FOR NOW, they seem duplicated data compared to the shapes array @@ -3387,6 +3470,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_navigation_layer", "layer_index"), &TileSet::remove_navigation_layer); ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers); ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers); + ClassDB::bind_method(D_METHOD("set_navigation_layer_layer_value", "layer_index", "layer_number", "value"), &TileSet::set_navigation_layer_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_layer_value", "layer_index", "layer_number"), &TileSet::get_navigation_layer_layer_value); // Custom data ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count); @@ -4282,19 +4367,11 @@ Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int return Rect2(origin, region_size); } -Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); - ERR_FAIL_COND_V_MSG(!has_alternative_tile(p_atlas_coords, p_alternative_tile), Vector2i(), vformat("TileSetAtlasSource has no alternative tile with id %d at %s.", p_alternative_tile, String(p_atlas_coords))); - ERR_FAIL_COND_V(!tile_set, Vector2i()); +bool TileSetAtlasSource::is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const { + Size2 size = get_tile_texture_region(p_atlas_coords).size; + Rect2 rect = Rect2(-size / 2 - get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_origin(), size); - Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2; - margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); - Vector2i effective_texture_offset = get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_offset(); - if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { - effective_texture_offset = effective_texture_offset.clamp(-margin, margin); - } - - return effective_texture_offset; + return rect.has_point(p_position); } // Getters for texture and tile region (padded or not) @@ -5046,7 +5123,7 @@ TileData *TileData::duplicate() { output->flip_h = flip_h; output->flip_v = flip_v; output->transpose = transpose; - output->tex_offset = tex_offset; + output->texture_origin = texture_origin; output->material = material; output->modulate = modulate; output->z_index = z_index; @@ -5096,13 +5173,13 @@ bool TileData::get_transpose() const { return transpose; } -void TileData::set_texture_offset(Vector2i p_texture_offset) { - tex_offset = p_texture_offset; +void TileData::set_texture_origin(Vector2i p_texture_origin) { + texture_origin = p_texture_origin; emit_signal(SNAME("changed")); } -Vector2i TileData::get_texture_offset() const { - return tex_offset; +Vector2i TileData::get_texture_origin() const { + return texture_origin; } void TileData::set_material(Ref<Material> p_material) { @@ -5390,6 +5467,13 @@ Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const { } bool TileData::_set(const StringName &p_name, const Variant &p_value) { +#ifndef DISABLE_DEPRECATED + if (p_name == "texture_offset") { + texture_origin = p_value; + return true; + } +#endif + Vector<String> components = String(p_name).split("/", true, 2); if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { @@ -5511,6 +5595,13 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { } bool TileData::_get(const StringName &p_name, Variant &r_ret) const { +#ifndef DISABLE_DEPRECATED + if (p_name == "texture_offset") { + r_ret = texture_origin; + return true; + } +#endif + Vector<String> components = String(p_name).split("/", true, 2); if (tile_set) { @@ -5692,8 +5783,8 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose); ClassDB::bind_method(D_METHOD("set_material", "material"), &TileData::set_material); ClassDB::bind_method(D_METHOD("get_material"), &TileData::get_material); - ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset); - ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset); + ClassDB::bind_method(D_METHOD("set_texture_origin", "texture_origin"), &TileData::set_texture_origin); + ClassDB::bind_method(D_METHOD("get_texture_origin"), &TileData::get_texture_origin); ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate); ClassDB::bind_method(D_METHOD("get_modulate"), &TileData::get_modulate); ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &TileData::set_z_index); @@ -5746,7 +5837,7 @@ void TileData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "get_flip_h"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "get_flip_v"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_offset", "get_texture_offset"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_origin", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_origin", "get_texture_origin"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index"); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index c2ed798f2b..ad25629a1c 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -198,6 +198,7 @@ private: HashMap<int, CompatibilityTileData *> compatibility_data; HashMap<int, int> compatibility_tilemap_mapping_tile_modes; HashMap<int, RBMap<Array, Array>> compatibility_tilemap_mapping; + HashMap<Vector2i, int> compatibility_size_count; void _compatibility_conversion(); @@ -475,6 +476,8 @@ public: void remove_navigation_layer(int p_index); void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers); uint32_t get_navigation_layer_layers(int p_layer_index) const; + void set_navigation_layer_layer_value(int p_layer_index, int p_layer_number, bool p_value); + bool get_navigation_layer_layer_value(int p_layer_index, int p_layer_number) const; // Custom data int get_custom_data_layers_count() const; @@ -719,7 +722,7 @@ public: // Helpers. Vector2i get_atlas_grid_size() const; Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const; - Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const; + bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const; // Getters for texture and tile region (padded or not) Ref<Texture2D> get_runtime_texture() const; @@ -785,7 +788,7 @@ private: bool flip_h = false; bool flip_v = false; bool transpose = false; - Vector2i tex_offset; + Vector2i texture_origin; Ref<Material> material = Ref<Material>(); Color modulate = Color(1.0, 1.0, 1.0, 1.0); int z_index = 0; @@ -864,8 +867,8 @@ public: void set_transpose(bool p_transpose); bool get_transpose() const; - void set_texture_offset(Vector2i p_texture_offset); - Vector2i get_texture_offset() const; + void set_texture_origin(Vector2i p_texture_origin); + Vector2i get_texture_origin() const; void set_material(Ref<Material> p_material); Ref<Material> get_material() const; void set_modulate(Color p_modulate); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index e78d9b924d..12be0f46a6 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -3690,16 +3690,47 @@ String VisualShaderNodeDerivativeFunc::get_output_port_name(int p_port) const { String VisualShaderNodeDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { static const char *functions[FUNC_MAX] = { - "fwidth($)", - "dFdx($)", - "dFdy($)" + "fwidth$($)", + "dFdx$($)", + "dFdy$($)" + }; + + static const char *precisions[PRECISION_MAX] = { + "", + "Coarse", + "Fine" }; String code; - code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n"; + if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { + code += " " + p_output_vars[0] + " = " + String(functions[func]).replace_first("$", "").replace_first("$", p_input_vars[0]) + ";\n"; + return code; + } + + code += " " + p_output_vars[0] + " = " + String(functions[func]).replace_first("$", String(precisions[precision])).replace_first("$", p_input_vars[0]) + ";\n"; return code; } +String VisualShaderNodeDerivativeFunc::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { + if (precision != PRECISION_NONE && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { + String precision_str; + switch (precision) { + case PRECISION_COARSE: { + precision_str = "Coarse"; + } break; + case PRECISION_FINE: { + precision_str = "Fine"; + } break; + default: { + } break; + } + + return vformat(RTR("`%s` precision mode is not available for `gl_compatibility` profile.\nReverted to `None` precision."), precision_str); + } + + return String(); +} + void VisualShaderNodeDerivativeFunc::set_op_type(OpType p_op_type) { ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX)); if (op_type == p_op_type) { @@ -3742,10 +3773,24 @@ VisualShaderNodeDerivativeFunc::Function VisualShaderNodeDerivativeFunc::get_fun return func; } +void VisualShaderNodeDerivativeFunc::set_precision(Precision p_precision) { + ERR_FAIL_INDEX(int(p_precision), int(PRECISION_MAX)); + if (precision == p_precision) { + return; + } + precision = p_precision; + emit_changed(); +} + +VisualShaderNodeDerivativeFunc::Precision VisualShaderNodeDerivativeFunc::get_precision() const { + return precision; +} + Vector<StringName> VisualShaderNodeDerivativeFunc::get_editable_properties() const { Vector<StringName> props; props.push_back("op_type"); props.push_back("function"); + props.push_back("precision"); return props; } @@ -3756,8 +3801,12 @@ void VisualShaderNodeDerivativeFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeDerivativeFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeDerivativeFunc::get_function); + ClassDB::bind_method(D_METHOD("set_precision", "precision"), &VisualShaderNodeDerivativeFunc::set_precision); + ClassDB::bind_method(D_METHOD("get_precision"), &VisualShaderNodeDerivativeFunc::get_precision); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "precision", PROPERTY_HINT_ENUM, "None,Coarse,Fine"), "set_precision", "get_precision"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); @@ -3769,6 +3818,11 @@ void VisualShaderNodeDerivativeFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_X); BIND_ENUM_CONSTANT(FUNC_Y); BIND_ENUM_CONSTANT(FUNC_MAX); + + BIND_ENUM_CONSTANT(PRECISION_NONE); + BIND_ENUM_CONSTANT(PRECISION_COARSE); + BIND_ENUM_CONSTANT(PRECISION_FINE); + BIND_ENUM_CONSTANT(PRECISION_MAX); } VisualShaderNodeDerivativeFunc::VisualShaderNodeDerivativeFunc() { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index e3b101cf84..fa6b134526 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1536,9 +1536,17 @@ public: FUNC_MAX, }; + enum Precision { + PRECISION_NONE, + PRECISION_COARSE, + PRECISION_FINE, + PRECISION_MAX, + }; + protected: OpType op_type = OP_TYPE_SCALAR; Function func = FUNC_SUM; + Precision precision = PRECISION_NONE; protected: static void _bind_methods(); @@ -1555,6 +1563,7 @@ public: virtual String get_output_port_name(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override; void set_op_type(OpType p_op_type); OpType get_op_type() const; @@ -1562,6 +1571,9 @@ public: void set_function(Function p_func); Function get_function() const; + void set_precision(Precision p_precision); + Precision get_precision() const; + virtual Vector<StringName> get_editable_properties() const override; VisualShaderNodeDerivativeFunc(); @@ -1569,6 +1581,7 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::OpType) VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::Function) +VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::Precision) /////////////////////////////////////// /// FACEFORWARD diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index c35f7360b5..2a70139bcb 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -47,6 +47,13 @@ RID World2D::get_space() const { } RID World2D::get_navigation_map() const { + if (navigation_map.is_null()) { + navigation_map = NavigationServer2D::get_singleton()->map_create(); + NavigationServer2D::get_singleton()->map_set_active(navigation_map, true); + NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_GET("navigation/2d/default_cell_size")); + NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_GET("navigation/2d/default_edge_connection_margin")); + NavigationServer2D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_GET("navigation/2d/default_link_connection_radius")); + } return navigation_map; } @@ -77,13 +84,6 @@ World2D::World2D() { PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/2d/default_gravity_vector", Vector2(0, 1))); PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 0.1)); PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 1.0)); - - // Create and configure the navigation_map to be more friendly with pixels than meters. - navigation_map = NavigationServer2D::get_singleton()->map_create(); - NavigationServer2D::get_singleton()->map_set_active(navigation_map, true); - NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 1)); - NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 1)); - NavigationServer2D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_DEF("navigation/2d/default_link_connection_radius", 4)); } World2D::~World2D() { @@ -92,5 +92,7 @@ World2D::~World2D() { ERR_FAIL_NULL(NavigationServer2D::get_singleton()); RenderingServer::get_singleton()->free(canvas); PhysicsServer2D::get_singleton()->free(space); - NavigationServer2D::get_singleton()->free(navigation_map); + if (navigation_map.is_valid()) { + NavigationServer2D::get_singleton()->free(navigation_map); + } } diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 92239ed167..f02dddd2fe 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -44,7 +44,7 @@ class World2D : public Resource { RID canvas; RID space; - RID navigation_map; + mutable RID navigation_map; HashSet<Viewport *> viewports; diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index 536edd334b..cc4d261c0d 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -55,6 +55,13 @@ RID World3D::get_space() const { } RID World3D::get_navigation_map() const { + if (navigation_map.is_null()) { + navigation_map = NavigationServer3D::get_singleton()->map_create(); + NavigationServer3D::get_singleton()->map_set_active(navigation_map, true); + NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_GET("navigation/3d/default_cell_size")); + NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_GET("navigation/3d/default_edge_connection_margin")); + NavigationServer3D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_GET("navigation/3d/default_link_connection_radius")); + } return navigation_map; } @@ -146,12 +153,6 @@ World3D::World3D() { PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1)); PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1)); - - navigation_map = NavigationServer3D::get_singleton()->map_create(); - NavigationServer3D::get_singleton()->map_set_active(navigation_map, true); - NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.25)); - NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.25)); - NavigationServer3D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_DEF("navigation/3d/default_link_connection_radius", 1.0)); } World3D::~World3D() { @@ -160,5 +161,7 @@ World3D::~World3D() { ERR_FAIL_NULL(NavigationServer3D::get_singleton()); PhysicsServer3D::get_singleton()->free(space); RenderingServer::get_singleton()->free(scenario); - NavigationServer3D::get_singleton()->free(navigation_map); + if (navigation_map.is_valid()) { + NavigationServer3D::get_singleton()->free(navigation_map); + } } diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 7cbc9f08c9..ad17daf466 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -46,8 +46,8 @@ class World3D : public Resource { private: RID space; - RID navigation_map; RID scenario; + mutable RID navigation_map; Ref<Environment> environment; Ref<Environment> fallback_environment; diff --git a/scene/resources/world_boundary_shape_2d.cpp b/scene/resources/world_boundary_shape_2d.cpp index 49f0873a3e..35cb8ef13d 100644 --- a/scene/resources/world_boundary_shape_2d.cpp +++ b/scene/resources/world_boundary_shape_2d.cpp @@ -76,11 +76,12 @@ real_t WorldBoundaryShape2D::get_distance() const { void WorldBoundaryShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector2 point = get_distance() * get_normal(); + real_t line_width = 3.0; Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }; - RS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, 3); - Vector2 l2[2] = { point, point + get_normal() * 30 }; - RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3); + RS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, line_width); + Vector2 l2[2] = { point + get_normal().normalized() * (0.5 * line_width), point + get_normal() * 30 }; + RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, line_width); } Rect2 WorldBoundaryShape2D::get_rect() const { diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 39f14439f4..2d65cea432 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -344,7 +344,6 @@ DisplayServer::MouseMode DisplayServer::mouse_get_mode() const { } void DisplayServer::warp_mouse(const Point2i &p_position) { - WARN_PRINT("Mouse warping is not supported by this display server."); } Point2i DisplayServer::mouse_get_position() const { @@ -572,7 +571,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("global_menu_add_icon_check_item", "menu_root", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("global_menu_add_radio_check_item", "menu_root", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("global_menu_add_icon_radio_check_item", "menu_root", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("global_menu_add_multistate_item", "menu_root", "labe", "max_states", "default_state", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_multistate_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("global_menu_add_multistate_item", "menu_root", "label", "max_states", "default_state", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_multistate_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("global_menu_add_separator", "menu_root", "index"), &DisplayServer::global_menu_add_separator, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("global_menu_get_item_index_from_text", "menu_root", "text"), &DisplayServer::global_menu_get_item_index_from_text); diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index 943b2ae467..2e4087c1de 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -81,35 +81,12 @@ NavigationServer2D *NavigationServer2D::singleton = nullptr; return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1))); \ } -#define FORWARD_4(FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \ - NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) { \ - return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3)); \ - } - -#define FORWARD_4_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \ - NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \ - const { \ - return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3))); \ - } - -#define FORWARD_4_C(FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \ - NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \ - const { \ - return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3)); \ - } - #define FORWARD_5_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, T_4, D_4, CONV_0, CONV_1, CONV_2, CONV_3, CONV_4) \ NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3, T_4 D_4) \ const { \ return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3), CONV_4(D_4))); \ } -#define FORWARD_5_C(FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, T_4, D_4, CONV_0, CONV_1, CONV_2, CONV_3, CONV_4) \ - NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3, T_4 D_4) \ - const { \ - return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3), CONV_4(D_4)); \ - } - static RID rid_to_rid(const RID d) { return d; } @@ -155,18 +132,14 @@ static Transform3D trf2_to_trf3(const Transform2D &d) { return Transform3D(b, o); } -static StringName sn_to_sn(const StringName &d) { - return d; -} - -static Variant var_to_var(const Variant &d) { - return d; -} - static ObjectID id_to_id(const ObjectID &id) { return id; } +static Callable callable_to_callable(const Callable &c) { + return c; +} + static Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) { if (d.is_valid()) { return d->get_navigation_mesh(); @@ -285,10 +258,10 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer2D::link_is_bidirectional); ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer2D::link_set_navigation_layers); ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer2D::link_get_navigation_layers); - ClassDB::bind_method(D_METHOD("link_set_start_location", "link", "location"), &NavigationServer2D::link_set_start_location); - ClassDB::bind_method(D_METHOD("link_get_start_location", "link"), &NavigationServer2D::link_get_start_location); - ClassDB::bind_method(D_METHOD("link_set_end_location", "link", "location"), &NavigationServer2D::link_set_end_location); - ClassDB::bind_method(D_METHOD("link_get_end_location", "link"), &NavigationServer2D::link_get_end_location); + ClassDB::bind_method(D_METHOD("link_set_start_position", "link", "position"), &NavigationServer2D::link_set_start_position); + ClassDB::bind_method(D_METHOD("link_get_start_position", "link"), &NavigationServer2D::link_get_start_position); + ClassDB::bind_method(D_METHOD("link_set_end_position", "link", "position"), &NavigationServer2D::link_set_end_position); + ClassDB::bind_method(D_METHOD("link_get_end_position", "link"), &NavigationServer2D::link_get_end_position); ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer2D::link_set_enter_cost); ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer2D::link_get_enter_cost); ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer2D::link_set_travel_cost); @@ -308,7 +281,7 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &NavigationServer2D::agent_set_target_velocity); ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer2D::agent_set_position); ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer2D::agent_is_map_changed); - ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "object_id", "method", "userdata"), &NavigationServer2D::agent_set_callback, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "callback"), &NavigationServer2D::agent_set_callback); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer2D::free); @@ -392,10 +365,10 @@ void FORWARD_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional, rid_t bool FORWARD_1_C(link_is_bidirectional, RID, p_link, rid_to_rid); void FORWARD_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32); uint32_t FORWARD_1_C(link_get_navigation_layers, RID, p_link, rid_to_rid); -void FORWARD_2(link_set_start_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3); -Vector2 FORWARD_1_R_C(v3_to_v2, link_get_start_location, RID, p_link, rid_to_rid); -void FORWARD_2(link_set_end_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3); -Vector2 FORWARD_1_R_C(v3_to_v2, link_get_end_location, RID, p_link, rid_to_rid); +void FORWARD_2(link_set_start_position, RID, p_link, Vector2, p_position, rid_to_rid, v2_to_v3); +Vector2 FORWARD_1_R_C(v3_to_v2, link_get_start_position, RID, p_link, rid_to_rid); +void FORWARD_2(link_set_end_position, RID, p_link, Vector2, p_position, rid_to_rid, v2_to_v3); +Vector2 FORWARD_1_R_C(v3_to_v2, link_get_end_position, RID, p_link, rid_to_rid); void FORWARD_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost, rid_to_rid, real_to_real); real_t FORWARD_1_C(link_get_enter_cost, RID, p_link, rid_to_rid); void FORWARD_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost, rid_to_rid, real_to_real); @@ -420,7 +393,7 @@ void FORWARD_2(agent_set_target_velocity, RID, p_agent, Vector2, p_velocity, rid void FORWARD_2(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3); void FORWARD_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore, rid_to_rid, bool_to_bool); bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid); -void FORWARD_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, rid_to_rid, id_to_id, sn_to_sn, var_to_var); +void FORWARD_2(agent_set_callback, RID, p_agent, Callable, p_callback, rid_to_rid, callable_to_callable); void FORWARD_1(free, RID, p_object, rid_to_rid); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index 0adf736b2a..cd18476182 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -130,7 +130,7 @@ public: virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const; - /// Creates a new link between locations in the nav map. + /// Creates a new link between positions in the nav map. virtual RID link_create(); /// Set the map of this link. @@ -145,13 +145,13 @@ public: virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers); virtual uint32_t link_get_navigation_layers(RID p_link) const; - /// Set the start location of the link. - virtual void link_set_start_location(RID p_link, Vector2 p_location); - virtual Vector2 link_get_start_location(RID p_link) const; + /// Set the start position of the link. + virtual void link_set_start_position(RID p_link, Vector2 p_position); + virtual Vector2 link_get_start_position(RID p_link) const; - /// Set the end location of the link. - virtual void link_set_end_location(RID p_link, Vector2 p_location); - virtual Vector2 link_get_end_location(RID p_link) const; + /// Set the end position of the link. + virtual void link_set_end_position(RID p_link, Vector2 p_position); + virtual Vector2 link_get_end_position(RID p_link) const; /// Set the enter cost of the link. virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost); @@ -223,7 +223,7 @@ public: virtual bool agent_is_map_changed(RID p_agent) const; /// Callback called at the end of the RVO process - virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()); + virtual void agent_set_callback(RID p_agent, Callable p_callback); virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const; diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 253b28a623..8f2cff0e04 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -30,9 +30,7 @@ #include "navigation_server_3d.h" -#ifdef DEBUG_ENABLED #include "core/config/project_settings.h" -#endif // DEBUG_ENABLED NavigationServer3D *NavigationServer3D::singleton = nullptr; @@ -90,10 +88,10 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer3D::link_is_bidirectional); ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer3D::link_set_navigation_layers); ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer3D::link_get_navigation_layers); - ClassDB::bind_method(D_METHOD("link_set_start_location", "link", "location"), &NavigationServer3D::link_set_start_location); - ClassDB::bind_method(D_METHOD("link_get_start_location", "link"), &NavigationServer3D::link_get_start_location); - ClassDB::bind_method(D_METHOD("link_set_end_location", "link", "location"), &NavigationServer3D::link_set_end_location); - ClassDB::bind_method(D_METHOD("link_get_end_location", "link"), &NavigationServer3D::link_get_end_location); + ClassDB::bind_method(D_METHOD("link_set_start_position", "link", "position"), &NavigationServer3D::link_set_start_position); + ClassDB::bind_method(D_METHOD("link_get_start_position", "link"), &NavigationServer3D::link_get_start_position); + ClassDB::bind_method(D_METHOD("link_set_end_position", "link", "position"), &NavigationServer3D::link_set_end_position); + ClassDB::bind_method(D_METHOD("link_get_end_position", "link"), &NavigationServer3D::link_get_end_position); ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer3D::link_set_enter_cost); ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer3D::link_get_enter_cost); ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer3D::link_set_travel_cost); @@ -113,7 +111,7 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &NavigationServer3D::agent_set_target_velocity); ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer3D::agent_set_position); ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer3D::agent_is_map_changed); - ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "object_id", "method", "userdata"), &NavigationServer3D::agent_set_callback, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "callback"), &NavigationServer3D::agent_set_callback); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free); @@ -145,6 +143,14 @@ NavigationServer3D::NavigationServer3D() { ERR_FAIL_COND(singleton != nullptr); singleton = this; + GLOBAL_DEF("navigation/2d/default_cell_size", 1); + GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 1); + GLOBAL_DEF("navigation/2d/default_link_connection_radius", 4); + + GLOBAL_DEF("navigation/3d/default_cell_size", 0.25); + GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.25); + GLOBAL_DEF("navigation/3d/default_link_connection_radius", 1.0); + #ifdef DEBUG_ENABLED debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0)); debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0)); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 6c4bd2e151..9c468d1b10 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -145,7 +145,7 @@ public: virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; - /// Creates a new link between locations in the nav map. + /// Creates a new link between positions in the nav map. virtual RID link_create() = 0; /// Set the map of this link. @@ -160,13 +160,13 @@ public: virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) = 0; virtual uint32_t link_get_navigation_layers(RID p_link) const = 0; - /// Set the start location of the link. - virtual void link_set_start_location(RID p_link, Vector3 p_location) = 0; - virtual Vector3 link_get_start_location(RID p_link) const = 0; + /// Set the start position of the link. + virtual void link_set_start_position(RID p_link, Vector3 p_position) = 0; + virtual Vector3 link_get_start_position(RID p_link) const = 0; - /// Set the end location of the link. - virtual void link_set_end_location(RID p_link, Vector3 p_location) = 0; - virtual Vector3 link_get_end_location(RID p_link) const = 0; + /// Set the end position of the link. + virtual void link_set_end_position(RID p_link, Vector3 p_position) = 0; + virtual Vector3 link_get_end_position(RID p_link) const = 0; /// Set the enter cost of the link. virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) = 0; @@ -238,7 +238,7 @@ public: virtual bool agent_is_map_changed(RID p_agent) const = 0; /// Callback called at the end of the RVO process - virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) = 0; + virtual void agent_set_callback(RID p_agent, Callable p_callback) = 0; /// Destroy the `RID` virtual void free(RID p_object) = 0; diff --git a/servers/physics_2d/godot_area_2d.cpp b/servers/physics_2d/godot_area_2d.cpp index 4d93bbfa61..4d2148aa07 100644 --- a/servers/physics_2d/godot_area_2d.cpp +++ b/servers/physics_2d/godot_area_2d.cpp @@ -145,11 +145,8 @@ void GodotArea2D::set_param(PhysicsServer2D::AreaParameter p_param, const Varian case PhysicsServer2D::AREA_PARAM_GRAVITY_IS_POINT: gravity_is_point = p_value; break; - case PhysicsServer2D::AREA_PARAM_GRAVITY_DISTANCE_SCALE: - gravity_distance_scale = p_value; - break; - case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_ATTENUATION: - point_attenuation = p_value; + case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: + gravity_point_unit_distance = p_value; break; case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: _set_space_override_mode(linear_damping_override_mode, (PhysicsServer2D::AreaSpaceOverrideMode)(int)p_value); @@ -179,10 +176,8 @@ Variant GodotArea2D::get_param(PhysicsServer2D::AreaParameter p_param) const { return gravity_vector; case PhysicsServer2D::AREA_PARAM_GRAVITY_IS_POINT: return gravity_is_point; - case PhysicsServer2D::AREA_PARAM_GRAVITY_DISTANCE_SCALE: - return gravity_distance_scale; - case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_ATTENUATION: - return point_attenuation; + case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: + return gravity_point_unit_distance; case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: return linear_damping_override_mode; case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP: @@ -304,13 +299,13 @@ void GodotArea2D::call_queries() { void GodotArea2D::compute_gravity(const Vector2 &p_position, Vector2 &r_gravity) const { if (is_gravity_point()) { - const real_t gr_distance_scale = get_gravity_distance_scale(); + const real_t gr_unit_dist = get_gravity_point_unit_distance(); Vector2 v = get_transform().xform(get_gravity_vector()) - p_position; - if (gr_distance_scale > 0) { - const real_t v_length = v.length(); - if (v_length > 0) { - const real_t v_scaled = v_length * gr_distance_scale; - r_gravity = (v.normalized() * (get_gravity() / (v_scaled * v_scaled))); + if (gr_unit_dist > 0) { + const real_t v_length_sq = v.length_squared(); + if (v_length_sq > 0) { + const real_t gravity_strength = get_gravity() * gr_unit_dist * gr_unit_dist / v_length_sq; + r_gravity = v.normalized() * gravity_strength; } else { r_gravity = Vector2(); } diff --git a/servers/physics_2d/godot_area_2d.h b/servers/physics_2d/godot_area_2d.h index 07c89ecb88..234e4eb9a9 100644 --- a/servers/physics_2d/godot_area_2d.h +++ b/servers/physics_2d/godot_area_2d.h @@ -48,8 +48,7 @@ class GodotArea2D : public GodotCollisionObject2D { real_t gravity = 9.80665; Vector2 gravity_vector = Vector2(0, -1); bool gravity_is_point = false; - real_t gravity_distance_scale = 0.0; - real_t point_attenuation = 1.0; + real_t gravity_point_unit_distance = 0.0; real_t linear_damp = 0.1; real_t angular_damp = 1.0; int priority = 0; @@ -125,11 +124,8 @@ public: _FORCE_INLINE_ void set_gravity_as_point(bool p_enable) { gravity_is_point = p_enable; } _FORCE_INLINE_ bool is_gravity_point() const { return gravity_is_point; } - _FORCE_INLINE_ void set_gravity_distance_scale(real_t scale) { gravity_distance_scale = scale; } - _FORCE_INLINE_ real_t get_gravity_distance_scale() const { return gravity_distance_scale; } - - _FORCE_INLINE_ void set_point_attenuation(real_t p_point_attenuation) { point_attenuation = p_point_attenuation; } - _FORCE_INLINE_ real_t get_point_attenuation() const { return point_attenuation; } + _FORCE_INLINE_ void set_gravity_point_unit_distance(real_t scale) { gravity_point_unit_distance = scale; } + _FORCE_INLINE_ real_t get_gravity_point_unit_distance() const { return gravity_point_unit_distance; } _FORCE_INLINE_ void set_linear_damp(real_t p_linear_damp) { linear_damp = p_linear_damp; } _FORCE_INLINE_ real_t get_linear_damp() const { return linear_damp; } diff --git a/servers/physics_3d/godot_area_3d.cpp b/servers/physics_3d/godot_area_3d.cpp index cd1f3b52b9..5bf16aa688 100644 --- a/servers/physics_3d/godot_area_3d.cpp +++ b/servers/physics_3d/godot_area_3d.cpp @@ -152,11 +152,8 @@ void GodotArea3D::set_param(PhysicsServer3D::AreaParameter p_param, const Varian case PhysicsServer3D::AREA_PARAM_GRAVITY_IS_POINT: gravity_is_point = p_value; break; - case PhysicsServer3D::AREA_PARAM_GRAVITY_DISTANCE_SCALE: - gravity_distance_scale = p_value; - break; - case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_ATTENUATION: - point_attenuation = p_value; + case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: + gravity_point_unit_distance = p_value; break; case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: _set_space_override_mode(linear_damping_override_mode, (PhysicsServer3D::AreaSpaceOverrideMode)(int)p_value); @@ -200,10 +197,8 @@ Variant GodotArea3D::get_param(PhysicsServer3D::AreaParameter p_param) const { return gravity_vector; case PhysicsServer3D::AREA_PARAM_GRAVITY_IS_POINT: return gravity_is_point; - case PhysicsServer3D::AREA_PARAM_GRAVITY_DISTANCE_SCALE: - return gravity_distance_scale; - case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_ATTENUATION: - return point_attenuation; + case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: + return gravity_point_unit_distance; case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: return linear_damping_override_mode; case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP: @@ -333,13 +328,13 @@ void GodotArea3D::call_queries() { void GodotArea3D::compute_gravity(const Vector3 &p_position, Vector3 &r_gravity) const { if (is_gravity_point()) { - const real_t gr_distance_scale = get_gravity_distance_scale(); + const real_t gr_unit_dist = get_gravity_point_unit_distance(); Vector3 v = get_transform().xform(get_gravity_vector()) - p_position; - if (gr_distance_scale > 0) { - const real_t v_length = v.length(); - if (v_length > 0) { - const real_t v_scaled = v_length * gr_distance_scale; - r_gravity = (v.normalized() * (get_gravity() / (v_scaled * v_scaled))); + if (gr_unit_dist > 0) { + const real_t v_length_sq = v.length_squared(); + if (v_length_sq > 0) { + const real_t gravity_strength = get_gravity() * gr_unit_dist * gr_unit_dist / v_length_sq; + r_gravity = v.normalized() * gravity_strength; } else { r_gravity = Vector3(); } diff --git a/servers/physics_3d/godot_area_3d.h b/servers/physics_3d/godot_area_3d.h index 0961da5b7d..f05d0f9019 100644 --- a/servers/physics_3d/godot_area_3d.h +++ b/servers/physics_3d/godot_area_3d.h @@ -49,8 +49,7 @@ class GodotArea3D : public GodotCollisionObject3D { real_t gravity = 9.80665; Vector3 gravity_vector = Vector3(0, -1, 0); bool gravity_is_point = false; - real_t gravity_distance_scale = 0.0; - real_t point_attenuation = 1.0; + real_t gravity_point_unit_distance = 0.0; real_t linear_damp = 0.1; real_t angular_damp = 0.1; real_t wind_force_magnitude = 0.0; @@ -134,11 +133,8 @@ public: _FORCE_INLINE_ void set_gravity_as_point(bool p_enable) { gravity_is_point = p_enable; } _FORCE_INLINE_ bool is_gravity_point() const { return gravity_is_point; } - _FORCE_INLINE_ void set_gravity_distance_scale(real_t scale) { gravity_distance_scale = scale; } - _FORCE_INLINE_ real_t get_gravity_distance_scale() const { return gravity_distance_scale; } - - _FORCE_INLINE_ void set_point_attenuation(real_t p_point_attenuation) { point_attenuation = p_point_attenuation; } - _FORCE_INLINE_ real_t get_point_attenuation() const { return point_attenuation; } + _FORCE_INLINE_ void set_gravity_point_unit_distance(real_t scale) { gravity_point_unit_distance = scale; } + _FORCE_INLINE_ real_t get_gravity_point_unit_distance() const { return gravity_point_unit_distance; } _FORCE_INLINE_ void set_linear_damp(real_t p_linear_damp) { linear_damp = p_linear_damp; } _FORCE_INLINE_ real_t get_linear_damp() const { return linear_damp; } diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 214de27b35..15c2749484 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -811,8 +811,7 @@ void PhysicsServer2D::_bind_methods() { BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY); BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_VECTOR); BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_IS_POINT); - BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_DISTANCE_SCALE); - BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_POINT_ATTENUATION); + BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE); BIND_ENUM_CONSTANT(AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE); BIND_ENUM_CONSTANT(AREA_PARAM_LINEAR_DAMP); BIND_ENUM_CONSTANT(AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE); diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index 836ab5bd76..3e254e610e 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -286,8 +286,7 @@ public: AREA_PARAM_GRAVITY, AREA_PARAM_GRAVITY_VECTOR, AREA_PARAM_GRAVITY_IS_POINT, - AREA_PARAM_GRAVITY_DISTANCE_SCALE, - AREA_PARAM_GRAVITY_POINT_ATTENUATION, + AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE, AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE, AREA_PARAM_LINEAR_DAMP, AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE, diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index f1272a985d..864774374b 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -976,8 +976,7 @@ void PhysicsServer3D::_bind_methods() { BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY); BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_VECTOR); BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_IS_POINT); - BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_DISTANCE_SCALE); - BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_POINT_ATTENUATION); + BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE); BIND_ENUM_CONSTANT(AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE); BIND_ENUM_CONSTANT(AREA_PARAM_LINEAR_DAMP); BIND_ENUM_CONSTANT(AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE); diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index d1c644d51a..abf22e68a4 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -316,8 +316,7 @@ public: AREA_PARAM_GRAVITY, AREA_PARAM_GRAVITY_VECTOR, AREA_PARAM_GRAVITY_IS_POINT, - AREA_PARAM_GRAVITY_DISTANCE_SCALE, - AREA_PARAM_GRAVITY_POINT_ATTENUATION, + AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE, AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE, AREA_PARAM_LINEAR_DAMP, AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE, diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index 2d66c225e8..aba362c956 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -126,6 +126,7 @@ public: virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override {} virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override {} virtual void mesh_instance_check_for_update(RID p_mesh_instance) override {} + virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) override {} virtual void update_mesh_instances() override {} /* MULTIMESH API */ diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 1dbad79477..814d145b66 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -1648,7 +1648,10 @@ void SkyRD::update_dirty_skys() { tf.mipmaps = MIN(mipmaps, layers); tf.width = w; tf.height = h; - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) { + tf.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT; + } sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView()); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index ad63d3fd4b..2e844269e3 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -667,7 +667,7 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { scene_state.ubo.ss_effects_flags = 0; - } else if (is_environment(p_render_data->environment)) { + } else if (p_render_data->reflection_probe.is_null() && is_environment(p_render_data->environment)) { scene_state.ubo.ssao_ao_affect = environment_get_ssao_ao_channel_affect(p_render_data->environment); scene_state.ubo.ssao_light_affect = environment_get_ssao_direct_light_affect(p_render_data->environment); uint32_t ss_flags = 0; @@ -1550,16 +1550,18 @@ void RenderForwardClustered::_process_sss(Ref<RenderSceneBuffersRD> p_render_buf void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - Ref<RenderSceneBuffersRD> rb; + ERR_FAIL_NULL(p_render_data); + + Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); Ref<RenderBufferDataForwardClustered> rb_data; - if (p_render_data && p_render_data->render_buffers.is_valid()) { - rb = p_render_data->render_buffers; - if (rb->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) { - // Our forward clustered custom data buffer will only be available when we're rendering our normal view. - // This will not be available when rendering reflection probes. - rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); - } + if (rb->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) { + // Our forward clustered custom data buffer will only be available when we're rendering our normal view. + // This will not be available when rendering reflection probes. + rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); } + bool is_reflection_probe = p_render_data->reflection_probe.is_valid(); + static const int texture_multisamples[RS::VIEWPORT_MSAA_MAX] = { 1, 2, 4, 8 }; //first of all, make a new render pass @@ -1588,18 +1590,16 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co p_render_data->voxel_gi_count = 0; - if (rb.is_valid()) { - if (rb->has_custom_data(RB_SCOPE_SDFGI)) { - Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); - if (sdfgi.is_valid()) { - sdfgi->update_cascades(); - sdfgi->pre_process_gi(p_render_data->scene_data->cam_transform, p_render_data); - sdfgi->update_light(); - } + if (rb->has_custom_data(RB_SCOPE_SDFGI)) { + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + if (sdfgi.is_valid()) { + sdfgi->update_cascades(); + sdfgi->pre_process_gi(p_render_data->scene_data->cam_transform, p_render_data); + sdfgi->update_light(); } - - gi.setup_voxel_gi_instances(p_render_data, p_render_data->render_buffers, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances, p_render_data->voxel_gi_count); } + + gi.setup_voxel_gi_instances(p_render_data, p_render_data->render_buffers, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances, p_render_data->voxel_gi_count); } else { ERR_PRINT("No render buffer nor reflection atlas, bug"); //should never happen, will crash current_cluster_builder = nullptr; @@ -1618,7 +1618,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co // check if we need motion vectors if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) { p_render_data->scene_data->calculate_motion_vectors = true; - } else if (rb.is_valid() && rb->get_use_taa()) { + } else if (!is_reflection_probe && rb->get_use_taa()) { p_render_data->scene_data->calculate_motion_vectors = true; } else { p_render_data->scene_data->calculate_motion_vectors = false; @@ -1641,9 +1641,9 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool using_sdfgi = false; bool using_voxelgi = false; bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; - bool using_ssil = p_render_data->environment.is_valid() && environment_get_ssil_enabled(p_render_data->environment); + bool using_ssil = !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssil_enabled(p_render_data->environment); - if (p_render_data->reflection_probe.is_valid()) { + if (is_reflection_probe) { uint32_t resolution = light_storage->reflection_probe_instance_get_resolution(p_render_data->reflection_probe); screen_size.x = resolution; screen_size.y = resolution; @@ -1657,7 +1657,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } reverse_cull = true; // for some reason our views are inverted - } else if (rb.is_valid()) { + } else { screen_size = rb->get_internal_size(); if (rb->get_use_taa() || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) { @@ -1685,8 +1685,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); color_only_framebuffer = rb_data->get_color_only_fb(); - } else { - ERR_FAIL(); //bug? } p_render_data->scene_data->emissive_exposure_normalization = -1.0; @@ -1695,7 +1693,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->scene_data->cam_transform); _setup_voxelgis(*p_render_data->voxel_gi_instances); - _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); + _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); _update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example) @@ -1707,7 +1705,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_end_label(); - if (rb.is_valid() && !p_render_data->reflection_probe.is_valid()) { + if (!is_reflection_probe) { if (using_voxelgi) { depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI; } else if (p_render_data->environment.is_valid()) { @@ -1739,7 +1737,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } } - bool using_sss = rb_data.is_valid() && scene_state.used_sss && ss_effects->sss_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED; + bool using_sss = rb_data.is_valid() && !is_reflection_probe && scene_state.used_sss && ss_effects->sss_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED; if (using_sss && !using_separate_specular) { using_separate_specular = true; @@ -1772,7 +1770,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if ((rb.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) || environment_get_fog_enabled(p_render_data->environment)) { + if ((rb->has_custom_data(RB_SCOPE_FOG)) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -1782,7 +1780,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if ((rb.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) || environment_get_fog_enabled(p_render_data->environment)) { + if ((rb->has_custom_data(RB_SCOPE_FOG)) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -1791,7 +1789,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co draw_sky = true; } break; case RS::ENV_BG_CANVAS: { - if (rb.is_valid()) { + if (!is_reflection_probe) { RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target()); copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true); } @@ -1812,7 +1810,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_begin_label("Setup Sky"); // Setup our sky render information for this frame/viewport - if (p_render_data->reflection_probe.is_valid()) { + if (is_reflection_probe) { Vector3 eye_offset; Projection correction; correction.set_depth_correction(true); @@ -1849,7 +1847,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool debug_sdfgi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES; bool depth_pre_pass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")) && depth_framebuffer.is_valid(); - bool using_ssao = depth_pre_pass && rb.is_valid() && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment); + bool using_ssao = depth_pre_pass && !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment); bool continue_depth = false; if (depth_pre_pass) { //depth pre pass @@ -1881,7 +1879,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co _pre_resolve_render(p_render_data, using_sdfgi || using_voxelgi); } - if (rb.is_valid() && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + if (rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { RENDER_TIMESTAMP("Resolve Depth Pre-Pass (MSAA)"); RD::get_singleton()->draw_command_begin_label("Resolve Depth Pre-Pass (MSAA)"); if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI) { @@ -1915,7 +1913,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co p_render_data->scene_data->directional_light_count = p_render_data->directional_light_count; p_render_data->scene_data->opaque_prepass_threshold = 0.0f; - _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, rb.is_valid()); + _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, true); RENDER_TIMESTAMP("Render Opaque Pass"); @@ -1996,7 +1994,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_end_label(); } - if (rb.is_valid() && !can_continue_color && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + if (!can_continue_color && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { // Handle views individual, might want to look at rewriting our resolve to do both layers in one pass. for (uint32_t v = 0; v < rb->get_view_count(); v++) { RD::get_singleton()->texture_resolve_multisample(rb_data->get_color_msaa(v), rb->get_internal_texture(v)); @@ -2008,7 +2006,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } } - if (rb.is_valid() && !can_continue_depth && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + if (!can_continue_depth && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { for (uint32_t v = 0; v < rb->get_view_count(); v++) { resolve_effects->resolve_depth(rb_data->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]); } @@ -2054,7 +2052,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true); - _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); + _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); { uint32_t transparent_color_pass_flags = (color_pass_flags | COLOR_PASS_FLAG_TRANSPARENT) & ~(COLOR_PASS_FLAG_SEPARATE_SPECULAR); @@ -3768,6 +3766,7 @@ void RenderForwardClustered::_geometry_instance_dependency_changed(Dependency::D case Dependency::DEPENDENCY_CHANGED_MULTIMESH: case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceForwardClustered *>(p_tracker->userdata)->data->dirty_dependencies = true; } break; case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { GeometryInstanceForwardClustered *ginstance = static_cast<GeometryInstanceForwardClustered *>(p_tracker->userdata); @@ -3782,6 +3781,7 @@ void RenderForwardClustered::_geometry_instance_dependency_changed(Dependency::D } void RenderForwardClustered::_geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceForwardClustered *>(p_tracker->userdata)->data->dirty_dependencies = true; } RenderGeometryInstance *RenderForwardClustered::geometry_instance_create(RID p_base) { diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 412406c0b4..2aaea4288b 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -717,6 +717,8 @@ void SceneShaderForwardClustered::init(const String p_defines) { actions.global_buffer_array_variable = "global_shader_uniforms.data"; actions.instance_uniform_index_variable = "instances.data[instance_index_interp].instance_uniforms_ofs"; + actions.check_multiview_samplers = true; // make sure we check sampling multiview textures + compiler.initialize(actions); } diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 25204f1abf..7d9ca070b0 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -640,16 +640,18 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - Ref<RenderSceneBuffersRD> rb; + ERR_FAIL_NULL(p_render_data); + + Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); + Ref<RenderBufferDataForwardMobile> rb_data; - if (p_render_data->render_buffers.is_valid()) { - rb = p_render_data->render_buffers; - if (rb->has_custom_data(RB_SCOPE_MOBILE)) { - // Our forward mobile custom data buffer will only be available when we're rendering our normal view. - // This will not be available when rendering reflection probes. - rb_data = rb->get_custom_data(RB_SCOPE_MOBILE); - } + if (rb->has_custom_data(RB_SCOPE_MOBILE)) { + // Our forward mobile custom data buffer will only be available when we're rendering our normal view. + // This will not be available when rendering reflection probes. + rb_data = rb->get_custom_data(RB_SCOPE_MOBILE); } + bool is_reflection_probe = p_render_data->reflection_probe.is_valid(); RENDER_TIMESTAMP("Prepare 3D Scene"); @@ -697,7 +699,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = p_render_data->instances->size(); } - if (p_render_data->reflection_probe.is_valid()) { + if (is_reflection_probe) { uint32_t resolution = light_storage->reflection_probe_instance_get_resolution(p_render_data->reflection_probe); screen_size.x = resolution; screen_size.y = resolution; @@ -754,7 +756,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_begin_label("Render Setup"); _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->scene_data->cam_transform); - _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); + _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); _update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example) @@ -832,7 +834,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_begin_label("Setup Sky"); // Setup our sky render information for this frame/viewport - if (p_render_data->reflection_probe.is_valid()) { + if (is_reflection_probe) { Vector3 eye_offset; Projection correction; correction.set_depth_correction(true); @@ -896,7 +898,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color p_render_data->scene_data->directional_light_count = p_render_data->directional_light_count; - _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, p_render_data->render_buffers.is_valid()); + _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, p_render_data->render_buffers.is_valid()); if (using_subpass_transparent && using_subpass_post_process) { RENDER_TIMESTAMP("Render Opaque + Transparent + Tonemap"); @@ -1025,7 +1027,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } // this may be needed if we re-introduced steps that change info, not sure which do so in the previous implementation - // _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); + // _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); @@ -1065,9 +1067,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color _disable_clear_request(p_render_data); } - if (rb.is_valid()) { - _render_buffers_debug_draw(rb, p_render_data->shadow_atlas, p_render_data->occluder_debug_tex); - } + _render_buffers_debug_draw(rb, p_render_data->shadow_atlas, p_render_data->occluder_debug_tex); } /* these are being called from RendererSceneRenderRD::_pre_opaque_render */ @@ -1924,7 +1924,6 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const } void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_pancake_shadows, int p_index) { - Ref<RenderSceneBuffersRD> rd = p_render_data->render_buffers; RID env = is_environment(p_render_data->environment) ? p_render_data->environment : RID(); RID reflection_probe_instance = p_render_data->reflection_probe.is_valid() ? RendererRD::LightStorage::get_singleton()->reflection_probe_instance_get_probe(p_render_data->reflection_probe) : RID(); @@ -2237,6 +2236,9 @@ RenderGeometryInstance *RenderForwardMobile::geometry_instance_create(RID p_base ginstance->data->base = p_base; ginstance->data->base_type = type; + ginstance->data->dependency_tracker.userdata = ginstance; + ginstance->data->dependency_tracker.changed_callback = _geometry_instance_dependency_changed; + ginstance->data->dependency_tracker.deleted_callback = _geometry_instance_dependency_deleted; ginstance->_mark_dirty(); @@ -2673,6 +2675,7 @@ void RenderForwardMobile::_geometry_instance_dependency_changed(Dependency::Depe case Dependency::DEPENDENCY_CHANGED_MULTIMESH: case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceForwardMobile *>(p_tracker->userdata)->data->dirty_dependencies = true; } break; case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { GeometryInstanceForwardMobile *ginstance = static_cast<GeometryInstanceForwardMobile *>(p_tracker->userdata); @@ -2687,6 +2690,7 @@ void RenderForwardMobile::_geometry_instance_dependency_changed(Dependency::Depe } void RenderForwardMobile::_geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) { static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); + static_cast<GeometryInstanceForwardMobile *>(p_tracker->userdata)->data->dirty_dependencies = true; } /* misc */ diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 3d1d78c63d..f9eecf4dc7 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -609,6 +609,7 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.instance_uniform_index_variable = "draw_call.instance_uniforms_ofs"; actions.apply_luminance_multiplier = true; // apply luminance multiplier to screen texture + actions.check_multiview_samplers = true; // make sure we check sampling multiview textures compiler.initialize(actions); } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 638fe44266..bd8c11186e 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1431,6 +1431,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p const Item::CommandMesh *cm = static_cast<const Item::CommandMesh *>(c); if (cm->mesh_instance.is_valid()) { mesh_storage->mesh_instance_check_for_update(cm->mesh_instance); + mesh_storage->mesh_instance_set_canvas_item_transform(cm->mesh_instance, canvas_transform_inverse * ci->final_transform); update_skeletons = true; } } @@ -1549,6 +1550,9 @@ void RendererCanvasRenderRD::light_set_texture(RID p_rid, RID p_texture) { if (cl->texture == p_texture) { return; } + + ERR_FAIL_COND(p_texture.is_valid() && !texture_storage->owns_texture(p_texture)); + if (cl->texture.is_valid()) { texture_storage->texture_remove_from_decal_atlas(cl->texture); } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 2a87c4fa8d..885ea18151 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -253,6 +253,11 @@ void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderData Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); + if (!rb->has_internal_texture()) { + // We're likely rendering reflection probes where we can't use our backbuffers. + return; + } + RD::get_singleton()->draw_command_begin_label("Copy screen texture"); rb->allocate_blur_textures(); @@ -292,6 +297,11 @@ void RendererSceneRenderRD::_render_buffers_copy_depth_texture(const RenderDataR Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); + if (!rb->has_depth_texture()) { + // We're likely rendering reflection probes where we can't use our backbuffers. + return; + } + RD::get_singleton()->draw_command_begin_label("Copy depth texture"); // note, this only creates our back depth texture if we haven't already created it. @@ -321,9 +331,13 @@ void RendererSceneRenderRD::_render_buffers_copy_depth_texture(const RenderDataR void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + ERR_FAIL_NULL(p_render_data); + Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); + ERR_FAIL_COND_MSG(p_render_data->reflection_probe.is_valid(), "Post processes should not be applied on reflection probes."); + // Glow, auto exposure and DoF (if enabled). Size2i internal_size = rb->get_internal_size(); @@ -918,11 +932,9 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); // getting this here now so we can direct call a bunch of things more easily - Ref<RenderSceneBuffersRD> rb; - if (p_render_buffers.is_valid()) { - rb = p_render_buffers; // cast it... - ERR_FAIL_COND(rb.is_null()); - } + ERR_FAIL_COND(p_render_buffers.is_null()); + Ref<RenderSceneBuffersRD> rb = p_render_buffers; + ERR_FAIL_COND(rb.is_null()); // setup scene data RenderSceneDataRD scene_data; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 91c2000c1a..c6a3c42e57 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -118,13 +118,13 @@ layout(location = 10) out flat uint instance_index_interp; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif // has_VK_KHR_multiview -vec3 normal_roughness_uv(vec2 uv) { +vec3 multiview_uv(vec2 uv) { return vec3(uv, ViewIndex); } #else // USE_MULTIVIEW // Set to zero, not supported in non stereo #define ViewIndex 0 -vec2 normal_roughness_uv(vec2 uv) { +vec2 multiview_uv(vec2 uv) { return uv; } #endif //USE_MULTIVIEW @@ -550,13 +550,13 @@ layout(location = 10) in flat uint instance_index_interp; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif // has_VK_KHR_multiview -vec3 normal_roughness_uv(vec2 uv) { +vec3 multiview_uv(vec2 uv) { return vec3(uv, ViewIndex); } #else // USE_MULTIVIEW // Set to zero, not supported in non stereo #define ViewIndex 0 -vec2 normal_roughness_uv(vec2 uv) { +vec2 multiview_uv(vec2 uv) { return uv; } #endif //USE_MULTIVIEW diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index 1f524313f2..8ff7a784dc 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -271,15 +271,16 @@ layout(r32ui, set = 1, binding = 13) uniform restrict uimage3D geom_facing_grid; #define multiviewSampler sampler2D #else -layout(set = 1, binding = 10) uniform texture2D depth_buffer; -layout(set = 1, binding = 11) uniform texture2D color_buffer; - #ifdef USE_MULTIVIEW +layout(set = 1, binding = 10) uniform texture2DArray depth_buffer; +layout(set = 1, binding = 11) uniform texture2DArray color_buffer; layout(set = 1, binding = 12) uniform texture2DArray normal_roughness_buffer; layout(set = 1, binding = 14) uniform texture2DArray ambient_buffer; layout(set = 1, binding = 15) uniform texture2DArray reflection_buffer; #define multiviewSampler sampler2DArray #else // USE_MULTIVIEW +layout(set = 1, binding = 10) uniform texture2D depth_buffer; +layout(set = 1, binding = 11) uniform texture2D color_buffer; layout(set = 1, binding = 12) uniform texture2D normal_roughness_buffer; layout(set = 1, binding = 14) uniform texture2D ambient_buffer; layout(set = 1, binding = 15) uniform texture2D reflection_buffer; diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 2966a2ff65..90e902ca33 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -112,9 +112,15 @@ layout(location = 9) out highp float dp_clip; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif +vec3 multiview_uv(vec2 uv) { + return vec3(uv, ViewIndex); +} #else // Set to zero, not supported in non stereo #define ViewIndex 0 +vec2 multiview_uv(vec2 uv) { + return uv; +} #endif //USE_MULTIVIEW invariant gl_Position; @@ -523,9 +529,15 @@ layout(location = 9) highp in float dp_clip; // !BAS! This needs to become an input once we implement our fallback! #define ViewIndex 0 #endif +vec3 multiview_uv(vec2 uv) { + return vec3(uv, ViewIndex); +} #else // Set to zero, not supported in non stereo #define ViewIndex 0 +vec2 multiview_uv(vec2 uv) { + return uv; +} #endif //USE_MULTIVIEW //defines to keep compatibility with vertex diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 631ff0575b..78b39a356d 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -154,8 +154,15 @@ layout(set = 1, binding = 5) uniform highp texture2D directional_shadow_atlas; // this needs to change to providing just the lightmap we're using.. layout(set = 1, binding = 6) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES]; +#ifdef USE_MULTIVIEW +layout(set = 1, binding = 9) uniform highp texture2DArray depth_buffer; +layout(set = 1, binding = 10) uniform mediump texture2DArray color_buffer; +#define multiviewSampler sampler2DArray +#else layout(set = 1, binding = 9) uniform highp texture2D depth_buffer; layout(set = 1, binding = 10) uniform mediump texture2D color_buffer; +#define multiviewSampler sampler2D +#endif // USE_MULTIVIEW /* Set 2 Skeleton & Instancing (can change per item) */ diff --git a/servers/rendering/renderer_rd/shaders/skeleton.glsl b/servers/rendering/renderer_rd/shaders/skeleton.glsl index f5b233cca0..59c161548c 100644 --- a/servers/rendering/renderer_rd/shaders/skeleton.glsl +++ b/servers/rendering/renderer_rd/shaders/skeleton.glsl @@ -51,6 +51,15 @@ layout(push_constant, std430) uniform Params { bool normalized_blend_shapes; uint pad0; uint pad1; + + vec2 skeleton_transform_x; + vec2 skeleton_transform_y; + + vec2 skeleton_transform_offset; + vec2 inverse_transform_x; + + vec2 inverse_transform_y; + vec2 inverse_transform_offset; } params; @@ -158,8 +167,12 @@ void main() { m += mat4(bone_transforms.data[bones_23.x], bone_transforms.data[bones_23.x + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.x; m += mat4(bone_transforms.data[bones_23.y], bone_transforms.data[bones_23.y + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.y; - //reverse order because its transposed - vertex = (vec4(vertex, 0.0, 1.0) * m).xy; + mat4 skeleton_matrix = mat4(vec4(params.skeleton_transform_x, 0.0, 0.0), vec4(params.skeleton_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(params.skeleton_transform_offset, 0.0, 1.0)); + mat4 inverse_matrix = mat4(vec4(params.inverse_transform_x, 0.0, 0.0), vec4(params.inverse_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(params.inverse_transform_offset, 0.0, 1.0)); + + m = skeleton_matrix * transpose(m) * inverse_matrix; + + vertex = (m * vec4(vertex, 0.0, 1.0)).xy; } uint dst_offset = index * params.vertex_stride; diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 33e35a7a64..ff859a668a 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -232,6 +232,8 @@ void LightStorage::light_set_projector(RID p_light, RID p_texture) { return; } + ERR_FAIL_COND(p_texture.is_valid() && !texture_storage->owns_texture(p_texture)); + if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) { texture_storage->texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); } @@ -1464,7 +1466,6 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_ fb.push_back(atlas->depth_buffer); atlas->depth_fb = RD::get_singleton()->framebuffer_create(fb); - atlas->render_buffers->cleanup(); atlas->render_buffers->configure_for_reflections(Size2i(atlas->size, atlas->size)); } diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 46a0d85ba8..9124886764 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -842,7 +842,7 @@ void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int } void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { - for (const RendererRD::MeshStorage::MeshInstance::Surface surface : mi->surfaces) { + for (const RendererRD::MeshStorage::MeshInstance::Surface &surface : mi->surfaces) { if (surface.versions) { for (uint32_t j = 0; j < surface.version_count; j++) { RD::get_singleton()->free(surface.versions[j].vertex_array); @@ -930,6 +930,11 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { } } +void MeshStorage::mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) { + MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); + mi->canvas_item_transform_2d = p_transform; +} + void MeshStorage::update_mesh_instances() { while (dirty_mesh_instance_weights.first()) { MeshInstance *mi = dirty_mesh_instance_weights.first()->self(); @@ -981,6 +986,22 @@ void MeshStorage::update_mesh_instances() { push_constant.skin_stride = (mi->mesh->surfaces[i]->skin_buffer_size / mi->mesh->surfaces[i]->vertex_count) / 4; push_constant.skin_weight_offset = (mi->mesh->surfaces[i]->format & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS) ? 4 : 2; + Transform2D transform = mi->canvas_item_transform_2d.affine_inverse() * sk->base_transform_2d; + push_constant.skeleton_transform_x[0] = transform.columns[0][0]; + push_constant.skeleton_transform_x[1] = transform.columns[0][1]; + push_constant.skeleton_transform_y[0] = transform.columns[1][0]; + push_constant.skeleton_transform_y[1] = transform.columns[1][1]; + push_constant.skeleton_transform_offset[0] = transform.columns[2][0]; + push_constant.skeleton_transform_offset[1] = transform.columns[2][1]; + + Transform2D inverse_transform = transform.affine_inverse(); + push_constant.inverse_transform_x[0] = inverse_transform.columns[0][0]; + push_constant.inverse_transform_x[1] = inverse_transform.columns[0][1]; + push_constant.inverse_transform_y[0] = inverse_transform.columns[1][0]; + push_constant.inverse_transform_y[1] = inverse_transform.columns[1][1]; + push_constant.inverse_transform_offset[0] = inverse_transform.columns[2][0]; + push_constant.inverse_transform_offset[1] = inverse_transform.columns[2][1]; + push_constant.blend_shape_count = mi->mesh->blend_shape_count; push_constant.normalized_blend_shapes = mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED; push_constant.pad0 = 0; diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index b62da5fd7b..c921523941 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -178,6 +178,7 @@ private: bool weights_dirty = false; SelfList<MeshInstance> weight_update_list; SelfList<MeshInstance> array_update_list; + Transform2D canvas_item_transform_2d; MeshInstance() : weight_update_list(this), array_update_list(this) {} }; @@ -256,6 +257,14 @@ private: uint32_t normalized_blend_shapes; uint32_t pad0; uint32_t pad1; + float skeleton_transform_x[2]; + float skeleton_transform_y[2]; + + float skeleton_transform_offset[2]; + float inverse_transform_x[2]; + + float inverse_transform_y[2]; + float inverse_transform_offset[2]; }; enum { @@ -548,6 +557,7 @@ public: virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override; virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override; virtual void mesh_instance_check_for_update(RID p_mesh_instance) override; + virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) override; virtual void update_mesh_instances() override; /* MULTIMESH API */ diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index 7771f75a65..255719183f 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -490,6 +490,16 @@ Ref<RenderBufferCustomDataRD> RenderSceneBuffersRD::get_custom_data(const String // Depth texture +bool RenderSceneBuffersRD::has_depth_texture() { + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + RID depth = texture_storage->render_target_get_override_depth(render_target); + if (depth.is_valid()) { + return true; + } else { + return has_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH); + } +} + RID RenderSceneBuffersRD::get_depth_texture() { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RID depth = texture_storage->render_target_get_override_depth(render_target); diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index dc849fd56a..50fb06c0d9 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -185,6 +185,9 @@ public: // For our internal textures we provide some easy access methods. + _FORCE_INLINE_ bool has_internal_texture() const { + return has_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR); + } _FORCE_INLINE_ RID get_internal_texture() const { return get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR); } @@ -192,6 +195,7 @@ public: return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_COLOR, p_layer, 0); } + bool has_depth_texture(); RID get_depth_texture(); RID get_depth_texture(const uint32_t p_layer); diff --git a/servers/rendering/renderer_rd/storage_rd/utilities.cpp b/servers/rendering/renderer_rd/storage_rd/utilities.cpp index d2f5e6f224..cabac4e9ee 100644 --- a/servers/rendering/renderer_rd/storage_rd/utilities.cpp +++ b/servers/rendering/renderer_rd/storage_rd/utilities.cpp @@ -286,6 +286,10 @@ bool Utilities::has_os_feature(const String &p_feature) const { return true; } + if (p_feature == "astc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { + return true; + } + return false; } diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 9011cdd98a..6f4bb115fc 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -2964,11 +2964,13 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c Scenario *scenario = scenario_owner.get_or_null(p_scenario); + ERR_FAIL_COND(p_render_buffers.is_null()); + render_pass++; scene_render->set_scene_pass(render_pass); - if (p_render_buffers.is_valid() && p_reflection_probe.is_null()) { + if (p_reflection_probe.is_null()) { //no rendering code here, this is only to set up what needs to be done, request regions, etc. scene_render->sdfgi_update(p_render_buffers, p_environment, p_camera_data->main_transform.origin); //update conditions for SDFGI (whether its used or not) } @@ -3050,7 +3052,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c { //sdfgi cull.sdfgi.region_count = 0; - if (p_render_buffers.is_valid() && p_reflection_probe.is_null()) { + if (p_reflection_probe.is_null()) { cull.sdfgi.cascade_light_count = 0; uint32_t prev_cascade = 0xFFFFFFFF; @@ -3279,7 +3281,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } } - if (p_render_buffers.is_valid() && p_reflection_probe.is_null()) { + if (p_reflection_probe.is_null()) { sdfgi_update_data.directional_lights = &directional_lights; sdfgi_update_data.positional_light_instances = scenario->dynamic_lights.ptr(); sdfgi_update_data.positional_light_count = scenario->dynamic_lights.size(); diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 3886f5b379..aecd0593bc 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -236,7 +236,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) { } } - if (!p_viewport->disable_2d && !p_viewport->disable_environment && RSG::scene->is_scenario(p_viewport->scenario)) { + if (!p_viewport->disable_2d && !viewport_is_environment_disabled(p_viewport) && RSG::scene->is_scenario(p_viewport->scenario)) { RID environment = RSG::scene->scenario_get_environment(p_viewport->scenario); if (RSG::scene->is_environment(environment)) { scenario_draw_canvas_bg = RSG::scene->environment_get_background(environment) == RS::ENV_BG_CANVAS; @@ -992,11 +992,21 @@ void RendererViewport::viewport_set_disable_2d(RID p_viewport, bool p_disable) { viewport->disable_2d = p_disable; } -void RendererViewport::viewport_set_disable_environment(RID p_viewport, bool p_disable) { +void RendererViewport::viewport_set_environment_mode(RID p_viewport, RS::ViewportEnvironmentMode p_mode) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_COND(!viewport); - viewport->disable_environment = p_disable; + viewport->disable_environment = p_mode; +} + +bool RendererViewport::viewport_is_environment_disabled(Viewport *viewport) { + ERR_FAIL_COND_V(!viewport, false); + + if (viewport->parent.is_valid() && viewport->disable_environment == RS::VIEWPORT_ENVIRONMENT_INHERIT) { + Viewport *parent = viewport_owner.get_or_null(viewport->parent); + return viewport_is_environment_disabled(parent); + } + return viewport->disable_environment == RS::VIEWPORT_ENVIRONMENT_DISABLED; } void RendererViewport::viewport_set_disable_3d(RID p_viewport, bool p_disable) { diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index 9b32cc3774..c24275de6e 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -85,7 +85,7 @@ public: bool viewport_render_direct_to_screen; bool disable_2d = false; - bool disable_environment = false; + RS::ViewportEnvironmentMode disable_environment = RS::VIEWPORT_ENVIRONMENT_INHERIT; bool disable_3d = false; bool measure_render_time = false; @@ -238,9 +238,11 @@ public: const RendererSceneRender::CameraData *viewport_get_prev_camera_data(RID p_viewport); void viewport_set_disable_2d(RID p_viewport, bool p_disable); - void viewport_set_disable_environment(RID p_viewport, bool p_disable); + void viewport_set_environment_mode(RID p_viewport, RS::ViewportEnvironmentMode p_mode); void viewport_set_disable_3d(RID p_viewport, bool p_disable); + bool viewport_is_environment_disabled(Viewport *viewport); + void viewport_attach_camera(RID p_viewport, RID p_camera); void viewport_set_scenario(RID p_viewport, RID p_scenario); void viewport_attach_canvas(RID p_viewport, RID p_canvas); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 8ac522bafe..bcaaba8d65 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -608,7 +608,7 @@ public: FUNC1RC(RID, viewport_get_texture, RID) FUNC2(viewport_set_disable_2d, RID, bool) - FUNC2(viewport_set_disable_environment, RID, bool) + FUNC2(viewport_set_environment_mode, RID, ViewportEnvironmentMode) FUNC2(viewport_set_disable_3d, RID, bool) FUNC2(viewport_set_canvas_cull_mask, RID, uint32_t) diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 68542f32af..2710724066 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -1173,9 +1173,10 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene code += "("; - // if normal roughness texture is used, we will add logic to automatically switch between + // if color backbuffer, depth backbuffer or normal roughness texture is used, + // we will add logic to automatically switch between // sampler2D and sampler2D array and vec2 UV and vec3 UV. - bool normal_roughness_texture_used = false; + bool multiview_uv_needed = false; for (int i = 1; i < onode->arguments.size(); i++) { if (i > 1) { @@ -1220,8 +1221,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } String node_code = _dump_node_code(onode->arguments[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - if (!RS::get_singleton()->is_low_end() && is_texture_func && i == 1) { - //need to map from texture to sampler in order to sample when using Vulkan GLSL + if (is_texture_func && i == 1) { + // If we're doing a texture lookup we need to check our texture argument StringName texture_uniform; bool correct_texture_uniform = false; @@ -1240,8 +1241,10 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene break; } - if (correct_texture_uniform) { + if (correct_texture_uniform && !RS::get_singleton()->is_low_end()) { + // Need to map from texture to sampler in order to sample when using Vulkan GLSL. String sampler_name; + bool is_depth_texture = false; bool is_normal_roughness_texture = false; if (actions.custom_samplers.has(texture_uniform)) { @@ -1251,6 +1254,8 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[texture_uniform]; if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) { is_screen_texture = true; + } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { + is_depth_texture = true; } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) { is_normal_roughness_texture = true; } @@ -1281,24 +1286,40 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } String data_type_name = ""; - if (is_normal_roughness_texture) { + if (actions.check_multiview_samplers && (is_screen_texture || is_depth_texture || is_normal_roughness_texture)) { data_type_name = "multiviewSampler"; - normal_roughness_texture_used = true; + multiview_uv_needed = true; } else { data_type_name = ShaderLanguage::get_datatype_name(onode->arguments[i]->get_datatype()); } code += data_type_name + "(" + node_code + ", " + sampler_name + ")"; + } else if (actions.check_multiview_samplers && correct_texture_uniform && RS::get_singleton()->is_low_end()) { + // Texture function on low end hardware (i.e. OpenGL). + // We just need to know if the texture supports multiview. + + if (shader->uniforms.has(texture_uniform)) { + const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[texture_uniform]; + if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) { + multiview_uv_needed = true; + } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { + multiview_uv_needed = true; + } else if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) { + multiview_uv_needed = true; + } + } + + code += node_code; } else { code += node_code; } - } else { - if (normal_roughness_texture_used && i == 2) { - // UV coordinate after using normal roughness texture. - node_code = "normal_roughness_uv(" + node_code + ".xy)"; - } + } else if (multiview_uv_needed && i == 2) { + // UV coordinate after using color, depth or normal roughness texture. + node_code = "multiview_uv(" + node_code + ".xy)"; code += node_code; + } else { + code += node_code; } } code += ")"; diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h index 43bea213da..3bb29a545b 100644 --- a/servers/rendering/shader_compiler.h +++ b/servers/rendering/shader_compiler.h @@ -101,6 +101,7 @@ public: String instance_uniform_index_variable; uint32_t base_varying_index = 0; bool apply_luminance_multiplier = false; + bool check_multiview_samplers = false; }; private: diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index ba7ffda00a..11a9303f11 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -90,8 +90,9 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "IDENTIFIER", "TRUE", "FALSE", - "REAL_CONSTANT", + "FLOAT_CONSTANT", "INT_CONSTANT", + "UINT_CONSTANT", "TYPE_VOID", "TYPE_BOOL", "TYPE_BVEC2", @@ -126,6 +127,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "INTERPOLATION_FLAT", "INTERPOLATION_SMOOTH", "CONST", + "STRUCT", "PRECISION_LOW", "PRECISION_MID", "PRECISION_HIGH", @@ -169,6 +171,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "CF_DO", "CF_SWITCH", "CF_CASE", + "CF_DEFAULT", "CF_BREAK", "CF_CONTINUE", "CF_RETURN", @@ -185,19 +188,26 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "SEMICOLON", "PERIOD", "UNIFORM", + "UNIFORM_GROUP", "INSTANCE", "GLOBAL", "VARYING", - "IN", - "OUT", - "INOUT", + "ARG_IN", + "ARG_OUT", + "ARG_INOUT", "RENDER_MODE", - "SOURCE_COLOR", "HINT_DEFAULT_WHITE_TEXTURE", "HINT_DEFAULT_BLACK_TEXTURE", "HINT_DEFAULT_TRANSPARENT_TEXTURE", "HINT_NORMAL_TEXTURE", + "HINT_ROUGHNESS_NORMAL_TEXTURE", + "HINT_ROUGHNESS_R", + "HINT_ROUGHNESS_G", + "HINT_ROUGHNESS_B", + "HINT_ROUGHNESS_A", + "HINT_ROUGHNESS_GRAY", "HINT_ANISOTROPY_TEXTURE", + "HINT_SOURCE_COLOR", "HINT_RANGE", "HINT_INSTANCE_INDEX", "HINT_SCREEN_TEXTURE", @@ -612,7 +622,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { char_idx += 2; include_positions.resize(include_positions.size() - 1); // Pop back. - tk_line = include_positions[include_positions.size() - 1].line; // Restore line. + tk_line = include_positions[include_positions.size() - 1].line - 1; // Restore line. } else { return _make_token(TK_ERROR, "Invalid include enter/exit hint token (@@> and @@<)"); @@ -2827,6 +2837,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "dFdx", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, { "dFdx", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, + // dFdxCoarse + + { "dFdxCoarse", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxCoarse", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxCoarse", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxCoarse", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // dFdxFine + + { "dFdxFine", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxFine", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxFine", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdxFine", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + // dFdy { "dFdy", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, @@ -2834,6 +2858,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "dFdy", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, { "dFdy", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, + // dFdyCoarse + + { "dFdyCoarse", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyCoarse", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyCoarse", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyCoarse", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // dFdyFine + + { "dFdyFine", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyFine", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyFine", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdyFine", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + // fwidth { "fwidth", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, @@ -2841,6 +2879,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "fwidth", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, { "fwidth", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, false }, + // fwidthCoarse + + { "fwidthCoarse", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthCoarse", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthCoarse", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthCoarse", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // fwidthFine + + { "fwidthFine", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthFine", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthFine", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidthFine", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + // Sub-functions. // array @@ -8233,6 +8285,10 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f _set_error(vformat(RTR("Uniform instances are not yet implemented for '%s' shaders."), shader_type_identifier)); return ERR_PARSE_ERROR; } + if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { + _set_error(RTR("Uniform instances are not supported in gl_compatibility shaders.")); + return ERR_PARSE_ERROR; + } if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) { tk = _get_token(); if (tk.type != TK_UNIFORM) { @@ -8703,6 +8759,10 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f new_hint = ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE; --texture_uniforms; --texture_binding; + if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { + _set_error(RTR("'hint_normal_roughness_texture is not supported in gl_compatibility shaders.")); + return ERR_PARSE_ERROR; + } } break; case TK_HINT_DEPTH_TEXTURE: { new_hint = ShaderNode::Uniform::HINT_DEPTH_TEXTURE; diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp index 97efd2df94..b45a7c0db3 100644 --- a/servers/rendering/shader_preprocessor.cpp +++ b/servers/rendering/shader_preprocessor.cpp @@ -78,19 +78,46 @@ char32_t ShaderPreprocessor::Tokenizer::peek() { return 0; } +int ShaderPreprocessor::Tokenizer::consume_line_continuations(int p_offset) { + int skips = 0; + + for (int i = index + p_offset; i < size; i++) { + char32_t c = code[i]; + if (c == '\\') { + if (i + 1 < size && code[i + 1] == '\n') { + // This line ends with "\" and "\n" continuation. + add_generated(Token('\n', line)); + line++; + skips++; + + i = i + 2; + index = i; + } else { + break; + } + } else if (!is_whitespace(c)) { + break; + } + } + return skips; +} + LocalVector<ShaderPreprocessor::Token> ShaderPreprocessor::Tokenizer::advance(char32_t p_what) { LocalVector<ShaderPreprocessor::Token> tokens; while (index < size) { char32_t c = code[index++]; - - tokens.push_back(ShaderPreprocessor::Token(c, line)); + if (c == '\\' && consume_line_continuations(-1) > 0) { + continue; + } if (c == '\n') { add_generated(ShaderPreprocessor::Token('\n', line)); line++; } + tokens.push_back(ShaderPreprocessor::Token(c, line)); + if (c == p_what || c == 0) { return tokens; } @@ -104,6 +131,11 @@ void ShaderPreprocessor::Tokenizer::skip_whitespace() { } } +bool ShaderPreprocessor::Tokenizer::consume_empty_line() { + // Read until newline and return true if the content was all whitespace/empty. + return tokens_to_string(advance('\n')).strip_edges().size() == 0; +} + String ShaderPreprocessor::Tokenizer::get_identifier(bool *r_is_cursor, bool p_started) { if (r_is_cursor != nullptr) { *r_is_cursor = false; @@ -113,6 +145,10 @@ String ShaderPreprocessor::Tokenizer::get_identifier(bool *r_is_cursor, bool p_s while (true) { char32_t c = peek(); + if (c == '\\' && consume_line_continuations(0) > 0) { + continue; + } + if (is_char_end(c) || c == '(' || c == ')' || c == ',' || c == ';') { break; } @@ -146,8 +182,10 @@ String ShaderPreprocessor::Tokenizer::get_identifier(bool *r_is_cursor, bool p_s String ShaderPreprocessor::Tokenizer::peek_identifier() { const int original = index; + const int original_line = line; String id = get_identifier(); index = original; + line = original_line; return id; } @@ -485,7 +523,9 @@ void ShaderPreprocessor::process_else(Tokenizer *p_tokenizer) { state->previous_region->to_line = line - 1; } - p_tokenizer->advance('\n'); + if (!p_tokenizer->consume_empty_line()) { + set_error(RTR("Invalid else."), p_tokenizer->get_line()); + } bool skip = false; for (int i = 0; i < state->current_branch->conditions.size(); i++) { @@ -508,17 +548,21 @@ void ShaderPreprocessor::process_else(Tokenizer *p_tokenizer) { } void ShaderPreprocessor::process_endif(Tokenizer *p_tokenizer) { + const int line = p_tokenizer->get_line(); + state->condition_depth--; if (state->condition_depth < 0) { - set_error(RTR("Unmatched endif."), p_tokenizer->get_line()); + set_error(RTR("Unmatched endif."), line); return; } if (state->previous_region != nullptr) { - state->previous_region->to_line = p_tokenizer->get_line() - 1; + state->previous_region->to_line = line - 1; state->previous_region = state->previous_region->parent; } - p_tokenizer->advance('\n'); + if (!p_tokenizer->consume_empty_line()) { + set_error(RTR("Invalid endif."), line); + } state->current_branch = state->current_branch->parent; state->branches.pop_back(); @@ -574,12 +618,10 @@ void ShaderPreprocessor::process_ifdef(Tokenizer *p_tokenizer) { return; } - p_tokenizer->skip_whitespace(); - if (!is_char_end(p_tokenizer->peek())) { + if (!p_tokenizer->consume_empty_line()) { set_error(RTR("Invalid ifdef."), line); return; } - p_tokenizer->advance('\n'); bool success = state->defines.has(label); start_branch_condition(p_tokenizer, success); @@ -598,12 +640,10 @@ void ShaderPreprocessor::process_ifndef(Tokenizer *p_tokenizer) { return; } - p_tokenizer->skip_whitespace(); - if (!is_char_end(p_tokenizer->peek())) { + if (!p_tokenizer->consume_empty_line()) { set_error(RTR("Invalid ifndef."), line); return; } - p_tokenizer->advance('\n'); bool success = !state->defines.has(label); start_branch_condition(p_tokenizer, success); @@ -628,13 +668,17 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) { } } path = path.substr(0, path.length() - 1); - p_tokenizer->skip_whitespace(); - if (path.is_empty() || !is_char_end(p_tokenizer->peek())) { + if (path.is_empty() || !p_tokenizer->consume_empty_line()) { set_error(RTR("Invalid path."), line); return; } + path = path.simplify_path(); + if (path.is_relative_path()) { + path = state->current_filename.get_base_dir().path_join(path); + } + Ref<Resource> res = ResourceLoader::load(path); if (res.is_null()) { set_error(RTR("Shader include load failed. Does the shader include exist? Is there a cyclic dependency?"), line); @@ -728,25 +772,24 @@ void ShaderPreprocessor::process_pragma(Tokenizer *p_tokenizer) { return; } - p_tokenizer->advance('\n'); + if (!p_tokenizer->consume_empty_line()) { + set_error(RTR("Invalid pragma directive."), line); + return; + } } void ShaderPreprocessor::process_undef(Tokenizer *p_tokenizer) { const int line = p_tokenizer->get_line(); const String label = p_tokenizer->get_identifier(); - if (label.is_empty() || !state->defines.has(label)) { - set_error(RTR("Invalid name."), line); - return; - } - - p_tokenizer->skip_whitespace(); - if (!is_char_end(p_tokenizer->peek())) { + if (label.is_empty() || !p_tokenizer->consume_empty_line()) { set_error(RTR("Invalid undef."), line); return; } - memdelete(state->defines[label]); - state->defines.erase(label); + if (state->defines.has(label)) { + memdelete(state->defines[label]); + state->defines.erase(label); + } } void ShaderPreprocessor::add_region(int p_line, bool p_enabled, Region *p_parent_region) { @@ -957,15 +1000,57 @@ bool ShaderPreprocessor::expand_macros_once(const String &p_line, int p_line_num String body = define->body; if (define->arguments.size() > 0) { // Complex macro with arguments. - int args_start = index + key.length(); - int args_end = p_line.find(")", args_start); - if (args_start == -1 || args_end == -1) { - set_error(RTR("Missing macro argument parenthesis."), p_line_number); - return false; + + int args_start = -1; + int args_end = -1; + int brackets_open = 0; + Vector<String> args; + for (int i = index_start - 1; i < p_line.length(); i++) { + bool add_argument = false; + bool reached_end = false; + char32_t c = p_line[i]; + + if (c == '(') { + brackets_open++; + if (brackets_open == 1) { + args_start = i + 1; + args_end = -1; + } + } else if (c == ')') { + brackets_open--; + if (brackets_open == 0) { + args_end = i; + add_argument = true; + reached_end = true; + } + } else if (c == ',') { + if (brackets_open == 1) { + args_end = i; + add_argument = true; + } + } + + if (add_argument) { + if (args_start == -1 || args_end == -1) { + set_error(RTR("Invalid macro argument list."), p_line_number); + return false; + } + + String arg = p_line.substr(args_start, args_end - args_start).strip_edges(); + if (arg.is_empty()) { + set_error(RTR("Invalid macro argument."), p_line_number); + return false; + } + args.append(arg); + + args_start = args_end + 1; + } + + if (reached_end) { + break; + } } - String values = result.substr(args_start + 1, args_end - (args_start + 1)); - Vector<String> args = values.split(","); if (args.size() != define->arguments.size()) { set_error(RTR("Invalid macro argument count."), p_line_number); return false; @@ -987,9 +1072,6 @@ bool ShaderPreprocessor::expand_macros_once(const String &p_line, int p_line_num result = result.substr(0, index) + " " + body + " " + result.substr(args_end + 1, result.length()); } else { result = result.substr(0, index) + body + result.substr(index + key.length(), result.length() - (index + key.length())); - // Manually reset index_start to where the body value of the define finishes. - // This ensures we don't skip another instance of this macro in the string. - index_start = index + body.length() + 1; } r_expanded = result; diff --git a/servers/rendering/shader_preprocessor.h b/servers/rendering/shader_preprocessor.h index 6e5533c575..2b7d2c274e 100644 --- a/servers/rendering/shader_preprocessor.h +++ b/servers/rendering/shader_preprocessor.h @@ -93,11 +93,13 @@ private: int get_line() const; int get_index() const; char32_t peek(); + int consume_line_continuations(int p_offset); void get_and_clear_generated(Vector<Token> *r_out); void backtrack(char32_t p_what); LocalVector<Token> advance(char32_t p_what); void skip_whitespace(); + bool consume_empty_line(); String get_identifier(bool *r_is_cursor = nullptr, bool p_started = false); String peek_identifier(); Token get_token(); diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h index 704c2e015c..76e46a696a 100644 --- a/servers/rendering/storage/mesh_storage.h +++ b/servers/rendering/storage/mesh_storage.h @@ -83,6 +83,7 @@ public: virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) = 0; virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) = 0; virtual void mesh_instance_check_for_update(RID p_mesh_instance) = 0; + virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) = 0; virtual void update_mesh_instances() = 0; /* MULTIMESH API */ diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 4ef562adcb..cea7912574 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2200,7 +2200,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("viewport_get_texture", "viewport"), &RenderingServer::viewport_get_texture); ClassDB::bind_method(D_METHOD("viewport_set_disable_3d", "viewport", "disable"), &RenderingServer::viewport_set_disable_3d); ClassDB::bind_method(D_METHOD("viewport_set_disable_2d", "viewport", "disable"), &RenderingServer::viewport_set_disable_2d); - ClassDB::bind_method(D_METHOD("viewport_set_disable_environment", "viewport", "disabled"), &RenderingServer::viewport_set_disable_environment); + ClassDB::bind_method(D_METHOD("viewport_set_environment_mode", "viewport", "mode"), &RenderingServer::viewport_set_environment_mode); ClassDB::bind_method(D_METHOD("viewport_attach_camera", "viewport", "camera"), &RenderingServer::viewport_attach_camera); ClassDB::bind_method(D_METHOD("viewport_set_scenario", "viewport", "scenario"), &RenderingServer::viewport_set_scenario); ClassDB::bind_method(D_METHOD("viewport_attach_canvas", "viewport", "canvas"), &RenderingServer::viewport_attach_canvas); @@ -2255,6 +2255,11 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_CLEAR_NEVER); BIND_ENUM_CONSTANT(VIEWPORT_CLEAR_ONLY_NEXT_FRAME); + BIND_ENUM_CONSTANT(VIEWPORT_ENVIRONMENT_DISABLED); + BIND_ENUM_CONSTANT(VIEWPORT_ENVIRONMENT_ENABLED); + BIND_ENUM_CONSTANT(VIEWPORT_ENVIRONMENT_INHERIT); + BIND_ENUM_CONSTANT(VIEWPORT_ENVIRONMENT_MAX); + BIND_ENUM_CONSTANT(VIEWPORT_SDF_OVERSIZE_100_PERCENT); BIND_ENUM_CONSTANT(VIEWPORT_SDF_OVERSIZE_120_PERCENT); BIND_ENUM_CONSTANT(VIEWPORT_SDF_OVERSIZE_150_PERCENT); @@ -2849,10 +2854,8 @@ RenderingServer::RenderingServer() { } void RenderingServer::init() { - GLOBAL_DEF_RST("rendering/textures/vram_compression/import_bptc", false); - GLOBAL_DEF_RST("rendering/textures/vram_compression/import_s3tc", true); - GLOBAL_DEF_RST("rendering/textures/vram_compression/import_etc", false); - GLOBAL_DEF_RST("rendering/textures/vram_compression/import_etc2", true); + GLOBAL_DEF_RST("rendering/textures/vram_compression/import_s3tc_bptc", OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC); + GLOBAL_DEF_RST("rendering/textures/vram_compression/import_etc2_astc", OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC); GLOBAL_DEF("rendering/textures/lossless_compression/force_png", false); @@ -2875,7 +2878,7 @@ void RenderingServer::init() { GLOBAL_DEF("rendering/2d/shadow_atlas/size", 2048); // Number of commands that can be drawn per frame. - GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/gl_compatibility/item_buffer_size", PROPERTY_HINT_RANGE, "1024,1048576,1"), 16384); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/gl_compatibility/item_buffer_size", PROPERTY_HINT_RANGE, "128,1048576,1"), 16384); GLOBAL_DEF("rendering/shader_compiler/shader_cache/enabled", true); GLOBAL_DEF("rendering/shader_compiler/shader_cache/compress", true); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 231139e9df..b416ed30e2 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -840,7 +840,14 @@ public: virtual RID viewport_get_texture(RID p_viewport) const = 0; - virtual void viewport_set_disable_environment(RID p_viewport, bool p_disable) = 0; + enum ViewportEnvironmentMode { + VIEWPORT_ENVIRONMENT_DISABLED, + VIEWPORT_ENVIRONMENT_ENABLED, + VIEWPORT_ENVIRONMENT_INHERIT, + VIEWPORT_ENVIRONMENT_MAX, + }; + + virtual void viewport_set_environment_mode(RID p_viewport, ViewportEnvironmentMode p_mode) = 0; virtual void viewport_set_disable_3d(RID p_viewport, bool p_disable) = 0; virtual void viewport_set_disable_2d(RID p_viewport, bool p_disable) = 0; @@ -1639,6 +1646,7 @@ VARIANT_ENUM_CAST(RenderingServer::FogVolumeShape); VARIANT_ENUM_CAST(RenderingServer::ViewportScaling3DMode); VARIANT_ENUM_CAST(RenderingServer::ViewportUpdateMode); VARIANT_ENUM_CAST(RenderingServer::ViewportClearMode); +VARIANT_ENUM_CAST(RenderingServer::ViewportEnvironmentMode); VARIANT_ENUM_CAST(RenderingServer::ViewportMSAA); VARIANT_ENUM_CAST(RenderingServer::ViewportScreenSpaceAA); VARIANT_ENUM_CAST(RenderingServer::ViewportRenderInfo); diff --git a/tests/core/input/test_input_event_mouse.h b/tests/core/input/test_input_event_mouse.h new file mode 100644 index 0000000000..0da4f14160 --- /dev/null +++ b/tests/core/input/test_input_event_mouse.h @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* test_input_event_mouse.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 TEST_INPUT_EVENT_MOUSE_H +#define TEST_INPUT_EVENT_MOUSE_H + +#include "core/input/input_event.h" +#include "tests/test_macros.h" + +namespace TestInputEventMouse { + +TEST_CASE("[InputEventMouse] Mouse button mask is set correctly") { + InputEventMouse mousekey; + + mousekey.set_button_mask(MouseButtonMask::LEFT); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::LEFT)); + + mousekey.set_button_mask(MouseButtonMask::MB_XBUTTON1); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MB_XBUTTON1)); + + mousekey.set_button_mask(MouseButtonMask::MB_XBUTTON2); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MB_XBUTTON2)); + + mousekey.set_button_mask(MouseButtonMask::MIDDLE); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MIDDLE)); + + mousekey.set_button_mask(MouseButtonMask::RIGHT); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::RIGHT)); +} + +TEST_CASE("[InputEventMouse] Setting the mouse position works correctly") { + InputEventMouse mousekey; + + mousekey.set_position(Vector2{ 10, 10 }); + CHECK(mousekey.get_position() == Vector2{ 10, 10 }); + + mousekey.set_position(Vector2{ -1, -1 }); + CHECK(mousekey.get_position() == Vector2{ -1, -1 }); +} + +TEST_CASE("[InputEventMouse] Setting the global mouse position works correctly") { + InputEventMouse mousekey; + + mousekey.set_global_position(Vector2{ 10, 10 }); + CHECK(mousekey.get_global_position() == Vector2{ 10, 10 }); + CHECK(mousekey.get_global_position() != Vector2{ 1, 1 }); + + mousekey.set_global_position(Vector2{ -1, -1 }); + CHECK(mousekey.get_global_position() == Vector2{ -1, -1 }); + CHECK(mousekey.get_global_position() != Vector2{ 1, 1 }); +} +} // namespace TestInputEventMouse + +#endif // TEST_INPUT_EVENT_MOUSE_H diff --git a/tests/core/io/test_file_access.h b/tests/core/io/test_file_access.h index 7117cb137d..243b75626f 100644 --- a/tests/core/io/test_file_access.h +++ b/tests/core/io/test_file_access.h @@ -39,6 +39,7 @@ namespace TestFileAccess { TEST_CASE("[FileAccess] CSV read") { Ref<FileAccess> f = FileAccess::open(TestUtils::get_data_path("translations.csv"), FileAccess::READ); + REQUIRE(!f.is_null()); Vector<String> header = f->get_csv_line(); // Default delimiter: ",". REQUIRE(header.size() == 3); @@ -81,6 +82,7 @@ TEST_CASE("[FileAccess] CSV read") { TEST_CASE("[FileAccess] Get as UTF-8 String") { Ref<FileAccess> f_lf = FileAccess::open(TestUtils::get_data_path("line_endings_lf.test.txt"), FileAccess::READ); + REQUIRE(!f_lf.is_null()); String s_lf = f_lf->get_as_utf8_string(); f_lf->seek(0); String s_lf_nocr = f_lf->get_as_utf8_string(true); @@ -88,6 +90,7 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") { CHECK(s_lf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n"); Ref<FileAccess> f_crlf = FileAccess::open(TestUtils::get_data_path("line_endings_crlf.test.txt"), FileAccess::READ); + REQUIRE(!f_crlf.is_null()); String s_crlf = f_crlf->get_as_utf8_string(); f_crlf->seek(0); String s_crlf_nocr = f_crlf->get_as_utf8_string(true); @@ -95,6 +98,7 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") { CHECK(s_crlf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n"); Ref<FileAccess> f_cr = FileAccess::open(TestUtils::get_data_path("line_endings_cr.test.txt"), FileAccess::READ); + REQUIRE(!f_cr.is_null()); String s_cr = f_cr->get_as_utf8_string(); f_cr->seek(0); String s_cr_nocr = f_cr->get_as_utf8_string(true); diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 3d52bc96d1..763eaed2f8 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -107,6 +107,7 @@ TEST_CASE("[Image] Saving and loading") { // Load BMP Ref<Image> image_bmp = memnew(Image()); Ref<FileAccess> f_bmp = FileAccess::open(TestUtils::get_data_path("images/icon.bmp"), FileAccess::READ, &err); + REQUIRE(!f_bmp.is_null()); PackedByteArray data_bmp; data_bmp.resize(f_bmp->get_length() + 1); f_bmp->get_buffer(data_bmp.ptrw(), f_bmp->get_length()); @@ -117,6 +118,7 @@ TEST_CASE("[Image] Saving and loading") { // Load JPG Ref<Image> image_jpg = memnew(Image()); Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err); + REQUIRE(!f_jpg.is_null()); PackedByteArray data_jpg; data_jpg.resize(f_jpg->get_length() + 1); f_jpg->get_buffer(data_jpg.ptrw(), f_jpg->get_length()); @@ -127,6 +129,7 @@ TEST_CASE("[Image] Saving and loading") { // Load WebP Ref<Image> image_webp = memnew(Image()); Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err); + REQUIRE(!f_webp.is_null()); PackedByteArray data_webp; data_webp.resize(f_webp->get_length() + 1); f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length()); @@ -137,6 +140,7 @@ TEST_CASE("[Image] Saving and loading") { // Load PNG Ref<Image> image_png = memnew(Image()); Ref<FileAccess> f_png = FileAccess::open(TestUtils::get_data_path("images/icon.png"), FileAccess::READ, &err); + REQUIRE(!f_png.is_null()); PackedByteArray data_png; data_png.resize(f_png->get_length() + 1); f_png->get_buffer(data_png.ptrw(), f_png->get_length()); @@ -147,6 +151,7 @@ TEST_CASE("[Image] Saving and loading") { // Load TGA Ref<Image> image_tga = memnew(Image()); Ref<FileAccess> f_tga = FileAccess::open(TestUtils::get_data_path("images/icon.tga"), FileAccess::READ, &err); + REQUIRE(!f_tga.is_null()); PackedByteArray data_tga; data_tga.resize(f_tga->get_length() + 1); f_tga->get_buffer(data_tga.ptrw(), f_tga->get_length()); diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h index dc2b6e2ba8..ca27776180 100644 --- a/tests/core/math/test_transform_2d.h +++ b/tests/core/math/test_transform_2d.h @@ -84,6 +84,19 @@ TEST_CASE("[Transform2D] rotation") { CHECK(orig.rotated_local(phi) == orig * R); } +TEST_CASE("[Transform2D] Interpolation") { + Transform2D rotate_scale_skew_pos = Transform2D(Math::deg_to_rad(170.0), Vector2(3.6, 8.0), Math::deg_to_rad(20.0), Vector2(2.4, 6.8)); + Transform2D rotate_scale_skew_pos_halfway = Transform2D(Math::deg_to_rad(85.0), Vector2(2.3, 4.5), Math::deg_to_rad(10.0), Vector2(1.2, 3.4)); + Transform2D interpolated = Transform2D().interpolate_with(rotate_scale_skew_pos, 0.5); + CHECK(interpolated.get_origin().is_equal_approx(rotate_scale_skew_pos_halfway.get_origin())); + CHECK(interpolated.get_rotation() == doctest::Approx(rotate_scale_skew_pos_halfway.get_rotation())); + CHECK(interpolated.get_scale().is_equal_approx(rotate_scale_skew_pos_halfway.get_scale())); + CHECK(interpolated.get_skew() == doctest::Approx(rotate_scale_skew_pos_halfway.get_skew())); + CHECK(interpolated.is_equal_approx(rotate_scale_skew_pos_halfway)); + interpolated = rotate_scale_skew_pos.interpolate_with(Transform2D(), 0.5); + CHECK(interpolated.is_equal_approx(rotate_scale_skew_pos_halfway)); +} + TEST_CASE("[Transform2D] Finite number checks") { const Vector2 x(0, 1); const Vector2 infinite(NAN, NAN); diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h index 85b53264f2..dfbaa36af8 100644 --- a/tests/core/variant/test_variant.h +++ b/tests/core/variant/test_variant.h @@ -868,6 +868,196 @@ TEST_CASE("[Variant] Basic comparison") { CHECK_NE(Variant(Dictionary()), Variant()); } +TEST_CASE("[Variant] Identity comparison") { + // Value types are compared by value + Variant aabb = AABB(); + CHECK(aabb.identity_compare(aabb)); + CHECK(aabb.identity_compare(AABB())); + CHECK_FALSE(aabb.identity_compare(AABB(Vector3(1, 2, 3), Vector3(1, 2, 3)))); + + Variant basis = Basis(); + CHECK(basis.identity_compare(basis)); + CHECK(basis.identity_compare(Basis())); + CHECK_FALSE(basis.identity_compare(Basis(Quaternion(Vector3(1, 2, 3).normalized(), 45)))); + + Variant bool_var = true; + CHECK(bool_var.identity_compare(bool_var)); + CHECK(bool_var.identity_compare(true)); + CHECK_FALSE(bool_var.identity_compare(false)); + + Variant callable = Callable(); + CHECK(callable.identity_compare(callable)); + CHECK(callable.identity_compare(Callable())); + CHECK_FALSE(callable.identity_compare(Callable(ObjectID(), StringName("lambda")))); + + Variant color = Color(); + CHECK(color.identity_compare(color)); + CHECK(color.identity_compare(Color())); + CHECK_FALSE(color.identity_compare(Color(255, 0, 255))); + + Variant float_var = 1.0; + CHECK(float_var.identity_compare(float_var)); + CHECK(float_var.identity_compare(1.0)); + CHECK_FALSE(float_var.identity_compare(2.0)); + + Variant int_var = 1; + CHECK(int_var.identity_compare(int_var)); + CHECK(int_var.identity_compare(1)); + CHECK_FALSE(int_var.identity_compare(2)); + + Variant nil = Variant(); + CHECK(nil.identity_compare(nil)); + CHECK(nil.identity_compare(Variant())); + CHECK_FALSE(nil.identity_compare(true)); + + Variant node_path = NodePath("godot"); + CHECK(node_path.identity_compare(node_path)); + CHECK(node_path.identity_compare(NodePath("godot"))); + CHECK_FALSE(node_path.identity_compare(NodePath("waiting"))); + + Variant plane = Plane(); + CHECK(plane.identity_compare(plane)); + CHECK(plane.identity_compare(Plane())); + CHECK_FALSE(plane.identity_compare(Plane(Vector3(1, 2, 3), 42))); + + Variant projection = Projection(); + CHECK(projection.identity_compare(projection)); + CHECK(projection.identity_compare(Projection())); + CHECK_FALSE(projection.identity_compare(Projection(Transform3D(Basis(Vector3(1, 2, 3).normalized(), 45), Vector3(1, 2, 3))))); + + Variant quaternion = Quaternion(); + CHECK(quaternion.identity_compare(quaternion)); + CHECK(quaternion.identity_compare(Quaternion())); + CHECK_FALSE(quaternion.identity_compare(Quaternion(Vector3(1, 2, 3).normalized(), 45))); + + Variant rect2 = Rect2(); + CHECK(rect2.identity_compare(rect2)); + CHECK(rect2.identity_compare(Rect2())); + CHECK_FALSE(rect2.identity_compare(Rect2(Point2(Vector2(1, 2)), Size2(Vector2(1, 2))))); + + Variant rect2i = Rect2i(); + CHECK(rect2i.identity_compare(rect2i)); + CHECK(rect2i.identity_compare(Rect2i())); + CHECK_FALSE(rect2i.identity_compare(Rect2i(Point2i(Vector2i(1, 2)), Size2i(Vector2i(1, 2))))); + + Variant rid = RID(); + CHECK(rid.identity_compare(rid)); + CHECK(rid.identity_compare(RID())); + CHECK_FALSE(rid.identity_compare(RID::from_uint64(123))); + + Variant signal = Signal(); + CHECK(signal.identity_compare(signal)); + CHECK(signal.identity_compare(Signal())); + CHECK_FALSE(signal.identity_compare(Signal(ObjectID(), StringName("lambda")))); + + Variant str = "godot"; + CHECK(str.identity_compare(str)); + CHECK(str.identity_compare("godot")); + CHECK_FALSE(str.identity_compare("waiting")); + + Variant str_name = StringName("godot"); + CHECK(str_name.identity_compare(str_name)); + CHECK(str_name.identity_compare(StringName("godot"))); + CHECK_FALSE(str_name.identity_compare(StringName("waiting"))); + + Variant transform2d = Transform2D(); + CHECK(transform2d.identity_compare(transform2d)); + CHECK(transform2d.identity_compare(Transform2D())); + CHECK_FALSE(transform2d.identity_compare(Transform2D(45, Vector2(1, 2)))); + + Variant transform3d = Transform3D(); + CHECK(transform3d.identity_compare(transform3d)); + CHECK(transform3d.identity_compare(Transform3D())); + CHECK_FALSE(transform3d.identity_compare(Transform3D(Basis(Quaternion(Vector3(1, 2, 3).normalized(), 45)), Vector3(1, 2, 3)))); + + Variant vect2 = Vector2(); + CHECK(vect2.identity_compare(vect2)); + CHECK(vect2.identity_compare(Vector2())); + CHECK_FALSE(vect2.identity_compare(Vector2(1, 2))); + + Variant vect2i = Vector2i(); + CHECK(vect2i.identity_compare(vect2i)); + CHECK(vect2i.identity_compare(Vector2i())); + CHECK_FALSE(vect2i.identity_compare(Vector2i(1, 2))); + + Variant vect3 = Vector3(); + CHECK(vect3.identity_compare(vect3)); + CHECK(vect3.identity_compare(Vector3())); + CHECK_FALSE(vect3.identity_compare(Vector3(1, 2, 3))); + + Variant vect3i = Vector3i(); + CHECK(vect3i.identity_compare(vect3i)); + CHECK(vect3i.identity_compare(Vector3i())); + CHECK_FALSE(vect3i.identity_compare(Vector3i(1, 2, 3))); + + Variant vect4 = Vector4(); + CHECK(vect4.identity_compare(vect4)); + CHECK(vect4.identity_compare(Vector4())); + CHECK_FALSE(vect4.identity_compare(Vector4(1, 2, 3, 4))); + + Variant vect4i = Vector4i(); + CHECK(vect4i.identity_compare(vect4i)); + CHECK(vect4i.identity_compare(Vector4i())); + CHECK_FALSE(vect4i.identity_compare(Vector4i(1, 2, 3, 4))); + + // Reference types are compared by reference + Variant array = Array(); + CHECK(array.identity_compare(array)); + CHECK_FALSE(array.identity_compare(Array())); + + Variant dictionary = Dictionary(); + CHECK(dictionary.identity_compare(dictionary)); + CHECK_FALSE(dictionary.identity_compare(Dictionary())); + + Variant packed_byte_array = PackedByteArray(); + CHECK(packed_byte_array.identity_compare(packed_byte_array)); + CHECK_FALSE(packed_byte_array.identity_compare(PackedByteArray())); + + Variant packed_color_array = PackedColorArray(); + CHECK(packed_color_array.identity_compare(packed_color_array)); + CHECK_FALSE(packed_color_array.identity_compare(PackedColorArray())); + + Variant packed_float32_array = PackedFloat32Array(); + CHECK(packed_float32_array.identity_compare(packed_float32_array)); + CHECK_FALSE(packed_float32_array.identity_compare(PackedFloat32Array())); + + Variant packed_float64_array = PackedFloat64Array(); + CHECK(packed_float64_array.identity_compare(packed_float64_array)); + CHECK_FALSE(packed_float64_array.identity_compare(PackedFloat64Array())); + + Variant packed_int32_array = PackedInt32Array(); + CHECK(packed_int32_array.identity_compare(packed_int32_array)); + CHECK_FALSE(packed_int32_array.identity_compare(PackedInt32Array())); + + Variant packed_int64_array = PackedInt64Array(); + CHECK(packed_int64_array.identity_compare(packed_int64_array)); + CHECK_FALSE(packed_int64_array.identity_compare(PackedInt64Array())); + + Variant packed_string_array = PackedStringArray(); + CHECK(packed_string_array.identity_compare(packed_string_array)); + CHECK_FALSE(packed_string_array.identity_compare(PackedStringArray())); + + Variant packed_vector2_array = PackedVector2Array(); + CHECK(packed_vector2_array.identity_compare(packed_vector2_array)); + CHECK_FALSE(packed_vector2_array.identity_compare(PackedVector2Array())); + + Variant packed_vector3_array = PackedVector3Array(); + CHECK(packed_vector3_array.identity_compare(packed_vector3_array)); + CHECK_FALSE(packed_vector3_array.identity_compare(PackedVector3Array())); + + Object obj_one = Object(); + Variant obj_one_var = &obj_one; + Object obj_two = Object(); + Variant obj_two_var = &obj_two; + CHECK(obj_one_var.identity_compare(obj_one_var)); + CHECK_FALSE(obj_one_var.identity_compare(obj_two_var)); + + Variant obj_null_one_var = Variant((Object *)nullptr); + Variant obj_null_two_var = Variant((Object *)nullptr); + CHECK(obj_null_one_var.identity_compare(obj_null_one_var)); + CHECK(obj_null_one_var.identity_compare(obj_null_two_var)); +} + TEST_CASE("[Variant] Nested array comparison") { Array a1 = build_array(1, build_array(2, 3)); Array a2 = build_array(1, build_array(2, 3)); diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 8c563f94ac..c7f2d4cbfb 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -32,6 +32,7 @@ #include "tests/core/config/test_project_settings.h" #include "tests/core/input/test_input_event_key.h" +#include "tests/core/input/test_input_event_mouse.h" #include "tests/core/input/test_shortcut.h" #include "tests/core/io/test_config_file.h" #include "tests/core/io/test_file_access.h" diff --git a/thirdparty/enet/enet/godot_ext.h b/thirdparty/enet/enet/godot_ext.h index 648f3d2f24..06a621b790 100644 --- a/thirdparty/enet/enet/godot_ext.h +++ b/thirdparty/enet/enet/godot_ext.h @@ -11,8 +11,8 @@ */ ENET_API void enet_address_set_ip(ENetAddress * address, const uint8_t * ip, size_t size); -ENET_API int enet_host_dtls_server_setup (ENetHost *, void *, void *); -ENET_API int enet_host_dtls_client_setup (ENetHost *, void *, uint8_t, const char *); +ENET_API int enet_host_dtls_server_setup (ENetHost *, void *); +ENET_API int enet_host_dtls_client_setup (ENetHost *, const char *, void *); ENET_API void enet_host_refuse_new_connections (ENetHost *, int); #endif // __ENET_GODOT_EXT_H__ diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp index 47298dcf6a..ea7f4957a2 100644 --- a/thirdparty/enet/godot.cpp +++ b/thirdparty/enet/godot.cpp @@ -164,16 +164,14 @@ class ENetDTLSClient : public ENetGodotSocket { bool connected = false; Ref<PacketPeerUDP> udp; Ref<PacketPeerDTLS> dtls; - bool verify = false; + Ref<TLSOptions> tls_options; String for_hostname; - Ref<X509Certificate> cert; IPAddress local_address; public: - ENetDTLSClient(ENetUDP *p_base, Ref<X509Certificate> p_cert, bool p_verify, String p_for_hostname) { - verify = p_verify; + ENetDTLSClient(ENetUDP *p_base, String p_for_hostname, Ref<TLSOptions> p_options) { for_hostname = p_for_hostname; - cert = p_cert; + tls_options = p_options; udp.instantiate(); dtls = Ref<PacketPeerDTLS>(PacketPeerDTLS::create()); if (p_base->bound) { @@ -205,7 +203,7 @@ public: Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) { if (!connected) { udp->connect_to_host(p_ip, p_port); - if (dtls->connect_to_peer(udp, verify, for_hostname, cert)) { + if (dtls->connect_to_peer(udp, for_hostname, tls_options)) { return FAILED; } connected = true; @@ -265,7 +263,7 @@ class ENetDTLSServer : public ENetGodotSocket { IPAddress local_address; public: - ENetDTLSServer(ENetUDP *p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert) { + ENetDTLSServer(ENetUDP *p_base, Ref<TLSOptions> p_options) { udp_server.instantiate(); if (p_base->bound) { uint16_t port; @@ -274,7 +272,7 @@ public: bind(local_address, port); } server = Ref<DTLSServer>(DTLSServer::create()); - server->setup(p_key, p_cert); + server->setup(p_options); } ~ENetDTLSServer() { @@ -437,22 +435,22 @@ ENetSocket enet_socket_create(ENetSocketType type) { return socket; } -int enet_host_dtls_server_setup(ENetHost *host, void *p_key, void *p_cert) { +int enet_host_dtls_server_setup(ENetHost *host, void *p_options) { ENetGodotSocket *sock = (ENetGodotSocket *)host->socket; if (!sock->can_upgrade()) { return -1; } - host->socket = memnew(ENetDTLSServer((ENetUDP *)sock, Ref<CryptoKey>((CryptoKey *)p_key), Ref<X509Certificate>((X509Certificate *)p_cert))); + host->socket = memnew(ENetDTLSServer(static_cast<ENetUDP *>(sock), Ref<TLSOptions>(static_cast<TLSOptions *>(p_options)))); memdelete(sock); return 0; } -int enet_host_dtls_client_setup(ENetHost *host, void *p_cert, uint8_t p_verify, const char *p_for_hostname) { +int enet_host_dtls_client_setup(ENetHost *host, const char *p_for_hostname, void *p_options) { ENetGodotSocket *sock = (ENetGodotSocket *)host->socket; if (!sock->can_upgrade()) { return -1; } - host->socket = memnew(ENetDTLSClient((ENetUDP *)sock, Ref<X509Certificate>((X509Certificate *)p_cert), p_verify, String::utf8(p_for_hostname))); + host->socket = memnew(ENetDTLSClient(static_cast<ENetUDP *>(sock), String::utf8(p_for_hostname), Ref<TLSOptions>(static_cast<TLSOptions *>(p_options)))); memdelete(sock); return 0; } |