diff options
Diffstat (limited to 'modules')
169 files changed, 9017 insertions, 7363 deletions
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 2070e0e633..cf8549030d 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -76,13 +76,13 @@ private: public: BulletPhysicsDirectSpaceState(SpaceBullet *p_space); - virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) override; - virtual int intersect_shape(const RID &p_shape, const Transform3D &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool cast_motion(const RID &p_shape, const Transform3D &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &r_closest_safe, real_t &r_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) override; + virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) override; + virtual int intersect_shape(const RID &p_shape, const Transform3D &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool cast_motion(const RID &p_shape, const Transform3D &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &r_closest_safe, real_t &r_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) override; /// Returns the list of contacts pairs in this order: Local contact, other body contact - virtual bool collide_shape(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; - virtual bool rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool collide_shape(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; + virtual bool rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override; virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const override; }; diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index 3a37b396a4..79dc77d41d 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -81,7 +81,7 @@ <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true"> Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server. </member> - <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" /> + <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" /> </members> <constants> </constants> diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index 38ca38385c..afd31207f6 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -41,11 +41,11 @@ int ENetMultiplayerPeer::get_transfer_channel() const { return transfer_channel; } -void ENetMultiplayerPeer::set_transfer_mode(TransferMode p_mode) { +void ENetMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { transfer_mode = p_mode; } -MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_transfer_mode() const { +Multiplayer::TransferMode ENetMultiplayerPeer::get_transfer_mode() const { return transfer_mode; } @@ -393,7 +393,9 @@ bool ENetMultiplayerPeer::is_server() const { } void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) { - ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active."); + if (!_is_active()) { + return; + } _pop_current_packet(); @@ -453,15 +455,15 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size channel = SYSCH_MAX + transfer_channel - 1; } else { switch (transfer_mode) { - case TRANSFER_MODE_UNRELIABLE: { + case Multiplayer::TRANSFER_MODE_UNRELIABLE: { packet_flags = ENET_PACKET_FLAG_UNSEQUENCED; channel = SYSCH_UNRELIABLE; } break; - case TRANSFER_MODE_UNRELIABLE_ORDERED: { + case Multiplayer::TRANSFER_MODE_ORDERED: { packet_flags = 0; channel = SYSCH_UNRELIABLE; } break; - case TRANSFER_MODE_RELIABLE: { + case Multiplayer::TRANSFER_MODE_RELIABLE: { packet_flags = ENET_PACKET_FLAG_RELIABLE; channel = SYSCH_RELIABLE; } break; diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h index 78e280db7c..b5316b8292 100644 --- a/modules/enet/enet_multiplayer_peer.h +++ b/modules/enet/enet_multiplayer_peer.h @@ -32,7 +32,7 @@ #define NETWORKED_MULTIPLAYER_ENET_H #include "core/crypto/crypto.h" -#include "core/io/multiplayer_peer.h" +#include "core/multiplayer/multiplayer_peer.h" #include "enet_connection.h" #include <enet/enet.h> @@ -66,7 +66,7 @@ private: int target_peer = 0; int transfer_channel = 0; - TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; + Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; bool refuse_connections = false; bool server_relay = true; @@ -104,8 +104,8 @@ public: virtual void set_transfer_channel(int p_channel) override; virtual int get_transfer_channel() const override; - virtual void set_transfer_mode(TransferMode p_mode) override; - virtual TransferMode get_transfer_mode() const override; + virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override; + virtual Multiplayer::TransferMode get_transfer_mode() const override; virtual void set_target_peer(int p_peer) override; virtual int get_packet_peer() const override; diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 45354ce692..21ee39f3ed 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -17,7 +17,6 @@ env_gdnative.Prepend(CPPPATH=["#modules/gdnative/include/"]) Export("env_gdnative") SConscript("net/SCsub") -SConscript("xr/SCsub") SConscript("pluginscript/SCsub") SConscript("videodecoder/SCsub") SConscript("text/SCsub") diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index fd860e9763..fa985501b5 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -8,7 +8,6 @@ def configure(env): def get_doc_classes(): return [ - "XRInterfaceGDNative", "GDNative", "GDNativeLibrary", "MultiplayerPeerGDNative", diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index f84d4e60f3..94eae3cd06 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -4,7 +4,7 @@ An external library containing functions or script classes to use in Godot. </brief_description> <description> - A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [XRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on. + A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as XRInterfaceGDNative. The library must be compiled for each platform and architecture that the project will run on. </description> <tutorials> <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link> diff --git a/modules/gdnative/doc_classes/XRInterfaceGDNative.xml b/modules/gdnative/doc_classes/XRInterfaceGDNative.xml deleted file mode 100644 index 13de815793..0000000000 --- a/modules/gdnative/doc_classes/XRInterfaceGDNative.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="XRInterfaceGDNative" inherits="XRInterface" version="4.0"> - <brief_description> - GDNative wrapper for an XR interface. - </brief_description> - <description> - This is a wrapper class for GDNative implementations of the XR interface. To use a GDNative XR interface, simply instantiate this object and set your GDNative library containing the XR interface implementation. - </description> - <tutorials> - </tutorials> - <methods> - </methods> - <constants> - </constants> -</class> diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 8c65447e5d..66d2dc267d 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5048,169 +5048,6 @@ ] }, { - "name": "xr", - "type": "XR", - "version": { - "major": 1, - "minor": 1 - }, - "next": null, - "api": [ - { - "name": "godot_xr_register_interface", - "return_type": "void", - "arguments": [ - [ - "const godot_xr_interface_gdnative *", - "p_interface" - ] - ] - }, - { - "name": "godot_xr_get_worldscale", - "return_type": "godot_real_t", - "arguments": [] - }, - { - "name": "godot_xr_get_reference_frame", - "return_type": "godot_transform3d", - "arguments": [] - }, - { - "name": "godot_xr_blit", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_eye" - ], - [ - "godot_rid *", - "p_render_target" - ], - [ - "godot_rect2 *", - "p_screen_rect" - ] - ] - }, - { - "name": "godot_xr_get_texid", - "return_type": "godot_int", - "arguments": [ - [ - "godot_rid *", - "p_render_target" - ] - ] - }, - { - "name": "godot_xr_add_controller", - "return_type": "godot_int", - "arguments": [ - [ - "char *", - "p_device_name" - ], - [ - "godot_int", - "p_hand" - ], - [ - "godot_bool", - "p_tracks_orientation" - ], - [ - "godot_bool", - "p_tracks_position" - ] - ] - }, - { - "name": "godot_xr_remove_controller", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ] - ] - }, - { - "name": "godot_xr_set_controller_transform", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ], - [ - "godot_transform3d *", - "p_transform" - ], - [ - "godot_bool", - "p_tracks_orientation" - ], - [ - "godot_bool", - "p_tracks_position" - ] - ] - }, - { - "name": "godot_xr_set_controller_button", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ], - [ - "godot_int", - "p_button" - ], - [ - "godot_bool", - "p_is_pressed" - ] - ] - }, - { - "name": "godot_xr_set_controller_axis", - "return_type": "void", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ], - [ - "godot_int", - "p_exis" - ], - [ - "godot_real_t", - "p_value" - ], - [ - "godot_bool", - "p_can_be_negative" - ] - ] - }, - { - "name": "godot_xr_get_controller_rumble", - "return_type": "godot_real_t", - "arguments": [ - [ - "godot_int", - "p_controller_id" - ] - ] - } - ] - }, - { "name": "videodecoder", "type": "VIDEODECODER", "version": { @@ -5517,7 +5354,7 @@ }, { "name": "godot_glyph_get_advance", - "return_type": "godot_float", + "return_type": "godot_real_t", "arguments": [ [ "const godot_glyph *", @@ -5534,7 +5371,7 @@ "p_self" ], [ - "godot_float", + "godot_real_t", "p_advance" ] ] diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py index d03298d7a9..181fd71b82 100644 --- a/modules/gdnative/gdnative_builders.py +++ b/modules/gdnative/gdnative_builders.py @@ -19,7 +19,6 @@ def _build_gdnative_api_struct_header(api): "", "#include <gdnative/gdnative.h>", "#include <android/godot_android.h>", - "#include <xr/godot_xr.h>", "#include <nativescript/godot_nativescript.h>", "#include <net/godot_net.h>", "#include <pluginscript/godot_pluginscript.h>", diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index c97f5f0389..09eac2492f 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -39,12 +39,8 @@ extern "C" { typedef enum { GODOT_METHOD_RPC_MODE_DISABLED, - GODOT_METHOD_RPC_MODE_REMOTE, - GODOT_METHOD_RPC_MODE_MASTER, - GODOT_METHOD_RPC_MODE_PUPPET, - GODOT_METHOD_RPC_MODE_REMOTESYNC, - GODOT_METHOD_RPC_MODE_MASTERSYNC, - GODOT_METHOD_RPC_MODE_PUPPETSYNC, + GODOT_METHOD_RPC_MODE_ANY, + GODOT_METHOD_RPC_MODE_AUTHORITY, } godot_nativescript_method_rpc_mode; typedef enum { diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h index 6428f2f149..940cfd11f8 100644 --- a/modules/gdnative/include/text/godot_text.h +++ b/modules/gdnative/include/text/godot_text.h @@ -60,68 +60,109 @@ typedef struct { typedef struct { godot_gdnative_api_version version; + void *(*constructor)(godot_object *); void (*destructor)(void *); + godot_string (*get_name)(const void *); godot_bool (*has_feature)(const void *, godot_int); + + void (*free)(void *, godot_rid *); + bool (*has)(void *, godot_rid *); + bool (*load_support_data)(void *, const godot_string *); godot_string (*get_support_data_filename)(const void *); godot_string (*get_support_data_info)(const void *); bool (*save_support_data)(void *, const godot_string *); + bool (*is_locale_right_to_left)(void *, const godot_string *); - void (*free)(void *, godot_rid *); - bool (*has)(void *, godot_rid *); - godot_rid (*create_font_system)(void *, const godot_string *, int); - godot_rid (*create_font_resource)(void *, const godot_string *, int); - godot_rid (*create_font_memory)(void *, const uint8_t *, size_t, godot_string *, int); - godot_rid (*create_font_bitmap)(void *, float, float, int); - void (*font_bitmap_add_texture)(void *, godot_rid *, const godot_object *); - void (*font_bitmap_add_char)(void *, godot_rid *, char32_t, int, const godot_rect2 *, const godot_vector2 *, float); - void (*font_bitmap_add_kerning_pair)(void *, godot_rid *, char32_t, char32_t, int); - float (*font_get_height)(void *, godot_rid *, int); - float (*font_get_ascent)(void *, godot_rid *, int); - float (*font_get_descent)(void *, godot_rid *, int); - float (*font_get_underline_position)(void *, godot_rid *, int); - float (*font_get_underline_thickness)(void *, godot_rid *, int); - int (*font_get_spacing_space)(void *, godot_rid *); - void (*font_set_spacing_space)(void *, godot_rid *, int); - int (*font_get_spacing_glyph)(void *, godot_rid *); - void (*font_set_spacing_glyph)(void *, godot_rid *, int); + int32_t (*name_to_tag)(const void *, const godot_string *); + godot_string (*tag_to_name)(const void *, int32_t); + + godot_rid (*create_font)(void *); + void (*font_set_data)(void *, godot_rid *, const godot_packed_byte_array *); + void (*font_set_data_ptr)(void *, godot_rid *, const uint8_t *, size_t); void (*font_set_antialiased)(void *, godot_rid *, bool); - bool (*font_get_antialiased)(void *, godot_rid *); - godot_dictionary (*font_get_feature_list)(void *, godot_rid *); - godot_dictionary (*font_get_variation_list)(void *, godot_rid *); - void (*font_set_variation)(void *, godot_rid *, const godot_string *, double); - double (*font_get_variation)(void *, godot_rid *, const godot_string *); - void (*font_set_distance_field_hint)(void *, godot_rid *, bool); - bool (*font_get_distance_field_hint)(void *, godot_rid *); - void (*font_set_hinting)(void *, godot_rid *, godot_int); - godot_int (*font_get_hinting)(void *, godot_rid *); + bool (*font_is_antialiased)(const void *, godot_rid *); + void (*font_set_multichannel_signed_distance_field)(void *, godot_rid *, bool); + bool (*font_is_multichannel_signed_distance_field)(const void *, godot_rid *); + void (*font_set_msdf_pixel_range)(void *, godot_rid *, godot_int); + godot_int (*font_get_msdf_pixel_range)(const void *, godot_rid *); + void (*font_set_msdf_size)(void *, godot_rid *, godot_int); + godot_int (*font_get_msdf_size)(const void *, godot_rid *); + void (*font_set_fixed_size)(void *, godot_rid *, godot_int); + godot_int (*font_get_fixed_size)(const void *, godot_rid *); void (*font_set_force_autohinter)(void *, godot_rid *, bool); - bool (*font_get_force_autohinter)(void *, godot_rid *); - bool (*font_has_char)(void *, godot_rid *, char32_t); - godot_string (*font_get_supported_chars)(void *, godot_rid *); - bool (*font_has_outline)(void *, godot_rid *); - int (*font_get_base_size)(void *, godot_rid *); - bool (*font_is_language_supported)(void *, godot_rid *, const godot_string *); + bool (*font_is_force_autohinter)(const void *, godot_rid *); + void (*font_set_hinting)(void *, godot_rid *, godot_int); + godot_int (*font_get_hinting)(const void *, godot_rid *); + void (*font_set_variation_coordinates)(void *, godot_rid *, const godot_dictionary *); + godot_dictionary (*font_get_variation_coordinates)(const void *, godot_rid *); + void (*font_set_oversampling)(void *, godot_rid *, godot_real_t); + godot_real_t (*font_get_oversampling)(const void *, godot_rid *); + godot_array (*font_get_size_cache_list)(const void *, godot_rid *); + void (*font_clear_size_cache)(void *, godot_rid *); + void (*font_remove_size_cache)(void *, godot_rid *, const godot_vector2i *); + void (*font_set_ascent)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_ascent)(const void *, godot_rid *, godot_int); + void (*font_set_descent)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_descent)(const void *, godot_rid *, godot_int); + void (*font_set_underline_position)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_underline_position)(const void *, godot_rid *, godot_int); + void (*font_set_underline_thickness)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_underline_thickness)(const void *, godot_rid *, godot_int); + void (*font_set_scale)(void *, godot_rid *, godot_int, godot_real_t); + godot_real_t (*font_get_scale)(const void *, godot_rid *, godot_int); + void (*font_set_spacing)(void *, godot_rid *, godot_int, godot_int, godot_int); + godot_int (*font_get_spacing)(const void *, godot_rid *, godot_int, godot_int); + godot_int (*font_get_texture_count)(const void *, godot_rid *, const godot_vector2i *); + void (*font_clear_textures)(void *, godot_rid *, const godot_vector2i *); + void (*font_remove_texture)(void *, godot_rid *, const godot_vector2i *, godot_int); + void (*font_set_texture_image)(void *, godot_rid *, const godot_vector2i *, godot_int, const godot_object *); + godot_object *(*font_get_texture_image)(const void *, godot_rid *, const godot_vector2i *, godot_int); + void (*font_set_texture_offsets)(void *, godot_rid *, const godot_vector2i *, godot_int, const godot_packed_int32_array *); + godot_packed_int32_array (*font_get_texture_offsets)(const void *, godot_rid *, const godot_vector2i *, godot_int); + godot_array (*font_get_glyph_list)(const void *, godot_rid *, const godot_vector2i *); + void (*font_clear_glyphs)(void *, godot_rid *, const godot_vector2i *); + void (*font_remove_glyph)(void *, godot_rid *, const godot_vector2i *, int32_t); + godot_vector2 (*font_get_glyph_advance)(const void *, godot_rid *, godot_int, int32_t); + void (*font_set_glyph_advance)(void *, godot_rid *, godot_int, int32_t, const godot_vector2 *); + godot_vector2 (*font_get_glyph_offset)(const void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_set_glyph_offset)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_vector2 *); + godot_vector2 (*font_get_glyph_size)(const void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_set_glyph_size)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_vector2 *); + godot_rect2 (*font_get_glyph_uv_rect)(const void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_set_glyph_uv_rect)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_rect2 *); + godot_int (*font_get_glyph_texture_idx)(const void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_set_glyph_texture_idx)(void *, godot_rid *, const godot_vector2i *, int32_t, godot_int); + bool (*font_get_glyph_contours)(const void *, godot_rid *, godot_int, int32_t, godot_packed_vector3_array *, godot_packed_int32_array *, bool *); + godot_array (*font_get_kerning_list)(const void *, godot_rid *, godot_int); + void (*font_clear_kerning_map)(void *, godot_rid *, godot_int); + void (*font_remove_kerning)(void *, godot_rid *, godot_int, const godot_vector2i *); + void (*font_set_kerning)(void *, godot_rid *, godot_int, const godot_vector2i *, const godot_vector2 *); + godot_vector2 (*font_get_kerning)(const void *, godot_rid *, godot_int, const godot_vector2i *); + int32_t (*font_get_glyph_index)(const void *, godot_rid *, godot_int, char32_t, char32_t); + bool (*font_has_char)(const void *, godot_rid *, char32_t); + godot_string (*font_get_supported_chars)(const void *, godot_rid *); + void (*font_render_range)(void *, godot_rid *, const godot_vector2i *, char32_t, char32_t); + void (*font_render_glyph)(void *, godot_rid *, const godot_vector2i *, int32_t); + void (*font_draw_glyph)(const void *, godot_rid *, godot_rid *, godot_int, const godot_vector2 *, int32_t, const godot_color *); + void (*font_draw_glyph_outline)(const void *, godot_rid *, godot_rid *, godot_int, godot_int, const godot_vector2 *, int32_t, const godot_color *); + bool (*font_is_language_supported)(const void *, godot_rid *, const godot_string *); void (*font_set_language_support_override)(void *, godot_rid *, const godot_string *, bool); - bool (*font_get_language_support_override)(void *, godot_rid *, const godot_string *); + bool (*font_get_language_support_override)(const void *, godot_rid *, const godot_string *); void (*font_remove_language_support_override)(void *, godot_rid *, const godot_string *); - godot_packed_string_array (*font_get_language_support_overrides)(void *, godot_rid *); - bool (*font_is_script_supported)(void *, godot_rid *, const godot_string *); + godot_packed_string_array (*font_get_language_support_overrides)(const void *, godot_rid *); + bool (*font_is_script_supported)(const void *, godot_rid *, const godot_string *); void (*font_set_script_support_override)(void *, godot_rid *, const godot_string *, bool); - bool (*font_get_script_support_override)(void *, godot_rid *, const godot_string *); + bool (*font_get_script_support_override)(const void *, godot_rid *, const godot_string *); void (*font_remove_script_support_override)(void *, godot_rid *, const godot_string *); - godot_packed_string_array (*font_get_script_support_overrides)(void *, godot_rid *); - uint32_t (*font_get_glyph_index)(void *, godot_rid *, char32_t, char32_t); - godot_vector2 (*font_get_glyph_advance)(void *, godot_rid *, uint32_t, int); - godot_vector2 (*font_get_glyph_kerning)(void *, godot_rid *, uint32_t, uint32_t, int); - godot_vector2 (*font_draw_glyph)(void *, godot_rid *, godot_rid *, int, const godot_vector2 *, uint32_t, const godot_color *); - godot_vector2 (*font_draw_glyph_outline)(void *, godot_rid *, godot_rid *, int, int, const godot_vector2 *, uint32_t, const godot_color *); - bool (*font_get_glyph_contours)(void *, godot_rid *, int, uint32_t, godot_packed_vector3_array *, godot_packed_int32_array *, bool *); - float (*font_get_oversampling)(void *); - void (*font_set_oversampling)(void *, float); - godot_packed_string_array (*get_system_fonts)(void *); + godot_packed_string_array (*font_get_script_support_overrides)(const void *, godot_rid *); + godot_dictionary (*font_supported_feature_list)(const void *, godot_rid *); + godot_dictionary (*font_supported_variation_list)(const void *, godot_rid *); + godot_real_t (*font_get_global_oversampling)(const void *); + void (*font_set_global_oversampling)(void *, godot_real_t); + godot_rid (*create_shaped_text)(void *, godot_int, godot_int); void (*shaped_text_clear)(void *, godot_rid *); void (*shaped_text_set_direction)(void *, godot_rid *, godot_int); @@ -138,27 +179,28 @@ typedef struct { bool (*shaped_text_resize_object)(void *, godot_rid *, const godot_variant *, const godot_vector2 *, godot_int); godot_rid (*shaped_text_substr)(void *, godot_rid *, godot_int, godot_int); godot_rid (*shaped_text_get_parent)(void *, godot_rid *); - float (*shaped_text_fit_to_width)(void *, godot_rid *, float, uint8_t); - float (*shaped_text_tab_align)(void *, godot_rid *, godot_packed_float32_array *); + godot_real_t (*shaped_text_fit_to_width)(void *, godot_rid *, godot_real_t, uint8_t); + godot_real_t (*shaped_text_tab_align)(void *, godot_rid *, godot_packed_float32_array *); bool (*shaped_text_shape)(void *, godot_rid *); bool (*shaped_text_update_breaks)(void *, godot_rid *); bool (*shaped_text_update_justification_ops)(void *, godot_rid *); - void (*shaped_text_overrun_trim_to_width)(void *, godot_rid *, float, uint8_t); + void (*shaped_text_overrun_trim_to_width)(void *, godot_rid *, godot_real_t, uint8_t); bool (*shaped_text_is_ready)(void *, godot_rid *); godot_packed_glyph_array (*shaped_text_get_glyphs)(void *, godot_rid *); godot_vector2i (*shaped_text_get_range)(void *, godot_rid *); godot_packed_glyph_array (*shaped_text_sort_logical)(void *, godot_rid *); godot_packed_vector2i_array (*shaped_text_get_line_breaks_adv)(void *, godot_rid *, godot_packed_float32_array *, int, bool, uint8_t); - godot_packed_vector2i_array (*shaped_text_get_line_breaks)(void *, godot_rid *, float, int, uint8_t); + godot_packed_vector2i_array (*shaped_text_get_line_breaks)(void *, godot_rid *, godot_real_t, int, uint8_t); godot_packed_vector2i_array (*shaped_text_get_word_breaks)(void *, godot_rid *, int); godot_array (*shaped_text_get_objects)(void *, godot_rid *); godot_rect2 (*shaped_text_get_object_rect)(void *, godot_rid *, const godot_variant *); godot_vector2 (*shaped_text_get_size)(void *, godot_rid *); - float (*shaped_text_get_ascent)(void *, godot_rid *); - float (*shaped_text_get_descent)(void *, godot_rid *); - float (*shaped_text_get_width)(void *, godot_rid *); - float (*shaped_text_get_underline_position)(void *, godot_rid *); - float (*shaped_text_get_underline_thickness)(void *, godot_rid *); + godot_real_t (*shaped_text_get_ascent)(void *, godot_rid *); + godot_real_t (*shaped_text_get_descent)(void *, godot_rid *); + godot_real_t (*shaped_text_get_width)(void *, godot_rid *); + godot_real_t (*shaped_text_get_underline_position)(void *, godot_rid *); + godot_real_t (*shaped_text_get_underline_thickness)(void *, godot_rid *); + godot_string (*format_number)(void *, const godot_string *, const godot_string *); godot_string (*parse_number)(void *, const godot_string *, const godot_string *); godot_string (*percent_sign)(void *, const godot_string *); @@ -185,8 +227,8 @@ void GDAPI godot_glyph_set_flags(godot_glyph *p_self, godot_int p_flags); godot_vector2 GDAPI godot_glyph_get_offset(const godot_glyph *p_self); void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_offset); -godot_float GDAPI godot_glyph_get_advance(const godot_glyph *p_self); -void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_float p_advance); +godot_real_t GDAPI godot_glyph_get_advance(const godot_glyph *p_self); +void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real_t p_advance); godot_rid GDAPI godot_glyph_get_font(const godot_glyph *p_self); void GDAPI godot_glyph_set_font(godot_glyph *p_self, godot_rid *p_font); diff --git a/modules/gdnative/include/xr/godot_xr.h b/modules/gdnative/include/xr/godot_xr.h deleted file mode 100644 index 53cb830cbb..0000000000 --- a/modules/gdnative/include/xr/godot_xr.h +++ /dev/null @@ -1,98 +0,0 @@ -/*************************************************************************/ -/* godot_xr.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GODOT_NATIVEXR_H -#define GODOT_NATIVEXR_H - -#include <gdnative/gdnative.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// For future versions of the API we should only add new functions at the end of the structure and use the -// version info to detect whether a call is available - -// Use these to populate version in your plugin -#define GODOTVR_API_MAJOR 4 -#define GODOTVR_API_MINOR 0 - -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - void *(*constructor)(godot_object *); - void (*destructor)(void *); - godot_string (*get_name)(const void *); - godot_int (*get_capabilities)(const void *); - godot_bool (*get_anchor_detection_is_enabled)(const void *); - void (*set_anchor_detection_is_enabled)(void *, godot_bool); - godot_int (*get_view_count)(const void *); - godot_bool (*is_initialized)(const void *); - godot_bool (*initialize)(void *); - void (*uninitialize)(void *); - godot_vector2 (*get_render_targetsize)(const void *); - - godot_transform3d (*get_camera_transform)(void *); - godot_transform3d (*get_transform_for_view)(void *, godot_int, godot_transform3d *); - void (*fill_projection_for_view)(void *, godot_real_t *, godot_int, godot_real_t, godot_real_t, godot_real_t); - void (*commit_views)(void *, godot_rid *, godot_rect2 *); - - void (*process)(void *); - void (*notification)(void *, godot_int); - godot_int (*get_camera_feed_id)(void *); - - // possibly deprecate but adding/keeping as a reminder these are in Godot 3 - void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); - godot_int (*get_external_texture_for_eye)(void *, godot_int); - godot_int (*get_external_depth_for_eye)(void *, godot_int); -} godot_xr_interface_gdnative; - -void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface); - -// helper functions to access XRServer data -godot_real_t GDAPI godot_xr_get_worldscale(); -godot_transform3d GDAPI godot_xr_get_reference_frame(); - -// helper functions for rendering -void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect); -godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target); - -// helper functions for updating XR controllers -godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position); -void GDAPI godot_xr_remove_controller(godot_int p_controller_id); -void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform3d *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position); -void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed); -void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative); -godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id); - -#ifdef __cplusplus -} -#endif - -#endif /* !GODOT_NATIVEXR_H */ diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp index 70b14836bf..dadd1a9d10 100644 --- a/modules/gdnative/nativescript/godot_nativescript.cpp +++ b/modules/gdnative/nativescript/godot_nativescript.cpp @@ -127,9 +127,9 @@ void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const cha E->get().methods.insert(p_function_name, method); if (p_attr.rpc_type != GODOT_METHOD_RPC_MODE_DISABLED) { - MultiplayerAPI::RPCConfig nd; + Multiplayer::RPCConfig nd; nd.name = String(p_name); - nd.rpc_mode = MultiplayerAPI::RPCMode(p_attr.rpc_type); + nd.rpc_mode = Multiplayer::RPCMode(p_attr.rpc_type); E->get().rpc_methods.push_back(nd); } } diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 26d3aed702..92ba9bd452 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -431,9 +431,9 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const { } } -const Vector<MultiplayerAPI::RPCConfig> NativeScript::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> NativeScript::get_rpc_methods() const { NativeScriptDesc *script_data = get_script_desc(); - ERR_FAIL_COND_V(!script_data, Vector<MultiplayerAPI::RPCConfig>()); + ERR_FAIL_COND_V(!script_data, Vector<Multiplayer::RPCConfig>()); return script_data->rpc_methods; } @@ -828,7 +828,7 @@ Ref<Script> NativeScriptInstance::get_script() const { return script; } -const Vector<MultiplayerAPI::RPCConfig> NativeScriptInstance::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> NativeScriptInstance::get_rpc_methods() const { return script->get_rpc_methods(); } diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 777a878660..a7647e8c59 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -71,7 +71,7 @@ struct NativeScriptDesc { }; Map<StringName, Method> methods; - Vector<MultiplayerAPI::RPCConfig> rpc_methods; + Vector<Multiplayer::RPCConfig> rpc_methods; OrderedHashMap<StringName, Property> properties; Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals StringName base; @@ -175,7 +175,7 @@ public: virtual void get_script_method_list(List<MethodInfo> *p_list) const override; virtual void get_script_property_list(List<PropertyInfo> *p_list) const override; - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override; String get_class_documentation() const; String get_method_documentation(const StringName &p_method) const; @@ -213,7 +213,7 @@ public: String to_string(bool *r_valid); virtual Ref<Script> get_script() const; - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const; virtual ScriptLanguage *get_language(); diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp index 9908ed4533..575d5f5060 100644 --- a/modules/gdnative/net/multiplayer_peer_gdnative.cpp +++ b/modules/gdnative/net/multiplayer_peer_gdnative.cpp @@ -72,14 +72,14 @@ int MultiplayerPeerGDNative::get_transfer_channel() const { return interface->get_transfer_channel(interface->data); } -void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) { +void MultiplayerPeerGDNative::set_transfer_mode(Multiplayer::TransferMode p_mode) { ERR_FAIL_COND(interface == nullptr); interface->set_transfer_mode(interface->data, (godot_int)p_mode); } -MultiplayerPeer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const { - ERR_FAIL_COND_V(interface == nullptr, TRANSFER_MODE_UNRELIABLE); - return (TransferMode)interface->get_transfer_mode(interface->data); +Multiplayer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const { + ERR_FAIL_COND_V(interface == nullptr, Multiplayer::TRANSFER_MODE_UNRELIABLE); + return (Multiplayer::TransferMode)interface->get_transfer_mode(interface->data); } void MultiplayerPeerGDNative::set_target_peer(int p_peer_id) { @@ -124,7 +124,7 @@ MultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status void MultiplayerPeerGDNative::_bind_methods() { ADD_PROPERTY_DEFAULT("transfer_channel", 0); - ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_UNRELIABLE); + ADD_PROPERTY_DEFAULT("transfer_mode", Multiplayer::TRANSFER_MODE_UNRELIABLE); ADD_PROPERTY_DEFAULT("refuse_new_connections", true); } diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h index ab084faae6..33e424d284 100644 --- a/modules/gdnative/net/multiplayer_peer_gdnative.h +++ b/modules/gdnative/net/multiplayer_peer_gdnative.h @@ -31,7 +31,7 @@ #ifndef MULTIPLAYER_PEER_GDNATIVE_H #define MULTIPLAYER_PEER_GDNATIVE_H -#include "core/io/multiplayer_peer.h" +#include "core/multiplayer/multiplayer_peer.h" #include "modules/gdnative/gdnative.h" #include "modules/gdnative/include/net/godot_net.h" @@ -58,8 +58,8 @@ public: /* Specific to MultiplayerPeer */ virtual void set_transfer_channel(int p_channel) override; virtual int get_transfer_channel() const override; - virtual void set_transfer_mode(TransferMode p_mode) override; - virtual TransferMode get_transfer_mode() const override; + virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override; + virtual Multiplayer::TransferMode get_transfer_mode() const override; virtual void set_target_peer(int p_peer_id) override; virtual int get_packet_peer() const override; diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp index ed1c0af3ed..feae81397e 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.cpp +++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp @@ -100,7 +100,7 @@ String PluginScriptInstance::to_string(bool *r_valid) { return str_ret; } -const Vector<MultiplayerAPI::RPCConfig> PluginScriptInstance::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> PluginScriptInstance::get_rpc_methods() const { return _script->get_rpc_methods(); } diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h index 25b62ae8ab..bdae265db2 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.h +++ b/modules/gdnative/pluginscript/pluginscript_instance.h @@ -71,7 +71,7 @@ public: void set_path(const String &p_path); - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const; virtual void refcount_incremented(); virtual bool refcount_decremented(); diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 5380858582..2b4ceda49d 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -334,9 +334,9 @@ Error PluginScript::reload(bool p_keep_state) { // rpc_mode is passed as an optional field and is not part of MethodInfo Variant var = v["rpc_mode"]; if (var != Variant()) { - MultiplayerAPI::RPCConfig nd; + Multiplayer::RPCConfig nd; nd.name = mi.name; - nd.rpc_mode = MultiplayerAPI::RPCMode(int(var)); + nd.rpc_mode = Multiplayer::RPCMode(int(var)); // TODO Transfer Channel if (_rpc_methods.find(nd) == -1) { _rpc_methods.push_back(nd); @@ -345,7 +345,7 @@ Error PluginScript::reload(bool p_keep_state) { } // Sort so we are 100% that they are always the same. - _rpc_methods.sort_custom<MultiplayerAPI::SortRPCConfig>(); + _rpc_methods.sort_custom<Multiplayer::SortRPCConfig>(); Array *signals = (Array *)&manifest.signals; for (int i = 0; i < signals->size(); ++i) { @@ -484,7 +484,7 @@ int PluginScript::get_member_line(const StringName &p_member) const { return -1; } -const Vector<MultiplayerAPI::RPCConfig> PluginScript::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> PluginScript::get_rpc_methods() const { return _rpc_methods; } diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h index 838195147f..1a12a130d1 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.h +++ b/modules/gdnative/pluginscript/pluginscript_script.h @@ -61,7 +61,7 @@ private: Map<StringName, PropertyInfo> _properties_info; Map<StringName, MethodInfo> _signals_info; Map<StringName, MethodInfo> _methods_info; - Vector<MultiplayerAPI::RPCConfig> _rpc_methods; + Vector<Multiplayer::RPCConfig> _rpc_methods; Set<Object *> _instances; //exported members @@ -136,7 +136,7 @@ public: virtual int get_member_line(const StringName &p_member) const override; - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override; PluginScript(); void init(PluginScriptLanguage *language); diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index a41c4f7b19..e4c2b20224 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -38,7 +38,6 @@ #include "net/register_types.h" #include "pluginscript/register_types.h" #include "videodecoder/register_types.h" -#include "xr/register_types.h" #include "core/config/engine.h" #include "core/config/project_settings.h" @@ -267,7 +266,6 @@ void register_gdnative_types() { GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall); register_net_types(); - register_xr_types(); register_nativescript_types(); register_pluginscript_types(); register_videodecoder_types(); @@ -331,7 +329,6 @@ void unregister_gdnative_types() { unregister_videodecoder_types(); unregister_pluginscript_types(); unregister_nativescript_types(); - unregister_xr_types(); unregister_net_types(); memdelete(GDNativeCallRegistry::singleton); diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp index 3ed3f5449e..39db8ae636 100644 --- a/modules/gdnative/text/text_server_gdnative.cpp +++ b/modules/gdnative/text/text_server_gdnative.cpp @@ -88,299 +88,479 @@ bool TextServerGDNative::is_locale_right_to_left(const String &p_locale) { return interface->is_locale_right_to_left(data, (godot_string *)&p_locale); } +int32_t TextServerGDNative::name_to_tag(const String &p_name) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->name_to_tag(data, (godot_string *)&p_name); +} + +String TextServerGDNative::tag_to_name(int32_t p_tag) const { + ERR_FAIL_COND_V(interface == nullptr, String()); + godot_string result = interface->tag_to_name(data, p_tag); + String name = *(String *)&result; + godot_string_destroy(&result); + return name; +} + /*************************************************************************/ -/* Font interface */ +/* Font */ /*************************************************************************/ -RID TextServerGDNative::create_font_system(const String &p_name, int p_base_size) { +RID TextServerGDNative::create_font() { ERR_FAIL_COND_V(interface == nullptr, RID()); - godot_rid result = interface->create_font_system(data, (const godot_string *)&p_name, p_base_size); + godot_rid result = interface->create_font(data); RID rid = *(RID *)&result; return rid; } -RID TextServerGDNative::create_font_resource(const String &p_filename, int p_base_size) { - ERR_FAIL_COND_V(interface == nullptr, RID()); - godot_rid result = interface->create_font_resource(data, (const godot_string *)&p_filename, p_base_size); - RID rid = *(RID *)&result; - return rid; +void TextServerGDNative::font_set_data(RID p_font_rid, const PackedByteArray &p_data) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_data(data, (godot_rid *)&p_font_rid, (const godot_packed_byte_array *)&p_data); } -RID TextServerGDNative::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) { - ERR_FAIL_COND_V(interface == nullptr, RID()); - godot_rid result = interface->create_font_memory(data, p_data, p_size, (godot_string *)&p_type, p_base_size); - RID rid = *(RID *)&result; - return rid; +void TextServerGDNative::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_data_ptr(data, (godot_rid *)&p_font_rid, p_data_ptr, p_data_size); } -RID TextServerGDNative::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { - ERR_FAIL_COND_V(interface == nullptr, RID()); - godot_rid result = interface->create_font_bitmap(data, p_height, p_ascent, p_base_size); - RID rid = *(RID *)&result; - return rid; +void TextServerGDNative::font_set_antialiased(RID p_font_rid, bool p_antialiased) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_antialiased(data, (godot_rid *)&p_font_rid, p_antialiased); } -void TextServerGDNative::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { +bool TextServerGDNative::font_is_antialiased(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_antialiased(data, (godot_rid *)&p_font_rid); +} + +void TextServerGDNative::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { ERR_FAIL_COND(interface == nullptr); - interface->font_bitmap_add_texture(data, (godot_rid *)&p_font, (const godot_object *)p_texture.ptr()); + interface->font_set_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid, p_msdf); +} + +bool TextServerGDNative::font_is_multichannel_signed_distance_field(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid); } -void TextServerGDNative::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { +void TextServerGDNative::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) { ERR_FAIL_COND(interface == nullptr); - interface->font_bitmap_add_char(data, (godot_rid *)&p_font, p_char, p_texture_idx, (const godot_rect2 *)&p_rect, (const godot_vector2 *)&p_align, p_advance); + interface->font_set_msdf_pixel_range(data, (godot_rid *)&p_font_rid, p_msdf_pixel_range); } -void TextServerGDNative::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { +int TextServerGDNative::font_get_msdf_pixel_range(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_msdf_pixel_range(data, (godot_rid *)&p_font_rid); +} + +void TextServerGDNative::font_set_msdf_size(RID p_font_rid, int p_msdf_size) { ERR_FAIL_COND(interface == nullptr); - interface->font_bitmap_add_kerning_pair(data, (godot_rid *)&p_font, p_A, p_B, p_kerning); + interface->font_set_msdf_size(data, (godot_rid *)&p_font_rid, p_msdf_size); } -float TextServerGDNative::font_get_height(RID p_font, int p_size) const { - ERR_FAIL_COND_V(interface == nullptr, 0.f); - return interface->font_get_height(data, (godot_rid *)&p_font, p_size); +int TextServerGDNative::font_get_msdf_size(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_msdf_size(data, (godot_rid *)&p_font_rid); } -float TextServerGDNative::font_get_ascent(RID p_font, int p_size) const { - ERR_FAIL_COND_V(interface == nullptr, 0.f); - return interface->font_get_ascent(data, (godot_rid *)&p_font, p_size); +void TextServerGDNative::font_set_fixed_size(RID p_font_rid, int p_fixed_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_fixed_size(data, (godot_rid *)&p_font_rid, p_fixed_size); } -float TextServerGDNative::font_get_descent(RID p_font, int p_size) const { - ERR_FAIL_COND_V(interface == nullptr, 0.f); - return interface->font_get_descent(data, (godot_rid *)&p_font, p_size); +int TextServerGDNative::font_get_fixed_size(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_fixed_size(data, (godot_rid *)&p_font_rid); } -float TextServerGDNative::font_get_underline_position(RID p_font, int p_size) const { - ERR_FAIL_COND_V(interface == nullptr, 0.f); - return interface->font_get_underline_position(data, (godot_rid *)&p_font, p_size); +void TextServerGDNative::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_force_autohinter(data, (godot_rid *)&p_font_rid, p_force_autohinter); } -float TextServerGDNative::font_get_underline_thickness(RID p_font, int p_size) const { - ERR_FAIL_COND_V(interface == nullptr, 0.f); - return interface->font_get_underline_thickness(data, (godot_rid *)&p_font, p_size); +bool TextServerGDNative::font_is_force_autohinter(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_force_autohinter(data, (godot_rid *)&p_font_rid); } -int TextServerGDNative::font_get_spacing_space(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->font_get_spacing_space(data, (godot_rid *)&p_font); +void TextServerGDNative::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_hinting(data, (godot_rid *)&p_font_rid, (godot_int)p_hinting); } -void TextServerGDNative::font_set_spacing_space(RID p_font, int p_value) { +TextServer::Hinting TextServerGDNative::font_get_hinting(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, TextServer::HINTING_NONE); + return (TextServer::Hinting)interface->font_get_hinting(data, (godot_rid *)&p_font_rid); +} + +void TextServerGDNative::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_variation_coordinates(data, (godot_rid *)&p_font_rid, (const godot_dictionary *)&p_variation_coordinates); +} + +Dictionary TextServerGDNative::font_get_variation_coordinates(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, Dictionary()); + godot_dictionary result = interface->font_get_variation_coordinates(data, (godot_rid *)&p_font_rid); + Dictionary dict = *(Dictionary *)&result; + godot_dictionary_destroy(&result); + return dict; +} + +void TextServerGDNative::font_set_oversampling(RID p_font_rid, real_t p_oversampling) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_oversampling(data, (godot_rid *)&p_font_rid, p_oversampling); +} + +real_t TextServerGDNative::font_get_oversampling(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_oversampling(data, (godot_rid *)&p_font_rid); +} + +Array TextServerGDNative::font_get_size_cache_list(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, Array()); + godot_array result = interface->font_get_size_cache_list(data, (godot_rid *)&p_font_rid); + Array list = *(Array *)&result; + godot_array_destroy(&result); + return list; +} + +void TextServerGDNative::font_clear_size_cache(RID p_font_rid) { + ERR_FAIL_COND(interface == nullptr); + interface->font_clear_size_cache(data, (godot_rid *)&p_font_rid); +} + +void TextServerGDNative::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_size_cache(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); +} + +void TextServerGDNative::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_spacing_space(data, (godot_rid *)&p_font, p_value); + interface->font_set_ascent(data, (godot_rid *)&p_font_rid, p_size, p_ascent); +} + +real_t TextServerGDNative::font_get_ascent(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_ascent(data, (godot_rid *)&p_font_rid, p_size); } -int TextServerGDNative::font_get_spacing_glyph(RID p_font) const { +void TextServerGDNative::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_descent(data, (godot_rid *)&p_font_rid, p_size, p_descent); +} + +real_t TextServerGDNative::font_get_descent(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_descent(data, (godot_rid *)&p_font_rid, p_size); +} + +void TextServerGDNative::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_underline_position(data, (godot_rid *)&p_font_rid, p_size, p_underline_position); +} + +real_t TextServerGDNative::font_get_underline_position(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_underline_position(data, (godot_rid *)&p_font_rid, p_size); +} + +void TextServerGDNative::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_underline_thickness(data, (godot_rid *)&p_font_rid, p_size, p_underline_thickness); +} + +real_t TextServerGDNative::font_get_underline_thickness(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_underline_thickness(data, (godot_rid *)&p_font_rid, p_size); +} + +void TextServerGDNative::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_scale(data, (godot_rid *)&p_font_rid, p_size, p_scale); +} + +real_t TextServerGDNative::font_get_scale(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_scale(data, (godot_rid *)&p_font_rid, p_size); +} + +void TextServerGDNative::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_spacing(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing, p_value); +} + +int TextServerGDNative::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const { ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->font_get_spacing_glyph(data, (godot_rid *)&p_font); + return interface->font_get_spacing(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing); } -void TextServerGDNative::font_set_spacing_glyph(RID p_font, int p_value) { +int TextServerGDNative::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const { + ERR_FAIL_COND_V(interface == nullptr, -1); + return interface->font_get_texture_count(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); +} + +void TextServerGDNative::font_clear_textures(RID p_font_rid, const Vector2i &p_size) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_spacing_glyph(data, (godot_rid *)&p_font, p_value); + interface->font_clear_textures(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); } -void TextServerGDNative::font_set_antialiased(RID p_font, bool p_antialiased) { +void TextServerGDNative::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_antialiased(data, (godot_rid *)&p_font, p_antialiased); + interface->font_remove_texture(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index); } -bool TextServerGDNative::font_get_antialiased(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_get_antialiased(data, (godot_rid *)&p_font); +void TextServerGDNative::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_texture_image(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index, (const godot_object *)p_image.ptr()); } -Dictionary TextServerGDNative::font_get_variation_list(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, Dictionary()); - godot_dictionary result = interface->font_get_variation_list(data, (godot_rid *)&p_font); - Dictionary info = *(Dictionary *)&result; - godot_dictionary_destroy(&result); +Ref<Image> TextServerGDNative::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + ERR_FAIL_COND_V(interface == nullptr, Ref<Image>()); + godot_object *result = interface->font_get_texture_image(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index); + return Ref<Image>((Image *)result); +} - return info; +void TextServerGDNative::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_texture_offsets(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index, (const godot_packed_int32_array *)&p_offset); +} + +PackedInt32Array TextServerGDNative::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + ERR_FAIL_COND_V(interface == nullptr, PackedInt32Array()); + godot_packed_int32_array result = interface->font_get_texture_offsets(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index); + PackedInt32Array offset = *(PackedInt32Array *)&result; + godot_packed_int32_array_destroy(&result); + return offset; +} + +Array TextServerGDNative::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const { + ERR_FAIL_COND_V(interface == nullptr, Array()); + godot_array result = interface->font_get_glyph_list(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); + Array list = *(Array *)&result; + godot_array_destroy(&result); + return list; +} + +void TextServerGDNative::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_clear_glyphs(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size); } -void TextServerGDNative::font_set_variation(RID p_font, const String &p_name, double p_value) { +void TextServerGDNative::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name, p_value); + interface->font_remove_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); } -double TextServerGDNative::font_get_variation(RID p_font, const String &p_name) const { - return interface->font_get_variation(data, (godot_rid *)&p_font, (godot_string *)&p_name); +Vector2 TextServerGDNative::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, Vector2()); + godot_vector2 result = interface->font_get_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph); + Vector2 adv = *(Vector2 *)&result; + return adv; } -void TextServerGDNative::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { +void TextServerGDNative::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_hinting(data, (godot_rid *)&p_font, (godot_int)p_hinting); + interface->font_set_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph, (const godot_vector2 *)&p_advance); } -TextServer::Hinting TextServerGDNative::font_get_hinting(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, TextServer::HINTING_NONE); - return (TextServer::Hinting)interface->font_get_hinting(data, (godot_rid *)&p_font); +Vector2 TextServerGDNative::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, Vector2()); + godot_vector2 result = interface->font_get_glyph_offset(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); + Vector2 off = *(Vector2 *)&result; + return off; } -Dictionary TextServerGDNative::font_get_feature_list(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, Dictionary()); - godot_dictionary result = interface->font_get_feature_list(data, (godot_rid *)&p_font); - Dictionary info = *(Dictionary *)&result; - godot_dictionary_destroy(&result); +void TextServerGDNative::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_glyph_offset(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_offset); +} - return info; +Vector2 TextServerGDNative::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, Vector2()); + godot_vector2 result = interface->font_get_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); + Vector2 sz = *(Vector2 *)&result; + return sz; } -void TextServerGDNative::font_set_distance_field_hint(RID p_font, bool p_distance_field) { +void TextServerGDNative::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_distance_field_hint(data, (godot_rid *)&p_font, p_distance_field); + interface->font_set_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_gl_size); } -bool TextServerGDNative::font_get_distance_field_hint(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_get_distance_field_hint(data, (godot_rid *)&p_font); +Rect2 TextServerGDNative::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, Rect2()); + godot_rect2 result = interface->font_get_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); + Rect2 uv = *(Rect2 *)&result; + return uv; } -void TextServerGDNative::font_set_force_autohinter(RID p_font, bool p_enabeld) { +void TextServerGDNative::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { ERR_FAIL_COND(interface == nullptr); - interface->font_set_force_autohinter(data, (godot_rid *)&p_font, p_enabeld); + interface->font_set_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_rect2 *)&p_uv_rect); } -bool TextServerGDNative::font_get_force_autohinter(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_get_force_autohinter(data, (godot_rid *)&p_font); +int TextServerGDNative::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(interface == nullptr, -1); + return interface->font_get_glyph_texture_idx(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph); +} + +void TextServerGDNative::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_glyph_texture_idx(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, p_texture_idx); } -bool TextServerGDNative::font_has_char(RID p_font, char32_t p_char) const { +bool TextServerGDNative::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_has_char(data, (godot_rid *)&p_font, p_char); + return interface->font_get_glyph_contours(data, (godot_rid *)&p_font_rid, p_size, p_index, (godot_packed_vector3_array *)&r_points, (godot_packed_int32_array *)&r_contours, &r_orientation); } -String TextServerGDNative::font_get_supported_chars(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, String()); - godot_string result = interface->font_get_supported_chars(data, (godot_rid *)&p_font); - String ret = *(String *)&result; - godot_string_destroy(&result); - return ret; +Array TextServerGDNative::font_get_kerning_list(RID p_font_rid, int p_size) const { + ERR_FAIL_COND_V(interface == nullptr, Array()); + godot_array result = interface->font_get_kerning_list(data, (godot_rid *)&p_font_rid, p_size); + Array list = *(Array *)&result; + godot_array_destroy(&result); + return list; } -bool TextServerGDNative::font_has_outline(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_has_outline(data, (godot_rid *)&p_font); +void TextServerGDNative::font_clear_kerning_map(RID p_font_rid, int p_size) { + ERR_FAIL_COND(interface == nullptr); + interface->font_clear_kerning_map(data, (godot_rid *)&p_font_rid, p_size); } -float TextServerGDNative::font_get_base_size(RID p_font) const { - ERR_FAIL_COND_V(interface == nullptr, 0.f); - return interface->font_get_base_size(data, (godot_rid *)&p_font); +void TextServerGDNative::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair); +} + +void TextServerGDNative::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair, (const godot_vector2 *)&p_kerning); +} + +Vector2 TextServerGDNative::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const { + ERR_FAIL_COND_V(interface == nullptr, Vector2()); + godot_vector2 result = interface->font_get_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair); + Vector2 kern = *(Vector2 *)&result; + return kern; } -bool TextServerGDNative::font_is_language_supported(RID p_font, const String &p_language) const { +int32_t TextServerGDNative::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_glyph_index(data, (godot_rid *)&p_font_rid, p_size, p_char, p_variation_selector); +} + +bool TextServerGDNative::font_has_char(RID p_font_rid, char32_t p_char) const { ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_is_language_supported(data, (godot_rid *)&p_font, (godot_string *)&p_language); + return interface->font_has_char(data, (godot_rid *)&p_font_rid, p_char); +} + +String TextServerGDNative::font_get_supported_chars(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, String()); + godot_string result = interface->font_get_supported_chars(data, (godot_rid *)&p_font_rid); + String chars = *(String *)&result; + godot_string_destroy(&result); + return chars; } -void TextServerGDNative::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) { +void TextServerGDNative::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) { ERR_FAIL_COND(interface == nullptr); - return interface->font_set_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language, p_supported); + interface->font_render_range(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_start, p_end); } -bool TextServerGDNative::font_get_language_support_override(RID p_font, const String &p_language) { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_get_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language); +void TextServerGDNative::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) { + ERR_FAIL_COND(interface == nullptr); + interface->font_render_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_index); } -void TextServerGDNative::font_remove_language_support_override(RID p_font, const String &p_language) { +void TextServerGDNative::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { ERR_FAIL_COND(interface == nullptr); - interface->font_remove_language_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_language); + interface->font_draw_glyph(data, (godot_rid *)&p_font_rid, (godot_rid *)&p_canvas, p_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color); } -Vector<String> TextServerGDNative::font_get_language_support_overrides(RID p_font) { - ERR_FAIL_COND_V(interface == nullptr, Vector<String>()); - godot_packed_string_array result = interface->font_get_language_support_overrides(data, (godot_rid *)&p_font); - Vector<String> ret = *(Vector<String> *)&result; - godot_packed_string_array_destroy(&result); - return ret; +void TextServerGDNative::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + ERR_FAIL_COND(interface == nullptr); + interface->font_draw_glyph_outline(data, (godot_rid *)&p_font_rid, (godot_rid *)&p_canvas, p_size, p_outline_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color); } -bool TextServerGDNative::font_is_script_supported(RID p_font, const String &p_script) const { +bool TextServerGDNative::font_is_language_supported(RID p_font_rid, const String &p_language) const { ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_is_script_supported(data, (godot_rid *)&p_font, (godot_string *)&p_script); + return interface->font_is_language_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); } -void TextServerGDNative::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) { +void TextServerGDNative::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) { ERR_FAIL_COND(interface == nullptr); - return interface->font_set_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script, p_supported); + interface->font_set_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language, p_supported); } -bool TextServerGDNative::font_get_script_support_override(RID p_font, const String &p_script) { +bool TextServerGDNative::font_get_language_support_override(RID p_font_rid, const String &p_language) { ERR_FAIL_COND_V(interface == nullptr, false); - return interface->font_get_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script); + return interface->font_get_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); } -void TextServerGDNative::font_remove_script_support_override(RID p_font, const String &p_script) { +void TextServerGDNative::font_remove_language_support_override(RID p_font_rid, const String &p_language) { ERR_FAIL_COND(interface == nullptr); - interface->font_remove_script_support_override(data, (godot_rid *)&p_font, (godot_string *)&p_script); + interface->font_remove_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language); } -Vector<String> TextServerGDNative::font_get_script_support_overrides(RID p_font) { - ERR_FAIL_COND_V(interface == nullptr, Vector<String>()); - godot_packed_string_array result = interface->font_get_script_support_overrides(data, (godot_rid *)&p_font); - Vector<String> ret = *(Vector<String> *)&result; +Vector<String> TextServerGDNative::font_get_language_support_overrides(RID p_font_rid) { + ERR_FAIL_COND_V(interface == nullptr, PackedStringArray()); + godot_packed_string_array result = interface->font_get_language_support_overrides(data, (godot_rid *)&p_font_rid); + PackedStringArray list = *(PackedStringArray *)&result; godot_packed_string_array_destroy(&result); - return ret; + return list; } -uint32_t TextServerGDNative::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->font_get_glyph_index(data, (godot_rid *)&p_font, p_char, p_variation_selector); +bool TextServerGDNative::font_is_script_supported(RID p_font_rid, const String &p_script) const { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_is_script_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script); } -Vector2 TextServerGDNative::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const { - ERR_FAIL_COND_V(interface == nullptr, Vector2()); - godot_vector2 result = interface->font_get_glyph_advance(data, (godot_rid *)&p_font, p_index, p_size); - Vector2 advance = *(Vector2 *)&result; - return advance; +void TextServerGDNative::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script, p_supported); } -Vector2 TextServerGDNative::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const { - ERR_FAIL_COND_V(interface == nullptr, Vector2()); - godot_vector2 result = interface->font_get_glyph_kerning(data, (godot_rid *)&p_font, p_index_a, p_index_b, p_size); - Vector2 kerning = *(Vector2 *)&result; - return kerning; +bool TextServerGDNative::font_get_script_support_override(RID p_font_rid, const String &p_script) { + ERR_FAIL_COND_V(interface == nullptr, false); + return interface->font_get_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script); } -Vector2 TextServerGDNative::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - ERR_FAIL_COND_V(interface == nullptr, Vector2()); - godot_vector2 result = interface->font_draw_glyph(data, (godot_rid *)&p_font, (godot_rid *)&p_canvas, p_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color); - Vector2 advance = *(Vector2 *)&result; - return advance; +void TextServerGDNative::font_remove_script_support_override(RID p_font_rid, const String &p_script) { + ERR_FAIL_COND(interface == nullptr); + interface->font_remove_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script); } -Vector2 TextServerGDNative::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - ERR_FAIL_COND_V(interface == nullptr, Vector2()); - godot_vector2 result = interface->font_draw_glyph_outline(data, (godot_rid *)&p_font, (godot_rid *)&p_canvas, p_size, p_outline_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color); - Vector2 advance = *(Vector2 *)&result; - return advance; +Vector<String> TextServerGDNative::font_get_script_support_overrides(RID p_font_rid) { + ERR_FAIL_COND_V(interface == nullptr, PackedStringArray()); + godot_packed_string_array result = interface->font_get_script_support_overrides(data, (godot_rid *)&p_font_rid); + PackedStringArray list = *(PackedStringArray *)&result; + godot_packed_string_array_destroy(&result); + return list; } -bool TextServerGDNative::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - ERR_FAIL_COND_V(interface == nullptr, false); - ERR_FAIL_COND_V(interface->font_get_glyph_contours == nullptr, false); - return interface->font_get_glyph_contours(data, (godot_rid *)&p_font, p_size, p_index, (godot_packed_vector3_array *)&r_points, (godot_packed_int32_array *)&r_contours, (bool *)&r_orientation); +Dictionary TextServerGDNative::font_supported_feature_list(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, Dictionary()); + godot_dictionary result = interface->font_supported_feature_list(data, (godot_rid *)&p_font_rid); + Dictionary dict = *(Dictionary *)&result; + godot_dictionary_destroy(&result); + return dict; } -float TextServerGDNative::font_get_oversampling() const { - ERR_FAIL_COND_V(interface == nullptr, 1.f); - return interface->font_get_oversampling(data); +Dictionary TextServerGDNative::font_supported_variation_list(RID p_font_rid) const { + ERR_FAIL_COND_V(interface == nullptr, Dictionary()); + godot_dictionary result = interface->font_supported_variation_list(data, (godot_rid *)&p_font_rid); + Dictionary dict = *(Dictionary *)&result; + godot_dictionary_destroy(&result); + return dict; } -void TextServerGDNative::font_set_oversampling(float p_oversampling) { - ERR_FAIL_COND(interface == nullptr); - return interface->font_set_oversampling(data, p_oversampling); +real_t TextServerGDNative::font_get_global_oversampling() const { + ERR_FAIL_COND_V(interface == nullptr, 0.0f); + return interface->font_get_global_oversampling(data); } -Vector<String> TextServerGDNative::get_system_fonts() const { - ERR_FAIL_COND_V(interface == nullptr, Vector<String>()); - godot_packed_string_array result = interface->get_system_fonts(data); - Vector<String> fonts = *(Vector<String> *)&result; - godot_packed_string_array_destroy(&result); - return fonts; +void TextServerGDNative::font_set_global_oversampling(real_t p_oversampling) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_global_oversampling(data, p_oversampling); } /*************************************************************************/ @@ -473,12 +653,12 @@ RID TextServerGDNative::shaped_text_get_parent(RID p_shaped) const { return rid; } -float TextServerGDNative::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t p_jst_flags) { +real_t TextServerGDNative::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t p_jst_flags) { ERR_FAIL_COND_V(interface == nullptr, 0.f); return interface->shaped_text_fit_to_width(data, (godot_rid *)&p_shaped, p_width, p_jst_flags); } -float TextServerGDNative::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) { +real_t TextServerGDNative::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) { ERR_FAIL_COND_V(interface == nullptr, 0.f); return interface->shaped_text_tab_align(data, (godot_rid *)&p_shaped, (godot_packed_float32_array *)&p_tab_stops); } @@ -498,7 +678,7 @@ bool TextServerGDNative::shaped_text_update_justification_ops(RID p_shaped) { return interface->shaped_text_update_justification_ops(data, (godot_rid *)&p_shaped); } -void TextServerGDNative::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_trim_flags) { +void TextServerGDNative::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) { ERR_FAIL_COND(interface == nullptr); interface->shaped_text_overrun_trim_to_width(data, (godot_rid *)&p_shaped_line, p_width, p_trim_flags); }; @@ -531,7 +711,7 @@ Vector<TextServer::Glyph> TextServerGDNative::shaped_text_sort_logical(RID p_sha return glyphs; } -Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start, bool p_once, uint8_t p_break_flags) const { +Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start, bool p_once, uint8_t p_break_flags) const { ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>()); if (interface->shaped_text_get_line_breaks_adv != nullptr) { godot_packed_vector2i_array result = interface->shaped_text_get_line_breaks_adv(data, (godot_rid *)&p_shaped, (godot_packed_float32_array *)&p_width, p_start, p_once, p_break_flags); @@ -543,7 +723,7 @@ Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shape } } -Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start, uint8_t p_break_flags) const { +Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t p_break_flags) const { ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>()); if (interface->shaped_text_get_line_breaks != nullptr) { godot_packed_vector2i_array result = interface->shaped_text_get_line_breaks(data, (godot_rid *)&p_shaped, p_width, p_start, p_break_flags); @@ -588,27 +768,27 @@ Size2 TextServerGDNative::shaped_text_get_size(RID p_shaped) const { return size; } -float TextServerGDNative::shaped_text_get_ascent(RID p_shaped) const { +real_t TextServerGDNative::shaped_text_get_ascent(RID p_shaped) const { ERR_FAIL_COND_V(interface == nullptr, 0.f); return interface->shaped_text_get_ascent(data, (godot_rid *)&p_shaped); } -float TextServerGDNative::shaped_text_get_descent(RID p_shaped) const { +real_t TextServerGDNative::shaped_text_get_descent(RID p_shaped) const { ERR_FAIL_COND_V(interface == nullptr, 0.f); return interface->shaped_text_get_descent(data, (godot_rid *)&p_shaped); } -float TextServerGDNative::shaped_text_get_width(RID p_shaped) const { +real_t TextServerGDNative::shaped_text_get_width(RID p_shaped) const { ERR_FAIL_COND_V(interface == nullptr, 0.f); return interface->shaped_text_get_width(data, (godot_rid *)&p_shaped); } -float TextServerGDNative::shaped_text_get_underline_position(RID p_shaped) const { +real_t TextServerGDNative::shaped_text_get_underline_position(RID p_shaped) const { ERR_FAIL_COND_V(interface == nullptr, 0.f); return interface->shaped_text_get_underline_position(data, (godot_rid *)&p_shaped); } -float TextServerGDNative::shaped_text_get_underline_thickness(RID p_shaped) const { +real_t TextServerGDNative::shaped_text_get_underline_thickness(RID p_shaped) const { ERR_FAIL_COND_V(interface == nullptr, 0.f); return interface->shaped_text_get_underline_thickness(data, (godot_rid *)&p_shaped); } @@ -756,12 +936,12 @@ void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_of self->y_off = offset->y; } -godot_float GDAPI godot_glyph_get_advance(const godot_glyph *p_self) { +godot_real_t GDAPI godot_glyph_get_advance(const godot_glyph *p_self) { const TextServer::Glyph *self = (const TextServer::Glyph *)p_self; return self->advance; } -void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_float p_advance) { +void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real_t p_advance) { TextServer::Glyph *self = (TextServer::Glyph *)p_self; self->advance = p_advance; } diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h index e1b36f4264..f081637e23 100644 --- a/modules/gdnative/text/text_server_gdnative.h +++ b/modules/gdnative/text/text_server_gdnative.h @@ -60,78 +60,130 @@ public: virtual bool is_locale_right_to_left(const String &p_locale) override; + virtual int32_t name_to_tag(const String &p_name) const override; + virtual String tag_to_name(int32_t p_tag) const override; + /* Font interface */ - virtual RID create_font_system(const String &p_name, int p_base_size = 16) override; - virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override; - virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override; - virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + virtual RID create_font() override; + + virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override; + virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override; + + virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) const override; + + virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override; + virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override; + + virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override; + virtual int font_get_msdf_pixel_range(RID p_font_rid) const override; + + virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override; + virtual int font_get_msdf_size(RID p_font_rid) const override; + + virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override; + virtual int font_get_fixed_size(RID p_font_rid) const override; + + virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override; + virtual bool font_is_force_autohinter(RID p_font_rid) const override; + + virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override; + virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override; + + virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override; + virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override; + + virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) const override; + + virtual Array font_get_size_cache_list(RID p_font_rid) const override; + virtual void font_clear_size_cache(RID p_font_rid) override; + virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override; + + virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override; + virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override; + + virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override; + virtual real_t font_get_descent(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override; + virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override; + virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override; + + virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override; + virtual real_t font_get_scale(RID p_font_rid, int p_size) const override; + + virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override; + virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override; - virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; - virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; + virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) override; - virtual float font_get_height(RID p_font, int p_size) const override; - virtual float font_get_ascent(RID p_font, int p_size) const override; - virtual float font_get_descent(RID p_font, int p_size) const override; + virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override; + virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual float font_get_underline_position(RID p_font, int p_size) const override; - virtual float font_get_underline_thickness(RID p_font, int p_size) const override; + virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override; + virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) override; + virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override; - virtual int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override; - virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) const override; + virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override; - virtual Dictionary font_get_feature_list(RID p_font) const override; - virtual Dictionary font_get_variation_list(RID p_font) const override; + virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) override; - virtual void font_set_variation(RID p_font, const String &p_name, double p_value) override; - virtual double font_get_variation(RID p_font, const String &p_name) const override; + virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override; - virtual void font_set_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) const override; + virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override; - virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override; - virtual bool font_get_distance_field_hint(RID p_font) const override; + virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; - virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override; - virtual bool font_get_force_autohinter(RID p_font) const override; + virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override; + virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override; + virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) override; - virtual bool font_has_char(RID p_font, char32_t p_char) const override; - virtual String font_get_supported_chars(RID p_font) const override; + virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override; + virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override; - virtual bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) const override; + virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) const override; - virtual bool font_is_language_supported(RID p_font, const String &p_language) const override; - virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override; - virtual bool font_get_language_support_override(RID p_font, const String &p_language) override; - virtual void font_remove_language_support_override(RID p_font, const String &p_language) override; - Vector<String> font_get_language_support_overrides(RID p_font) override; + virtual bool font_has_char(RID p_font_rid, char32_t p_char) const override; + virtual String font_get_supported_chars(RID p_font_rid) const override; - virtual bool font_is_script_supported(RID p_font, const String &p_script) const override; - virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override; - virtual bool font_get_script_support_override(RID p_font, const String &p_script) override; - virtual void font_remove_script_support_override(RID p_font, const String &p_script) override; - Vector<String> font_get_script_support_overrides(RID p_font) override; + virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override; + virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) override; - virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override; - virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override; - virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override; + virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; - virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; - virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const override; + virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override; + virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override; + virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override; + virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override; - virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; + virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override; + virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override; + virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override; + virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override; + virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override; - virtual float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) override; + virtual Dictionary font_supported_feature_list(RID p_font_rid) const override; + virtual Dictionary font_supported_variation_list(RID p_font_rid) const override; - virtual Vector<String> get_system_fonts() const override; + virtual real_t font_get_global_oversampling() const override; + virtual void font_set_global_oversampling(real_t p_oversampling) override; /* Shaped text buffer interface */ @@ -160,14 +212,14 @@ public: virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; - virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; - virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override; + virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; + virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override; virtual bool shaped_text_shape(RID p_shaped) override; virtual bool shaped_text_update_breaks(RID p_shaped) override; virtual bool shaped_text_update_justification_ops(RID p_shaped) override; - virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_trim_flags) override; + virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override; virtual bool shaped_text_is_ready(RID p_shaped) const override; @@ -176,18 +228,18 @@ public: virtual Vector2i shaped_text_get_range(RID p_shaped) const override; virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override; - virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<float> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override; - virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, float p_width, int p_start = 0, uint8_t p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override; + virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override; + virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start = 0, uint8_t p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override; virtual Vector<Vector2i> shaped_text_get_word_breaks(RID p_shaped, int p_grapheme_flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_PUNCTUATION) const override; virtual Array shaped_text_get_objects(RID p_shaped) const override; virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override; virtual Size2 shaped_text_get_size(RID p_shaped) const override; - virtual float shaped_text_get_ascent(RID p_shaped) const override; - virtual float shaped_text_get_descent(RID p_shaped) const override; - virtual float shaped_text_get_width(RID p_shaped) const override; - virtual float shaped_text_get_underline_position(RID p_shaped) const override; - virtual float shaped_text_get_underline_thickness(RID p_shaped) const override; + virtual real_t shaped_text_get_ascent(RID p_shaped) const override; + virtual real_t shaped_text_get_descent(RID p_shaped) const override; + virtual real_t shaped_text_get_width(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_position(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override; virtual String format_number(const String &p_string, const String &p_language = "") const override; virtual String parse_number(const String &p_string, const String &p_language = "") const override; diff --git a/modules/gdnative/xr/SCsub b/modules/gdnative/xr/SCsub deleted file mode 100644 index 0b2db3b504..0000000000 --- a/modules/gdnative/xr/SCsub +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_gdnative") - -env_gdnative.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/gdnative/xr/xr_interface_gdnative.cpp b/modules/gdnative/xr/xr_interface_gdnative.cpp deleted file mode 100644 index e51542e23d..0000000000 --- a/modules/gdnative/xr/xr_interface_gdnative.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/*************************************************************************/ -/* xr_interface_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "xr_interface_gdnative.h" -#include "core/input/input.h" -#include "servers/rendering/rendering_server_globals.h" -#include "servers/xr/xr_positional_tracker.h" - -void XRInterfaceGDNative::_bind_methods() { - ADD_PROPERTY_DEFAULT("interface_is_initialized", false); - ADD_PROPERTY_DEFAULT("ar_is_anchor_detection_enabled", false); -} - -XRInterfaceGDNative::XRInterfaceGDNative() { - print_verbose("Construct gdnative interface\n"); - - // we won't have our data pointer until our library gets set - data = nullptr; - - interface = nullptr; -} - -XRInterfaceGDNative::~XRInterfaceGDNative() { - print_verbose("Destruct gdnative interface\n"); - - if (interface != nullptr && is_initialized()) { - uninitialize(); - }; - - // cleanup after ourselves - cleanup(); -} - -void XRInterfaceGDNative::cleanup() { - if (interface != nullptr) { - interface->destructor(data); - data = nullptr; - interface = nullptr; - } -} - -void XRInterfaceGDNative::set_interface(const godot_xr_interface_gdnative *p_interface) { - // this should only be called once, just being paranoid.. - if (interface) { - cleanup(); - interface = NULL; - } - - // validate - ERR_FAIL_NULL(p_interface); - ERR_FAIL_COND_MSG(p_interface->version.major < 4, "This is an incompatible GDNative XR plugin."); - - // bind to our interface - interface = p_interface; - - // Now we do our constructing... - data = interface->constructor((godot_object *)this); -} - -StringName XRInterfaceGDNative::get_name() const { - ERR_FAIL_COND_V(interface == nullptr, StringName()); - - godot_string result = interface->get_name(data); - - StringName name = *(String *)&result; - - godot_string_destroy(&result); - - return name; -} - -int XRInterfaceGDNative::get_capabilities() const { - int capabilities; - - ERR_FAIL_COND_V(interface == nullptr, 0); // 0 = None - - capabilities = interface->get_capabilities(data); - - return capabilities; -} - -bool XRInterfaceGDNative::get_anchor_detection_is_enabled() const { - ERR_FAIL_COND_V(interface == nullptr, false); - - return interface->get_anchor_detection_is_enabled(data); -} - -void XRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { - ERR_FAIL_COND(interface == nullptr); - - interface->set_anchor_detection_is_enabled(data, p_enable); -} - -int XRInterfaceGDNative::get_camera_feed_id() { - ERR_FAIL_COND_V(interface == nullptr, 0); - - return (unsigned int)interface->get_camera_feed_id(data); -} - -uint32_t XRInterfaceGDNative::get_view_count() { - uint32_t view_count; - - ERR_FAIL_COND_V(interface == nullptr, 1); - - view_count = interface->get_view_count(data); - - return view_count; -} - -bool XRInterfaceGDNative::is_initialized() const { - ERR_FAIL_COND_V(interface == nullptr, false); - - return interface->is_initialized(data); -} - -bool XRInterfaceGDNative::initialize() { - ERR_FAIL_COND_V(interface == nullptr, false); - - bool initialized = interface->initialize(data); - - if (initialized) { - // if we successfully initialize our interface and we don't have a primary interface yet, this becomes our primary interface - - XRServer *xr_server = XRServer::get_singleton(); - if ((xr_server != nullptr) && (xr_server->get_primary_interface() == nullptr)) { - xr_server->set_primary_interface(this); - }; - }; - - return initialized; -} - -void XRInterfaceGDNative::uninitialize() { - ERR_FAIL_COND(interface == nullptr); - - XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr) { - // Whatever happens, make sure this is no longer our primary interface - xr_server->clear_primary_interface_if(this); - } - - interface->uninitialize(data); -} - -Size2 XRInterfaceGDNative::get_render_targetsize() { - ERR_FAIL_COND_V(interface == nullptr, Size2()); - - godot_vector2 result = interface->get_render_targetsize(data); - Vector2 *vec = (Vector2 *)&result; - - return *vec; -} - -Transform3D XRInterfaceGDNative::get_camera_transform() { - Transform3D *ret; - - ERR_FAIL_COND_V(interface == nullptr, Transform3D()); - - godot_transform3d t = interface->get_camera_transform(data); - - ret = (Transform3D *)&t; - - return *ret; -} - -Transform3D XRInterfaceGDNative::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { - Transform3D *ret; - - ERR_FAIL_COND_V(interface == nullptr, Transform3D()); - - godot_transform3d t = interface->get_transform_for_view(data, (int)p_view, (godot_transform3d *)&p_cam_transform); - - ret = (Transform3D *)&t; - - return *ret; -} - -CameraMatrix XRInterfaceGDNative::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) { - CameraMatrix cm; - - ERR_FAIL_COND_V(interface == nullptr, CameraMatrix()); - - interface->fill_projection_for_view(data, (godot_real_t *)cm.matrix, (godot_int)p_view, p_aspect, p_z_near, p_z_far); - - return cm; -} - -Vector<BlitToScreen> XRInterfaceGDNative::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { - // possibly move this as a member variable and add a callback to populate? - Vector<BlitToScreen> blit_to_screen; - - ERR_FAIL_COND_V(interface == nullptr, blit_to_screen); - - // must implement - interface->commit_views(data, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); - - return blit_to_screen; -} - -unsigned int XRInterfaceGDNative::get_external_texture_for_eye(XRInterface::Eyes p_eye) { - ERR_FAIL_COND_V(interface == nullptr, 0); - - return (unsigned int)interface->get_external_texture_for_eye(data, (godot_int)p_eye); -} - -void XRInterfaceGDNative::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { - ERR_FAIL_COND(interface == nullptr); - - interface->commit_for_eye(data, (godot_int)p_eye, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); -} - -void XRInterfaceGDNative::process() { - ERR_FAIL_COND(interface == nullptr); - - interface->process(data); -} - -void XRInterfaceGDNative::notification(int p_what) { - ERR_FAIL_COND(interface == nullptr); - - interface->notification(data, p_what); -} - -///////////////////////////////////////////////////////////////////////////////////// -// some helper callbacks - -extern "C" { - -void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface) { - // Must be on a version 4 plugin - ERR_FAIL_COND_MSG(p_interface->version.major < 4, "GDNative XR interfaces build for Godot 3.x are not supported."); - - Ref<XRInterfaceGDNative> new_interface; - new_interface.instantiate(); - new_interface->set_interface((const godot_xr_interface_gdnative *)p_interface); - XRServer::get_singleton()->add_interface(new_interface); -} - -godot_real_t GDAPI godot_xr_get_worldscale() { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, 1.0); - - return xr_server->get_world_scale(); -} - -godot_transform3d GDAPI godot_xr_get_reference_frame() { - godot_transform3d reference_frame; - Transform3D *reference_frame_ptr = (Transform3D *)&reference_frame; - - XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr) { - *reference_frame_ptr = xr_server->get_reference_frame(); - } else { - memnew_placement(&reference_frame, Transform3D); - } - - return reference_frame; -} - -void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect) { - // blits out our texture as is, handy for preview display of one of the eyes that is already rendered with lens distortion on an external HMD - XRInterface::Eyes eye = (XRInterface::Eyes)p_eye; -#if 0 - RID *render_target = (RID *)p_render_target; -#endif - Rect2 screen_rect = *(Rect2 *)p_rect; - - if (eye == XRInterface::EYE_LEFT) { - screen_rect.size.x /= 2.0; - } else if (p_eye == XRInterface::EYE_RIGHT) { - screen_rect.size.x /= 2.0; - screen_rect.position.x += screen_rect.size.x; - } -#ifndef _MSC_VER -#warning this needs to be redone -#endif -#if 0 - RSG::rasterizer->blit_render_target_to_screen(*render_target, screen_rect, 0); -#endif -} - -godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target) { - // In order to send off our textures to display on our hardware we need the opengl texture ID instead of the render target RID - // This is a handy function to expose that. -#if 0 - RID *render_target = (RID *)p_render_target; - - RID eye_texture = RSG::storage->render_target_get_texture(*render_target); -#endif - -#ifndef _MSC_VER -#warning need to obtain this ID again -#endif - uint32_t texid = 0; //RS::get_singleton()->texture_get_texid(eye_texture); - - return texid; -} - -godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, 0); - - Input *input = Input::get_singleton(); - ERR_FAIL_NULL_V(input, 0); - - Ref<XRPositionalTracker> new_tracker; - new_tracker.instantiate(); - new_tracker->set_tracker_name(p_device_name); - new_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); - if (p_hand == 1) { - new_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT); - } else if (p_hand == 2) { - new_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT); - } - - // also register as joystick... - int joyid = input->get_unused_joy_id(); - if (joyid != -1) { - new_tracker->set_joy_id(joyid); - input->joy_connection_changed(joyid, true, p_device_name, ""); - } - - if (p_tracks_orientation) { - Basis orientation; - new_tracker->set_orientation(orientation); - } - if (p_tracks_position) { - Vector3 position; - new_tracker->set_position(position); - } - - // add our tracker to our server and remember its pointer - xr_server->add_tracker(new_tracker); - - // note, this ID is only unique within controllers! - return new_tracker->get_tracker_id(); -} - -void GDAPI godot_xr_remove_controller(godot_int p_controller_id) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - Input *input = Input::get_singleton(); - ERR_FAIL_NULL(input); - - Ref<XRPositionalTracker> remove_tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (remove_tracker.is_valid()) { - // unset our joystick if applicable - int joyid = remove_tracker->get_joy_id(); - if (joyid != -1) { - input->joy_connection_changed(joyid, false, "", ""); - remove_tracker->set_joy_id(-1); - } - - // remove our tracker from our server - xr_server->remove_tracker(remove_tracker); - remove_tracker.unref(); - } -} - -void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform3d *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker.is_valid()) { - Transform3D *transform = (Transform3D *)p_transform; - if (p_tracks_orientation) { - tracker->set_orientation(transform->basis); - } - if (p_tracks_position) { - tracker->set_rw_position(transform->origin); - } - } -} - -void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - Input *input = Input::get_singleton(); - ERR_FAIL_NULL(input); - - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker.is_valid()) { - int joyid = tracker->get_joy_id(); - if (joyid != -1) { - input->joy_button(joyid, (JoyButton)p_button, p_is_pressed); - } - } -} - -void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL(xr_server); - - Input *input = Input::get_singleton(); - ERR_FAIL_NULL(input); - - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker.is_valid()) { - int joyid = tracker->get_joy_id(); - if (joyid != -1) { - Input::JoyAxisValue jx; - jx.min = p_can_be_negative ? -1 : 0; - jx.value = p_value; - input->joy_axis(joyid, (JoyAxis)p_axis, jx); - } - } -} - -godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) { - XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, 0.0); - - Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker.is_valid()) { - return tracker->get_rumble(); - } - - return 0.0; -} -} diff --git a/modules/gdnative/xr/xr_interface_gdnative.h b/modules/gdnative/xr/xr_interface_gdnative.h deleted file mode 100644 index 42e9206c1f..0000000000 --- a/modules/gdnative/xr/xr_interface_gdnative.h +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************/ -/* xr_interface_gdnative.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef XR_INTERFACE_GDNATIVE_H -#define XR_INTERFACE_GDNATIVE_H - -#include "modules/gdnative/gdnative.h" -#include "servers/xr/xr_interface.h" - -/** - @authors Hinsbart & Karroffel & Mux213 - - This subclass of our AR/VR interface forms a bridge to GDNative. -*/ - -class XRInterfaceGDNative : public XRInterface { - GDCLASS(XRInterfaceGDNative, XRInterface); - - void cleanup(); - -protected: - const godot_xr_interface_gdnative *interface; - void *data; - - static void _bind_methods(); - -public: - /** general interface information **/ - XRInterfaceGDNative(); - ~XRInterfaceGDNative(); - - void set_interface(const godot_xr_interface_gdnative *p_interface); - - virtual StringName get_name() const override; - virtual int get_capabilities() const override; - - virtual bool is_initialized() const override; - virtual bool initialize() override; - virtual void uninitialize() override; - - /** specific to AR **/ - virtual bool get_anchor_detection_is_enabled() const override; - virtual void set_anchor_detection_is_enabled(bool p_enable) override; - virtual int get_camera_feed_id() override; - - /** rendering and internal **/ - virtual Size2 get_render_targetsize() override; - virtual uint32_t get_view_count() override; - virtual Transform3D get_camera_transform() override; - virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; - - // we expose a Vector<float> version of this function to GDNative - Vector<float> _get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); - - // and a CameraMatrix version to XRServer - virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; - - virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; - - virtual void process() override; - virtual void notification(int p_what) override; - - // deprecated - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; - virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; -}; - -#endif // XR_INTERFACE_GDNATIVE_H diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 8b9bd2659d..ab441d194a 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -471,9 +471,9 @@ void GDScriptSyntaxHighlighter::_update_cache() { } /* Autoloads. */ - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->value(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.value(); if (info.is_singleton) { keywords[info.name] = usertype_color; } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 4589cf1a36..5409b3ff79 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -625,9 +625,9 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc String path = ""; if (String(c->extends_path) != "" && String(c->extends_path) != get_path()) { path = c->extends_path; - if (path.is_rel_path()) { + if (path.is_relative_path()) { String base = get_path(); - if (base == "" || base.is_rel_path()) { + if (base == "" || base.is_relative_path()) { ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data()); } else { path = base.get_base_dir().plus_file(path); @@ -900,7 +900,7 @@ void GDScript::get_members(Set<StringName> *p_members) { } } -const Vector<MultiplayerAPI::RPCConfig> GDScript::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> GDScript::get_rpc_methods() const { return rpc_functions; } @@ -1164,8 +1164,8 @@ void GDScript::_init_rpc_methods_properties() { while (cscript) { // RPC Methods for (Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.front(); E; E = E->next()) { - MultiplayerAPI::RPCConfig config = E->get()->get_rpc_config(); - if (config.rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) { + Multiplayer::RPCConfig config = E->get()->get_rpc_config(); + if (config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) { config.name = E->get()->get_name(); if (rpc_functions.find(config) == -1) { rpc_functions.push_back(config); @@ -1185,7 +1185,7 @@ void GDScript::_init_rpc_methods_properties() { } // Sort so we are 100% that they are always the same. - rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>(); + rpc_functions.sort_custom<Multiplayer::SortRPCConfig>(); } GDScript::~GDScript() { @@ -1541,7 +1541,7 @@ ScriptLanguage *GDScriptInstance::get_language() { return GDScriptLanguage::get_singleton(); } -const Vector<MultiplayerAPI::RPCConfig> GDScriptInstance::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> GDScriptInstance::get_rpc_methods() const { return script->get_rpc_methods(); } @@ -2059,7 +2059,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b 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_rel_path()) { + } else if (c->icon_path.is_relative_path()) { *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); } } @@ -2087,7 +2087,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b break; } String subpath = subclass->extends_path; - if (subpath.is_rel_path()) { + if (subpath.is_relative_path()) { subpath = path.get_base_dir().plus_file(subpath).simplify_path(); } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 24809ad5fd..a6fd7b3b5d 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -85,7 +85,7 @@ class GDScript : public Script { Map<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script. Map<StringName, Ref<GDScript>> subclasses; Map<StringName, Vector<StringName>> _signals; - Vector<MultiplayerAPI::RPCConfig> rpc_functions; + Vector<Multiplayer::RPCConfig> rpc_functions; #ifdef TOOLS_ENABLED @@ -245,7 +245,7 @@ public: virtual void get_constants(Map<StringName, Variant> *p_constants) override; virtual void get_members(Set<StringName> *p_members) override; - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override; #ifdef TOOLS_ENABLED virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; } @@ -298,7 +298,7 @@ public: void reload_members(); - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const; GDScriptInstance(); ~GDScriptInstance(); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 6b7403d854..f2b601dc2c 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -132,6 +132,76 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) { return type; } +bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class) { + if (p_class->members_indices.has(p_member_name)) { + int index = p_class->members_indices[p_member_name]; + const GDScriptParser::ClassNode::Member *member = &p_class->members[index]; + + if (member->type == GDScriptParser::ClassNode::Member::VARIABLE || + member->type == GDScriptParser::ClassNode::Member::CONSTANT || + member->type == GDScriptParser::ClassNode::Member::ENUM || + member->type == GDScriptParser::ClassNode::Member::ENUM_VALUE || + member->type == GDScriptParser::ClassNode::Member::CLASS || + member->type == GDScriptParser::ClassNode::Member::SIGNAL) { + return true; + } + } + + return false; +} + +bool GDScriptAnalyzer::has_member_name_conflict_in_native_type(const StringName &p_member_name, const StringName &p_native_type_string) { + if (ClassDB::has_signal(p_native_type_string, p_member_name)) { + return true; + } + if (ClassDB::has_property(p_native_type_string, p_member_name)) { + return true; + } + if (ClassDB::has_integer_constant(p_native_type_string, p_member_name)) { + return true; + } + + return false; +} + +Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string) { + if (has_member_name_conflict_in_native_type(p_member_name, p_native_type_string)) { + push_error(vformat(R"(Member "%s" redefined (original in native class '%s'))", p_member_name, p_native_type_string), p_member_node); + return ERR_PARSE_ERROR; + } + + if (class_exists(p_member_name)) { + push_error(vformat(R"(The class "%s" shadows a native class.)", p_member_name), p_member_node); + return ERR_PARSE_ERROR; + } + + return OK; +} + +Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node) { + const GDScriptParser::DataType *current_data_type = &p_class_node->base_type; + while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) { + GDScriptParser::ClassNode *current_class_node = current_data_type->class_type; + if (has_member_name_conflict_in_script_class(p_member_name, current_class_node)) { + push_error(vformat(R"(The member "%s" already exists in a parent class.)", p_member_name), + p_member_node); + return ERR_PARSE_ERROR; + } + current_data_type = ¤t_class_node->base_type; + } + + if (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::NATIVE) { + if (current_data_type->native_type != StringName("")) { + return check_native_member_name_conflict( + p_member_name, + p_member_node, + current_data_type->native_type); + } + } + + return OK; +} + Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) { if (p_class->base_type.is_set()) { // Already resolved @@ -525,6 +595,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas switch (member.type) { case GDScriptParser::ClassNode::Member::VARIABLE: { + check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable); + GDScriptParser::DataType datatype; datatype.kind = GDScriptParser::DataType::VARIANT; datatype.type_source = GDScriptParser::DataType::UNDETECTED; @@ -598,6 +670,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::CONSTANT: { + check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant); + reduce_expression(member.constant->initializer); GDScriptParser::DataType specified_type; @@ -647,6 +721,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::SIGNAL: { + check_class_member_name_conflict(p_class, member.signal->identifier->name, member.signal); + for (int j = 0; j < member.signal->parameters.size(); j++) { GDScriptParser::DataType signal_type = resolve_datatype(member.signal->parameters[j]->datatype_specifier); signal_type.is_meta_type = false; @@ -666,6 +742,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::ENUM: { + check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum); + GDScriptParser::DataType enum_type; enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; enum_type.kind = GDScriptParser::DataType::ENUM; @@ -717,6 +795,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas break; case GDScriptParser::ClassNode::Member::ENUM_VALUE: { if (member.enum_value.custom_value) { + check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value); + current_enum = member.enum_value.parent_enum; reduce_expression(member.enum_value.custom_value); current_enum = nullptr; @@ -730,6 +810,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas member.enum_value.resolved = true; } } else { + check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.parent_enum); + if (member.enum_value.index > 0) { member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1; } else { @@ -742,7 +824,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas p_class->members.write[i].enum_value = member.enum_value; } break; case GDScriptParser::ClassNode::Member::CLASS: - break; // Done later. + check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class); + break; case GDScriptParser::ClassNode::Member::UNDEFINED: ERR_PRINT("Trying to resolve undefined member."); break; @@ -2411,6 +2494,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod case GDScriptParser::ClassNode::Member::VARIABLE: p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; p_identifier->variable_source = member.variable; + member.variable->usages += 1; break; case GDScriptParser::ClassNode::Member::FUNCTION: resolve_function_signature(member.function); @@ -2727,7 +2811,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { } else { p_preload->resolved_path = p_preload->path->reduced_value; // TODO: Save this as script dependency. - if (p_preload->resolved_path.is_rel_path()) { + if (p_preload->resolved_path.is_relative_path()) { p_preload->resolved_path = parser->script_path.get_base_dir().plus_file(p_preload->resolved_path); } p_preload->resolved_path = p_preload->resolved_path.simplify_path(); diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 32bf049fa1..2e17e15452 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -44,6 +44,12 @@ class GDScriptAnalyzer { const GDScriptParser::EnumNode *current_enum = nullptr; List<const GDScriptParser::LambdaNode *> lambda_stack; + // Tests for detecting invalid overloading of script members + static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node); + static _FORCE_INLINE_ bool has_member_name_conflict_in_native_type(const StringName &p_name, const StringName &p_native_type_string); + Error check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string); + Error check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node); + Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true); GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 5958326315..bed67b55f0 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -155,7 +155,7 @@ void GDScriptByteCodeGenerator::end_parameters() { function->default_arguments.reverse(); } -void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) { +void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) { function = memnew(GDScriptFunction); debug_stack = EngineDebugger::is_active(); diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 3d6fb291ad..ce1a043b28 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -419,7 +419,7 @@ public: virtual void start_block() override; virtual void end_block() override; - virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) override; + virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) override; virtual GDScriptFunction *write_end() override; #ifdef DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index ecc86c37f3..7713d13bc8 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -31,7 +31,7 @@ #ifndef GDSCRIPT_CODEGEN #define GDSCRIPT_CODEGEN -#include "core/io/multiplayer_api.h" +#include "core/multiplayer/multiplayer.h" #include "core/string/string_name.h" #include "core/variant/variant.h" #include "gdscript_function.h" @@ -80,7 +80,7 @@ public: virtual void start_block() = 0; virtual void end_block() = 0; - virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) = 0; + virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) = 0; virtual GDScriptFunction *write_end() = 0; #ifdef DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index ddf4f281b4..736f6eae79 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1857,7 +1857,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ StringName func_name; bool is_static = false; - MultiplayerAPI::RPCConfig rpc_config; + Multiplayer::RPCConfig rpc_config; GDScriptDataType return_type; return_type.has_type = true; return_type.kind = GDScriptDataType::BUILTIN; @@ -2086,7 +2086,7 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script); } - codegen.generator->write_start(p_script, func_name, false, MultiplayerAPI::RPCConfig(), return_type); + codegen.generator->write_start(p_script, func_name, false, Multiplayer::RPCConfig(), return_type); if (p_is_setter) { uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype())); @@ -2186,6 +2186,13 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->tool = parser->is_tool(); p_script->name = p_class->identifier ? p_class->identifier->name : ""; + if (p_script->name != "") { + if (ClassDB::class_exists(p_script->name) && ClassDB::is_class_exposed(p_script->name)) { + _set_error("The class '" + p_script->name + "' shadows a native class", p_class); + return ERR_ALREADY_EXISTS; + } + } + Ref<GDScriptNativeClass> native; GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type); @@ -2337,28 +2344,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar const GDScriptParser::SignalNode *signal = member.signal; StringName name = signal->identifier->name; - GDScript *c = p_script; - - while (c) { - if (c->_signals.has(name)) { - _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class); - return ERR_ALREADY_EXISTS; - } - - if (c->base.is_valid()) { - c = c->base.ptr(); - } else { - c = nullptr; - } - } - - if (native.is_valid()) { - if (ClassDB::has_signal(native->get_name(), name)) { - _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class); - return ERR_ALREADY_EXISTS; - } - } - Vector<StringName> parameters_names; parameters_names.resize(signal->parameters.size()); for (int j = 0; j < signal->parameters.size(); j++) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 372a726d71..f809a4dab8 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -731,9 +731,9 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio } // Autoload singletons - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->get(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.get(); if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") { continue; } @@ -1086,12 +1086,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool kwa++; } - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) { - if (!E->value().is_singleton) { + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + if (!E.value().is_singleton) { continue; } - ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_CONSTANT); + ScriptCodeCompletionOption option(E.key(), ScriptCodeCompletionOption::KIND_CONSTANT); r_result.insert(option.display, option); } @@ -1359,12 +1359,12 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]); found = true; } else { - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - String name = E->key(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + String name = E.key(); if (name == which) { - String script = E->value().path; + String script = E.value().path; if (!script.begins_with("res://")) { script = "res://" + script; @@ -2660,10 +2660,10 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } // Get autoloads. - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - String path = "/root/" + E->key(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + String path = "/root/" + E.key(); ScriptCodeCompletionOption option(path.quote(quote_style), ScriptCodeCompletionOption::KIND_NODE_PATH); options.insert(option.display, option); } @@ -3078,6 +3078,15 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co r_result.class_member = p_symbol; return OK; } + } else { + List<StringName> utility_functions; + Variant::get_utility_function_list(&utility_functions); + if (utility_functions.find(p_symbol) != nullptr) { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE; + r_result.class_name = "@GlobalScope"; + r_result.class_member = p_symbol; + return OK; + } } } } break; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 87d8c03494..b21cb47910 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -468,7 +468,7 @@ private: int _initial_line = 0; bool _static = false; - MultiplayerAPI::RPCConfig rpc_config; + Multiplayer::RPCConfig rpc_config; GDScript *_script = nullptr; @@ -588,7 +588,7 @@ public: void disassemble(const Vector<String> &p_code_lines) const; #endif - _FORCE_INLINE_ MultiplayerAPI::RPCConfig get_rpc_config() const { return rpc_config; } + _FORCE_INLINE_ Multiplayer::RPCConfig get_rpc_config() const { return rpc_config; } GDScriptFunction(); ~GDScriptFunction(); }; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index af872e49e2..64a393f90c 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -133,7 +133,7 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>); register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>); // Networking. - register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPET>, 4, true); + register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true); // TODO: Warning annotations. } @@ -2365,6 +2365,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode } assignment->assignee = p_previous_operand; assignment->assigned_value = parse_expression(false); + if (assignment->assigned_value == nullptr) { + push_error(R"(Expected an expression after "=".)"); + } #ifdef DEBUG_ENABLED if (has_operator && source_variable != nullptr && source_variable->assignments == 0) { @@ -2377,7 +2380,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_previous_operand, bool p_can_assign) { AwaitNode *await = alloc_node<AwaitNode>(); - await->to_await = parse_precedence(PREC_AWAIT, false); + ExpressionNode *element = parse_precedence(PREC_AWAIT, false); + if (element == nullptr) { + push_error(R"(Expected signal or coroutine after "await".)"); + } + await->to_await = element; current_function->is_coroutine = true; @@ -3386,55 +3393,47 @@ bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Nod ERR_FAIL_V_MSG(false, "Not implemented."); } -template <MultiplayerAPI::RPCMode t_mode> +template <Multiplayer::RPCMode t_mode> bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Node *p_node) { ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE && p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to variables and functions.)", p_annotation->name)); - MultiplayerAPI::RPCConfig rpc_config; + Multiplayer::RPCConfig rpc_config; rpc_config.rpc_mode = t_mode; - for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) { - if (i == 0) { + if (p_annotation->resolved_arguments.size()) { + int last = p_annotation->resolved_arguments.size() - 1; + if (p_annotation->resolved_arguments[last].get_type() == Variant::INT) { + rpc_config.channel = p_annotation->resolved_arguments[last].operator int(); + last -= 1; + } + if (last > 3) { + push_error(R"(Invalid RPC arguments. At most 4 arguments are allowed, where only the last argument can be an integer to specify the channel.')", p_annotation); + return false; + } + for (int i = last; i >= 0; i--) { String mode = p_annotation->resolved_arguments[i].operator String(); if (mode == "any") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_REMOTE; - } else if (mode == "master") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_MASTER; - } else if (mode == "puppet") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_PUPPET; - } else { - push_error(R"(Invalid RPC mode. Must be one of: 'any', 'master', or 'puppet')", p_annotation); - return false; - } - } else if (i == 1) { - String sync = p_annotation->resolved_arguments[i].operator String(); - if (sync == "sync") { + rpc_config.rpc_mode = Multiplayer::RPC_MODE_ANY; + } else if (mode == "auth") { + rpc_config.rpc_mode = Multiplayer::RPC_MODE_AUTHORITY; + } else if (mode == "sync") { rpc_config.sync = true; - } else if (sync == "nosync") { + } else if (mode == "nosync") { rpc_config.sync = false; - } else { - push_error(R"(Invalid RPC sync mode. Must be one of: 'sync' or 'nosync')", p_annotation); - return false; - } - } else if (i == 2) { - String mode = p_annotation->resolved_arguments[i].operator String(); - if (mode == "reliable") { - rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; + } else if (mode == "reliable") { + rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; } else if (mode == "unreliable") { - rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE; + rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE; } else if (mode == "ordered") { - rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED; + rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_ORDERED; } else { - push_error(R"(Invalid RPC transfer mode. Must be one of: 'reliable', 'unreliable', 'ordered')", p_annotation); - return false; + push_error(R"(Invalid RPC argument. Must be one of: 'sync'/'nosync' (local calls), 'any'/'auth' (permission), 'reliable'/'unreliable'/'ordered' (transfer mode).)", p_annotation); } - } else if (i == 3) { - rpc_config.channel = p_annotation->resolved_arguments[i].operator int(); } } switch (p_node->type) { case Node::FUNCTION: { FunctionNode *function = static_cast<FunctionNode *>(p_node); - if (function->rpc_config.rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) { + if (function->rpc_config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) { push_error(R"(RPC annotations can only be used once per function.)", p_annotation); return false; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 0bce8d7ddd..4902f0d4a6 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -31,8 +31,8 @@ #ifndef GDSCRIPT_PARSER_H #define GDSCRIPT_PARSER_H -#include "core/io/multiplayer_api.h" #include "core/io/resource.h" +#include "core/multiplayer/multiplayer.h" #include "core/object/ref_counted.h" #include "core/object/script_language.h" #include "core/string/string_name.h" @@ -729,7 +729,7 @@ public: SuiteNode *body = nullptr; bool is_static = false; bool is_coroutine = false; - MultiplayerAPI::RPCConfig rpc_config; + Multiplayer::RPCConfig rpc_config; MethodInfo info; LambdaNode *source_lambda = nullptr; #ifdef TOOLS_ENABLED @@ -1340,7 +1340,7 @@ private: template <PropertyHint t_hint, Variant::Type t_type> bool export_annotations(const AnnotationNode *p_annotation, Node *p_target); bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target); - template <MultiplayerAPI::RPCMode t_mode> + template <Multiplayer::RPCMode t_mode> bool network_annotations(const AnnotationNode *p_annotation, Node *p_target); // Statements. Node *parse_statement(); diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md new file mode 100644 index 0000000000..6e54085962 --- /dev/null +++ b/modules/gdscript/tests/README.md @@ -0,0 +1,8 @@ +# GDScript integration tests + +The `scripts/` folder contains integration tests in the form of GDScript files +and output files. + +See the +[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html#integration-tests-for-gdscript) +for information about creating and running GDScript integration tests. diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index 03a48bf071..6225e5d1eb 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -48,11 +48,11 @@ namespace GDScriptTests { void init_autoloads() { - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); // First pass, add the constants so they exist before any script is loaded. - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->get(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.get(); if (info.is_singleton) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { @@ -62,8 +62,8 @@ void init_autoloads() { } // Second pass, load into global constants. - for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { - const ProjectSettings::AutoloadInfo &info = E->get(); + for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) { + const ProjectSettings::AutoloadInfo &info = E.get(); if (!info.is_singleton) { // Skip non-singletons since we don't have a scene tree here anyway. diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out index dc4348d9c3..2c8cc9c03f 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out +++ b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out @@ -1,3 +1,3 @@ GDTEST_OK Name is equal -True +true diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out index 75b002aa62..ad6beeb646 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out +++ b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out @@ -1,3 +1,3 @@ GDTEST_OK -True +true OK diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.gd new file mode 100644 index 0000000000..17c65ad60a --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.gd @@ -0,0 +1,3 @@ +func test(): + var a = 0 + a = diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out new file mode 100644 index 0000000000..1369a7a0dc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected an expression after "=". diff --git a/modules/gdscript/tests/scripts/parser/features/str_preserves_case.gd b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.gd new file mode 100644 index 0000000000..9e48a7f0da --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.gd @@ -0,0 +1,7 @@ +func test(): + var null_var = null + var true_var: bool = true + var false_var: bool = false + print(str(null_var)) + print(str(true_var)) + print(str(false_var)) diff --git a/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out new file mode 100644 index 0000000000..abba38e87c --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out @@ -0,0 +1,4 @@ +GDTEST_OK +null +true +false diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 2cc974322d..fb741f6266 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -37,11 +37,13 @@ #include "core/io/file_access.h" -void AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { - ERR_FAIL_COND(!active); +int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { + ERR_FAIL_COND_V(!active, 0); int todo = p_frames; + int frames_mixed_this_step = p_frames; + while (todo && active) { mp3dec_frame_info_t frame_info; mp3d_sample_t *buf_frame = nullptr; @@ -60,6 +62,7 @@ void AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { seek(mp3_stream->loop_offset); loops++; } else { + frames_mixed_this_step = p_frames - todo; //fill remainder with silence for (int i = p_frames - todo; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); @@ -69,6 +72,7 @@ void AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { } } } + return frames_mixed_this_step; } float AudioStreamPlaybackMP3::get_stream_sampling_rate() { diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h index ce001fc418..5dd88779f8 100644 --- a/modules/minimp3/audio_stream_mp3.h +++ b/modules/minimp3/audio_stream_mp3.h @@ -51,7 +51,7 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled { Ref<AudioStreamMP3> mp3_stream; protected: - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; public: diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 590b95ab79..bbf1db689d 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -39,7 +39,7 @@ StringName MobileVRInterface::get_name() const { return "Native mobile"; }; -int MobileVRInterface::get_capabilities() const { +uint32_t MobileVRInterface::get_capabilities() const { return XRInterface::XR_STEREO; }; @@ -305,6 +305,10 @@ uint32_t MobileVRInterface::get_view_count() { return 2; }; +XRInterface::TrackingStatus MobileVRInterface::get_tracking_status() const { + return tracking_state; +} + bool MobileVRInterface::is_initialized() const { return (initialized); }; @@ -340,16 +344,16 @@ bool MobileVRInterface::initialize() { void MobileVRInterface::uninitialize() { if (initialized) { XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr) { + if (xr_server != nullptr && xr_server->get_primary_interface() == this) { // no longer our primary interface - xr_server->clear_primary_interface_if(this); + xr_server->set_primary_interface(nullptr); } initialized = false; }; }; -Size2 MobileVRInterface::get_render_targetsize() { +Size2 MobileVRInterface::get_render_target_size() { _THREAD_SAFE_METHOD_ // we use half our window size @@ -429,31 +433,6 @@ CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, real_t return eye; }; -void MobileVRInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { - _THREAD_SAFE_METHOD_ - - // We must have a valid render target - ERR_FAIL_COND(!p_render_target.is_valid()); - - // Because we are rendering to our device we must use our main viewport! - ERR_FAIL_COND(p_screen_rect == Rect2()); - - Rect2 dest = p_screen_rect; - Vector2 eye_center; - - // we output half a screen - dest.size.x *= 0.5; - - if (p_eye == XRInterface::EYE_LEFT) { - eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0); - } else if (p_eye == XRInterface::EYE_RIGHT) { - dest.position.x = dest.size.x; - eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); - } - // we don't offset the eye center vertically (yet) - eye_center.y = 0.0; -} - Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { _THREAD_SAFE_METHOD_ @@ -476,16 +455,16 @@ Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const blit.lens_distortion.aspect_ratio = aspect; // left eye - blit.rect = p_screen_rect; - blit.rect.size.width *= 0.5; + blit.dst_rect = p_screen_rect; + blit.dst_rect.size.width *= 0.5; blit.multi_view.layer = 0; blit.lens_distortion.eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0); blit_to_screen.push_back(blit); // right eye - blit.rect = p_screen_rect; - blit.rect.size.width *= 0.5; - blit.rect.position.x = blit.rect.size.width; + blit.dst_rect = p_screen_rect; + blit.dst_rect.size.width *= 0.5; + blit.dst_rect.position.x = blit.dst_rect.size.width; blit.multi_view.layer = 1; blit.lens_distortion.eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); blit_to_screen.push_back(blit); diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index 0c05dc1ebb..48b76ec187 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -52,6 +52,7 @@ class MobileVRInterface : public XRInterface { private: bool initialized = false; + XRInterface::TrackingStatus tracking_state; Basis orientation; // Just set some defaults for these. At some point we need to look at adding a lookup table for common device + headset combos and/or support reading cardboard QR codes @@ -131,13 +132,15 @@ public: real_t get_k2() const; virtual StringName get_name() const override; - virtual int get_capabilities() const override; + virtual uint32_t get_capabilities() const override; + + virtual TrackingStatus get_tracking_status() const override; virtual bool is_initialized() const override; virtual bool initialize() override; virtual void uninitialize() override; - virtual Size2 get_render_targetsize() override; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; @@ -145,13 +148,9 @@ public: virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; virtual void process() override; - virtual void notification(int p_what) override {} MobileVRInterface(); ~MobileVRInterface(); - - // deprecated - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; }; #endif // !MOBILE_VR_INTERFACE_H diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 1f7f1390ea..98519707a9 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2124,7 +2124,7 @@ bool CSharpInstance::refcount_decremented() { return ref_dying; } -const Vector<MultiplayerAPI::RPCConfig> CSharpInstance::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> CSharpInstance::get_rpc_methods() const { return script->get_rpc_methods(); } @@ -3034,13 +3034,13 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) { Vector<GDMonoMethod *> methods = top->get_all_methods(); for (int i = 0; i < methods.size(); i++) { if (!methods[i]->is_static()) { - MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]); - if (MultiplayerAPI::RPC_MODE_DISABLED != mode) { - MultiplayerAPI::RPCConfig nd; + Multiplayer::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]); + if (Multiplayer::RPC_MODE_DISABLED != mode) { + Multiplayer::RPCConfig nd; nd.name = methods[i]->get_name(); nd.rpc_mode = mode; // TODO Transfer mode, channel - nd.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; + nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; nd.channel = 0; if (-1 == p_script->rpc_functions.find(nd)) { p_script->rpc_functions.push_back(nd); @@ -3054,7 +3054,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) { } // Sort so we are 100% that they are always the same. - p_script->rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>(); + p_script->rpc_functions.sort_custom<Multiplayer::SortRPCConfig>(); p_script->load_script_signals(p_script->script_class, p_script->native); } @@ -3464,21 +3464,18 @@ int CSharpScript::get_member_line(const StringName &p_member) const { return -1; } -MultiplayerAPI::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const { +Multiplayer::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const { if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) { - return MultiplayerAPI::RPC_MODE_REMOTE; - } - if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) { - return MultiplayerAPI::RPC_MODE_MASTER; + return Multiplayer::RPC_MODE_ANY; } if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute))) { - return MultiplayerAPI::RPC_MODE_PUPPET; + return Multiplayer::RPC_MODE_AUTHORITY; } - return MultiplayerAPI::RPC_MODE_DISABLED; + return Multiplayer::RPC_MODE_DISABLED; } -const Vector<MultiplayerAPI::RPCConfig> CSharpScript::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> CSharpScript::get_rpc_methods() const { return rpc_functions; } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 4552f376d0..e3bbb20dec 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -136,7 +136,7 @@ private: Map<StringName, EventSignal> event_signals; bool signals_invalidated = true; - Vector<MultiplayerAPI::RPCConfig> rpc_functions; + Vector<Multiplayer::RPCConfig> rpc_functions; #ifdef TOOLS_ENABLED List<PropertyInfo> exported_members_cache; // members_cache @@ -179,7 +179,7 @@ private: static void update_script_class_info(Ref<CSharpScript> p_script); static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native); - MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const; + Multiplayer::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const; protected: static void _bind_methods(); @@ -234,7 +234,7 @@ public: int get_member_line(const StringName &p_member) const override; - const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override; + const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override; #ifdef TOOLS_ENABLED bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; } @@ -311,7 +311,7 @@ public: void refcount_incremented() override; bool refcount_decremented() override; - const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override; + const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override; void notification(int p_notification) override; void _call_notification(int p_notification); diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs index 9b7b422276..2bf1cb7a18 100644 --- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs @@ -12,12 +12,16 @@ namespace GodotTools.BuildLogger public string Parameters { get; set; } public LoggerVerbosity Verbosity { get; set; } + private StreamWriter _logStreamWriter; + private StreamWriter _issuesStreamWriter; + private int _indent; + public void Initialize(IEventSource eventSource) { if (null == Parameters) throw new LoggerException("Log directory parameter not specified."); - var parameters = Parameters.Split(new[] { ';' }); + string[] parameters = Parameters.Split(new[] { ';' }); string logDir = parameters[0]; @@ -35,8 +39,8 @@ namespace GodotTools.BuildLogger if (!Directory.Exists(logDir)) Directory.CreateDirectory(logDir); - logStreamWriter = new StreamWriter(logFile); - issuesStreamWriter = new StreamWriter(issuesFile); + _logStreamWriter = new StreamWriter(logFile); + _issuesStreamWriter = new StreamWriter(issuesFile); } catch (Exception ex) { @@ -66,12 +70,12 @@ namespace GodotTools.BuildLogger private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e) { WriteLine(e.Message); - indent++; + _indent++; } private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e) { - indent--; + _indent--; WriteLine(e.Message); } @@ -87,7 +91,7 @@ namespace GodotTools.BuildLogger string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," + $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," + $"{e.ProjectFile?.CsvEscape() ?? string.Empty}"; - issuesStreamWriter.WriteLine(errorLine); + _issuesStreamWriter.WriteLine(errorLine); } private void eventSource_WarningRaised(object sender, BuildWarningEventArgs e) @@ -102,7 +106,7 @@ namespace GodotTools.BuildLogger string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," + $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," + $"{e.ProjectFile?.CsvEscape() ?? string.Empty}"; - issuesStreamWriter.WriteLine(warningLine); + _issuesStreamWriter.WriteLine(warningLine); } private void eventSource_MessageRaised(object sender, BuildMessageEventArgs e) @@ -136,28 +140,24 @@ namespace GodotTools.BuildLogger private void WriteLine(string line) { - for (int i = indent; i > 0; i--) + for (int i = _indent; i > 0; i--) { - logStreamWriter.Write("\t"); + _logStreamWriter.Write("\t"); } - logStreamWriter.WriteLine(line); + _logStreamWriter.WriteLine(line); } public void Shutdown() { - logStreamWriter.Close(); - issuesStreamWriter.Close(); + _logStreamWriter.Close(); + _issuesStreamWriter.Close(); } private bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity) { return Verbosity >= checkVerbosity; } - - private StreamWriter logStreamWriter; - private StreamWriter issuesStreamWriter; - private int indent; } internal static class StringExtensions diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs index 43d40f2ad9..a4d7dedbd5 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs @@ -7,7 +7,7 @@ namespace GodotTools.Core { public static class ProcessExtensions { - public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default(CancellationToken)) + public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default) { var tcs = new TaskCompletionSource<bool>(); diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs index b217ae4bf7..60a4f297c9 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs @@ -7,6 +7,8 @@ namespace GodotTools.Core { public static class StringExtensions { + private static readonly string _driveRoot = Path.GetPathRoot(Environment.CurrentDirectory); + public static string RelativeToPath(this string path, string dir) { // Make sure the directory ends with a path separator @@ -49,13 +51,11 @@ namespace GodotTools.Core return Path.DirectorySeparatorChar + path; } - private static readonly string DriveRoot = Path.GetPathRoot(Environment.CurrentDirectory); - public static bool IsAbsolutePath(this string path) { return path.StartsWith("/", StringComparison.Ordinal) || path.StartsWith("\\", StringComparison.Ordinal) || - path.StartsWith(DriveRoot, StringComparison.Ordinal); + path.StartsWith(_driveRoot, StringComparison.Ordinal); } public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false) diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs index 284e94810a..355b21d63a 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs @@ -9,15 +9,40 @@ namespace GodotTools.ProjectEditor { public class DotNetSolution { - private string directoryPath; - private readonly Dictionary<string, ProjectInfo> projects = new Dictionary<string, ProjectInfo>(); + private const string _solutionTemplate = +@"Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +{0} +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution +{1} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution +{2} + EndGlobalSection +EndGlobal +"; + + private const string _projectDeclaration = +@"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""{1}"", ""{{{2}}}"" +EndProject"; + + private const string _solutionPlatformsConfig = +@" {0}|Any CPU = {0}|Any CPU"; + + private const string _projectPlatformsConfig = +@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU + {{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU"; + + private string _directoryPath; + private readonly Dictionary<string, ProjectInfo> _projects = new Dictionary<string, ProjectInfo>(); public string Name { get; } public string DirectoryPath { - get => directoryPath; - set => directoryPath = value.IsAbsolutePath() ? value : Path.GetFullPath(value); + get => _directoryPath; + set => _directoryPath = value.IsAbsolutePath() ? value : Path.GetFullPath(value); } public class ProjectInfo @@ -29,22 +54,22 @@ namespace GodotTools.ProjectEditor public void AddNewProject(string name, ProjectInfo projectInfo) { - projects[name] = projectInfo; + _projects[name] = projectInfo; } public bool HasProject(string name) { - return projects.ContainsKey(name); + return _projects.ContainsKey(name); } public ProjectInfo GetProjectInfo(string name) { - return projects[name]; + return _projects[name]; } public bool RemoveProject(string name) { - return projects.Remove(name); + return _projects.Remove(name); } public void Save() @@ -58,7 +83,7 @@ namespace GodotTools.ProjectEditor bool isFirstProject = true; - foreach (var pair in projects) + foreach (var pair in _projects) { string name = pair.Key; ProjectInfo projectInfo = pair.Value; @@ -66,7 +91,7 @@ namespace GodotTools.ProjectEditor if (!isFirstProject) projectsDecl += "\n"; - projectsDecl += string.Format(ProjectDeclaration, + projectsDecl += string.Format(_projectDeclaration, name, projectInfo.PathRelativeToSolution.Replace("/", "\\"), projectInfo.Guid); for (int i = 0; i < projectInfo.Configs.Count; i++) @@ -79,15 +104,15 @@ namespace GodotTools.ProjectEditor projPlatformsCfg += "\n"; } - slnPlatformsCfg += string.Format(SolutionPlatformsConfig, config); - projPlatformsCfg += string.Format(ProjectPlatformsConfig, projectInfo.Guid, config); + slnPlatformsCfg += string.Format(_solutionPlatformsConfig, config); + projPlatformsCfg += string.Format(_projectPlatformsConfig, projectInfo.Guid, config); } isFirstProject = false; } string solutionPath = Path.Combine(DirectoryPath, Name + ".sln"); - string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg); + string content = string.Format(_solutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg); File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM } @@ -97,37 +122,12 @@ namespace GodotTools.ProjectEditor Name = name; } - const string SolutionTemplate = -@"Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -{0} -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution -{1} - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution -{2} - EndGlobalSection -EndGlobal -"; - - const string ProjectDeclaration = -@"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""{1}"", ""{{{2}}}"" -EndProject"; - - const string SolutionPlatformsConfig = -@" {0}|Any CPU = {0}|Any CPU"; - - const string ProjectPlatformsConfig = -@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU - {{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU"; - public static void MigrateFromOldConfigNames(string slnPath) { if (!File.Exists(slnPath)) return; - var input = File.ReadAllText(slnPath); + string input = File.ReadAllText(slnPath); if (!Regex.IsMatch(input, Regex.Escape("Tools|Any CPU"))) return; @@ -151,7 +151,7 @@ EndProject"; }; var regex = new Regex(string.Join("|", dict.Keys.Select(Regex.Escape))); - var result = regex.Replace(input, m => dict[m.Value]); + string result = regex.Replace(input, m => dict[m.Value]); if (result != input) { diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs index ed77076df3..31363df9ef 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs @@ -91,7 +91,7 @@ namespace GodotTools.ProjectEditor return identifier; } - static bool IsKeyword(string value, bool anyDoubleUnderscore) + private static bool IsKeyword(string value, bool anyDoubleUnderscore) { // Identifiers that start with double underscore are meant to be used for reserved keywords. // Only existing keywords are enforced, but it may be useful to forbid any identifier @@ -103,14 +103,14 @@ namespace GodotTools.ProjectEditor } else { - if (DoubleUnderscoreKeywords.Contains(value)) + if (_doubleUnderscoreKeywords.Contains(value)) return true; } - return Keywords.Contains(value); + return _keywords.Contains(value); } - private static readonly HashSet<string> DoubleUnderscoreKeywords = new HashSet<string> + private static readonly HashSet<string> _doubleUnderscoreKeywords = new HashSet<string> { "__arglist", "__makeref", @@ -118,7 +118,7 @@ namespace GodotTools.ProjectEditor "__refvalue", }; - private static readonly HashSet<string> Keywords = new HashSet<string> + private static readonly HashSet<string> _keywords = new HashSet<string> { "as", "do", diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs index 27737c3da0..28bf57dc21 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs @@ -13,7 +13,8 @@ namespace GodotTools.Build public string[] Targets { get; } public string Configuration { get; } public bool Restore { get; } - public Array<string> CustomProperties { get; } = new Array<string>(); // TODO Use List once we have proper serialization + // TODO Use List once we have proper serialization + public Array<string> CustomProperties { get; } = new Array<string>(); public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}"); @@ -32,12 +33,12 @@ namespace GodotTools.Build unchecked { int hash = 17; - hash = hash * 29 + Solution.GetHashCode(); - hash = hash * 29 + Targets.GetHashCode(); - hash = hash * 29 + Configuration.GetHashCode(); - hash = hash * 29 + Restore.GetHashCode(); - hash = hash * 29 + CustomProperties.GetHashCode(); - hash = hash * 29 + LogsDirPath.GetHashCode(); + hash = (hash * 29) + Solution.GetHashCode(); + hash = (hash * 29) + Targets.GetHashCode(); + hash = (hash * 29) + Configuration.GetHashCode(); + hash = (hash * 29) + Restore.GetHashCode(); + hash = (hash * 29) + CustomProperties.GetHashCode(); + hash = (hash * 29) + LogsDirPath.GetHashCode(); return hash; } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs index 2b6f972529..21bff70b15 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs @@ -32,7 +32,7 @@ namespace GodotTools.Build private static void RemoveOldIssuesFile(BuildInfo buildInfo) { - var issuesFile = GetIssuesFilePath(buildInfo); + string issuesFile = GetIssuesFilePath(buildInfo); if (!File.Exists(issuesFile)) return; diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs index 25e260beed..b53347fc4c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs @@ -22,14 +22,6 @@ namespace GodotTools.Build public string ProjectFile { get; set; } } - private readonly Array<BuildIssue> issues = new Array<BuildIssue>(); // TODO Use List once we have proper serialization - private ItemList issuesList; - private TextEdit buildLog; - private PopupMenu issuesListContextMenu; - - private readonly object pendingBuildLogTextLock = new object(); - [NotNull] private string pendingBuildLogText = string.Empty; - [Signal] public event Action BuildStateChanged; public bool HasBuildExited { get; private set; } = false; @@ -60,13 +52,21 @@ namespace GodotTools.Build } } - private BuildInfo BuildInfo { get; set; } - public bool LogVisible { - set => buildLog.Visible = value; + set => _buildLog.Visible = value; } + // TODO Use List once we have proper serialization. + private readonly Array<BuildIssue> _issues = new Array<BuildIssue>(); + private ItemList _issuesList; + private PopupMenu _issuesListContextMenu; + private TextEdit _buildLog; + private BuildInfo _buildInfo; + + private readonly object _pendingBuildLogTextLock = new object(); + [NotNull] private string _pendingBuildLogText = string.Empty; + private void LoadIssuesFromFile(string csvFile) { using (var file = new Godot.File()) @@ -107,7 +107,7 @@ namespace GodotTools.Build else ErrorCount += 1; - issues.Add(issue); + _issues.Add(issue); } } finally @@ -119,21 +119,21 @@ namespace GodotTools.Build private void IssueActivated(int idx) { - if (idx < 0 || idx >= issuesList.GetItemCount()) + if (idx < 0 || idx >= _issuesList.GetItemCount()) throw new IndexOutOfRangeException("Item list index out of range"); // Get correct issue idx from issue list - int issueIndex = (int)(long)issuesList.GetItemMetadata(idx); + int issueIndex = (int)(long)_issuesList.GetItemMetadata(idx); - if (issueIndex < 0 || issueIndex >= issues.Count) + if (issueIndex < 0 || issueIndex >= _issues.Count) throw new IndexOutOfRangeException("Issue index out of range"); - BuildIssue issue = issues[issueIndex]; + BuildIssue issue = _issues[issueIndex]; if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File)) return; - string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : BuildInfo.Solution.GetBaseDir(); + string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : _buildInfo.Solution.GetBaseDir(); string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath()); @@ -153,14 +153,14 @@ namespace GodotTools.Build public void UpdateIssuesList() { - issuesList.Clear(); + _issuesList.Clear(); using (var warningIcon = GetThemeIcon("Warning", "EditorIcons")) using (var errorIcon = GetThemeIcon("Error", "EditorIcons")) { - for (int i = 0; i < issues.Count; i++) + for (int i = 0; i < _issues.Count; i++) { - BuildIssue issue = issues[i]; + BuildIssue issue = _issues[i]; if (!(issue.Warning ? WarningsVisible : ErrorsVisible)) continue; @@ -191,11 +191,11 @@ namespace GodotTools.Build int lineBreakIdx = text.IndexOf("\n", StringComparison.Ordinal); string itemText = lineBreakIdx == -1 ? text : text.Substring(0, lineBreakIdx); - issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon); + _issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon); - int index = issuesList.GetItemCount() - 1; - issuesList.SetItemTooltip(index, tooltip); - issuesList.SetItemMetadata(index, i); + int index = _issuesList.GetItemCount() - 1; + _issuesList.SetItemTooltip(index, tooltip); + _issuesList.SetItemMetadata(index, i); } } } @@ -205,12 +205,12 @@ namespace GodotTools.Build HasBuildExited = true; BuildResult = Build.BuildResult.Error; - issuesList.Clear(); + _issuesList.Clear(); var issue = new BuildIssue {Message = cause, Warning = false}; ErrorCount += 1; - issues.Add(issue); + _issues.Add(issue); UpdateIssuesList(); @@ -219,13 +219,13 @@ namespace GodotTools.Build private void BuildStarted(BuildInfo buildInfo) { - BuildInfo = buildInfo; + _buildInfo = buildInfo; HasBuildExited = false; - issues.Clear(); + _issues.Clear(); WarningCount = 0; ErrorCount = 0; - buildLog.Text = string.Empty; + _buildLog.Text = string.Empty; UpdateIssuesList(); @@ -237,7 +237,7 @@ namespace GodotTools.Build HasBuildExited = true; BuildResult = result; - LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName)); + LoadIssuesFromFile(Path.Combine(_buildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName)); UpdateIssuesList(); @@ -246,46 +246,46 @@ namespace GodotTools.Build private void UpdateBuildLogText() { - lock (pendingBuildLogTextLock) + lock (_pendingBuildLogTextLock) { - buildLog.Text += pendingBuildLogText; - pendingBuildLogText = string.Empty; + _buildLog.Text += _pendingBuildLogText; + _pendingBuildLogText = string.Empty; ScrollToLastNonEmptyLogLine(); } } private void StdOutputReceived(string text) { - lock (pendingBuildLogTextLock) + lock (_pendingBuildLogTextLock) { - if (pendingBuildLogText.Length == 0) + if (_pendingBuildLogText.Length == 0) CallDeferred(nameof(UpdateBuildLogText)); - pendingBuildLogText += text + "\n"; + _pendingBuildLogText += text + "\n"; } } private void StdErrorReceived(string text) { - lock (pendingBuildLogTextLock) + lock (_pendingBuildLogTextLock) { - if (pendingBuildLogText.Length == 0) + if (_pendingBuildLogText.Length == 0) CallDeferred(nameof(UpdateBuildLogText)); - pendingBuildLogText += text + "\n"; + _pendingBuildLogText += text + "\n"; } } private void ScrollToLastNonEmptyLogLine() { int line; - for (line = buildLog.GetLineCount(); line > 0; line--) + for (line = _buildLog.GetLineCount(); line > 0; line--) { - string lineText = buildLog.GetLine(line); + string lineText = _buildLog.GetLine(line); if (!string.IsNullOrEmpty(lineText) || !string.IsNullOrEmpty(lineText?.Trim())) break; } - buildLog.SetCaretLine(line); + _buildLog.SetCaretLine(line); } public void RestartBuild() @@ -318,11 +318,11 @@ namespace GodotTools.Build // We don't allow multi-selection but just in case that changes later... string text = null; - foreach (int issueIndex in issuesList.GetSelectedItems()) + foreach (int issueIndex in _issuesList.GetSelectedItems()) { if (text != null) text += "\n"; - text += issuesList.GetItemText(issueIndex); + text += _issuesList.GetItemText(issueIndex); } if (text != null) @@ -338,20 +338,20 @@ namespace GodotTools.Build { _ = index; // Unused - issuesListContextMenu.Clear(); - issuesListContextMenu.Size = new Vector2i(1, 1); + _issuesListContextMenu.Clear(); + _issuesListContextMenu.Size = new Vector2i(1, 1); - if (issuesList.IsAnythingSelected()) + if (_issuesList.IsAnythingSelected()) { // Add menu entries for the selected item - issuesListContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"), + _issuesListContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"), label: "Copy Error".TTR(), (int)IssuesContextMenuOption.Copy); } - if (issuesListContextMenu.GetItemCount() > 0) + if (_issuesListContextMenu.GetItemCount() > 0) { - issuesListContextMenu.Position = (Vector2i)(issuesList.RectGlobalPosition + atPosition); - issuesListContextMenu.Popup(); + _issuesListContextMenu.Position = (Vector2i)(_issuesList.RectGlobalPosition + atPosition); + _issuesListContextMenu.Popup(); } } @@ -368,27 +368,27 @@ namespace GodotTools.Build }; AddChild(hsc); - issuesList = new ItemList + _issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill, SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the build log }; - issuesList.ItemActivated += IssueActivated; - issuesList.AllowRmbSelect = true; - issuesList.ItemRmbSelected += IssuesListRmbSelected; - hsc.AddChild(issuesList); + _issuesList.ItemActivated += IssueActivated; + _issuesList.AllowRmbSelect = true; + _issuesList.ItemRmbSelected += IssuesListRmbSelected; + hsc.AddChild(_issuesList); - issuesListContextMenu = new PopupMenu(); - issuesListContextMenu.IdPressed += IssuesListContextOptionPressed; - issuesList.AddChild(issuesListContextMenu); + _issuesListContextMenu = new PopupMenu(); + _issuesListContextMenu.IdPressed += IssuesListContextOptionPressed; + _issuesList.AddChild(_issuesListContextMenu); - buildLog = new TextEdit + _buildLog = new TextEdit { Editable = false, SizeFlagsVertical = (int)SizeFlags.ExpandFill, SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list }; - hsc.AddChild(buildLog); + hsc.AddChild(_buildLog); AddBuildEventListeners(); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 5f35d506de..e9cf7911be 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -11,10 +11,10 @@ namespace GodotTools.Build { public BuildOutputView BuildOutputView { get; private set; } - private MenuButton buildMenuBtn; - private Button errorsBtn; - private Button warningsBtn; - private Button viewLogBtn; + private MenuButton _buildMenuBtn; + private Button _errorsBtn; + private Button _warningsBtn; + private Button _viewLogBtn; private void WarningsToggled(bool pressed) { @@ -132,16 +132,16 @@ namespace GodotTools.Build var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill }; AddChild(toolBarHBox); - buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") }; - toolBarHBox.AddChild(buildMenuBtn); + _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") }; + toolBarHBox.AddChild(_buildMenuBtn); - var buildMenu = buildMenuBtn.GetPopup(); + var buildMenu = _buildMenuBtn.GetPopup(); buildMenu.AddItem("Build Solution".TTR(), (int)BuildMenuOptions.BuildSolution); buildMenu.AddItem("Rebuild Solution".TTR(), (int)BuildMenuOptions.RebuildSolution); buildMenu.AddItem("Clean Solution".TTR(), (int)BuildMenuOptions.CleanSolution); buildMenu.IdPressed += BuildMenuOptionPressed; - errorsBtn = new Button + _errorsBtn = new Button { HintTooltip = "Show Errors".TTR(), Icon = GetThemeIcon("StatusError", "EditorIcons"), @@ -150,10 +150,10 @@ namespace GodotTools.Build Pressed = true, FocusMode = FocusModeEnum.None }; - errorsBtn.Toggled += ErrorsToggled; - toolBarHBox.AddChild(errorsBtn); + _errorsBtn.Toggled += ErrorsToggled; + toolBarHBox.AddChild(_errorsBtn); - warningsBtn = new Button + _warningsBtn = new Button { HintTooltip = "Show Warnings".TTR(), Icon = GetThemeIcon("NodeWarning", "EditorIcons"), @@ -162,18 +162,18 @@ namespace GodotTools.Build Pressed = true, FocusMode = FocusModeEnum.None }; - warningsBtn.Toggled += WarningsToggled; - toolBarHBox.AddChild(warningsBtn); + _warningsBtn.Toggled += WarningsToggled; + toolBarHBox.AddChild(_warningsBtn); - viewLogBtn = new Button + _viewLogBtn = new Button { Text = "Show Output".TTR(), ToggleMode = true, Pressed = true, FocusMode = FocusModeEnum.None }; - viewLogBtn.Toggled += ViewLogToggled; - toolBarHBox.AddChild(viewLogBtn); + _viewLogBtn.Toggled += ViewLogToggled; + toolBarHBox.AddChild(_viewLogBtn); BuildOutputView = new BuildOutputView(); AddChild(BuildOutputView); @@ -185,12 +185,12 @@ namespace GodotTools.Build if (what == NotificationThemeChanged) { - if (buildMenuBtn != null) - buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons"); - if (errorsBtn != null) - errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons"); - if (warningsBtn != null) - warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons"); + if (_buildMenuBtn != null) + _buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons"); + if (_errorsBtn != null) + _errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons"); + if (_warningsBtn != null) + _warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons"); } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs index 774c49e705..a859c6f717 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs @@ -61,7 +61,7 @@ namespace GodotTools.Build } case BuildTool.JetBrainsMsBuild: { - var editorPath = (string)editorSettings.GetSetting(RiderPathManager.EditorPathSettingName); + string editorPath = (string)editorSettings.GetSetting(RiderPathManager.EditorPathSettingName); if (!File.Exists(editorPath)) throw new FileNotFoundException($"Cannot find Rider executable. Tried with path: {editorPath}"); @@ -165,7 +165,9 @@ namespace GodotTools.Build // Try to find 15.0 with vswhere - var envNames = Internal.GodotIs32Bits() ? new[] {"ProgramFiles", "ProgramW6432"} : new[] {"ProgramFiles(x86)", "ProgramFiles"}; + string[] envNames = Internal.GodotIs32Bits() ? + envNames = new[] { "ProgramFiles", "ProgramW6432" } : + envNames = new[] { "ProgramFiles(x86)", "ProgramFiles" }; string vsWherePath = null; foreach (var envName in envNames) diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs index f69307104f..37e6a34977 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs @@ -65,12 +65,12 @@ namespace GodotTools.Export if (platform == OS.Platforms.iOS) { - var architectures = GetEnablediOSArchs(features).ToArray(); + string[] architectures = GetEnablediOSArchs(features).ToArray(); CompileAssembliesForiOS(exporter, isDebug, architectures, aotOpts, aotTempDir, assembliesPrepared, bclDir); } else if (platform == OS.Platforms.Android) { - var abis = GetEnabledAndroidAbis(features).ToArray(); + string[] abis = GetEnabledAndroidAbis(features).ToArray(); CompileAssembliesForAndroid(exporter, isDebug, abis, aotOpts, aotTempDir, assembliesPrepared, bclDir); } else @@ -138,7 +138,8 @@ namespace GodotTools.Export } else { - string outputDataLibDir = Path.Combine(outputDataDir, "Mono", platform == OS.Platforms.Windows ? "bin" : "lib"); + string libDir = platform == OS.Platforms.Windows ? "bin" : "lib"; + string outputDataLibDir = Path.Combine(outputDataDir, "Mono", libDir); File.Copy(tempOutputFilePath, Path.Combine(outputDataLibDir, outputFileName)); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 0b5aa72a81..3e46a89b7c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -20,7 +20,7 @@ namespace GodotTools.Export public class ExportPlugin : EditorExportPlugin { [Flags] - enum I18NCodesets : long + private enum I18NCodesets : long { None = 0, CJK = 1, @@ -31,6 +31,8 @@ namespace GodotTools.Export All = CJK | MidEast | Other | Rare | West } + private string _maybeLastExportError; + private void AddI18NAssemblies(Godot.Collections.Dictionary<string, string> assemblies, string bclDir) { var codesets = (I18NCodesets)ProjectSettings.GetSetting("mono/export/i18n_codesets"); @@ -83,8 +85,6 @@ namespace GodotTools.Export GlobalDef("mono/export/aot/android_toolchain_path", ""); } - private string maybeLastExportError; - private void AddFile(string srcPath, string dstPath, bool remap = false) { // Add file to the PCK @@ -129,14 +129,14 @@ namespace GodotTools.Export } catch (Exception e) { - maybeLastExportError = e.Message; + _maybeLastExportError = e.Message; // 'maybeLastExportError' cannot be null or empty if there was an error, so we // must consider the possibility of exceptions being thrown without a message. - if (string.IsNullOrEmpty(maybeLastExportError)) - maybeLastExportError = $"Exception thrown: {e.GetType().Name}"; + if (string.IsNullOrEmpty(_maybeLastExportError)) + _maybeLastExportError = $"Exception thrown: {e.GetType().Name}"; - GD.PushError($"Failed to export project: {maybeLastExportError}"); + GD.PushError($"Failed to export project: {_maybeLastExportError}"); Console.Error.WriteLine(e); // TODO: Do something on error once _ExportBegin supports failing. } @@ -317,10 +317,10 @@ namespace GodotTools.Export Directory.Delete(aotTempDir, recursive: true); // TODO: Just a workaround until the export plugins can be made to abort with errors - if (!string.IsNullOrEmpty(maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading + if (!string.IsNullOrEmpty(_maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading { - string lastExportError = maybeLastExportError; - maybeLastExportError = null; + string lastExportError = _maybeLastExportError; + _maybeLastExportError = null; GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "Failed to export C# project"); } @@ -470,7 +470,7 @@ namespace GodotTools.Export private static string DetermineDataDirNameForProject() { - var appName = (string)ProjectSettings.GetSetting("application/config/name"); + string appName = (string)ProjectSettings.GetSetting("application/config/name"); string appNameSafe = appName.ToSafeDirName(); return $"data_{appNameSafe}"; } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 1faa6eeac0..73cabf8561 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -21,18 +21,19 @@ namespace GodotTools { public class GodotSharpEditor : EditorPlugin, ISerializationListener { - private EditorSettings editorSettings; + private EditorSettings _editorSettings; - private PopupMenu menuPopup; + private PopupMenu _menuPopup; - private AcceptDialog errorDialog; + private AcceptDialog _errorDialog; - private Button bottomPanelBtn; - private Button toolBarBuildButton; + private Button _bottomPanelBtn; + private Button _toolBarBuildButton; - public GodotIdeManager GodotIdeManager { get; private set; } + // TODO Use WeakReference once we have proper serialization. + private WeakRef _exportPluginWeak; - private WeakRef exportPluginWeak; // TODO Use WeakReference once we have proper serialization + public GodotIdeManager GodotIdeManager { get; private set; } public MSBuildPanel MSBuildPanel { get; private set; } @@ -42,7 +43,7 @@ namespace GodotTools { get { - var projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name"); + string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name"); projectAssemblyName = projectAssemblyName.ToSafeDirName(); if (string.IsNullOrEmpty(projectAssemblyName)) projectAssemblyName = "UnnamedProject"; @@ -123,9 +124,9 @@ namespace GodotTools private void _RemoveCreateSlnMenuOption() { - menuPopup.RemoveItem(menuPopup.GetItemIndex((int)MenuOptions.CreateSln)); - bottomPanelBtn.Show(); - toolBarBuildButton.Show(); + _menuPopup.RemoveItem(_menuPopup.GetItemIndex((int)MenuOptions.CreateSln)); + _bottomPanelBtn.Show(); + _toolBarBuildButton.Show(); } private void _MenuOptionPressed(int id) @@ -181,9 +182,9 @@ namespace GodotTools public void ShowErrorDialog(string message, string title = "Error") { - errorDialog.Title = title; - errorDialog.DialogText = message; - errorDialog.PopupCentered(); + _errorDialog.Title = title; + _errorDialog.DialogText = message; + _errorDialog.PopupCentered(); } private static string _vsCodePath = string.Empty; @@ -196,7 +197,7 @@ namespace GodotTools [UsedImplicitly] public Error OpenInExternalEditor(Script script, int line, int col) { - var editorId = (ExternalEditorId)editorSettings.GetSetting("mono/editor/external_editor"); + var editorId = (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor"); switch (editorId) { @@ -287,7 +288,7 @@ namespace GodotTools } } - var resourcePath = ProjectSettings.GlobalizePath("res://"); + string resourcePath = ProjectSettings.GlobalizePath("res://"); args.Add(resourcePath); string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath); @@ -346,7 +347,7 @@ namespace GodotTools [UsedImplicitly] public bool OverridesExternalEditor() { - return (ExternalEditorId)editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None; + return (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None; } public override bool _Build() @@ -387,8 +388,8 @@ namespace GodotTools private void BuildStateChanged() { - if (bottomPanelBtn != null) - bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon; + if (_bottomPanelBtn != null) + _bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon; } public override void _EnablePlugin() @@ -402,29 +403,29 @@ namespace GodotTools var editorInterface = GetEditorInterface(); var editorBaseControl = editorInterface.GetBaseControl(); - editorSettings = editorInterface.GetEditorSettings(); + _editorSettings = editorInterface.GetEditorSettings(); - errorDialog = new AcceptDialog(); - editorBaseControl.AddChild(errorDialog); + _errorDialog = new AcceptDialog(); + editorBaseControl.AddChild(_errorDialog); MSBuildPanel = new MSBuildPanel(); - bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR()); + _bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR()); AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"}); - menuPopup = new PopupMenu(); - menuPopup.Hide(); + _menuPopup = new PopupMenu(); + _menuPopup.Hide(); - AddToolSubmenuItem("C#", menuPopup); + AddToolSubmenuItem("C#", _menuPopup); - toolBarBuildButton = new Button + _toolBarBuildButton = new Button { Text = "Build", HintTooltip = "Build solution", FocusMode = Control.FocusModeEnum.None }; - toolBarBuildButton.PressedSignal += BuildSolutionPressed; - AddControlToContainer(CustomControlContainer.Toolbar, toolBarBuildButton); + _toolBarBuildButton.PressedSignal += BuildSolutionPressed; + AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton); if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath)) { @@ -432,12 +433,12 @@ namespace GodotTools } else { - bottomPanelBtn.Hide(); - toolBarBuildButton.Hide(); - menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln); + _bottomPanelBtn.Hide(); + _toolBarBuildButton.Hide(); + _menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln); } - menuPopup.IdPressed += _MenuOptionPressed; + _menuPopup.IdPressed += _MenuOptionPressed; // External editor settings EditorDef("mono/editor/external_editor", ExternalEditorId.None); @@ -465,7 +466,7 @@ namespace GodotTools $",JetBrains Rider:{(int)ExternalEditorId.Rider}"; } - editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary + _editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary { ["type"] = Variant.Type.Int, ["name"] = "mono/editor/external_editor", @@ -477,7 +478,7 @@ namespace GodotTools var exportPlugin = new ExportPlugin(); AddExportPlugin(exportPlugin); exportPlugin.RegisterExportSettings(); - exportPluginWeak = WeakRef(exportPlugin); + _exportPluginWeak = WeakRef(exportPlugin); try { @@ -500,15 +501,15 @@ namespace GodotTools { base.Dispose(disposing); - if (exportPluginWeak != null) + if (_exportPluginWeak != null) { // We need to dispose our export plugin before the editor destroys EditorSettings. // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid // will be freed after EditorSettings already was, and its device polling thread // will try to access the EditorSettings singleton, resulting in null dereferencing. - (exportPluginWeak.GetRef() as ExportPlugin)?.Dispose(); + (_exportPluginWeak.GetRef() as ExportPlugin)?.Dispose(); - exportPluginWeak.Dispose(); + _exportPluginWeak.Dispose(); } GodotIdeManager?.Dispose(); diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs index b30c857c64..dd05f28af0 100644 --- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs +++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs @@ -6,7 +6,7 @@ namespace GodotTools { public class HotReloadAssemblyWatcher : Node { - private Timer watchTimer; + private Timer _watchTimer; public override void _Notification(int what) { @@ -27,22 +27,22 @@ namespace GodotTools public void RestartTimer() { - watchTimer.Stop(); - watchTimer.Start(); + _watchTimer.Stop(); + _watchTimer.Start(); } public override void _Ready() { base._Ready(); - watchTimer = new Timer + _watchTimer = new Timer { OneShot = false, WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5) }; - watchTimer.Timeout += TimerTimeout; - AddChild(watchTimer); - watchTimer.Start(); + _watchTimer.Timeout += TimerTimeout; + AddChild(_watchTimer); + _watchTimer.Start(); } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs index 451ce39f5c..23339fe50b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs @@ -10,22 +10,22 @@ namespace GodotTools.Ides { public sealed class GodotIdeManager : Node, ISerializationListener { - private MessagingServer MessagingServer { get; set; } + private MessagingServer _messagingServer; - private MonoDevelop.Instance monoDevelInstance; - private MonoDevelop.Instance vsForMacInstance; + private MonoDevelop.Instance _monoDevelInstance; + private MonoDevelop.Instance _vsForMacInstance; private MessagingServer GetRunningOrNewServer() { - if (MessagingServer != null && !MessagingServer.IsDisposed) - return MessagingServer; + if (_messagingServer != null && !_messagingServer.IsDisposed) + return _messagingServer; - MessagingServer?.Dispose(); - MessagingServer = new MessagingServer(OS.GetExecutablePath(), ProjectSettings.GlobalizePath(GodotSharpDirs.ResMetadataDir), new GodotLogger()); + _messagingServer?.Dispose(); + _messagingServer = new MessagingServer(OS.GetExecutablePath(), ProjectSettings.GlobalizePath(GodotSharpDirs.ResMetadataDir), new GodotLogger()); - _ = MessagingServer.Listen(); + _ = _messagingServer.Listen(); - return MessagingServer; + return _messagingServer; } public override void _Ready() @@ -48,7 +48,7 @@ namespace GodotTools.Ides if (disposing) { - MessagingServer?.Dispose(); + _messagingServer?.Dispose(); } } @@ -113,14 +113,14 @@ namespace GodotTools.Ides { if (Utils.OS.IsMacOS && editorId == ExternalEditorId.VisualStudioForMac) { - vsForMacInstance = (vsForMacInstance?.IsDisposed ?? true ? null : vsForMacInstance) ?? + _vsForMacInstance = (_vsForMacInstance?.IsDisposed ?? true ? null : _vsForMacInstance) ?? new MonoDevelop.Instance(solutionPath, MonoDevelop.EditorId.VisualStudioForMac); - return vsForMacInstance; + return _vsForMacInstance; } - monoDevelInstance = (monoDevelInstance?.IsDisposed ?? true ? null : monoDevelInstance) ?? + _monoDevelInstance = (_monoDevelInstance?.IsDisposed ?? true ? null : _monoDevelInstance) ?? new MonoDevelop.Instance(solutionPath, MonoDevelop.EditorId.MonoDevelop); - return monoDevelInstance; + return _monoDevelInstance; } try @@ -159,15 +159,15 @@ namespace GodotTools.Ides public readonly struct EditorPick { - private readonly string identity; + private readonly string _identity; public EditorPick(string identity) { - this.identity = identity; + _identity = identity; } public bool IsAnyConnected() => - GodotSharpEditor.Instance.GodotIdeManager.GetRunningOrNewServer().IsAnyConnected(identity); + GodotSharpEditor.Instance.GodotIdeManager.GetRunningOrNewServer().IsAnyConnected(_identity); private void SendRequest<TResponse>(Request request) where TResponse : Response, new() @@ -175,7 +175,7 @@ namespace GodotTools.Ides // Logs an error if no client is connected with the specified identity GodotSharpEditor.Instance.GodotIdeManager .GetRunningOrNewServer() - .BroadcastRequest<TResponse>(identity, request); + .BroadcastRequest<TResponse>(_identity, request); } public void SendOpenFile(string file) diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs index eb34a2d0f7..6f11831b80 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs @@ -21,24 +21,26 @@ namespace GodotTools.Ides { public sealed class MessagingServer : IDisposable { - private readonly ILogger logger; + private readonly ILogger _logger; - private readonly FileStream metaFile; - private string MetaFilePath { get; } + private readonly FileStream _metaFile; + private string _metaFilePath; - private readonly SemaphoreSlim peersSem = new SemaphoreSlim(1); + private readonly SemaphoreSlim _peersSem = new SemaphoreSlim(1); - private readonly TcpListener listener; + private readonly TcpListener _listener; - private readonly Dictionary<string, Queue<NotifyAwaiter<bool>>> clientConnectedAwaiters = new Dictionary<string, Queue<NotifyAwaiter<bool>>>(); - private readonly Dictionary<string, Queue<NotifyAwaiter<bool>>> clientDisconnectedAwaiters = new Dictionary<string, Queue<NotifyAwaiter<bool>>>(); + private readonly Dictionary<string, Queue<NotifyAwaiter<bool>>> _clientConnectedAwaiters = + new Dictionary<string, Queue<NotifyAwaiter<bool>>>(); + private readonly Dictionary<string, Queue<NotifyAwaiter<bool>>> _clientDisconnectedAwaiters = + new Dictionary<string, Queue<NotifyAwaiter<bool>>>(); public async Task<bool> AwaitClientConnected(string identity) { - if (!clientConnectedAwaiters.TryGetValue(identity, out var queue)) + if (!_clientConnectedAwaiters.TryGetValue(identity, out var queue)) { queue = new Queue<NotifyAwaiter<bool>>(); - clientConnectedAwaiters.Add(identity, queue); + _clientConnectedAwaiters.Add(identity, queue); } var awaiter = new NotifyAwaiter<bool>(); @@ -48,10 +50,10 @@ namespace GodotTools.Ides public async Task<bool> AwaitClientDisconnected(string identity) { - if (!clientDisconnectedAwaiters.TryGetValue(identity, out var queue)) + if (!_clientDisconnectedAwaiters.TryGetValue(identity, out var queue)) { queue = new Queue<NotifyAwaiter<bool>>(); - clientDisconnectedAwaiters.Add(identity, queue); + _clientDisconnectedAwaiters.Add(identity, queue); } var awaiter = new NotifyAwaiter<bool>(); @@ -77,7 +79,7 @@ namespace GodotTools.Ides if (IsDisposed) return; - using (await peersSem.UseAsync()) + using (await _peersSem.UseAsync()) { if (IsDisposed) // lock may not be fair return; @@ -95,19 +97,19 @@ namespace GodotTools.Ides foreach (var connection in Peers) connection.Dispose(); Peers.Clear(); - listener?.Stop(); + _listener?.Stop(); - metaFile?.Dispose(); + _metaFile?.Dispose(); - File.Delete(MetaFilePath); + File.Delete(_metaFilePath); } } public MessagingServer(string editorExecutablePath, string projectMetadataDir, ILogger logger) { - this.logger = logger; + this._logger = logger; - MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName); + _metaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName); // Make sure the directory exists Directory.CreateDirectory(projectMetadataDir); @@ -115,13 +117,13 @@ namespace GodotTools.Ides // The Godot editor's file system thread can keep the file open for writing, so we are forced to allow write sharing... const FileShare metaFileShare = FileShare.ReadWrite; - metaFile = File.Open(MetaFilePath, FileMode.Create, FileAccess.Write, metaFileShare); + _metaFile = File.Open(_metaFilePath, FileMode.Create, FileAccess.Write, metaFileShare); - listener = new TcpListener(new IPEndPoint(IPAddress.Loopback, port: 0)); - listener.Start(); + _listener = new TcpListener(new IPEndPoint(IPAddress.Loopback, port: 0)); + _listener.Start(); - int port = ((IPEndPoint)listener.Server.LocalEndPoint).Port; - using (var metaFileWriter = new StreamWriter(metaFile, Encoding.UTF8)) + int port = ((IPEndPoint)_listener.Server.LocalEndPoint).Port; + using (var metaFileWriter = new StreamWriter(_metaFile, Encoding.UTF8)) { metaFileWriter.WriteLine(port); metaFileWriter.WriteLine(editorExecutablePath); @@ -130,30 +132,30 @@ namespace GodotTools.Ides private async Task AcceptClient(TcpClient tcpClient) { - logger.LogDebug("Accept client..."); + _logger.LogDebug("Accept client..."); - using (var peer = new Peer(tcpClient, new ServerHandshake(), new ServerMessageHandler(), logger)) + using (var peer = new Peer(tcpClient, new ServerHandshake(), new ServerMessageHandler(), _logger)) { // ReSharper disable AccessToDisposedClosure peer.Connected += () => { - logger.LogInfo("Connection open with Ide Client"); + _logger.LogInfo("Connection open with Ide Client"); - if (clientConnectedAwaiters.TryGetValue(peer.RemoteIdentity, out var queue)) + if (_clientConnectedAwaiters.TryGetValue(peer.RemoteIdentity, out var queue)) { while (queue.Count > 0) queue.Dequeue().SetResult(true); - clientConnectedAwaiters.Remove(peer.RemoteIdentity); + _clientConnectedAwaiters.Remove(peer.RemoteIdentity); } }; peer.Disconnected += () => { - if (clientDisconnectedAwaiters.TryGetValue(peer.RemoteIdentity, out var queue)) + if (_clientDisconnectedAwaiters.TryGetValue(peer.RemoteIdentity, out var queue)) { while (queue.Count > 0) queue.Dequeue().SetResult(true); - clientDisconnectedAwaiters.Remove(peer.RemoteIdentity); + _clientDisconnectedAwaiters.Remove(peer.RemoteIdentity); } }; // ReSharper restore AccessToDisposedClosure @@ -162,17 +164,17 @@ namespace GodotTools.Ides { if (!await peer.DoHandshake("server")) { - logger.LogError("Handshake failed"); + _logger.LogError("Handshake failed"); return; } } catch (Exception e) { - logger.LogError("Handshake failed with unhandled exception: ", e); + _logger.LogError("Handshake failed with unhandled exception: ", e); return; } - using (await peersSem.UseAsync()) + using (await _peersSem.UseAsync()) Peers.Add(peer); try @@ -181,7 +183,7 @@ namespace GodotTools.Ides } finally { - using (await peersSem.UseAsync()) + using (await _peersSem.UseAsync()) Peers.Remove(peer); } } @@ -192,7 +194,7 @@ namespace GodotTools.Ides try { while (!IsDisposed) - _ = AcceptClient(await listener.AcceptTcpClientAsync()); + _ = AcceptClient(await _listener.AcceptTcpClientAsync()); } catch (Exception e) { @@ -204,11 +206,11 @@ namespace GodotTools.Ides public async void BroadcastRequest<TResponse>(string identity, Request request) where TResponse : Response, new() { - using (await peersSem.UseAsync()) + using (await _peersSem.UseAsync()) { if (!IsAnyConnected(identity)) { - logger.LogError("Cannot write request. No client connected to the Godot Ide Server."); + _logger.LogError("Cannot write request. No client connected to the Godot Ide Server."); return; } @@ -225,16 +227,19 @@ namespace GodotTools.Ides private class ServerHandshake : IHandshake { - private static readonly string ServerHandshakeBase = $"{Peer.ServerHandshakeName},Version={Peer.ProtocolVersionMajor}.{Peer.ProtocolVersionMinor}.{Peer.ProtocolVersionRevision}"; - private static readonly string ClientHandshakePattern = $@"{Regex.Escape(Peer.ClientHandshakeName)},Version=([0-9]+)\.([0-9]+)\.([0-9]+),([_a-zA-Z][_a-zA-Z0-9]{{0,63}})"; + private static readonly string _serverHandshakeBase = + $"{Peer.ServerHandshakeName},Version={Peer.ProtocolVersionMajor}.{Peer.ProtocolVersionMinor}.{Peer.ProtocolVersionRevision}"; - public string GetHandshakeLine(string identity) => $"{ServerHandshakeBase},{identity}"; + private static readonly string _clientHandshakePattern = + $@"{Regex.Escape(Peer.ClientHandshakeName)},Version=([0-9]+)\.([0-9]+)\.([0-9]+),([_a-zA-Z][_a-zA-Z0-9]{{0,63}})"; + + public string GetHandshakeLine(string identity) => $"{_serverHandshakeBase},{identity}"; public bool IsValidPeerHandshake(string handshake, out string identity, ILogger logger) { identity = null; - var match = Regex.Match(handshake, ClientHandshakePattern); + var match = Regex.Match(handshake, _clientHandshakePattern); if (!match.Success) return false; diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs index fd7bbd5578..3f1d5ac3ca 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs @@ -10,17 +10,17 @@ namespace GodotTools.Ides.MonoDevelop public class Instance : IDisposable { public DateTime LaunchTime { get; private set; } - private readonly string solutionFile; - private readonly EditorId editorId; + private readonly string _solutionFile; + private readonly EditorId _editorId; - private Process process; + private Process _process; - public bool IsRunning => process != null && !process.HasExited; + public bool IsRunning => _process != null && !_process.HasExited; public bool IsDisposed { get; private set; } public void Execute() { - bool newWindow = process == null || process.HasExited; + bool newWindow = _process == null || _process.HasExited; var args = new List<string>(); @@ -28,7 +28,7 @@ namespace GodotTools.Ides.MonoDevelop if (OS.IsMacOS) { - string bundleId = BundleIds[editorId]; + string bundleId = BundleIds[_editorId]; if (Internal.IsOsxAppBundleInstalled(bundleId)) { @@ -45,18 +45,18 @@ namespace GodotTools.Ides.MonoDevelop } else { - command = OS.PathWhich(ExecutableNames[editorId]); + command = OS.PathWhich(ExecutableNames[_editorId]); } } else { - command = OS.PathWhich(ExecutableNames[editorId]); + command = OS.PathWhich(ExecutableNames[_editorId]); } args.Add("--ipc-tcp"); if (newWindow) - args.Add("\"" + Path.GetFullPath(solutionFile) + "\""); + args.Add("\"" + Path.GetFullPath(_solutionFile) + "\""); if (command == null) throw new FileNotFoundException(); @@ -65,7 +65,7 @@ namespace GodotTools.Ides.MonoDevelop if (newWindow) { - process = Process.Start(new ProcessStartInfo + _process = Process.Start(new ProcessStartInfo { FileName = command, Arguments = string.Join(" ", args), @@ -88,14 +88,14 @@ namespace GodotTools.Ides.MonoDevelop if (editorId == EditorId.VisualStudioForMac && !OS.IsMacOS) throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform"); - this.solutionFile = solutionFile; - this.editorId = editorId; + _solutionFile = solutionFile; + _editorId = editorId; } public void Dispose() { IsDisposed = true; - process?.Dispose(); + _process?.Dispose(); } private static readonly IReadOnlyDictionary<EditorId, string> ExecutableNames; diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs index 821532f759..71055f0125 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs @@ -11,6 +11,7 @@ using Environment = System.Environment; using File = System.IO.File; using Path = System.IO.Path; using OS = GodotTools.Utils.OS; + // ReSharper disable UnassignedField.Local // ReSharper disable InconsistentNaming // ReSharper disable UnassignedField.Global @@ -53,10 +54,10 @@ namespace GodotTools.Ides.Rider private static RiderInfo[] CollectAllRiderPathsLinux() { var installInfos = new List<RiderInfo>(); - var home = Environment.GetEnvironmentVariable("HOME"); + string home = Environment.GetEnvironmentVariable("HOME"); if (!string.IsNullOrEmpty(home)) { - var toolboxRiderRootPath = GetToolboxBaseDir(); + string toolboxRiderRootPath = GetToolboxBaseDir(); installInfos.AddRange(CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider.sh", false) .Select(a => new RiderInfo(a, true)).ToList()); @@ -65,12 +66,12 @@ namespace GodotTools.Ides.Rider if (shortcut.Exists) { - var lines = File.ReadAllLines(shortcut.FullName); - foreach (var line in lines) + string[] lines = File.ReadAllLines(shortcut.FullName); + foreach (string line in lines) { if (!line.StartsWith("Exec=\"")) continue; - var path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault(); + string path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault(); if (string.IsNullOrEmpty(path)) continue; @@ -82,7 +83,7 @@ namespace GodotTools.Ides.Rider } // snap install - var snapInstallPath = "/snap/rider/current/bin/rider.sh"; + string snapInstallPath = "/snap/rider/current/bin/rider.sh"; if (new FileInfo(snapInstallPath).Exists) installInfos.Add(new RiderInfo(snapInstallPath, false)); @@ -98,15 +99,15 @@ namespace GodotTools.Ides.Rider if (folder.Exists) { installInfos.AddRange(folder.GetDirectories("*Rider*.app") - .Select(a => new RiderInfo(Path.Combine(a.FullName, "Contents/MacOS/rider"), false)) - .ToList()); + .Select(a => new RiderInfo(Path.Combine(a.FullName, "Contents/MacOS/rider"), false)) + .ToList()); } // /Users/user/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-1/181.3870.267/Rider EAP.app // should be combined with "Contents/MacOS/rider" - var toolboxRiderRootPath = GetToolboxBaseDir(); + string toolboxRiderRootPath = GetToolboxBaseDir(); var paths = CollectPathsFromToolbox(toolboxRiderRootPath, "", "Rider*.app", true) - .Select(a => new RiderInfo(Path.Combine(a, "Contents/MacOS/rider"), true)); + .Select(a => new RiderInfo(Path.Combine(a, "Contents/MacOS/rider"), true)); installInfos.AddRange(paths); return installInfos.ToArray(); @@ -134,7 +135,7 @@ namespace GodotTools.Ides.Rider { if (OS.IsWindows) { - var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); return GetToolboxRiderRootPath(localAppData); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs index 60dd565ef2..ac29efb716 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs @@ -49,7 +49,7 @@ namespace GodotTools.Ides.Rider if (!paths.Any()) return; - var newPath = paths.Last().Path; + string newPath = paths.Last().Path; Globals.EditorDef(EditorPathSettingName, newPath); editorSettings.SetSetting(EditorPathSettingName, newPath); } @@ -57,7 +57,8 @@ namespace GodotTools.Ides.Rider public static bool IsExternalEditorSetToRider(EditorSettings editorSettings) { - return editorSettings.HasSetting(EditorPathSettingName) && IsRider((string)editorSettings.GetSetting(EditorPathSettingName)); + return editorSettings.HasSetting(EditorPathSettingName) && + IsRider((string)editorSettings.GetSetting(EditorPathSettingName)); } public static bool IsRider(string path) @@ -66,7 +67,7 @@ namespace GodotTools.Ides.Rider return false; var fileInfo = new FileInfo(path); - var filename = fileInfo.Name.ToLowerInvariant(); + string filename = fileInfo.Name.ToLowerInvariant(); return filename.StartsWith("rider", StringComparison.Ordinal); } @@ -83,7 +84,7 @@ namespace GodotTools.Ides.Rider if (!paths.Any()) return null; - var newPath = paths.Last().Path; + string newPath = paths.Last().Path; editorSettings.SetSetting(EditorPathSettingName, newPath); Globals.EditorDef(EditorPathSettingName, newPath); return newPath; @@ -96,8 +97,8 @@ namespace GodotTools.Ides.Rider public static void OpenFile(string slnPath, string scriptPath, int line) { - var pathFromSettings = GetRiderPathFromSettings(); - var path = CheckAndUpdatePath(pathFromSettings); + string pathFromSettings = GetRiderPathFromSettings(); + string path = CheckAndUpdatePath(pathFromSettings); var args = new List<string>(); args.Add(slnPath); diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs index 6893bc1974..5e70c399b2 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs @@ -39,45 +39,57 @@ namespace GodotTools.Internals [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ResDataDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ResMetadataDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ResAssembliesBaseDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ResAssembliesDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ResConfigDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ResTempDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ResTempAssembliesBaseDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ResTempAssembliesDir(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_MonoUserDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_MonoLogsDir(); #region Tools-only [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_MonoSolutionsDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_BuildLogsDirs(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ProjectSlnPath(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_ProjectCsProjPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_DataEditorToolsDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_DataEditorPrebuiltApiDir(); #endregion [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_DataMonoEtcDir(); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_DataMonoLibDir(); diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs index c6724ccaf7..05499339b1 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs @@ -8,7 +8,7 @@ namespace GodotTools.Utils { public static class FsPathUtils { - private static readonly string ResourcePath = ProjectSettings.GlobalizePath("res://"); + private static readonly string _resourcePath = ProjectSettings.GlobalizePath("res://"); private static bool PathStartsWithAlreadyNorm(this string childPath, string parentPath) { @@ -34,7 +34,7 @@ namespace GodotTools.Utils public static string LocalizePathWithCaseChecked(string path) { string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar; - string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar; + string resourcePathNorm = _resourcePath.NormalizePath() + Path.DirectorySeparatorChar; if (!pathNorm.PathStartsWithAlreadyNorm(resourcePathNorm)) return null; diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs index 4624439665..93a1360cb6 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs @@ -13,10 +13,10 @@ namespace GodotTools.Utils public static class OS { [MethodImpl(MethodImplOptions.InternalCall)] - static extern string GetPlatformName(); + private static extern string GetPlatformName(); [MethodImpl(MethodImplOptions.InternalCall)] - static extern bool UnixFileHasExecutableAccess(string filePath); + private static extern bool UnixFileHasExecutableAccess(string filePath); public static class Names { @@ -106,7 +106,10 @@ namespace GodotTools.Utils public static string PathWhich([NotNull] string name) { - return IsWindows ? PathWhichWindows(name) : PathWhichUnix(name); + if (IsWindows) + return PathWhichWindows(name); + + return PathWhichUnix(name); } private static string PathWhichWindows([NotNull] string name) @@ -129,7 +132,8 @@ namespace GodotTools.Utils } string nameExt = Path.GetExtension(name); - bool hasPathExt = !string.IsNullOrEmpty(nameExt) && windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase); + bool hasPathExt = !string.IsNullOrEmpty(nameExt) && + windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase); searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 7fdef8ff45..67bcb34b1c 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -387,7 +387,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf xml_output.append(link_target); xml_output.append("</c>"); } - } else if (link_tag == "const") { + } else if (link_tag == "constant") { if (!target_itype || !target_itype->is_object_type) { if (OS::get_singleton()->is_stdout_verbose()) { if (target_itype) { diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 8a85a1acbd..51a27ee934 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -575,7 +575,7 @@ class BindingsGenerator { StaticCString::create(_STR(PackedByteArray)), StaticCString::create(_STR(PackedInt32Array)), - StaticCString::create(_STR(PackedInt64rray)), + StaticCString::create(_STR(PackedInt64Array)), StaticCString::create(_STR(PackedFloat32Array)), StaticCString::create(_STR(PackedFloat64Array)), StaticCString::create(_STR(PackedStringArray)), diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp index b7b36a92d8..d911f6461c 100644 --- a/modules/mono/editor/code_completion.cpp +++ b/modules/mono/editor/code_completion.cpp @@ -121,7 +121,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr case CompletionKind::NODE_PATHS: { { // AutoLoads - Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) { const ProjectSettings::AutoloadInfo &info = E.value; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs index 1a3b81487f..8b12537f7f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs @@ -20,7 +20,7 @@ namespace Godot private Vector3 _size; /// <summary> - /// Beginning corner. Typically has values lower than End. + /// Beginning corner. Typically has values lower than <see cref="End"/>. /// </summary> /// <value>Directly uses a private field.</value> public Vector3 Position @@ -30,7 +30,7 @@ namespace Godot } /// <summary> - /// Size from Position to End. Typically all components are positive. + /// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive. /// If the size is negative, you can use <see cref="Abs"/> to fix it. /// </summary> /// <value>Directly uses a private field.</value> @@ -44,7 +44,10 @@ namespace Godot /// Ending corner. This is calculated as <see cref="Position"/> plus /// <see cref="Size"/>. Setting this value will change the size. /// </summary> - /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value> + /// <value> + /// 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 Vector3 End { get { return _position + _size; } @@ -52,10 +55,10 @@ namespace Godot } /// <summary> - /// Returns an AABB with equivalent position and size, modified so that + /// 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 AABB.</returns> + /// <returns>The modified <see cref="AABB"/>.</returns> public AABB Abs() { Vector3 end = End; @@ -64,30 +67,32 @@ namespace Godot } /// <summary> - /// Returns true if this AABB completely encloses another one. + /// Returns <see langword="true"/> if this <see cref="AABB"/> completely encloses another one. /// </summary> - /// <param name="with">The other AABB that may be enclosed.</param> - /// <returns>A bool for whether or not this AABB encloses `b`.</returns> + /// <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"/>. + /// </returns> public bool Encloses(AABB with) { - Vector3 src_min = _position; - Vector3 src_max = _position + _size; - Vector3 dst_min = with._position; - Vector3 dst_max = with._position + with._size; + Vector3 srcMin = _position; + Vector3 srcMax = _position + _size; + Vector3 dstMin = with._position; + Vector3 dstMax = with._position + with._size; - return src_min.x <= dst_min.x && - src_max.x > dst_max.x && - src_min.y <= dst_min.y && - src_max.y > dst_max.y && - src_min.z <= dst_min.z && - src_max.z > dst_max.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 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 AABB.</returns> + /// <returns>The expanded <see cref="AABB"/>.</returns> public AABB Expand(Vector3 point) { Vector3 begin = _position; @@ -123,7 +128,7 @@ namespace Godot } /// <summary> - /// Returns the area of the AABB. + /// Returns the area of the <see cref="AABB"/>. /// </summary> /// <returns>The area.</returns> public real_t GetArea() @@ -132,10 +137,10 @@ namespace Godot } /// <summary> - /// Gets the position of one of the 8 endpoints of the AABB. + /// Gets the position of one of the 8 endpoints of the <see cref="AABB"/>. /// </summary> /// <param name="idx">Which endpoint to get.</param> - /// <returns>An endpoint of the AABB.</returns> + /// <returns>An endpoint of the <see cref="AABB"/>.</returns> public Vector3 GetEndpoint(int idx) { switch (idx) @@ -157,26 +162,29 @@ namespace Godot case 7: return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z); default: - throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx)); + { + throw new ArgumentOutOfRangeException(nameof(idx), + $"Index is {idx}, but a value from 0 to 7 is expected."); + } } } /// <summary> - /// Returns the normalized longest axis of the AABB. + /// Returns the normalized longest axis of the <see cref="AABB"/>. /// </summary> - /// <returns>A vector representing the normalized longest axis of the AABB.</returns> + /// <returns>A vector representing the normalized longest axis of the <see cref="AABB"/>.</returns> public Vector3 GetLongestAxis() { var axis = new Vector3(1f, 0f, 0f); - real_t max_size = _size.x; + real_t maxSize = _size.x; - if (_size.y > max_size) + if (_size.y > maxSize) { axis = new Vector3(0f, 1f, 0f); - max_size = _size.y; + maxSize = _size.y; } - if (_size.z > max_size) + if (_size.z > maxSize) { axis = new Vector3(0f, 0f, 1f); } @@ -185,21 +193,21 @@ namespace Godot } /// <summary> - /// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the 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 Vector3.Axis GetLongestAxisIndex() { var axis = Vector3.Axis.X; - real_t max_size = _size.x; + real_t maxSize = _size.x; - if (_size.y > max_size) + if (_size.y > maxSize) { axis = Vector3.Axis.Y; - max_size = _size.y; + maxSize = _size.y; } - if (_size.z > max_size) + if (_size.z > maxSize) { axis = Vector3.Axis.Z; } @@ -208,38 +216,38 @@ namespace Godot } /// <summary> - /// Returns the scalar length of the longest axis of the 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 AABB.</returns> + /// <returns>The scalar length of the longest axis of the <see cref="AABB"/>.</returns> public real_t GetLongestAxisSize() { - real_t max_size = _size.x; + real_t maxSize = _size.x; - if (_size.y > max_size) - max_size = _size.y; + if (_size.y > maxSize) + maxSize = _size.y; - if (_size.z > max_size) - max_size = _size.z; + if (_size.z > maxSize) + maxSize = _size.z; - return max_size; + return maxSize; } /// <summary> - /// Returns the normalized shortest axis of the AABB. + /// Returns the normalized shortest axis of the <see cref="AABB"/>. /// </summary> - /// <returns>A vector representing the normalized shortest axis of the AABB.</returns> + /// <returns>A vector representing the normalized shortest axis of the <see cref="AABB"/>.</returns> public Vector3 GetShortestAxis() { var axis = new Vector3(1f, 0f, 0f); - real_t max_size = _size.x; + real_t maxSize = _size.x; - if (_size.y < max_size) + if (_size.y < maxSize) { axis = new Vector3(0f, 1f, 0f); - max_size = _size.y; + maxSize = _size.y; } - if (_size.z < max_size) + if (_size.z < maxSize) { axis = new Vector3(0f, 0f, 1f); } @@ -248,21 +256,21 @@ namespace Godot } /// <summary> - /// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the 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 Vector3.Axis GetShortestAxisIndex() { var axis = Vector3.Axis.X; - real_t max_size = _size.x; + real_t maxSize = _size.x; - if (_size.y < max_size) + if (_size.y < maxSize) { axis = Vector3.Axis.Y; - max_size = _size.y; + maxSize = _size.y; } - if (_size.z < max_size) + if (_size.z < maxSize) { axis = Vector3.Axis.Z; } @@ -271,20 +279,20 @@ namespace Godot } /// <summary> - /// Returns the scalar length of the shortest axis of the 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 AABB.</returns> + /// <returns>The scalar length of the shortest axis of the <see cref="AABB"/>.</returns> public real_t GetShortestAxisSize() { - real_t max_size = _size.x; + real_t maxSize = _size.x; - if (_size.y < max_size) - max_size = _size.y; + if (_size.y < maxSize) + maxSize = _size.y; - if (_size.z < max_size) - max_size = _size.z; + if (_size.z < maxSize) + maxSize = _size.z; - return max_size; + return maxSize; } /// <summary> @@ -295,23 +303,23 @@ namespace Godot /// <returns>A vector representing the support.</returns> public Vector3 GetSupport(Vector3 dir) { - Vector3 half_extents = _size * 0.5f; - Vector3 ofs = _position + half_extents; + Vector3 halfExtents = _size * 0.5f; + Vector3 ofs = _position + halfExtents; return ofs + new Vector3( - dir.x > 0f ? -half_extents.x : half_extents.x, - dir.y > 0f ? -half_extents.y : half_extents.y, - dir.z > 0f ? -half_extents.z : half_extents.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 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 AABB.</returns> + /// <returns>The grown <see cref="AABB"/>.</returns> public AABB Grow(real_t by) { - var res = this; + AABB res = this; res._position.x -= by; res._position.y -= by; @@ -324,28 +332,37 @@ namespace Godot } /// <summary> - /// Returns true if the AABB is flat or empty, or false otherwise. + /// Returns <see langword="true"/> if the <see cref="AABB"/> is flat or empty, + /// or <see langword="false"/> otherwise. /// </summary> - /// <returns>A bool for whether or not the AABB has area.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has area. + /// </returns> public bool HasNoArea() { return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f; } /// <summary> - /// Returns true if the AABB has no surface (no size), or false otherwise. + /// Returns <see langword="true"/> if the <see cref="AABB"/> has no surface (no size), + /// or <see langword="false"/> otherwise. /// </summary> - /// <returns>A bool for whether or not the AABB has area.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has area. + /// </returns> public bool HasNoSurface() { return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f; } /// <summary> - /// Returns true if the AABB contains a point, or false otherwise. + /// 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 bool for whether or not the AABB contains `point`.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> contains <paramref name="point"/>. + /// </returns> public bool HasPoint(Vector3 point) { if (point.x < _position.x) @@ -365,56 +382,59 @@ namespace Godot } /// <summary> - /// Returns the intersection of this AABB and `b`. + /// Returns the intersection of this <see cref="AABB"/> and <paramref name="with"/>. /// </summary> - /// <param name="with">The other AABB.</param> - /// <returns>The clipped AABB.</returns> + /// <param name="with">The other <see cref="AABB"/>.</param> + /// <returns>The clipped <see cref="AABB"/>.</returns> public AABB Intersection(AABB with) { - Vector3 src_min = _position; - Vector3 src_max = _position + _size; - Vector3 dst_min = with._position; - Vector3 dst_max = with._position + with._size; + Vector3 srcMin = _position; + Vector3 srcMax = _position + _size; + Vector3 dstMin = with._position; + Vector3 dstMax = with._position + with._size; Vector3 min, max; - if (src_min.x > dst_max.x || src_max.x < dst_min.x) + if (srcMin.x > dstMax.x || srcMax.x < dstMin.x) { return new AABB(); } - min.x = src_min.x > dst_min.x ? src_min.x : dst_min.x; - max.x = src_max.x < dst_max.x ? src_max.x : dst_max.x; + min.x = srcMin.x > dstMin.x ? srcMin.x : dstMin.x; + max.x = srcMax.x < dstMax.x ? srcMax.x : dstMax.x; - if (src_min.y > dst_max.y || src_max.y < dst_min.y) + if (srcMin.y > dstMax.y || srcMax.y < dstMin.y) { return new AABB(); } - min.y = src_min.y > dst_min.y ? src_min.y : dst_min.y; - max.y = src_max.y < dst_max.y ? src_max.y : dst_max.y; + min.y = srcMin.y > dstMin.y ? srcMin.y : dstMin.y; + max.y = srcMax.y < dstMax.y ? srcMax.y : dstMax.y; - if (src_min.z > dst_max.z || src_max.z < dst_min.z) + if (srcMin.z > dstMax.z || srcMax.z < dstMin.z) { return new AABB(); } - min.z = src_min.z > dst_min.z ? src_min.z : dst_min.z; - max.z = src_max.z < dst_max.z ? src_max.z : dst_max.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); } /// <summary> - /// Returns true if the AABB overlaps with `b` + /// Returns <see langword="true"/> if the <see cref="AABB"/> overlaps with <paramref name="with"/> /// (i.e. they have at least one point in common). /// - /// If `includeBorders` is true, they will also be considered overlapping - /// if their borders touch, even without intersection. + /// If <paramref name="includeBorders"/> is <see langword="true"/>, + /// they will also be considered overlapping if their borders touch, + /// even without intersection. /// </summary> - /// <param name="with">The other AABB to check for intersections with.</param> + /// <param name="with">The other <see cref="AABB"/> to check for intersections with.</param> /// <param name="includeBorders">Whether or not to consider borders.</param> - /// <returns>A bool for whether or not they are intersecting.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not they are intersecting. + /// </returns> public bool Intersects(AABB with, bool includeBorders = false) { if (includeBorders) @@ -452,10 +472,12 @@ namespace Godot } /// <summary> - /// Returns true if the AABB is on both sides of `plane`. + /// Returns <see langword="true"/> if the <see cref="AABB"/> is on both sides of <paramref name="plane"/>. /// </summary> - /// <param name="plane">The plane to check for intersection.</param> - /// <returns>A bool for whether or not the AABB intersects the plane.</returns> + /// <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"/>. + /// </returns> public bool IntersectsPlane(Plane plane) { Vector3[] points = @@ -489,11 +511,14 @@ namespace Godot } /// <summary> - /// Returns true if the AABB intersects the line segment between `from` and `to`. + /// 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 bool for whether or not the AABB intersects the line segment.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> intersects the line segment. + /// </returns> public bool IntersectsSegment(Vector3 from, Vector3 to) { real_t min = 0f; @@ -549,10 +574,10 @@ namespace Godot } /// <summary> - /// Returns a larger AABB that contains this AABB and `b`. + /// Returns a larger <see cref="AABB"/> that contains this <see cref="AABB"/> and <paramref name="with"/>. /// </summary> - /// <param name="with">The other AABB.</param> - /// <returns>The merged AABB.</returns> + /// <param name="with">The other <see cref="AABB"/>.</param> + /// <returns>The merged <see cref="AABB"/>.</returns> public AABB Merge(AABB with) { Vector3 beg1 = _position; @@ -561,22 +586,22 @@ namespace Godot 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); } /// <summary> - /// Constructs an 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> @@ -587,7 +612,8 @@ namespace Godot } /// <summary> - /// Constructs an AABB from a position, width, height, and depth. + /// 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> @@ -600,7 +626,8 @@ namespace Godot } /// <summary> - /// Constructs an AABB from x, y, z, and size. + /// 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> @@ -613,7 +640,9 @@ namespace Godot } /// <summary> - /// Constructs an AABB from x, y, z, width, height, and depth. + /// 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> /// <param name="x">The position's X coordinate.</param> /// <param name="y">The position's Y coordinate.</param> @@ -637,6 +666,11 @@ namespace Godot return !left.Equals(right); } + /// <summary> + /// Returns <see langword="true"/> if this AABB and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the AABB structure and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is AABB) @@ -647,32 +681,49 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this AABB and <paramref name="other"/> are equal + /// </summary> + /// <param name="other">The other AABB to compare.</param> + /// <returns>Whether or not the AABBs are equal.</returns> public bool Equals(AABB other) { return _position == other._position && _size == other._size; } /// <summary> - /// Returns true if this AABB and `other` are approximately equal, by running - /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component. + /// Returns <see langword="true"/> if this AABB and <paramref name="other"/> are approximately equal, + /// by running <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component. /// </summary> /// <param name="other">The other AABB to compare.</param> - /// <returns>Whether or not the AABBs are approximately equal.</returns> + /// <returns>Whether or not the AABBs structures are approximately equal.</returns> public bool IsEqualApprox(AABB other) { return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size); } + /// <summary> + /// Serves as the hash function for <see cref="AABB"/>. + /// </summary> + /// <returns>A hash code for this AABB.</returns> public override int GetHashCode() { return _position.GetHashCode() ^ _size.GetHashCode(); } + /// <summary> + /// Converts this <see cref="AABB"/> to a string. + /// </summary> + /// <returns>A string representation of this AABB.</returns> public override string ToString() { return $"{_position}, {_size}"; } + /// <summary> + /// Converts this <see cref="AABB"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this AABB.</returns> public string ToString(string format) { return $"{_position.ToString(format)}, {_size.ToString(format)}"; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index f52a767018..a412047196 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -6,7 +6,7 @@ using System.Runtime.InteropServices; namespace Godot.Collections { - class ArraySafeHandle : SafeHandle + internal class ArraySafeHandle : SafeHandle { public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) { @@ -33,15 +33,15 @@ namespace Godot.Collections /// </summary> public class Array : IList, IDisposable { - ArraySafeHandle safeHandle; - bool disposed = false; + private ArraySafeHandle _safeHandle; + private bool _disposed = false; /// <summary> /// Constructs a new empty <see cref="Array"/>. /// </summary> public Array() { - safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); + _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); } /// <summary> @@ -69,31 +69,31 @@ namespace Godot.Collections { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); } - safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array)); + _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array)); } internal Array(ArraySafeHandle handle) { - safeHandle = handle; + _safeHandle = handle; } internal Array(IntPtr handle) { - safeHandle = new ArraySafeHandle(handle); + _safeHandle = new ArraySafeHandle(handle); } internal IntPtr GetPtr() { - if (disposed) + if (_disposed) throw new ObjectDisposedException(GetType().FullName); - return safeHandle.DangerousGetHandle(); + return _safeHandle.DangerousGetHandle(); } /// <summary> /// Duplicates this <see cref="Array"/>. /// </summary> - /// <param name="deep">If true, performs a deep copy.</param> + /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param> /// <returns>A new Godot Array.</returns> public Array Duplicate(bool deep = false) { @@ -136,16 +136,16 @@ namespace Godot.Collections /// </summary> public void Dispose() { - if (disposed) + if (_disposed) return; - if (safeHandle != null) + if (_safeHandle != null) { - safeHandle.Dispose(); - safeHandle = null; + _safeHandle.Dispose(); + _safeHandle = null; } - disposed = true; + _disposed = true; } // IList @@ -155,9 +155,9 @@ namespace Godot.Collections bool IList.IsFixedSize => false; /// <summary> - /// Returns the object at the given index. + /// Returns the object at the given <paramref name="index"/>. /// </summary> - /// <value>The object at the given index.</value> + /// <value>The object at the given <paramref name="index"/>.</value> public object this[int index] { get => godot_icall_Array_At(GetPtr(), index); @@ -166,7 +166,7 @@ namespace Godot.Collections /// <summary> /// Adds an object to the end of this <see cref="Array"/>. - /// This is the same as `append` or `push_back` in GDScript. + /// This is the same as <c>append</c> or <c>push_back</c> in GDScript. /// </summary> /// <param name="value">The object to add.</param> /// <returns>The new size after adding the object.</returns> @@ -203,7 +203,7 @@ namespace Godot.Collections public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value); /// <summary> - /// Removes the first occurrence of the specified value + /// Removes the first occurrence of the specified <paramref name="value"/> /// from this <see cref="Array"/>. /// </summary> /// <param name="value">The value to remove.</param> @@ -272,67 +272,67 @@ namespace Godot.Collections } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Array_Ctor(); + internal static extern IntPtr godot_icall_Array_Ctor(); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array); + internal static extern IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Dtor(IntPtr ptr); + internal static extern void godot_icall_Array_Dtor(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_Array_At(IntPtr ptr, int index); + internal static extern object godot_icall_Array_At(IntPtr ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass); + internal static extern object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + internal static extern void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Array_Count(IntPtr ptr); + internal static extern int godot_icall_Array_Count(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Array_Add(IntPtr ptr, object item); + internal static extern int godot_icall_Array_Add(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Clear(IntPtr ptr); + internal static extern void godot_icall_Array_Clear(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right); + internal static extern IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); + internal static extern bool godot_icall_Array_Contains(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex); + internal static extern void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep); + internal static extern IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); + internal static extern int godot_icall_Array_IndexOf(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + internal static extern void godot_icall_Array_Insert(IntPtr ptr, int index, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item); + internal static extern bool godot_icall_Array_Remove(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + internal static extern void godot_icall_Array_RemoveAt(IntPtr ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize); + internal static extern Error godot_icall_Array_Resize(IntPtr ptr, int newSize); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Error godot_icall_Array_Shuffle(IntPtr ptr); + internal static extern Error godot_icall_Array_Shuffle(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); + internal static extern void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_Array_ToString(IntPtr ptr); + internal static extern string godot_icall_Array_ToString(IntPtr ptr); } /// <summary> @@ -344,7 +344,7 @@ namespace Godot.Collections /// <typeparam name="T">The type of the array.</typeparam> public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> { - Array objectArray; + private Array _objectArray; internal static int elemTypeEncoding; internal static IntPtr elemTypeClass; @@ -359,7 +359,7 @@ namespace Godot.Collections /// </summary> public Array() { - objectArray = new Array(); + _objectArray = new Array(); } /// <summary> @@ -372,7 +372,7 @@ namespace Godot.Collections if (collection == null) throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); - objectArray = new Array(collection); + _objectArray = new Array(collection); } /// <summary> @@ -386,7 +386,7 @@ namespace Godot.Collections { throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); } - objectArray = new Array(array); + _objectArray = new Array(array); } /// <summary> @@ -395,22 +395,22 @@ namespace Godot.Collections /// <param name="array">The untyped array to construct from.</param> public Array(Array array) { - objectArray = array; + _objectArray = array; } internal Array(IntPtr handle) { - objectArray = new Array(handle); + _objectArray = new Array(handle); } internal Array(ArraySafeHandle handle) { - objectArray = new Array(handle); + _objectArray = new Array(handle); } internal IntPtr GetPtr() { - return objectArray.GetPtr(); + return _objectArray.GetPtr(); } /// <summary> @@ -419,17 +419,17 @@ namespace Godot.Collections /// <param name="from">The typed array to convert.</param> public static explicit operator Array(Array<T> from) { - return from.objectArray; + return from._objectArray; } /// <summary> /// Duplicates this <see cref="Array{T}"/>. /// </summary> - /// <param name="deep">If true, performs a deep copy.</param> + /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param> /// <returns>A new Godot Array.</returns> public Array<T> Duplicate(bool deep = false) { - return new Array<T>(objectArray.Duplicate(deep)); + return new Array<T>(_objectArray.Duplicate(deep)); } /// <summary> @@ -439,7 +439,7 @@ namespace Godot.Collections /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns> public Error Resize(int newSize) { - return objectArray.Resize(newSize); + return _objectArray.Resize(newSize); } /// <summary> @@ -447,7 +447,7 @@ namespace Godot.Collections /// </summary> public void Shuffle() { - objectArray.Shuffle(); + _objectArray.Shuffle(); } /// <summary> @@ -458,19 +458,19 @@ namespace Godot.Collections /// <returns>A new Godot Array with the contents of both arrays.</returns> public static Array<T> operator +(Array<T> left, Array<T> right) { - return new Array<T>(left.objectArray + right.objectArray); + return new Array<T>(left._objectArray + right._objectArray); } // IList<T> /// <summary> - /// Returns the value at the given index. + /// Returns the value at the given <paramref name="index"/>. /// </summary> - /// <value>The value at the given index.</value> + /// <value>The value at the given <paramref name="index"/>.</value> public T this[int index] { get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); } - set { objectArray[index] = value; } + set { _objectArray[index] = value; } } /// <summary> @@ -481,7 +481,7 @@ namespace Godot.Collections /// <returns>The index of the item, or -1 if not found.</returns> public int IndexOf(T item) { - return objectArray.IndexOf(item); + return _objectArray.IndexOf(item); } /// <summary> @@ -494,7 +494,7 @@ namespace Godot.Collections /// <param name="item">The item to insert.</param> public void Insert(int index, T item) { - objectArray.Insert(index, item); + _objectArray.Insert(index, item); } /// <summary> @@ -503,7 +503,7 @@ namespace Godot.Collections /// <param name="index">The index of the element to remove.</param> public void RemoveAt(int index) { - objectArray.RemoveAt(index); + _objectArray.RemoveAt(index); } // ICollection<T> @@ -515,20 +515,20 @@ namespace Godot.Collections /// <returns>The number of elements.</returns> public int Count { - get { return objectArray.Count; } + get { return _objectArray.Count; } } bool ICollection<T>.IsReadOnly => false; /// <summary> /// Adds an item to the end of this <see cref="Array{T}"/>. - /// This is the same as `append` or `push_back` in GDScript. + /// This is the same as <c>append</c> or <c>push_back</c> in GDScript. /// </summary> /// <param name="item">The item to add.</param> /// <returns>The new size after adding the item.</returns> public void Add(T item) { - objectArray.Add(item); + _objectArray.Add(item); } /// <summary> @@ -536,7 +536,7 @@ namespace Godot.Collections /// </summary> public void Clear() { - objectArray.Clear(); + _objectArray.Clear(); } /// <summary> @@ -546,7 +546,7 @@ namespace Godot.Collections /// <returns>Whether or not this array contains the given item.</returns> public bool Contains(T item) { - return objectArray.Contains(item); + return _objectArray.Contains(item); } /// <summary> @@ -566,7 +566,7 @@ namespace Godot.Collections // TODO This may be quite slow because every element access is an internal call. // It could be moved entirely to an internal call if we find out how to do the cast there. - int count = objectArray.Count; + int count = _objectArray.Count; if (array.Length < (arrayIndex + count)) throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); @@ -583,7 +583,7 @@ namespace Godot.Collections /// from this <see cref="Array{T}"/>. /// </summary> /// <param name="item">The value to remove.</param> - /// <returns>A bool indicating success or failure.</returns> + /// <returns>A <see langword="bool"/> indicating success or failure.</returns> public bool Remove(T item) { return Array.godot_icall_Array_Remove(GetPtr(), item); @@ -597,7 +597,7 @@ namespace Godot.Collections /// <returns>An enumerator.</returns> public IEnumerator<T> GetEnumerator() { - int count = objectArray.Count; + int count = _objectArray.Count; for (int i = 0; i < count; i++) { @@ -614,6 +614,6 @@ namespace Godot.Collections /// Converts this <see cref="Array{T}"/> to a string. /// </summary> /// <returns>A string representation of this array.</returns> - public override string ToString() => objectArray.ToString(); + public override string ToString() => _objectArray.ToString(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs index ac6cffceb2..e93bc89811 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs @@ -3,7 +3,5 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Class)] - public class DisableGodotGeneratorsAttribute : Attribute - { - } + public class DisableGodotGeneratorsAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs index 6cec8773b2..2dedba2be3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs @@ -3,11 +3,8 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Method)] - public class RemoteAttribute : Attribute {} + public class RemoteAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] - public class MasterAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Method)] - public class PuppetAttribute : Attribute {} + public class PuppetAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs index 39d5782db8..07a214f543 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs @@ -3,7 +3,5 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)] - public class SignalAttribute : Attribute - { - } + public 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 d0437409af..d2344389f4 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 class ToolAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 968f853c2d..0fb1df6c2f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -31,7 +31,7 @@ namespace Godot /// <summary> /// The basis matrix's X vector (column 0). /// </summary> - /// <value>Equivalent to <see cref="Column0"/> and array index `[0]`.</value> + /// <value>Equivalent to <see cref="Column0"/> and array index <c>[0]</c>.</value> public Vector3 x { get => Column0; @@ -41,7 +41,7 @@ namespace Godot /// <summary> /// The basis matrix's Y vector (column 1). /// </summary> - /// <value>Equivalent to <see cref="Column1"/> and array index `[1]`.</value> + /// <value>Equivalent to <see cref="Column1"/> and array index <c>[1]</c>.</value> public Vector3 y { get => Column1; @@ -51,7 +51,7 @@ namespace Godot /// <summary> /// The basis matrix's Z vector (column 2). /// </summary> - /// <value>Equivalent to <see cref="Column2"/> and array index `[2]`.</value> + /// <value>Equivalent to <see cref="Column2"/> and array index <c>[2]</c>.</value> public Vector3 z { get => Column2; @@ -82,45 +82,45 @@ namespace Godot /// <summary> /// Column 0 of the basis matrix (the X vector). /// </summary> - /// <value>Equivalent to <see cref="x"/> and array index `[0]`.</value> + /// <value>Equivalent to <see cref="x"/> and array index <c>[0]</c>.</value> public Vector3 Column0 { get => new Vector3(Row0.x, Row1.x, Row2.x); set { - this.Row0.x = value.x; - this.Row1.x = value.y; - this.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 `[1]`.</value> + /// <value>Equivalent to <see cref="y"/> and array index <c>[1]</c>.</value> public Vector3 Column1 { get => new Vector3(Row0.y, Row1.y, Row2.y); set { - this.Row0.y = value.x; - this.Row1.y = value.y; - this.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 `[2]`.</value> + /// <value>Equivalent to <see cref="z"/> and array index <c>[2]</c>.</value> public Vector3 Column2 { get => new Vector3(Row0.z, Row1.z, Row2.z); set { - this.Row0.z = value.x; - this.Row1.z = value.y; - this.Row2.z = value.z; + Row0.z = value.x; + Row1.z = value.y; + Row2.z = value.z; } } @@ -150,9 +150,10 @@ namespace Godot } /// <summary> - /// Access whole columns in the form of Vector3. + /// Access whole columns in the form of <see cref="Vector3"/>. /// </summary> /// <param name="column">Which column vector.</param> + /// <value>The basis column.</value> public Vector3 this[int column] { get @@ -193,6 +194,7 @@ namespace Godot /// </summary> /// <param name="column">Which column, the matrix horizontal position.</param> /// <param name="row">Which row, the matrix vertical position.</param> + /// <value>The matrix element.</value> public real_t this[int column, int row] { get @@ -207,6 +209,13 @@ namespace Godot } } + /// <summary> + /// Returns the <see cref="Basis"/>'s rotation in the form of a + /// <see cref="Quaternion"/>. See <see cref="GetEuler"/> if you + /// need Euler angles, but keep in mind quaternions should generally + /// be preferred to Euler angles. + /// </summary> + /// <returns>The basis rotation.</returns> public Quaternion GetRotationQuaternion() { Basis orthonormalizedBasis = Orthonormalized(); @@ -263,10 +272,10 @@ namespace Godot /// The returned vector contains the rotation angles in /// the format (X angle, Y angle, Z angle). /// - /// Consider using the <see cref="Basis.Quaternion()"/> method instead, which + /// Consider using the <see cref="Quaternion()"/> method instead, which /// returns a <see cref="Godot.Quaternion"/> quaternion instead of Euler angles. /// </summary> - /// <returns>A Vector3 representing the basis rotation in Euler angles.</returns> + /// <returns>A <see cref="Vector3"/> representing the basis rotation in Euler angles.</returns> public Vector3 GetEuler() { Basis m = Orthonormalized(); @@ -304,7 +313,10 @@ namespace Godot /// but are more efficient for some internal calculations. /// </summary> /// <param name="index">Which row.</param> - /// <returns>One of `Row0`, `Row1`, or `Row2`.</returns> + /// <exception cref="IndexOutOfRangeException"> + /// Thrown when the <paramref name="index"/> is not 0, 1 or 2. + /// </exception> + /// <returns>One of <c>Row0</c>, <c>Row1</c>, or <c>Row2</c>.</returns> public Vector3 GetRow(int index) { switch (index) @@ -326,6 +338,9 @@ namespace Godot /// </summary> /// <param name="index">Which row.</param> /// <param name="value">The vector to set the row to.</param> + /// <exception cref="IndexOutOfRangeException"> + /// Thrown when the <paramref name="index"/> is not 0, 1 or 2. + /// </exception> public void SetRow(int index, Vector3 value) { switch (index) @@ -452,8 +467,8 @@ namespace Godot } /// <summary> - /// Introduce an additional rotation around the given `axis` - /// by `phi` (in radians). The axis must be a normalized vector. + /// Introduce an additional rotation around the given <paramref name="axis"/> + /// by <paramref name="phi"/> (in radians). The axis must be a normalized vector. /// </summary> /// <param name="axis">The axis to rotate around. Must be normalized.</param> /// <param name="phi">The angle to rotate, in radians.</param> @@ -504,7 +519,7 @@ namespace Godot /// <returns>The resulting dot product.</returns> public real_t Tdotx(Vector3 with) { - return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2]; + return Row0[0] * with[0] + Row1[0] * with[1] + Row2[0] * with[2]; } /// <summary> @@ -514,7 +529,7 @@ namespace Godot /// <returns>The resulting dot product.</returns> public real_t Tdoty(Vector3 with) { - return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2]; + return Row0[1] * with[0] + Row1[1] * with[1] + Row2[1] * with[2]; } /// <summary> @@ -524,7 +539,7 @@ namespace Godot /// <returns>The resulting dot product.</returns> public real_t Tdotz(Vector3 with) { - return this.Row0[2] * with[0] + this.Row1[2] * with[1] + this.Row2[2] * with[2]; + return Row0[2] * with[0] + Row1[2] * with[1] + Row2[2] * with[2]; } /// <summary> @@ -533,7 +548,7 @@ namespace Godot /// <returns>The transposed basis matrix.</returns> public Basis Transposed() { - var tr = this; + Basis tr = this; real_t temp = tr.Row0[1]; tr.Row0[1] = tr.Row1[0]; @@ -553,15 +568,16 @@ namespace Godot /// <summary> /// Returns a vector transformed (multiplied) by the basis matrix. /// </summary> + /// <seealso cref="XformInv(Vector3)"/> /// <param name="v">A vector to transform.</param> /// <returns>The transformed vector.</returns> public Vector3 Xform(Vector3 v) { return new Vector3 ( - this.Row0.Dot(v), - this.Row1.Dot(v), - this.Row2.Dot(v) + Row0.Dot(v), + Row1.Dot(v), + Row2.Dot(v) ); } @@ -571,15 +587,16 @@ namespace Godot /// Note: This results in a multiplication by the inverse of the /// basis matrix only if it represents a rotation-reflection. /// </summary> + /// <seealso cref="Xform(Vector3)"/> /// <param name="v">A vector to inversely transform.</param> /// <returns>The inversely transformed vector.</returns> public Vector3 XformInv(Vector3 v) { return new Vector3 ( - this.Row0[0] * v.x + this.Row1[0] * v.y + this.Row2[0] * v.z, - this.Row0[1] * v.x + this.Row1[1] * v.y + this.Row2[1] * v.z, - this.Row0[2] * v.x + this.Row1[2] * v.y + this.Row2[2] * v.z + Row0[0] * v.x + Row1[0] * v.y + Row2[0] * v.z, + Row0[1] * v.x + Row1[1] * v.y + Row2[1] * v.z, + Row0[2] * v.x + Row1[2] * v.y + Row2[2] * v.z ); } @@ -675,25 +692,25 @@ namespace Godot /// <summary> /// The identity basis, with no rotation or scaling applied. - /// This is used as a replacement for `Basis()` in GDScript. - /// Do not use `new Basis()` with no arguments in C#, because it sets all values to zero. + /// This is used as a replacement for <c>Basis()</c> in GDScript. + /// Do not use <c>new Basis()</c> with no arguments in C#, because it sets all values to zero. /// </summary> - /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Back)`.</value> + /// <value>Equivalent to <c>new Basis(Vector3.Right, Vector3.Up, Vector3.Back)</c>.</value> public static Basis Identity { get { return _identity; } } /// <summary> /// The basis that will flip something along the X axis when used in a transformation. /// </summary> - /// <value>Equivalent to `new Basis(Vector3.Left, Vector3.Up, Vector3.Back)`.</value> + /// <value>Equivalent to <c>new Basis(Vector3.Left, Vector3.Up, Vector3.Back)</c>.</value> public static Basis FlipX { get { return _flipX; } } /// <summary> /// The basis that will flip something along the Y axis when used in a transformation. /// </summary> - /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Down, Vector3.Back)`.</value> + /// <value>Equivalent to <c>new Basis(Vector3.Right, Vector3.Down, Vector3.Back)</c>.</value> public static Basis FlipY { get { return _flipY; } } /// <summary> /// The basis that will flip something along the Z axis when used in a transformation. /// </summary> - /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Forward)`.</value> + /// <value>Equivalent to <c>new Basis(Vector3.Right, Vector3.Up, Vector3.Forward)</c>.</value> public static Basis FlipZ { get { return _flipZ; } } /// <summary> @@ -752,8 +769,8 @@ namespace Godot } /// <summary> - /// Constructs a pure rotation basis matrix, rotated around the given `axis` - /// by `phi` (in radians). The axis must be a normalized vector. + /// Constructs a pure rotation basis matrix, rotated around the given <paramref name="axis"/> + /// by <paramref name="phi"/> (in radians). The axis must be a normalized vector. /// </summary> /// <param name="axis">The axis to rotate around. Must be normalized.</param> /// <param name="phi">The angle to rotate, in radians.</param> @@ -830,6 +847,11 @@ namespace Godot return !left.Equals(right); } + /// <summary> + /// Returns <see langword="true"/> if this basis and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the basis and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Basis) @@ -840,32 +862,49 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this basis and <paramref name="other"/> are equal + /// </summary> + /// <param name="other">The other basis to compare.</param> + /// <returns>Whether or not the bases are equal.</returns> public bool Equals(Basis other) { return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2); } /// <summary> - /// Returns true if this basis and `other` are approximately equal, by running - /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component. + /// Returns <see langword="true"/> if this basis and <paramref name="other"/> are approximately equal, + /// by running <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component. /// </summary> /// <param name="other">The other basis to compare.</param> - /// <returns>Whether or not the matrices are approximately equal.</returns> + /// <returns>Whether or not the bases are approximately equal.</returns> public bool IsEqualApprox(Basis other) { return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2); } + /// <summary> + /// Serves as the hash function for <see cref="Basis"/>. + /// </summary> + /// <returns>A hash code for this basis.</returns> public override int GetHashCode() { return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Basis"/> to a string. + /// </summary> + /// <returns>A string representation of this basis.</returns> public override string ToString() { return $"[X: {x}, Y: {y}, Z: {z}]"; } + /// <summary> + /// Converts this <see cref="Basis"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this basis.</returns> public string ToString(string format) { return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, Z: {z.ToString(format)}]"; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs index c85cc1884c..a28a46896b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs @@ -2,18 +2,58 @@ using System; namespace Godot { + /// <summary> + /// Callable is a first class object which can be held in variables and passed to functions. + /// It represents a given method in an Object, and is typically used for signal callbacks. + /// </summary> + /// <example> + /// <code> + /// public void PrintArgs(object ar1, object arg2, object arg3 = null) + /// { + /// GD.PrintS(arg1, arg2, arg3); + /// } + /// + /// public void Test() + /// { + /// // This Callable object will call the PrintArgs method defined above. + /// Callable callable = new Callable(this, nameof(PrintArgs)); + /// callable.Call("hello", "world"); // Prints "hello world null". + /// callable.Call(Vector2.Up, 42, callable); // Prints "(0, -1) 42 Node(Node.cs)::PrintArgs". + /// callable.Call("invalid"); // Invalid call, should have at least 2 arguments. + /// } + /// </code> + /// </example> public struct Callable { private readonly Object _target; private readonly StringName _method; private readonly Delegate _delegate; + /// <summary> + /// Object that contains the method. + /// </summary> public Object Target => _target; + /// <summary> + /// Name of the method that will be called. + /// </summary> public StringName Method => _method; + /// <summary> + /// Delegate of the method that will be called. + /// </summary> public Delegate Delegate => _delegate; + /// <summary> + /// Converts a <see cref="Delegate"/> to a <see cref="Callable"/>. + /// </summary> + /// <param name="delegate">The delegate to convert.</param> public static implicit operator Callable(Delegate @delegate) => new Callable(@delegate); + /// <summary> + /// Constructs a new <see cref="Callable"/> for the method called <paramref name="method"/> + /// in the specified <paramref name="target"/>. + /// </summary> + /// <param name="target">Object that contains the method.</param> + /// <param name="method">Name of the method that will be called.</param> public Callable(Object target, StringName method) { _target = target; @@ -21,6 +61,10 @@ namespace Godot _delegate = null; } + /// <summary> + /// Constructs a new <see cref="Callable"/> for the given <paramref name="delegate"/>. + /// </summary> + /// <param name="delegate">Delegate method that will be called.</param> public Callable(Delegate @delegate) { _target = null; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index b9a98ba9c7..2a869bc335 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -7,11 +7,11 @@ namespace Godot /// A color represented by red, green, blue, and alpha (RGBA) components. /// The alpha component is often used for transparency. /// Values are in floating-point and usually range from 0 to 1. - /// Some properties (such as CanvasItem.modulate) may accept values + /// Some properties (such as <see cref="CanvasItem.Modulate"/>) may accept values /// greater than 1 (overbright or HDR colors). /// /// If you want to supply values in a range of 0 to 255, you should use - /// <see cref="Color8"/> and the `r8`/`g8`/`b8`/`a8` properties. + /// <see cref="Color8"/> and the <c>r8</c>/<c>g8</c>/<c>b8</c>/<c>a8</c> properties. /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential)] @@ -127,11 +127,11 @@ namespace Godot } 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; @@ -173,7 +173,7 @@ namespace Godot /// <summary> /// The HSV value (brightness) of this color, on the range 0 to 1. /// </summary> - /// <value>Getting is equivalent to using `Max()` on the RGB components. Setting uses <see cref="FromHSV"/>.</value> + /// <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 { get @@ -189,7 +189,12 @@ namespace Godot /// <summary> /// Access color components using their index. /// </summary> - /// <value>`[0]` is equivalent to `.r`, `[1]` is equivalent to `.g`, `[2]` is equivalent to `.b`, `[3]` is equivalent to `.a`.</value> + /// <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"/>. + /// </value> public float this[int index] { get @@ -236,30 +241,30 @@ namespace Godot /// The second color may have a range of alpha values. /// </summary> /// <param name="over">The color to blend over.</param> - /// <returns>This color blended over `over`.</returns> + /// <returns>This color blended over <paramref name="over"/>.</returns> public Color Blend(Color over) { Color res; float sa = 1.0f - over.a; - res.a = a * sa + over.a; + res.a = (a * sa) + over.a; 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; } /// <summary> /// Returns a new color with all components clamped between the - /// components of `min` and `max` using - /// <see cref="Mathf.Clamp(float, float, float)"/>. + /// components of <paramref name="min"/> and <paramref name="max"/> + /// using <see cref="Mathf.Clamp(float, float, float)"/>. /// </summary> /// <param name="min">The color with minimum allowed values.</param> /// <param name="max">The color with maximum allowed values.</param> @@ -286,14 +291,14 @@ namespace Godot public Color Darkened(float amount) { Color res = this; - res.r = res.r * (1.0f - amount); - res.g = res.g * (1.0f - amount); - res.b = res.b * (1.0f - amount); + res.r *= 1.0f - amount; + res.g *= 1.0f - amount; + res.b *= 1.0f - amount; return res; } /// <summary> - /// Returns the inverted color: `(1 - r, 1 - g, 1 - b, a)`. + /// Returns the inverted color: <c>(1 - r, 1 - g, 1 - b, a)</c>. /// </summary> /// <returns>The inverted color.</returns> public Color Inverted() @@ -315,15 +320,15 @@ namespace Godot public Color Lightened(float amount) { Color res = this; - res.r = res.r + (1.0f - res.r) * amount; - res.g = res.g + (1.0f - res.g) * amount; - res.b = 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; } /// <summary> /// Returns the result of the linear interpolation between - /// this color and `to` by amount `weight`. + /// this color and <paramref name="to"/> by amount <paramref name="weight"/>. /// </summary> /// <param name="to">The destination color for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> @@ -341,7 +346,7 @@ namespace Godot /// <summary> /// Returns the result of the linear interpolation between - /// this color and `to` by color amount `weight`. + /// this color and <paramref name="to"/> by color amount <paramref name="weight"/>. /// </summary> /// <param name="to">The destination color for interpolation.</param> /// <param name="weight">A color with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param> @@ -362,7 +367,7 @@ namespace Godot /// format (each byte represents a color channel). /// ABGR is the reversed version of the default format. /// </summary> - /// <returns>A uint representing this color in ABGR32 format.</returns> + /// <returns>A <see langword="uint"/> representing this color in ABGR32 format.</returns> public uint ToAbgr32() { uint c = (byte)Math.Round(a * 255); @@ -381,7 +386,7 @@ namespace Godot /// format (each word represents a color channel). /// ABGR is the reversed version of the default format. /// </summary> - /// <returns>A ulong representing this color in ABGR64 format.</returns> + /// <returns>A <see langword="ulong"/> representing this color in ABGR64 format.</returns> public ulong ToAbgr64() { ulong c = (ushort)Math.Round(a * 65535); @@ -400,7 +405,7 @@ namespace Godot /// format (each byte represents a color channel). /// ARGB is more compatible with DirectX, but not used much in Godot. /// </summary> - /// <returns>A uint representing this color in ARGB32 format.</returns> + /// <returns>A <see langword="uint"/> representing this color in ARGB32 format.</returns> public uint ToArgb32() { uint c = (byte)Math.Round(a * 255); @@ -419,7 +424,7 @@ namespace Godot /// format (each word represents a color channel). /// ARGB is more compatible with DirectX, but not used much in Godot. /// </summary> - /// <returns>A ulong representing this color in ARGB64 format.</returns> + /// <returns>A <see langword="ulong"/> representing this color in ARGB64 format.</returns> public ulong ToArgb64() { ulong c = (ushort)Math.Round(a * 65535); @@ -438,7 +443,7 @@ namespace Godot /// format (each byte represents a color channel). /// RGBA is Godot's default and recommended format. /// </summary> - /// <returns>A uint representing this color in RGBA32 format.</returns> + /// <returns>A <see langword="uint"/> representing this color in RGBA32 format.</returns> public uint ToRgba32() { uint c = (byte)Math.Round(r * 255); @@ -457,7 +462,7 @@ namespace Godot /// format (each word represents a color channel). /// RGBA is Godot's default and recommended format. /// </summary> - /// <returns>A ulong representing this color in RGBA64 format.</returns> + /// <returns>A <see langword="ulong"/> representing this color in RGBA64 format.</returns> public ulong ToRgba64() { ulong c = (ushort)Math.Round(r * 65535); @@ -474,11 +479,13 @@ namespace Godot /// <summary> /// Returns the color's HTML hexadecimal color string in RGBA format. /// </summary> - /// <param name="includeAlpha">Whether or not to include alpha. If false, the color is RGB instead of RGBA.</param> + /// <param name="includeAlpha"> + /// 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 string ToHTML(bool includeAlpha = true) { - var txt = string.Empty; + string txt = string.Empty; txt += ToHex32(r); txt += ToHex32(g); @@ -493,7 +500,7 @@ namespace Godot } /// <summary> - /// Constructs a color from RGBA values, typically on the range of 0 to 1. + /// Constructs a <see cref="Color"/> from RGBA values, typically on the range of 0 to 1. /// </summary> /// <param name="r">The color's red component, typically on the range of 0 to 1.</param> /// <param name="g">The color's green component, typically on the range of 0 to 1.</param> @@ -508,7 +515,7 @@ namespace Godot } /// <summary> - /// Constructs a color from an existing color and an alpha value. + /// Constructs a <see cref="Color"/> from an existing color and an alpha value. /// </summary> /// <param name="c">The color to construct from. Only its RGB values are used.</param> /// <param name="a">The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1.</param> @@ -521,10 +528,10 @@ namespace Godot } /// <summary> - /// Constructs a color from an unsigned 32-bit integer in RGBA format + /// Constructs a <see cref="Color"/> from an unsigned 32-bit integer in RGBA format /// (each byte represents a color channel). /// </summary> - /// <param name="rgba">The uint representing the color.</param> + /// <param name="rgba">The <see langword="uint"/> representing the color.</param> public Color(uint rgba) { a = (rgba & 0xFF) / 255.0f; @@ -537,10 +544,10 @@ namespace Godot } /// <summary> - /// Constructs a color from an unsigned 64-bit integer in RGBA format + /// Constructs a <see cref="Color"/> from an unsigned 64-bit integer in RGBA format /// (each word represents a color channel). /// </summary> - /// <param name="rgba">The ulong representing the color.</param> + /// <param name="rgba">The <see langword="ulong"/> representing the color.</param> public Color(ulong rgba) { a = (rgba & 0xFFFF) / 65535.0f; @@ -553,9 +560,9 @@ namespace Godot } /// <summary> - /// Constructs a color either from an HTML color code or from a - /// standardized color name. Supported - /// color names are the same as the <see cref="Colors"/> constants. + /// Constructs a <see cref="Color"/> either from an HTML color code or from a + /// standardized color name. Supported color names are the same as the + /// <see cref="Colors"/> constants. /// </summary> /// <param name="code">The HTML color code or color name to construct from.</param> public Color(string code) @@ -571,8 +578,8 @@ namespace Godot } /// <summary> - /// Constructs a color either from an HTML color code or from a - /// standardized color name, with `alpha` on the range of 0 to 1. Supported + /// Constructs a <see cref="Color"/> either from an HTML color code or from a + /// standardized color name, with <paramref name="alpha"/> on the range of 0 to 1. Supported /// color names are the same as the <see cref="Colors"/> constants. /// </summary> /// <param name="code">The HTML color code or color name to construct from.</param> @@ -584,9 +591,12 @@ namespace Godot } /// <summary> - /// Constructs a color from the HTML hexadecimal color string in RGBA format. + /// Constructs a <see cref="Color"/> from the HTML hexadecimal color string in RGBA format. /// </summary> /// <param name="rgba">A string for the HTML hexadecimal representation of this color.</param> + /// <exception name="ArgumentOutOfRangeException"> + /// Thrown when the given <paramref name="rgba"/> color code is invalid. + /// </exception> private static Color FromHTML(string rgba) { Color c; @@ -627,7 +637,8 @@ namespace Godot } else { - throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba); + throw new ArgumentOutOfRangeException( + $"Invalid color code. Length is {rgba.Length}, but a length of 6 or 8 is expected: {rgba}"); } c.a = 1.0f; @@ -697,11 +708,11 @@ namespace Godot /// <returns>The constructed color.</returns> private static Color Named(string name) { - name = name.Replace(" ", String.Empty); - name = name.Replace("-", String.Empty); - name = name.Replace("_", String.Empty); - name = name.Replace("'", String.Empty); - name = name.Replace(".", String.Empty); + name = name.Replace(" ", string.Empty); + name = name.Replace("-", string.Empty); + name = name.Replace("_", string.Empty); + name = name.Replace("'", string.Empty); + name = name.Replace(".", string.Empty); name = name.ToUpper(); if (!Colors.namedColors.ContainsKey(name)) @@ -715,7 +726,7 @@ namespace Godot /// <summary> /// Constructs a color from an HSV profile, with values on the /// range of 0 to 1. This is equivalent to using each of - /// the `h`/`s`/`v` properties, but much more efficient. + /// the <c>h</c>/<c>s</c>/<c>v</c> properties, but much more efficient. /// </summary> /// <param name="hue">The HSV hue, typically on the range of 0 to 1.</param> /// <param name="saturation">The HSV saturation, typically on the range of 0 to 1.</param> @@ -739,8 +750,8 @@ namespace Godot f = hue - i; p = value * (1 - saturation); - q = value * (1 - saturation * f); - t = value * (1 - saturation * (1 - f)); + q = value * (1 - (saturation * f)); + t = value * (1 - (saturation * (1 - f))); switch (i) { @@ -761,7 +772,7 @@ namespace Godot /// <summary> /// Converts a color to HSV values. This is equivalent to using each of - /// the `h`/`s`/`v` properties, but much more efficient. + /// the <c>h</c>/<c>s</c>/<c>v</c> properties, but much more efficient. /// </summary> /// <param name="hue">Output parameter for the HSV hue.</param> /// <param name="saturation">Output parameter for the HSV saturation.</param> @@ -785,22 +796,24 @@ namespace Godot } 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; if (hue < 0) - { hue += 1.0f; - } } - saturation = max == 0 ? 0 : 1f - 1f * min / max; + if (max == 0) + saturation = 0; + else + saturation = 1 - (min / max); + value = max; } @@ -977,6 +990,11 @@ namespace Godot return left.r > right.r; } + /// <summary> + /// Returns <see langword="true"/> if this color and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the color and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Color) @@ -987,14 +1005,19 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this color and <paramref name="other"/> are equal + /// </summary> + /// <param name="other">The other color to compare.</param> + /// <returns>Whether or not the colors are equal.</returns> public bool Equals(Color other) { return r == other.r && g == other.g && b == other.b && a == other.a; } /// <summary> - /// Returns true if this color and `other` are approximately equal, by running - /// <see cref="Godot.Mathf.IsEqualApprox(float, float)"/> on each component. + /// Returns <see langword="true"/> if this color and <paramref name="other"/> are approximately equal, + /// by running <see cref="Mathf.IsEqualApprox(float, float)"/> on each component. /// </summary> /// <param name="other">The other color to compare.</param> /// <returns>Whether or not the colors are approximately equal.</returns> @@ -1003,16 +1026,28 @@ namespace Godot return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a); } + /// <summary> + /// Serves as the hash function for <see cref="Color"/>. + /// </summary> + /// <returns>A hash code for this color.</returns> public override int GetHashCode() { return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Color"/> to a string. + /// </summary> + /// <returns>A string representation of this color.</returns> public override string ToString() { return $"({r}, {g}, {b}, {a})"; } + /// <summary> + /// Converts this <see cref="Color"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this color.</returns> public string ToString(string format) { return $"({r.ToString(format)}, {g.ToString(format)}, {b.ToString(format)}, {a.ToString(format)})"; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 61a34bfc87..2dfe304aaa 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -7,7 +7,7 @@ using System.Diagnostics.CodeAnalysis; namespace Godot.Collections { - class DictionarySafeHandle : SafeHandle + internal class DictionarySafeHandle : SafeHandle { public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) { @@ -31,19 +31,17 @@ namespace Godot.Collections /// typed elements allocated in the engine in C++. Useful when /// interfacing with the engine. /// </summary> - public class Dictionary : - IDictionary, - IDisposable + public class Dictionary : IDictionary, IDisposable { - DictionarySafeHandle safeHandle; - bool disposed = false; + private DictionarySafeHandle _safeHandle; + private bool _disposed = false; /// <summary> /// Constructs a new empty <see cref="Dictionary"/>. /// </summary> public Dictionary() { - safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); + _safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); } /// <summary> @@ -62,20 +60,20 @@ namespace Godot.Collections internal Dictionary(DictionarySafeHandle handle) { - safeHandle = handle; + _safeHandle = handle; } internal Dictionary(IntPtr handle) { - safeHandle = new DictionarySafeHandle(handle); + _safeHandle = new DictionarySafeHandle(handle); } internal IntPtr GetPtr() { - if (disposed) + if (_disposed) throw new ObjectDisposedException(GetType().FullName); - return safeHandle.DangerousGetHandle(); + return _safeHandle.DangerousGetHandle(); } /// <summary> @@ -83,16 +81,16 @@ namespace Godot.Collections /// </summary> public void Dispose() { - if (disposed) + if (_disposed) return; - if (safeHandle != null) + if (_safeHandle != null) { - safeHandle.Dispose(); - safeHandle = null; + _safeHandle.Dispose(); + _safeHandle = null; } - disposed = true; + _disposed = true; } /// <summary> @@ -230,17 +228,17 @@ namespace Godot.Collections private class DictionaryEnumerator : IDictionaryEnumerator { - private readonly Dictionary dictionary; - private readonly int count; - private int index = -1; - private bool dirty = true; + private readonly Dictionary _dictionary; + private readonly int _count; + private int _index = -1; + private bool _dirty = true; - private DictionaryEntry entry; + private DictionaryEntry _entry; public DictionaryEnumerator(Dictionary dictionary) { - this.dictionary = dictionary; - count = dictionary.Count; + _dictionary = dictionary; + _count = dictionary.Count; } public object Current => Entry; @@ -249,19 +247,19 @@ namespace Godot.Collections { get { - if (dirty) + if (_dirty) { UpdateEntry(); } - return entry; + return _entry; } } private void UpdateEntry() { - dirty = false; - godot_icall_Dictionary_KeyValuePairAt(dictionary.GetPtr(), index, out object key, out object value); - entry = new DictionaryEntry(key, value); + _dirty = false; + godot_icall_Dictionary_KeyValuePairAt(_dictionary.GetPtr(), _index, out object key, out object value); + _entry = new DictionaryEntry(key, value); } public object Key => Entry.Key; @@ -270,15 +268,15 @@ namespace Godot.Collections public bool MoveNext() { - index++; - dirty = true; - return index < count; + _index++; + _dirty = true; + return _index < _count; } public void Reset() { - index = -1; - dirty = true; + _index = -1; + _dirty = true; } } @@ -292,28 +290,28 @@ namespace Godot.Collections } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Dictionary_Ctor(); + internal static extern IntPtr godot_icall_Dictionary_Ctor(); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Dtor(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + internal static extern object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass); + internal static extern object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + internal static extern void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); + internal static extern IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + internal static extern IntPtr godot_icall_Dictionary_Values(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_Dictionary_Count(IntPtr ptr); + internal static extern int godot_icall_Dictionary_Count(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values); @@ -325,34 +323,34 @@ namespace Godot.Collections internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr); + internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep); + internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); + internal static extern bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); + internal static extern bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); + internal static extern bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass); + internal static extern bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); + internal static extern void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr); + internal static extern string godot_icall_Dictionary_ToString(IntPtr ptr); } /// <summary> @@ -363,10 +361,9 @@ namespace Godot.Collections /// </summary> /// <typeparam name="TKey">The type of the dictionary's keys.</typeparam> /// <typeparam name="TValue">The type of the dictionary's values.</typeparam> - public class Dictionary<TKey, TValue> : - IDictionary<TKey, TValue> + public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue> { - private readonly Dictionary objectDict; + private readonly Dictionary _objectDict; internal static int valTypeEncoding; internal static IntPtr valTypeClass; @@ -381,7 +378,7 @@ namespace Godot.Collections /// </summary> public Dictionary() { - objectDict = new Dictionary(); + _objectDict = new Dictionary(); } /// <summary> @@ -391,7 +388,7 @@ namespace Godot.Collections /// <returns>A new Godot Dictionary.</returns> public Dictionary(IDictionary<TKey, TValue> dictionary) { - objectDict = new Dictionary(); + _objectDict = new Dictionary(); if (dictionary == null) throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); @@ -413,17 +410,17 @@ namespace Godot.Collections /// <returns>A new Godot Dictionary.</returns> public Dictionary(Dictionary dictionary) { - objectDict = dictionary; + _objectDict = dictionary; } internal Dictionary(IntPtr handle) { - objectDict = new Dictionary(handle); + _objectDict = new Dictionary(handle); } internal Dictionary(DictionarySafeHandle handle) { - objectDict = new Dictionary(handle); + _objectDict = new Dictionary(handle); } /// <summary> @@ -432,12 +429,12 @@ namespace Godot.Collections /// <param name="from">The typed dictionary to convert.</param> public static explicit operator Dictionary(Dictionary<TKey, TValue> from) { - return from.objectDict; + return from._objectDict; } internal IntPtr GetPtr() { - return objectDict.GetPtr(); + return _objectDict.GetPtr(); } /// <summary> @@ -447,7 +444,7 @@ namespace Godot.Collections /// <returns>A new Godot Dictionary.</returns> public Dictionary<TKey, TValue> Duplicate(bool deep = false) { - return new Dictionary<TKey, TValue>(objectDict.Duplicate(deep)); + return new Dictionary<TKey, TValue>(_objectDict.Duplicate(deep)); } // IDictionary<TKey, TValue> @@ -458,8 +455,8 @@ namespace Godot.Collections /// <value>The value at the given <paramref name="key"/>.</value> public TValue this[TKey key] { - get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } - set { objectDict[key] = value; } + get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(_objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } + set { _objectDict[key] = value; } } /// <summary> @@ -469,7 +466,7 @@ namespace Godot.Collections { get { - IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr()); + IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(_objectDict.GetPtr()); return new Array<TKey>(new ArraySafeHandle(handle)); } } @@ -481,7 +478,7 @@ namespace Godot.Collections { get { - IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr()); + IntPtr handle = Dictionary.godot_icall_Dictionary_Values(_objectDict.GetPtr()); return new Array<TValue>(new ArraySafeHandle(handle)); } } @@ -500,7 +497,7 @@ namespace Godot.Collections /// <param name="value">The object to add.</param> public void Add(TKey key, TValue value) { - objectDict.Add(key, value); + _objectDict.Add(key, value); } /// <summary> @@ -510,7 +507,7 @@ namespace Godot.Collections /// <returns>Whether or not this dictionary contains the given key.</returns> public bool ContainsKey(TKey key) { - return objectDict.Contains(key); + return _objectDict.Contains(key); } /// <summary> @@ -544,14 +541,14 @@ namespace Godot.Collections /// <returns>The number of elements.</returns> public int Count { - get { return objectDict.Count; } + get { return _objectDict.Count; } } bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false; void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) { - objectDict.Add(item.Key, item.Value); + _objectDict.Add(item.Key, item.Value); } /// <summary> @@ -559,12 +556,12 @@ namespace Godot.Collections /// </summary> public void Clear() { - objectDict.Clear(); + _objectDict.Clear(); } bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) { - return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); + return _objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); } /// <summary> @@ -622,6 +619,6 @@ namespace Godot.Collections /// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string. /// </summary> /// <returns>A string representation of this dictionary.</returns> - public override string ToString() => objectDict.ToString(); + public override string ToString() => _objectDict.ToString(); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs index 0c21bcaa3f..26d5f9c796 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs @@ -7,20 +7,20 @@ using System.Runtime.CompilerServices; namespace Godot { /// <summary> - /// Represents an <see cref="Godot.Object"/> whose members can be dynamically accessed at runtime through the Variant API. + /// Represents an <see cref="Object"/> whose members can be dynamically accessed at runtime through the Variant API. /// </summary> /// <remarks> /// <para> - /// The <see cref="Godot.DynamicGodotObject"/> class enables access to the Variant - /// members of a <see cref="Godot.Object"/> instance at runtime. + /// The <see cref="DynamicGodotObject"/> class enables access to the Variant + /// members of a <see cref="Object"/> instance at runtime. /// </para> /// <para> /// This allows accessing the class members using their original names in the engine as well as the members from the - /// script attached to the <see cref="Godot.Object"/>, regardless of the scripting language it was written in. + /// script attached to the <see cref="Object"/>, regardless of the scripting language it was written in. /// </para> /// </remarks> /// <example> - /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Godot.Object"/>. + /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Object"/>. /// <code> /// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject; /// sprite.add_child(this); @@ -30,7 +30,7 @@ namespace Godot /// </code> /// </example> /// <example> - /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Godot.Object"/>. + /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Object"/>. /// <code> /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject; /// @@ -54,32 +54,34 @@ namespace Godot public class DynamicGodotObject : DynamicObject { /// <summary> - /// Gets the <see cref="Godot.Object"/> associated with this <see cref="Godot.DynamicGodotObject"/>. + /// Gets the <see cref="Object"/> associated with this <see cref="DynamicGodotObject"/>. /// </summary> public Object Value { get; } /// <summary> - /// Initializes a new instance of the <see cref="Godot.DynamicGodotObject"/> class. + /// Initializes a new instance of the <see cref="DynamicGodotObject"/> class. /// </summary> /// <param name="godotObject"> - /// The <see cref="Godot.Object"/> that will be associated with this <see cref="Godot.DynamicGodotObject"/>. + /// The <see cref="Object"/> that will be associated with this <see cref="DynamicGodotObject"/>. /// </param> - /// <exception cref="System.ArgumentNullException"> - /// Thrown when the <paramref name="godotObject"/> parameter is null. + /// <exception cref="ArgumentNullException"> + /// Thrown when the <paramref name="godotObject"/> parameter is <see langword="null"/>. /// </exception> public DynamicGodotObject(Object godotObject) { if (godotObject == null) throw new ArgumentNullException(nameof(godotObject)); - this.Value = godotObject; + Value = godotObject; } + /// <inheritdoc/> public override IEnumerable<string> GetDynamicMemberNames() { return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value)); } + /// <inheritdoc/> public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) { switch (binder.Operation) @@ -121,6 +123,7 @@ namespace Godot return base.TryBinaryOperation(binder, arg, out result); } + /// <inheritdoc/> public override bool TryConvert(ConvertBinder binder, out object result) { if (binder.Type == typeof(Object)) @@ -139,6 +142,7 @@ namespace Godot return base.TryConvert(binder, out result); } + /// <inheritdoc/> public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { if (indexes.Length == 1) @@ -152,16 +156,19 @@ namespace Godot return base.TryGetIndex(binder, indexes, out result); } + /// <inheritdoc/> public override bool TryGetMember(GetMemberBinder binder, out object result) { return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result); } + /// <inheritdoc/> public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result); } + /// <inheritdoc/> public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { if (indexes.Length == 1) @@ -175,22 +182,23 @@ namespace Godot return base.TrySetIndex(binder, indexes, value); } + /// <inheritdoc/> public override bool TrySetMember(SetMemberBinder binder, object value) { return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject); + internal static extern string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result); + internal static extern bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result); + internal static extern bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value); + internal static extern bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value); #region We don't override these methods diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs index 5d16260f5d..658582960f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs @@ -1,42 +1,190 @@ +using System; + namespace Godot { public partial class Node { + /// <summary> + /// Fetches a node. The <see cref="NodePath"/> can be either a relative path (from + /// the current node) or an absolute path (in the scene tree) to a node. If the path + /// does not exist, a <see langword="null"/> instance is returned and an error + /// is logged. Attempts to access methods on the return value will result in an + /// "Attempt to call <method> on a null instance." error. + /// Note: Fetching absolute paths only works when the node is inside the scene tree + /// (see <see cref="IsInsideTree"/>). + /// </summary> + /// <example> + /// Example: Assume your current node is Character and the following tree: + /// <code> + /// /root + /// /root/Character + /// /root/Character/Sword + /// /root/Character/Backpack/Dagger + /// /root/MyGame + /// /root/Swamp/Alligator + /// /root/Swamp/Mosquito + /// /root/Swamp/Goblin + /// </code> + /// Possible paths are: + /// <code> + /// GetNode("Sword"); + /// GetNode("Backpack/Dagger"); + /// GetNode("../Swamp/Alligator"); + /// GetNode("/root/MyGame"); + /// </code> + /// </example> + /// <seealso cref="GetNodeOrNull{T}(NodePath)"/> + /// <param name="path">The path to the node to fetch.</param> + /// <exception cref="InvalidCastException"> + /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>. + /// </exception> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns> + /// The <see cref="Node"/> at the given <paramref name="path"/>. + /// </returns> public T GetNode<T>(NodePath path) where T : class { return (T)(object)GetNode(path); } + /// <summary> + /// Fetches a node. The <see cref="NodePath"/> can be either a relative path (from + /// the current node) or an absolute path (in the scene tree) to a node. If the path + /// does not exist, a <see langword="null"/> instance is returned and an error + /// is logged. Attempts to access methods on the return value will result in an + /// "Attempt to call <method> on a null instance." error. + /// Note: Fetching absolute paths only works when the node is inside the scene tree + /// (see <see cref="IsInsideTree"/>). + /// </summary> + /// <example> + /// Example: Assume your current node is Character and the following tree: + /// <code> + /// /root + /// /root/Character + /// /root/Character/Sword + /// /root/Character/Backpack/Dagger + /// /root/MyGame + /// /root/Swamp/Alligator + /// /root/Swamp/Mosquito + /// /root/Swamp/Goblin + /// </code> + /// Possible paths are: + /// <code> + /// GetNode("Sword"); + /// GetNode("Backpack/Dagger"); + /// GetNode("../Swamp/Alligator"); + /// GetNode("/root/MyGame"); + /// </code> + /// </example> + /// <seealso cref="GetNode{T}(NodePath)"/> + /// <param name="path">The path to the node to fetch.</param> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>/// ///////// <returns> + /// The <see cref="Node"/> at the given <paramref name="path"/>, or <see langword="null"/> if not found. + /// </returns> public T GetNodeOrNull<T>(NodePath path) where T : class { return GetNodeOrNull(path) as T; } + /// <summary> + /// Returns a child node by its index (see <see cref="GetChildCount"/>). + /// This method is often used for iterating all children of a node. + /// Negative indices access the children from the last one. + /// To access a child node via its name, use <see cref="GetNode"/>. + /// </summary> + /// <seealso cref="GetChildOrNull{T}(int)"/> + /// <param name="idx">Child index.</param> + /// <exception cref="InvalidCastException"> + /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>. + /// </exception> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>/// ///////// <returns> + /// The child <see cref="Node"/> at the given index <paramref name="idx"/>. + /// </returns> public T GetChild<T>(int idx) where T : class { return (T)(object)GetChild(idx); } + /// <summary> + /// Returns a child node by its index (see <see cref="GetChildCount"/>). + /// This method is often used for iterating all children of a node. + /// Negative indices access the children from the last one. + /// To access a child node via its name, use <see cref="GetNode"/>. + /// </summary> + /// <seealso cref="GetChild{T}(int)"/> + /// <param name="idx">Child index.</param> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns> + /// The child <see cref="Node"/> at the given index <paramref name="idx"/>, or <see langword="null"/> if not found. + /// </returns> public T GetChildOrNull<T>(int idx) where T : class { return GetChild(idx) as T; } + /// <summary> + /// The node owner. A node can have any other node as owner (as long as it is + /// a valid parent, grandparent, etc. ascending in the tree). When saving a + /// node (using <see cref="PackedScene"/>), all the nodes it owns will be saved + /// with it. This allows for the creation of complex <see cref="SceneTree"/>s, + /// with instancing and subinstancing. + /// </summary> + /// <seealso cref="GetOwnerOrNull{T}"/> + /// <exception cref="InvalidCastException"> + /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>. + /// </exception> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns> + /// The owner <see cref="Node"/>. + /// </returns> public T GetOwner<T>() where T : class { return (T)(object)Owner; } + /// <summary> + /// The node owner. A node can have any other node as owner (as long as it is + /// a valid parent, grandparent, etc. ascending in the tree). When saving a + /// node (using <see cref="PackedScene"/>), all the nodes it owns will be saved + /// with it. This allows for the creation of complex <see cref="SceneTree"/>s, + /// with instancing and subinstancing. + /// </summary> + /// <seealso cref="GetOwner{T}"/> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns> + /// The owner <see cref="Node"/>, or <see langword="null"/> if there is no owner. + /// </returns> public T GetOwnerOrNull<T>() where T : class { return Owner as T; } + /// <summary> + /// Returns the parent node of the current node, or a <see langword="null"/> instance + /// if the node lacks a parent. + /// </summary> + /// <seealso cref="GetParentOrNull{T}"/> + /// <exception cref="InvalidCastException"> + /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>. + /// </exception> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns> + /// The parent <see cref="Node"/>. + /// </returns> public T GetParent<T>() where T : class { return (T)(object)GetParent(); } + /// <summary> + /// Returns the parent node of the current node, or a <see langword="null"/> instance + /// if the node lacks a parent. + /// </summary> + /// <seealso cref="GetParent{T}"/> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns> + /// The parent <see cref="Node"/>, or <see langword="null"/> if the node has no parent. + /// </returns> public T GetParentOrNull<T>() where T : class { return GetParent() as T; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs index 9ef0959750..691fd85964 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs @@ -5,17 +5,37 @@ 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) { - return godot_icall_Object_weakref(Object.GetPtr(obj)); + return godot_icall_Object_weakref(GetPtr(obj)); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static WeakRef godot_icall_Object_weakref(IntPtr obj); + internal static extern WeakRef godot_icall_Object_weakref(IntPtr obj); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs index 214bbf5179..435b59d5f3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs @@ -1,3 +1,5 @@ +using System; + namespace Godot { public partial class PackedScene @@ -5,20 +7,27 @@ namespace Godot /// <summary> /// Instantiates the scene's node hierarchy, erroring on failure. /// Triggers child scene instantiation(s). Triggers a - /// `Node.NotificationInstanced` notification on the root node. + /// <see cref="Node.NotificationInstanced"/> notification on the root node. /// </summary> - /// <typeparam name="T">The type to cast to. Should be a descendant of Node.</typeparam> + /// <seealso cref="InstantiateOrNull{T}(GenEditState)"/> + /// <exception cref="InvalidCastException"> + /// Thrown when the given the instantiated node can't be casted to the given type <typeparamref name="T"/>. + /// </exception> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns>The instantiated scene.</returns> public T Instantiate<T>(PackedScene.GenEditState editState = (PackedScene.GenEditState)0) where T : class { return (T)(object)Instantiate(editState); } /// <summary> - /// Instantiates the scene's node hierarchy, returning null on failure. + /// Instantiates the scene's node hierarchy, returning <see langword="null"/> on failure. /// Triggers child scene instantiation(s). Triggers a - /// `Node.NotificationInstanced` notification on the root node. + /// <see cref="Node.NotificationInstanced"/> notification on the root node. /// </summary> - /// <typeparam name="T">The type to cast to. Should be a descendant of Node.</typeparam> + /// <seealso cref="Instantiate{T}(GenEditState)"/> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> + /// <returns>The instantiated scene.</returns> public T InstantiateOrNull<T>(PackedScene.GenEditState editState = (PackedScene.GenEditState)0) where T : class { return Instantiate(editState) as T; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs index 74fa05d1fd..25c11d5cf6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs @@ -1,10 +1,30 @@ +using System; + namespace Godot { public static partial class ResourceLoader { - public static T Load<T>(string path, string typeHint = null, CacheMode noCache = CacheMode.Reuse) where T : class + /// <summary> + /// Loads a resource at the given <paramref name="path"/>, caching the result + /// for further access. + /// The registered <see cref="ResourceFormatLoader"/> instances are queried sequentially + /// to find the first one which can handle the file's extension, and then attempt + /// loading. If loading fails, the remaining ResourceFormatLoaders are also attempted. + /// An optional <paramref name="typeHint"/> can be used to further specify the + /// <see cref="Resource"/> type that should be handled by the <see cref="ResourceFormatLoader"/>. + /// Anything that inherits from <see cref="Resource"/> can be used as a type hint, + /// for example <see cref="Image"/>. + /// The <paramref name="cacheMode"/> property defines whether and how the cache should + /// be used or updated when loading the resource. See <see cref="CacheMode"/> for details. + /// Returns an empty resource if no <see cref="ResourceFormatLoader"/> could handle the file. + /// </summary> + /// <exception cref="InvalidCastException"> + /// Thrown when the given the loaded resource can't be casted to the given type <typeparamref name="T"/>. + /// </exception> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Resource"/>.</typeparam> + public static T Load<T>(string path, string typeHint = null, CacheMode cacheMode = CacheMode.Reuse) where T : class { - return (T)(object)Load(path, typeHint, noCache); + return (T)(object)Load(path, typeHint, cacheMode); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs index 20b11a48dd..df130a5c77 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs @@ -6,12 +6,16 @@ namespace Godot { public partial class SceneTree { + /// <summary> + /// Returns a list of all nodes assigned to the given <paramref name="group"/>. + /// </summary> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> public Array<T> GetNodesInGroup<T>(StringName group) where T : class { - return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(Object.GetPtr(this), StringName.GetPtr(group), typeof(T))); + return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), StringName.GetPtr(group), typeof(T))); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType); + internal static extern IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 71d0593916..53c02feaa2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -7,22 +7,56 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -// TODO: Add comments describing what this class does. It is not obvious. - namespace Godot { + /// <summary> + /// Godot's global functions. + /// </summary> 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). + /// </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 object Bytes2Var(byte[] bytes, bool allowObjects = false) { return godot_icall_GD_bytes2var(bytes, allowObjects); } + /// <summary> + /// Converts from a <c>Variant</c> type to another 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); + /// </code> + /// </example> + /// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns> public static object Convert(object what, Variant.Type type) { return godot_icall_GD_convert(what, type); } + /// <summary> + /// Converts from decibels to linear energy (audio). + /// </summary> + /// <seealso cref="Linear2Db(real_t)"/> + /// <param name="db">Decibels to convert.</param> + /// <returns>Audio volume as linear energy.</returns> public static real_t Db2Linear(real_t db) { return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); @@ -38,111 +72,360 @@ namespace Godot return Array.ConvertAll(parameters, x => x?.ToString() ?? "null"); } + /// <summary> + /// Returns the integer hash of the variable passed. + /// </summary> + /// <example> + /// <code> + /// GD.Print(GD.Hash("a")); // Prints 177670 + /// </code> + /// </example> + /// <param name="var">Variable that will be hashed.</param> + /// <returns>Hash of the variable passed.</returns> public static int Hash(object var) { return godot_icall_GD_hash(var); } + /// <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 godot_icall_GD_instance_from_id(instanceId); } + /// <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="Db2Linear(real_t)"/> + /// <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.Linear2Db(slider.value)); + /// </code> + /// </example> + /// <param name="linear">The linear energy to convert.</param> + /// <returns>Audio as decibels</returns> public static real_t Linear2Db(real_t linear) { return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321); } + /// <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, + /// especially when loading scenes. To avoid unnecessary delays when loading something + /// multiple times, either store the resource in a variable. + /// + /// Note: Resource paths can be obtained by right-clicking on a resource in the FileSystem + /// dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script. + /// + /// Important: The path must be absolute, a local path will just return <see langword="null"/>. + /// This method is a simplified version of <see cref="ResourceLoader.Load"/>, which can be used + /// for more advanced scenarios. + /// </summary> + /// <example> + /// <code> + /// // Load a scene called main located in the root of the project directory and cache it in a variable. + /// var main = GD.Load("res://main.tscn"); // main will contain a PackedScene resource. + /// </code> + /// </example> + /// <param name="path">Path of the <see cref="Resource"/> to load.</param> + /// <returns>The loaded <see cref="Resource"/>.</returns> public static Resource Load(string path) { return ResourceLoader.Load(path); } + /// <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, + /// especially when loading scenes. To avoid unnecessary delays when loading something + /// multiple times, either store the resource in a variable. + /// + /// Note: Resource paths can be obtained by right-clicking on a resource in the FileSystem + /// dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script. + /// + /// Important: The path must be absolute, a local path will just return <see langword="null"/>. + /// This method is a simplified version of <see cref="ResourceLoader.Load"/>, which can be used + /// for more advanced scenarios. + /// </summary> + /// <example> + /// <code> + /// // Load a scene called main located in the root of the project directory and cache it in a variable. + /// var main = GD.Load<PackedScene>("res://main.tscn"); // main will contain a PackedScene resource. + /// </code> + /// </example> + /// <param name="path">Path of the <see cref="Resource"/> to load.</param> + /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Resource"/>.</typeparam> public static T Load<T>(string path) where T : class { 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. + /// To print an error message and pause project execution in debug builds, + /// use [code]assert(false, "test error")[/code] instead. + /// </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) { godot_icall_GD_pusherror(message); } + /// <summary> + /// Pushes a warning message to Godot's built-in debugger and to the OS terminal. + /// </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) { godot_icall_GD_pushwarning(message); } + /// <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)"/> + /// 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 }; + /// 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) { godot_icall_GD_print(GetPrintParams(what)); } + /// <summary> + /// Prints the current stack trace information to the console. + /// </summary> public static void PrintStack() { Print(System.Environment.StackTrace); } + /// <summary> + /// Prints one or more arguments to strings in the best way possible to standard error line. + /// </summary> + /// <example> + /// <code> + /// GD.PrintErr("prints to stderr"); + /// </code> + /// </example> + /// <param name="what">Arguments that will be printed.</param> public static void PrintErr(params object[] what) { godot_icall_GD_printerr(GetPrintParams(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[])"/>. + /// </summary> + /// <example> + /// <code> + /// GD.PrintRaw("A"); + /// GD.PrintRaw("B"); + /// // Prints AB + /// </code> + /// </example> + /// <param name="what">Arguments that will be printed.</param> public static void PrintRaw(params object[] what) { godot_icall_GD_printraw(GetPrintParams(what)); } + /// <summary> + /// Prints one or more arguments to the console with a space between each argument. + /// </summary> + /// <example> + /// <code> + /// GD.PrintS("A", "B", "C"); // Prints A B C + /// </code> + /// </example> + /// <param name="what">Arguments that will be printed.</param> public static void PrintS(params object[] what) { godot_icall_GD_prints(GetPrintParams(what)); } + /// <summary> + /// Prints one or more arguments to the console with a tab between each argument. + /// </summary> + /// <example> + /// <code> + /// GD.PrintT("A", "B", "C"); // Prints A B C + /// </code> + /// </example> + /// <param name="what">Arguments that will be printed.</param> public static void PrintT(params object[] what) { godot_icall_GD_printt(GetPrintParams(what)); } + /// <summary> + /// Returns a random floating point value between <c>0.0</c> and <c>1.0</c> (inclusive). + /// </summary> + /// <example> + /// <code> + /// GD.Randf(); // Returns e.g. 0.375671 + /// </code> + /// </example> + /// <returns>A random <see langword="float"/> number.</returns> public static float Randf() { return godot_icall_GD_randf(); } + /// <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). + /// </summary> + /// <example> + /// <code> + /// 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 + /// </code> + /// </example> + /// <returns>A random <see langword="uint"/> number.</returns> public static uint Randi() { return godot_icall_GD_randi(); } + /// <summary> + /// Randomizes the seed (or the internal state) of the random number generator. + /// Current implementation reseeds using a number based on 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. + /// </summary> public static void Randomize() { godot_icall_GD_randomize(); } + /// <summary> + /// Returns a random floating point value on the interval 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 + /// </code> + /// </example> + /// <returns>A random <see langword="double"/> number inside the given range.</returns> public static double RandRange(double from, double to) { return godot_icall_GD_randf_range(from, to); } + /// <summary> + /// Returns a random signed 32-bit integer between <paramref name="from"/> + /// and <paramref name="to"/> (inclusive). If <paramref name="to"/> is lesser than + /// <paramref name="from"/>, they are swapped. + /// </summary> + /// <example> + /// <code> + /// GD.Print(GD.RandRange(0, 1)); // Prints 0 or 1 + /// GD.Print(GD.RangeRange(-10, 1000)); // Prints any number from -10 to 1000 + /// </code> + /// </example> + /// <returns>A random <see langword="int"/> number inside the given range.</returns> public static int RandRange(int from, int to) { return godot_icall_GD_randi_range(from, to); } + /// <summary> + /// Returns a random unsigned 32-bit integer, using the given <paramref name="seed"/>. + /// </summary> + /// <param name="seed"> + /// Seed to use to generate the random number. + /// If a different seed is used, its value will be modfied. + /// </param> + /// <returns>A random <see langword="uint"/> number.</returns> public static uint RandFromSeed(ref ulong seed) { return godot_icall_GD_rand_seed(seed, out seed); } + /// <summary> + /// Returns a <see cref="IEnumerable{T}"/> that iterates from + /// <c>0</c> to <paramref name="end"/> in steps of <c>1</c>. + /// </summary> + /// <param name="end">The last index.</param> public static IEnumerable<int> Range(int end) { return Range(0, end, 1); } + /// <summary> + /// Returns a <see cref="IEnumerable{T}"/> that iterates from + /// <paramref name="start"/> to <paramref name="end"/> in steps of <c>1</c>. + /// </summary> + /// <param name="start">The first index.</param> + /// <param name="end">The last index.</param> public static IEnumerable<int> Range(int start, int end) { return Range(start, end, 1); } + /// <summary> + /// Returns a <see cref="IEnumerable{T}"/> that iterates from + /// <paramref name="start"/> to <paramref name="end"/> in steps of <paramref name="step"/>. + /// </summary> + /// <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 (end < start && step > 0) @@ -163,109 +446,164 @@ namespace Godot } } + /// <summary> + /// Sets seed for the random number generator. + /// </summary> + /// <param name="seed">Seed that will be used.</param> public static void Seed(ulong seed) { godot_icall_GD_seed(seed); } + /// <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 object[] what) { return godot_icall_GD_str(what); } + /// <summary> + /// Converts a formatted string that was returned by <see cref="Var2Str(object)"/> to the original value. + /// </summary> + /// <example> + /// <code> + /// string a = "{\"a\": 1, \"b\": 2 }"; + /// var b = (Godot.Collections.Dictionary)GD.Str2Var(a); + /// GD.Print(b["a"]); // Prints 1 + /// </code> + /// </example> + /// <param name="str">String that will be converted to Variant.</param> + /// <returns>The decoded <c>Variant</c>.</returns> public static object Str2Var(string str) { return godot_icall_GD_str2var(str); } + /// <summary> + /// Returns whether the given class exists in <see cref="ClassDB"/>. + /// </summary> + /// <returns>If the class exists in <see cref="ClassDB"/>.</returns> public static bool TypeExists(StringName type) { return godot_icall_GD_type_exists(StringName.GetPtr(type)); } + /// <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="Bytes2Var(byte[], bool)"/>. + /// </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[] Var2Bytes(object var, bool fullObjects = false) { return godot_icall_GD_var2bytes(var, fullObjects); } + /// <summary> + /// Converts a <c>Variant</c> <paramref name="var"/> to a formatted string that + /// can later be parsed using <see cref="Str2Var(string)"/>. + /// </summary> + /// <example> + /// <code> + /// var a = new Godot.Collections.Dictionary { ["a"] = 1, ["b"] = 2 }; + /// GD.Print(GD.Var2Str(a)); + /// // Prints + /// // { + /// // "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> public static string Var2Str(object var) { return godot_icall_GD_var2str(var); } + /// <summary> + /// Get the <see cref="Variant.Type"/> that corresponds for the given <see cref="Type"/>. + /// </summary> + /// <returns>The <see cref="Variant.Type"/> for the given <paramref name="type"/>.</returns> public static Variant.Type TypeToVariantType(Type type) { return godot_icall_TypeToVariantType(type); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects); + internal static extern object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_convert(object what, Variant.Type type); + internal static extern object godot_icall_GD_convert(object what, Variant.Type type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_GD_hash(object var); + internal static extern int godot_icall_GD_hash(object var); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId); + internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_print(object[] what); + internal static extern void godot_icall_GD_print(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_printerr(object[] what); + internal static extern void godot_icall_GD_printerr(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_printraw(object[] what); + internal static extern void godot_icall_GD_printraw(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_prints(object[] what); + internal static extern void godot_icall_GD_prints(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_printt(object[] what); + internal static extern void godot_icall_GD_printt(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static float godot_icall_GD_randf(); + internal static extern float godot_icall_GD_randf(); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static uint godot_icall_GD_randi(); + internal static extern uint godot_icall_GD_randi(); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_randomize(); + internal static extern void godot_icall_GD_randomize(); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static double godot_icall_GD_randf_range(double from, double to); + internal static extern double godot_icall_GD_randf_range(double from, double to); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_GD_randi_range(int from, int to); + internal static extern int godot_icall_GD_randi_range(int from, int to); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed); + internal static extern uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_seed(ulong seed); + internal static extern void godot_icall_GD_seed(ulong seed); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_GD_str(object[] what); + internal static extern string godot_icall_GD_str(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_str2var(string str); + internal static extern object godot_icall_GD_str2var(string str); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool godot_icall_GD_type_exists(IntPtr type); + internal static extern bool godot_icall_GD_type_exists(IntPtr type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_GD_var2bytes(object what, bool fullObjects); + internal static extern byte[] godot_icall_GD_var2bytes(object what, bool fullObjects); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_GD_var2str(object var); + internal static extern string godot_icall_GD_var2str(object var); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_pusherror(string type); + internal static extern void godot_icall_GD_pusherror(string type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_pushwarning(string type); + internal static extern void godot_icall_GD_pushwarning(string type); [MethodImpl(MethodImplOptions.InternalCall)] private static extern Variant.Type godot_icall_TypeToVariantType(Type type); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs index 4b5e3f8761..c01c926e82 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs @@ -6,7 +6,8 @@ namespace Godot { public class GodotSynchronizationContext : SynchronizationContext { - private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); + private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = + new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); public override void Post(SendOrPostCallback d, object state) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs index a566b53659..9ccac1faaf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs @@ -24,7 +24,7 @@ namespace Godot try { - var stackTrace = new StackTrace(true).ToString(); + string stackTrace = new StackTrace(true).ToString(); GD.PrintErr(stackTrace); } catch diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs index f508211f68..3051bcedc7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -3,60 +3,135 @@ using System.Collections.Generic; namespace Godot { - static class MarshalUtils + internal static class MarshalUtils { /// <summary> /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>. + /// is <see cref="Collections.Array{T}"/>; otherwise returns <see langword="false"/>. /// </summary> - /// <exception cref="System.InvalidOperationException"> - /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="type"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. /// </exception> - static bool TypeIsGenericArray(Type type) => - type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); + private static bool TypeIsGenericArray(Type type) => + type.GetGenericTypeDefinition() == typeof(Collections.Array<>); /// <summary> /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> - /// is <see cref="Godot.Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. + /// is <see cref="Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. /// </summary> - /// <exception cref="System.InvalidOperationException"> - /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="type"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. /// </exception> - static bool TypeIsGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); + private static bool TypeIsGenericDictionary(Type type) => + type.GetGenericTypeDefinition() == typeof(Collections.Dictionary<,>); - static bool TypeIsSystemGenericList(Type type) => - type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.List<>); + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="List{T}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="type"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. + /// </exception> + private static bool TypeIsSystemGenericList(Type type) => + type.GetGenericTypeDefinition() == typeof(List<>); - static bool TypeIsSystemGenericDictionary(Type type) => - type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>); + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="type"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. + /// </exception> + private static bool TypeIsSystemGenericDictionary(Type type) => + type.GetGenericTypeDefinition() == typeof(Dictionary<,>); - static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>); + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="IEnumerable{T}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="type"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. + /// </exception> + private static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>); - static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>); + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="ICollection{T}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="type"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. + /// </exception> + private static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>); - static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="IDictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="type"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. + /// </exception> + private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); - static void ArrayGetElementType(Type arrayType, out Type elementType) + /// <summary> + /// Gets the element type for the given <paramref name="arrayType"/>. + /// </summary> + /// <param name="arrayType">Type for the generic array.</param> + /// <param name="elementType">Element type for the generic array.</param> + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="arrayType"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. + /// </exception> + private static void ArrayGetElementType(Type arrayType, out Type elementType) { elementType = arrayType.GetGenericArguments()[0]; } - static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) + /// <summary> + /// Gets the key type and the value type for the given <paramref name="dictionaryType"/>. + /// </summary> + /// <param name="dictionaryType">The type for the generic dictionary.</param> + /// <param name="keyType">Key type for the generic dictionary.</param> + /// <param name="valueType">Value type for the generic dictionary.</param> + /// <exception cref="InvalidOperationException"> + /// Thrown when the given <paramref name="dictionaryType"/> is not a generic type. + /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>. + /// </exception> + private static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) { var genericArgs = dictionaryType.GetGenericArguments(); keyType = genericArgs[0]; valueType = genericArgs[1]; } - static Type MakeGenericArrayType(Type elemType) + /// <summary> + /// Constructs a new <see cref="Type"/> from <see cref="Collections.Array{T}"/> + /// where the generic type for the elements is <paramref name="elemType"/>. + /// </summary> + /// <param name="elemType">Element type for the array.</param> + /// <returns>The generic array type with the specified element type.</returns> + private static Type MakeGenericArrayType(Type elemType) { - return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); + return typeof(Collections.Array<>).MakeGenericType(elemType); } - static Type MakeGenericDictionaryType(Type keyType, Type valueType) + /// <summary> + /// Constructs a new <see cref="Type"/> from <see cref="Collections.Dictionary{TKey, TValue}"/> + /// where the generic type for the keys is <paramref name="keyType"/> and + /// for the values is <paramref name="valueType"/>. + /// </summary> + /// <param name="keyType">Key type for the dictionary.</param> + /// <param name="valueType">Key type for the dictionary.</param> + /// <returns>The generic dictionary type with the specified key and value types.</returns> + private static Type MakeGenericDictionaryType(Type keyType, Type valueType) { - return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType); + return typeof(Collections.Dictionary<,>).MakeGenericType(keyType, valueType); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 213f84ad73..6f7fac7429 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -7,6 +7,9 @@ using System; namespace Godot { + /// <summary> + /// Provides constants and static methods for common mathematical functions. + /// </summary> public static partial class Mathf { // Define constants with Decimal precision and cast down to double or float. @@ -19,121 +22,120 @@ namespace Godot /// <summary> /// Constant that represents how many times the diameter of a circle - /// fits around its perimeter. This is equivalent to `Mathf.Tau / 2`. + /// fits around its perimeter. This is equivalent to <c>Mathf.Tau / 2</c>. /// </summary> // 3.1415927f and 3.14159265358979 public const real_t Pi = (real_t)3.1415926535897932384626433833M; /// <summary> - /// Positive infinity. For negative infinity, use `-Mathf.Inf`. + /// Positive infinity. For negative infinity, use <c>-Mathf.Inf</c>. /// </summary> public const real_t Inf = real_t.PositiveInfinity; /// <summary> - /// "Not a Number", an invalid value. `NaN` has special properties, including + /// "Not a Number", an invalid value. <c>NaN</c> has special properties, including /// that it is not equal to itself. It is output by some invalid operations, /// such as dividing zero by zero. /// </summary> public const real_t NaN = real_t.NaN; // 0.0174532924f and 0.0174532925199433 - private const real_t Deg2RadConst = (real_t)0.0174532925199432957692369077M; + private const real_t _deg2RadConst = (real_t)0.0174532925199432957692369077M; // 57.29578f and 57.2957795130823 - private const real_t Rad2DegConst = (real_t)57.295779513082320876798154814M; + private const real_t _rad2DegConst = (real_t)57.295779513082320876798154814M; /// <summary> - /// Returns the absolute value of `s` (i.e. positive value). + /// 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 `s`.</returns> + /// <returns>The absolute value of <paramref name="s"/>.</returns> public static int Abs(int s) { return Math.Abs(s); } /// <summary> - /// Returns the absolute value of `s` (i.e. positive value). + /// 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 `s`.</returns> + /// <returns>The absolute value of <paramref name="s"/>.</returns> public static real_t Abs(real_t s) { return Math.Abs(s); } /// <summary> - /// Returns the arc cosine of `s` in radians. Use to get the angle of cosine s. + /// 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 `0` to `Tau/2`.</returns> + /// <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) { return (real_t)Math.Acos(s); } /// <summary> - /// Returns the arc sine of `s` in radians. Use to get the angle of sine s. + /// 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 `-Tau/4` to `Tau/4`.</returns> + /// <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) { return (real_t)Math.Asin(s); } /// <summary> - /// Returns the arc tangent of `s` in radians. Use to get the angle of tangent s. + /// 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(real_t, real_t)"/> if you have both `y` and `x`. + /// See <see cref="Atan2(real_t, real_t)"/> 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 `-Tau/4` to `Tau/4`.</returns> + /// <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) { return (real_t)Math.Atan(s); } /// <summary> - /// Returns the arc tangent of `y` and `x` in radians. Use to get the angle - /// of the tangent of `y/x`. To compute the value, the method takes into + /// 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 `-Tau/2` to `Tau/2`.</returns> + /// <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) { return (real_t)Math.Atan2(y, x); } /// <summary> - /// Converts a 2D point expressed in the cartesian coordinate - /// system (X and Y axis) to the polar coordinate system - /// (a distance from the origin and an angle). - /// </summary> - /// <param name="x">The input X coordinate.</param> - /// <param name="y">The input Y coordinate.</param> - /// <returns>A <see cref="Vector2"/> with X representing the distance and Y representing the angle.</returns> - public static Vector2 Cartesian2Polar(real_t x, real_t y) - { - return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); - } - - /// <summary> - /// Rounds `s` upward (towards positive infinity). + /// 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 `s`.</returns> + /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> public static real_t Ceil(real_t s) { return (real_t)Math.Ceiling(s); } /// <summary> - /// Clamps a `value` so that it is not less than `min` and not more than `max`. + /// 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> @@ -145,7 +147,8 @@ namespace Godot } /// <summary> - /// Clamps a `value` so that it is not less than `min` and not more than `max`. + /// 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> @@ -157,7 +160,7 @@ namespace Godot } /// <summary> - /// Returns the cosine of angle `s` in radians. + /// 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> @@ -167,7 +170,7 @@ namespace Godot } /// <summary> - /// Returns the hyperbolic cosine of angle `s` in radians. + /// 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> @@ -183,16 +186,18 @@ namespace Godot /// <returns>The same angle expressed in radians.</returns> public static real_t Deg2Rad(real_t deg) { - return deg * Deg2RadConst; + return deg * _deg2RadConst; } /// <summary> - /// Easing function, based on exponent. The curve values are: - /// `0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out. + /// 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">`0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out.</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 real_t Ease(real_t s, real_t curve) { @@ -222,7 +227,7 @@ namespace Godot return 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 - Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f; } return 0f; @@ -230,20 +235,20 @@ namespace Godot /// <summary> /// The natural exponential function. It raises the mathematical - /// constant `e` to the power of `s` and returns it. + /// constant <c>e</c> to the power of <paramref name="s"/> and returns it. /// </summary> - /// <param name="s">The exponent to raise `e` to.</param> - /// <returns>`e` raised to the power of `s`.</returns> + /// <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) { return (real_t)Math.Exp(s); } /// <summary> - /// Rounds `s` downward (towards negative infinity). + /// 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 `s`.</returns> + /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> public static real_t Floor(real_t s) { return (real_t)Math.Floor(s); @@ -263,12 +268,13 @@ namespace Godot } /// <summary> - /// Returns true if `a` and `b` are approximately equal to each other. + /// 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 bool for whether or not the two values are approximately equal.</returns> + /// <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) { // Check for exact equality first, required to handle "infinity" values. @@ -286,33 +292,33 @@ namespace Godot } /// <summary> - /// Returns whether `s` is an infinity value (either positive infinity or negative infinity). + /// 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 bool for whether or not the value is an infinity value.</returns> + /// <returns>A <see langword="bool"/> for whether or not the value is an infinity value.</returns> public static bool IsInf(real_t s) { return real_t.IsInfinity(s); } /// <summary> - /// Returns whether `s` is a `NaN` ("Not a Number" or invalid) value. + /// 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 bool for whether or not the value is a `NaN` value.</returns> + /// <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) { return real_t.IsNaN(s); } /// <summary> - /// Returns true if `s` is approximately zero. + /// Returns <see langword="true"/> if <paramref name="s"/> is approximately 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 one value as zero. /// </summary> /// <param name="s">The value to check.</param> - /// <returns>A bool for whether or not the value is nearly zero.</returns> + /// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns> public static bool IsZeroApprox(real_t s) { return Abs(s) < Epsilon; @@ -328,7 +334,7 @@ namespace Godot /// <returns>The resulting value of the interpolation.</returns> public static real_t Lerp(real_t from, real_t to, real_t weight) { - return from + (to - from) * weight; + return from + ((to - from) * weight); } /// <summary> @@ -345,7 +351,7 @@ namespace Godot { real_t difference = (to - from) % Mathf.Tau; real_t distance = ((2 * difference) % Mathf.Tau) - difference; - return from + distance * weight; + return from + (distance * weight); } /// <summary> @@ -354,7 +360,7 @@ namespace Godot /// 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 `s`.</returns> + /// <returns>The natural log of <paramref name="s"/>.</returns> public static real_t Log(real_t s) { return (real_t)Math.Log(s); @@ -405,9 +411,9 @@ namespace Godot } /// <summary> - /// Moves `from` toward `to` by the `delta` value. + /// Moves <paramref name="from"/> toward <paramref name="to"/> by the <paramref name="delta"/> value. /// - /// Use a negative delta value to move away. + /// 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> @@ -415,11 +421,14 @@ namespace Godot /// <returns>The value after moving.</returns> public static real_t MoveToward(real_t from, real_t to, real_t delta) { - return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta; + if (Abs(to - from) <= delta) + return to; + + return from + (Sign(to - from) * delta); } /// <summary> - /// Returns the nearest larger power of 2 for the integer `value`. + /// Returns the nearest larger power of 2 for the integer <paramref name="value"/>. /// </summary> /// <param name="value">The input value.</param> /// <returns>The nearest larger power of 2.</returns> @@ -436,23 +445,10 @@ namespace Godot } /// <summary> - /// Converts a 2D point expressed in the polar coordinate - /// system (a distance from the origin `r` and an angle `th`) - /// to the cartesian coordinate system (X and Y axis). - /// </summary> - /// <param name="r">The distance from the origin.</param> - /// <param name="th">The angle of the point.</param> - /// <returns>A <see cref="Vector2"/> representing the cartesian coordinate.</returns> - public static Vector2 Polar2Cartesian(real_t r, real_t th) - { - return new Vector2(r * Cos(th), r * Sin(th)); - } - - /// <summary> - /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`. + /// 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, b)`.</param> + /// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param> /// <returns>The resulting output.</returns> public static int PosMod(int a, int b) { @@ -465,10 +461,10 @@ namespace Godot } /// <summary> - /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`. + /// 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, b)`.</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) { @@ -481,11 +477,11 @@ namespace Godot } /// <summary> - /// Returns the result of `x` raised to the power of `y`. + /// 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>`x` raised to the power of `y`.</returns> + /// <returns><paramref name="x"/> raised to the power of <paramref name="y"/>.</returns> public static real_t Pow(real_t x, real_t y) { return (real_t)Math.Pow(x, y); @@ -498,11 +494,11 @@ namespace Godot /// <returns>The same angle expressed in degrees.</returns> public static real_t Rad2Deg(real_t rad) { - return rad * Rad2DegConst; + return rad * _rad2DegConst; } /// <summary> - /// Rounds `s` to the nearest whole number, + /// 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> @@ -513,10 +509,11 @@ namespace Godot } /// <summary> - /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`. + /// 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: `1`, `-1`, or `0`.</returns> + /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> public static int Sign(int s) { if (s == 0) @@ -525,10 +522,11 @@ namespace Godot } /// <summary> - /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`. + /// 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: `1`, `-1`, or `0`.</returns> + /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> public static int Sign(real_t s) { if (s == 0) @@ -537,7 +535,7 @@ namespace Godot } /// <summary> - /// Returns the sine of angle `s` in radians. + /// 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> @@ -547,7 +545,7 @@ namespace Godot } /// <summary> - /// Returns the hyperbolic sine of angle `s` in radians. + /// 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> @@ -557,8 +555,8 @@ namespace Godot } /// <summary> - /// Returns a number smoothly interpolated between `from` and `to`, - /// based on the `weight`. Similar to <see cref="Lerp(real_t, real_t, real_t)"/>, + /// 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)"/>, /// but interpolates faster at the beginning and slower at the end. /// </summary> /// <param name="from">The start value for interpolation.</param> @@ -572,16 +570,16 @@ namespace Godot return from; } real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0); - return x * x * (3 - 2 * x); + return x * x * (3 - (2 * x)); } /// <summary> - /// Returns the square root of `s`, where `s` is a non-negative number. + /// Returns the square root of <paramref name="s"/>, where <paramref name="s"/> is a non-negative number. /// - /// If you need negative inputs, use `System.Numerics.Complex`. + /// 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 `s`.</returns> + /// <returns>The square root of <paramref name="s"/>.</returns> public static real_t Sqrt(real_t s) { return (real_t)Math.Sqrt(s); @@ -596,7 +594,8 @@ namespace Godot /// <returns>The position of the first non-zero digit.</returns> public static int StepDecimals(real_t step) { - double[] sd = new double[] { + double[] sd = new double[] + { 0.9999, 0.09999, 0.009999, @@ -607,7 +606,7 @@ namespace Godot 0.00000009999, 0.000000009999, }; - double abs = Mathf.Abs(step); + double abs = Abs(step); double decs = abs - (int)abs; // Strip away integer part for (int i = 0; i < sd.Length; i++) { @@ -620,9 +619,8 @@ namespace Godot } /// <summary> - /// Snaps float value `s` to a given `step`. - /// This can also be used to round a floating point - /// number to an arbitrary number of decimals. + /// 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> @@ -631,14 +629,14 @@ namespace Godot { if (step != 0f) { - return Floor(s / step + 0.5f) * step; + return Floor((s / step) + 0.5f) * step; } return s; } /// <summary> - /// Returns the tangent of angle `s` in radians. + /// 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> @@ -648,7 +646,7 @@ namespace Godot } /// <summary> - /// Returns the hyperbolic tangent of angle `s` in radians. + /// 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> @@ -658,8 +656,9 @@ namespace Godot } /// <summary> - /// Wraps `value` between `min` and `max`. Usable for creating loop-alike - /// behavior or infinite surfaces. If `min` is `0`, this is equivalent + /// 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(int, int)"/>, so prefer using that instead. /// </summary> /// <param name="value">The value to wrap.</param> @@ -669,12 +668,16 @@ namespace Godot public static int Wrap(int value, int min, int max) { int range = max - min; - return range == 0 ? min : min + ((value - min) % range + range) % range; + if (range == 0) + return min; + + return min + ((((value - min) % range) + range) % range); } /// <summary> - /// Wraps `value` between `min` and `max`. Usable for creating loop-alike - /// behavior or infinite surfaces. If `min` is `0`, this is equivalent + /// 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. /// </summary> /// <param name="value">The value to wrap.</param> @@ -684,7 +687,11 @@ namespace Godot public static real_t Wrap(real_t value, real_t min, real_t max) { real_t range = max - min; - return IsZeroApprox(range) ? min : min + ((value - min) % range + range) % range; + if (IsZeroApprox(range)) + { + return min; + } + return min + ((((value - min) % range) + range) % range); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs index 0888e33090..9bb73ce7dd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs @@ -12,7 +12,7 @@ namespace Godot // Define constants with Decimal precision and cast down to double or float. /// <summary> - /// The natural number `e`. + /// The natural number <c>e</c>. /// </summary> public const real_t E = (real_t)2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045 @@ -23,7 +23,7 @@ namespace Godot /// <summary> /// A very small number used for float comparison with error tolerance. - /// 1e-06 with single-precision floats, but 1e-14 if `REAL_T_IS_DOUBLE`. + /// 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. @@ -44,7 +44,7 @@ namespace Godot /// <summary> /// Returns the amount of digits after the decimal place. /// </summary> - /// <param name="s">The input <see cref="System.Decimal"/> value.</param> + /// <param name="s">The input <see cref="decimal"/> value.</param> /// <returns>The amount of digits.</returns> public static int DecimalCount(decimal s) { @@ -52,48 +52,51 @@ namespace Godot } /// <summary> - /// Rounds `s` upward (towards positive infinity). + /// Rounds <paramref name="s"/> upward (towards positive infinity). /// - /// This is the same as <see cref="Ceil(real_t)"/>, but returns an `int`. + /// This is the same as <see cref="Ceil(real_t)"/>, but returns an <c>int</c>. /// </summary> /// <param name="s">The number to ceil.</param> - /// <returns>The smallest whole number that is not less than `s`.</returns> + /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> public static int CeilToInt(real_t s) { return (int)Math.Ceiling(s); } /// <summary> - /// Rounds `s` downward (towards negative infinity). + /// Rounds <paramref name="s"/> downward (towards negative infinity). /// - /// This is the same as <see cref="Floor(real_t)"/>, but returns an `int`. + /// This is the same as <see cref="Floor(real_t)"/>, but returns an <c>int</c>. /// </summary> /// <param name="s">The number to floor.</param> - /// <returns>The largest whole number that is not more than `s`.</returns> + /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> public static int FloorToInt(real_t s) { return (int)Math.Floor(s); } /// <summary> + /// Rounds <paramref name="s"/> to the nearest whole number. /// + /// This is the same as <see cref="Round(real_t)"/>, but returns an <c>int</c>. /// </summary> - /// <param name="s"></param> - /// <returns></returns> + /// <param name="s">The number to round.</param> + /// <returns>The rounded number.</returns> public static int RoundToInt(real_t s) { return (int)Math.Round(s); } /// <summary> - /// Returns true if `a` and `b` are approximately equal to each other. + /// 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)"/>. /// </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 bool for whether or not the two values are equal.</returns> + /// <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) { // Check for exact equality first, required to handle "infinity" values. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 4ecc55f94e..f53b5dc904 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -3,9 +3,45 @@ using System.Runtime.CompilerServices; namespace Godot { + /// <summary> + /// A pre-parsed relative or absolute path in a scene tree, + /// for use with <see cref="Node.GetNode(NodePath)"/> and similar functions. + /// It can reference a node, a resource within a node, or a property + /// of a node or resource. + /// For instance, <c>"Path2D/PathFollow2D/Sprite2D:texture:size"</c> + /// would refer to the <c>size</c> property of the <c>texture</c> + /// resource on the node named <c>"Sprite2D"</c> which is a child of + /// the other named nodes in the path. + /// You will usually just pass a string to <see cref="Node.GetNode(NodePath)"/> + /// and it will be automatically converted, but you may occasionally + /// want to parse a path ahead of time with NodePath. + /// Exporting a NodePath variable will give you a node selection widget + /// in the properties panel of the editor, which can often be useful. + /// A NodePath is composed of a list of slash-separated node names + /// (like a filesystem path) and an optional colon-separated list of + /// "subnames" which can be resources or properties. + /// + /// Note: In the editor, NodePath properties are automatically updated when moving, + /// renaming or deleting a node in the scene tree, but they are never updated at runtime. + /// </summary> + /// <example> + /// Some examples of NodePaths include the following: + /// <code> + /// // No leading slash means it is relative to the current node. + /// new NodePath("A"); // Immediate child A. + /// new NodePath("A/B"); // A's child B. + /// new NodePath("."); // The current node. + /// new NodePath(".."); // The parent node. + /// new NodePath("../C"); // A sibling node C. + /// // A leading slash means it is absolute from the SceneTree. + /// new NodePath("/root"); // Equivalent to GetTree().Root + /// new NodePath("/root/Main"); // If your main scene's root node were named "Main". + /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene. + /// </code> + /// </example> public sealed partial class NodePath : IDisposable { - private bool disposed = false; + private bool _disposed = false; private IntPtr ptr; @@ -14,7 +50,7 @@ namespace Godot if (instance == null) throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null."); - if (instance.disposed) + if (instance._disposed) throw new ObjectDisposedException(instance.GetType().FullName); return instance.ptr; @@ -25,6 +61,9 @@ namespace Godot Dispose(false); } + /// <summary> + /// Disposes of this <see cref="NodePath"/>. + /// </summary> public void Dispose() { Dispose(true); @@ -33,7 +72,7 @@ namespace Godot private void Dispose(bool disposing) { - if (disposed) + if (_disposed) return; if (ptr != IntPtr.Zero) @@ -42,7 +81,7 @@ namespace Godot ptr = IntPtr.Zero; } - disposed = true; + _disposed = true; } internal NodePath(IntPtr ptr) @@ -50,57 +89,168 @@ namespace Godot this.ptr = ptr; } - public NodePath() : this(string.Empty) {} - + /// <summary> + /// Constructs an empty <see cref="NodePath"/>. + /// </summary> + public NodePath() : this(string.Empty) { } + + /// <summary> + /// Constructs a <see cref="NodePath"/> from a string <paramref name="path"/>, + /// e.g.: <c>"Path2D/PathFollow2D/Sprite2D:texture:size"</c>. + /// A path is absolute if it starts with a slash. Absolute paths + /// are only valid in the global scene tree, not within individual + /// scenes. In a relative path, <c>"."</c> and <c>".."</c> indicate + /// the current node and its parent. + /// The "subnames" optionally included after the path to the target + /// node can point to resources or properties, and can also be nested. + /// </summary> + /// <example> + /// Examples of valid NodePaths (assuming that those nodes exist and + /// have the referenced resources or properties): + /// <code> + /// // Points to the Sprite2D node. + /// "Path2D/PathFollow2D/Sprite2D" + /// // Points to the Sprite2D node and its "texture" resource. + /// // GetNode() would retrieve "Sprite2D", while GetNodeAndResource() + /// // would retrieve both the Sprite2D node and the "texture" resource. + /// "Path2D/PathFollow2D/Sprite2D:texture" + /// // Points to the Sprite2D node and its "position" property. + /// "Path2D/PathFollow2D/Sprite2D:position" + /// // Points to the Sprite2D node and the "x" component of its "position" property. + /// "Path2D/PathFollow2D/Sprite2D:position:x" + /// // Absolute path (from "root") + /// "/root/Level/Path2D" + /// </code> + /// </example> + /// <param name="path"></param> public NodePath(string path) { ptr = godot_icall_NodePath_Ctor(path); } + /// <summary> + /// Converts a string to a <see cref="NodePath"/>. + /// </summary> + /// <param name="from">The string to convert.</param> public static implicit operator NodePath(string from) => new NodePath(from); + /// <summary> + /// Converts this <see cref="NodePath"/> to a string. + /// </summary> + /// <param name="from">The <see cref="NodePath"/> to convert.</param> public static implicit operator string(NodePath from) => from.ToString(); + /// <summary> + /// Converts this <see cref="NodePath"/> to a string. + /// </summary> + /// <returns>A string representation of this <see cref="NodePath"/>.</returns> public override string ToString() { return godot_icall_NodePath_operator_String(GetPtr(this)); } + /// <summary> + /// Returns a node path with a colon character (<c>:</c>) prepended, + /// transforming it to a pure property path with no node name (defaults + /// to resolving from the current node). + /// </summary> + /// <example> + /// <code> + /// // This will be parsed as a node path to the "x" property in the "position" node. + /// var nodePath = new NodePath("position:x"); + /// // This will be parsed as a node path to the "x" component of the "position" property in the current node. + /// NodePath propertyPath = nodePath.GetAsPropertyPath(); + /// GD.Print(propertyPath); // :position:x + /// </code> + /// </example> + /// <returns>The <see cref="NodePath"/> as a pure property path.</returns> public NodePath GetAsPropertyPath() { return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this))); } + /// <summary> + /// Returns all subnames concatenated with a colon character (<c>:</c>) + /// as separator, i.e. the right side of the first colon in a node path. + /// </summary> + /// <example> + /// <code> + /// var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path"); + /// GD.Print(nodepath.GetConcatenatedSubnames()); // texture:load_path + /// </code> + /// </example> + /// <returns>The subnames concatenated with <c>:</c>.</returns> public string GetConcatenatedSubnames() { return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this)); } + /// <summary> + /// Gets the node name indicated by <paramref name="idx"/> (0 to <see cref="GetNameCount"/>). + /// </summary> + /// <example> + /// <code> + /// var nodePath = new NodePath("Path2D/PathFollow2D/Sprite2D"); + /// GD.Print(nodePath.GetName(0)); // Path2D + /// GD.Print(nodePath.GetName(1)); // PathFollow2D + /// GD.Print(nodePath.GetName(2)); // Sprite + /// </code> + /// </example> + /// <param name="idx">The name index.</param> + /// <returns>The name at the given index <paramref name="idx"/>.</returns> public string GetName(int idx) { return godot_icall_NodePath_get_name(GetPtr(this), idx); } + /// <summary> + /// Gets the number of node names which make up the path. + /// Subnames (see <see cref="GetSubnameCount"/>) are not included. + /// For example, <c>"Path2D/PathFollow2D/Sprite2D"</c> has 3 names. + /// </summary> + /// <returns>The number of node names which make up the path.</returns> public int GetNameCount() { return godot_icall_NodePath_get_name_count(GetPtr(this)); } + /// <summary> + /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubnameCount"/>). + /// </summary> + /// <param name="idx">The subname index.</param> + /// <returns>The subname at the given index <paramref name="idx"/>.</returns> public string GetSubname(int idx) { return godot_icall_NodePath_get_subname(GetPtr(this), idx); } + /// <summary> + /// Gets the number of resource or property names ("subnames") in the path. + /// Each subname is listed after a colon character (<c>:</c>) in the node path. + /// For example, <c>"Path2D/PathFollow2D/Sprite2D:texture:load_path"</c> has 2 subnames. + /// </summary> + /// <returns>The number of subnames in the path.</returns> public int GetSubnameCount() { return godot_icall_NodePath_get_subname_count(GetPtr(this)); } + /// <summary> + /// Returns <see langword="true"/> if the node path is absolute (as opposed to relative), + /// which means that it starts with a slash character (<c>/</c>). Absolute node paths can + /// be used to access the root node (<c>"/root"</c>) or autoloads (e.g. <c>"/global"</c> + /// if a "global" autoload was registered). + /// </summary> + /// <returns>If the <see cref="NodePath"/> is an absolute path.</returns> public bool IsAbsolute() { return godot_icall_NodePath_is_absolute(GetPtr(this)); } + /// <summary> + /// Returns <see langword="true"/> if the node path is empty. + /// </summary> + /// <returns>If the <see cref="NodePath"/> is empty.</returns> public bool IsEmpty() { return godot_icall_NodePath_is_empty(GetPtr(this)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index d486d79557..8fe08e7e1d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -5,13 +5,16 @@ namespace Godot { public partial class Object : IDisposable { - private bool disposed = false; + private bool _disposed = false; private static StringName nativeName = "Object"; internal IntPtr ptr; internal bool memoryOwn; + /// <summary> + /// Constructs a new <see cref="Object"/>. + /// </summary> public Object() : this(false) { if (ptr == IntPtr.Zero) @@ -29,6 +32,9 @@ namespace Godot this.memoryOwn = memoryOwn; } + /// <summary> + /// The pointer to the native instance of this <see cref="Object"/>. + /// </summary> public IntPtr NativeInstance { get { return ptr; } @@ -39,7 +45,7 @@ namespace Godot if (instance == null) return IntPtr.Zero; - if (instance.disposed) + if (instance._disposed) throw new ObjectDisposedException(instance.GetType().FullName); return instance.ptr; @@ -50,15 +56,21 @@ namespace Godot Dispose(false); } + /// <summary> + /// Disposes of this <see cref="Object"/>. + /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } + /// <summary> + /// Disposes implementation of this <see cref="Object"/>. + /// </summary> protected virtual void Dispose(bool disposing) { - if (disposed) + if (_disposed) return; if (ptr != IntPtr.Zero) @@ -73,19 +85,23 @@ namespace Godot godot_icall_Object_Disposed(this, ptr); } - this.ptr = IntPtr.Zero; + ptr = IntPtr.Zero; } - disposed = true; + _disposed = true; } + /// <summary> + /// Converts this <see cref="Object"/> to a string. + /// </summary> + /// <returns>A string representation of this object.</returns> public override string ToString() { return godot_icall_Object_ToString(GetPtr(this)); } /// <summary> - /// Returns a new <see cref="Godot.SignalAwaiter"/> awaiter configured to complete when the instance + /// Returns a new <see cref="SignalAwaiter"/> awaiter configured to complete when the instance /// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter. /// </summary> /// <param name="source"> @@ -107,13 +123,17 @@ namespace Godot /// } /// </code> /// </example> + /// <returns> + /// A <see cref="SignalAwaiter"/> that completes when + /// <paramref name="source"/> emits the <paramref name="signal"/>. + /// </returns> public SignalAwaiter ToSignal(Object source, StringName signal) { return new SignalAwaiter(source, signal, this); } /// <summary> - /// Gets a new <see cref="Godot.DynamicGodotObject"/> associated with this instance. + /// Gets a new <see cref="DynamicGodotObject"/> associated with this instance. /// </summary> public dynamic DynamicObject => new DynamicGodotObject(this); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 6972102730..66f7b745f7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -21,10 +21,10 @@ namespace Godot /// <summary> /// The normal of the plane, which must be normalized. - /// In the scalar equation of the plane `ax + by + cz = d`, this is - /// the vector `(a, b, c)`, where `d` is the <see cref="D"/> property. + /// 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 `x`, `y`, and `z`.</value> + /// <value>Equivalent to <see cref="x"/>, <see cref="y"/>, and <see cref="z"/>.</value> public Vector3 Normal { get { return _normal; } @@ -82,8 +82,8 @@ 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 `ax + by + cz = d`, - /// this is `d`, while the `(a, b, c)` coordinates are represented + /// 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> @@ -92,7 +92,7 @@ namespace Godot /// <summary> /// The center of the plane, the point where the normal line intersects the plane. /// </summary> - /// <value>Equivalent to <see cref="Normal"/> multiplied by `D`.</value> + /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value> public Vector3 Center { get @@ -107,7 +107,7 @@ namespace Godot } /// <summary> - /// Returns the shortest distance from this plane to the position `point`. + /// 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> @@ -117,12 +117,12 @@ namespace Godot } /// <summary> - /// Returns true if point is inside the plane. + /// Returns <see langword="true"/> if point is inside the plane. /// Comparison uses a custom minimum epsilon threshold. /// </summary> /// <param name="point">The point to check.</param> /// <param name="epsilon">The tolerance threshold.</param> - /// <returns>A bool for whether or not the plane has the point.</returns> + /// <returns>A <see langword="bool"/> for whether or not the plane has the point.</returns> public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) { real_t dist = _normal.Dot(point) - D; @@ -130,12 +130,12 @@ namespace Godot } /// <summary> - /// Returns the intersection point of the three planes: `b`, `c`, - /// and this plane. If no intersection is found, `null` is returned. + /// Returns the intersection point of the three planes: <paramref name="b"/>, <paramref name="c"/>, + /// and this plane. If no intersection is found, <see langword="null"/> is returned. /// </summary> /// <param name="b">One of the three planes to use in the calculation.</param> /// <param name="c">One of the three planes to use in the calculation.</param> - /// <returns>The intersection, or `null` if none is found.</returns> + /// <returns>The intersection, or <see langword="null"/> if none is found.</returns> public Vector3? Intersect3(Plane b, Plane c) { real_t denom = _normal.Cross(b._normal).Dot(c._normal); @@ -145,21 +145,21 @@ 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; } /// <summary> - /// Returns the intersection point of a ray consisting of the - /// position `from` and the direction normal `dir` with this plane. - /// If no intersection is found, `null` is returned. + /// Returns the intersection point of a ray consisting of the position <paramref name="from"/> + /// and the direction normal <paramref name="dir"/> with this plane. + /// If no intersection is found, <see langword="null"/> is returned. /// </summary> /// <param name="from">The start of the ray.</param> /// <param name="dir">The direction of the ray, normalized.</param> - /// <returns>The intersection, or `null` if none is found.</returns> + /// <returns>The intersection, or <see langword="null"/> if none is found.</returns> public Vector3? IntersectRay(Vector3 from, Vector3 dir) { real_t den = _normal.Dot(dir); @@ -182,12 +182,12 @@ namespace Godot /// <summary> /// Returns the intersection point of a line segment from - /// position `begin` to position `end` with this plane. - /// If no intersection is found, `null` is returned. + /// position <paramref name="begin"/> to position <paramref name="end"/> with this plane. + /// If no intersection is found, <see langword="null"/> is returned. /// </summary> /// <param name="begin">The start of the line segment.</param> /// <param name="end">The end of the line segment.</param> - /// <returns>The intersection, or `null` if none is found.</returns> + /// <returns>The intersection, or <see langword="null"/> if none is found.</returns> public Vector3? IntersectSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; @@ -210,10 +210,10 @@ namespace Godot } /// <summary> - /// Returns true if `point` is located above the plane. + /// Returns <see langword="true"/> if <paramref name="point"/> is located above the plane. /// </summary> /// <param name="point">The point to check.</param> - /// <returns>A bool for whether or not the point is above the plane.</returns> + /// <returns>A <see langword="bool"/> for whether or not the point is above the plane.</returns> public bool IsPointOver(Vector3 point) { return _normal.Dot(point) > D; @@ -236,13 +236,13 @@ namespace Godot } /// <summary> - /// Returns the orthogonal projection of `point` into the plane. + /// Returns the orthogonal projection of <paramref name="point"/> into the plane. /// </summary> /// <param name="point">The point to project.</param> /// <returns>The projected point.</returns> public Vector3 Project(Vector3 point) { - return point - _normal * DistanceTo(point); + return point - (_normal * DistanceTo(point)); } // Constants @@ -251,27 +251,28 @@ namespace Godot private static readonly Plane _planeXY = new Plane(0, 0, 1, 0); /// <summary> - /// A plane that extends in the Y and Z axes (normal vector points +X). + /// A <see cref="Plane"/> that extends in the Y and Z axes (normal vector points +X). /// </summary> - /// <value>Equivalent to `new Plane(1, 0, 0, 0)`.</value> + /// <value>Equivalent to <c>new Plane(1, 0, 0, 0)</c>.</value> public static Plane PlaneYZ { get { return _planeYZ; } } /// <summary> - /// A plane that extends in the X and Z axes (normal vector points +Y). + /// A <see cref="Plane"/> that extends in the X and Z axes (normal vector points +Y). /// </summary> - /// <value>Equivalent to `new Plane(0, 1, 0, 0)`.</value> + /// <value>Equivalent to <c>new Plane(0, 1, 0, 0)</c>.</value> public static Plane PlaneXZ { get { return _planeXZ; } } /// <summary> - /// A plane that extends in the X and Y axes (normal vector points +Z). + /// A <see cref="Plane"/> that extends in the X and Y axes (normal vector points +Z). /// </summary> - /// <value>Equivalent to `new Plane(0, 0, 1, 0)`.</value> + /// <value>Equivalent to <c>new Plane(0, 0, 1, 0)</c>.</value> public static Plane PlaneXY { get { return _planeXY; } } /// <summary> - /// Constructs a plane from four values. `a`, `b` and `c` become the + /// Constructs a <see cref="Plane"/> from four values. + /// <paramref name="a"/>, <paramref name="b"/> and <paramref name="c"/> become the /// components of the resulting plane's <see cref="Normal"/> vector. - /// `d` becomes the plane's distance from the origin. + /// <paramref name="d"/> becomes the plane's distance from the origin. /// </summary> /// <param name="a">The X component of the plane's normal vector.</param> /// <param name="b">The Y component of the plane's normal vector.</param> @@ -280,22 +281,23 @@ namespace Godot public Plane(real_t a, real_t b, real_t c, real_t d) { _normal = new Vector3(a, b, c); - this.D = d; + D = d; } /// <summary> - /// Constructs a plane from a normal vector and the plane's distance to the origin. + /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and + /// the plane's distance to the origin <paramref name="d"/>. /// </summary> /// <param name="normal">The normal of the plane, must be normalized.</param> /// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param> public Plane(Vector3 normal, real_t d) { - this._normal = normal; - this.D = d; + _normal = normal; + D = d; } /// <summary> - /// Constructs a plane from the three points, given in clockwise order. + /// Constructs a <see cref="Plane"/> from the three points, given in clockwise order. /// </summary> /// <param name="v1">The first point.</param> /// <param name="v2">The second point.</param> @@ -322,6 +324,11 @@ namespace Godot return !left.Equals(right); } + /// <summary> + /// Returns <see langword="true"/> if this plane and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the plane and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Plane) @@ -332,14 +339,19 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this plane and <paramref name="other"/> are equal. + /// </summary> + /// <param name="other">The other plane to compare.</param> + /// <returns>Whether or not the planes are equal.</returns> public bool Equals(Plane other) { return _normal == other._normal && D == other.D; } /// <summary> - /// Returns true if this plane and `other` are approximately equal, by running - /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. + /// Returns <see langword="true"/> if this plane and <paramref name="other"/> are + /// approximately equal, by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. /// </summary> /// <param name="other">The other plane to compare.</param> /// <returns>Whether or not the planes are approximately equal.</returns> @@ -348,16 +360,28 @@ namespace Godot return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D); } + /// <summary> + /// Serves as the hash function for <see cref="Plane"/>. + /// </summary> + /// <returns>A hash code for this plane.</returns> public override int GetHashCode() { return _normal.GetHashCode() ^ D.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Plane"/> to a string. + /// </summary> + /// <returns>A string representation of this plane.</returns> public override string ToString() { return $"{_normal}, {D}"; } + /// <summary> + /// Converts this <see cref="Plane"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this plane.</returns> public string ToString(string format) { return $"{_normal.ToString(format)}, {D.ToString(format)}"; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index 0fed55cc30..1694ac0320 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -12,10 +12,10 @@ namespace Godot /// A unit quaternion used for representing 3D rotations. /// Quaternions need to be normalized to be used for rotation. /// - /// It is similar to Basis, which implements matrix representation of - /// rotations, and can be parametrized using both an axis-angle pair - /// or Euler angles. Basis stores rotation, scale, and shearing, - /// while Quaternion only stores rotation. + /// It is similar to <see cref="Basis"/>, which implements matrix + /// representation of rotations, and can be parametrized using both + /// an axis-angle pair or Euler angles. Basis stores rotation, scale, + /// and shearing, while Quaternion only stores rotation. /// /// Due to its compactness and the way it is stored in memory, certain /// operations (obtaining axis-angle and performing SLERP, in particular) @@ -26,19 +26,19 @@ namespace Godot public struct Quaternion : IEquatable<Quaternion> { /// <summary> - /// X component of the quaternion (imaginary `i` axis part). + /// X component of the quaternion (imaginary <c>i</c> axis part). /// Quaternion components should usually not be manipulated directly. /// </summary> public real_t x; /// <summary> - /// Y component of the quaternion (imaginary `j` axis part). + /// Y component of the quaternion (imaginary <c>j</c> axis part). /// Quaternion components should usually not be manipulated directly. /// </summary> public real_t y; /// <summary> - /// Z component of the quaternion (imaginary `k` axis part). + /// Z component of the quaternion (imaginary <c>k</c> axis part). /// Quaternion components should usually not be manipulated directly. /// </summary> public real_t z; @@ -52,7 +52,12 @@ namespace Godot /// <summary> /// Access quaternion components using their index. /// </summary> - /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`, `[3]` is equivalent to `.w`.</value> + /// <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"/>. + /// </value> public real_t this[int index] { get @@ -96,7 +101,8 @@ namespace Godot /// <summary> /// Returns the length (magnitude) of the quaternion. /// </summary> - /// <value>Equivalent to `Mathf.Sqrt(LengthSquared)`.</value> + /// <seealso cref="LengthSquared"/> + /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value> public real_t Length { get { return Mathf.Sqrt(LengthSquared); } @@ -107,14 +113,14 @@ namespace Godot /// This method runs faster than <see cref="Length"/>, so prefer it if /// you need to compare quaternions or need the squared length for some formula. /// </summary> - /// <value>Equivalent to `Dot(this)`.</value> + /// <value>Equivalent to <c>Dot(this)</c>.</value> public real_t LengthSquared { get { return Dot(this); } } /// <summary> - /// Returns the angle between this quaternion and `to`. + /// Returns the angle between this quaternion and <paramref name="to"/>. /// This is the magnitude of the angle you would need to rotate /// by to get from one to the other. /// @@ -131,12 +137,12 @@ namespace Godot } /// <summary> - /// Performs a cubic spherical interpolation between quaternions `preA`, - /// this vector, `b`, and `postB`, by the given amount `t`. + /// Performs a cubic spherical interpolation between quaternions <paramref name="preA"/>, this quaternion, + /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>. /// </summary> /// <param name="b">The destination quaternion.</param> /// <param name="preA">A quaternion before this quaternion.</param> - /// <param name="postB">A quaternion after `b`.</param> + /// <param name="postB">A quaternion after <paramref name="b"/>.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated quaternion.</returns> public Quaternion CubicSlerp(Quaternion b, Quaternion preA, Quaternion postB, real_t weight) @@ -154,7 +160,7 @@ namespace Godot /// <returns>The dot product.</returns> public 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); } /// <summary> @@ -194,7 +200,7 @@ namespace Godot /// <summary> /// Returns whether the quaternion is normalized or not. /// </summary> - /// <returns>A bool for whether the quaternion is normalized or not.</returns> + /// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns> public bool IsNormalized() { return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon; @@ -211,7 +217,7 @@ namespace Godot /// <summary> /// Returns the result of the spherical linear interpolation between - /// this quaternion and `to` by amount `weight`. + /// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>. /// /// Note: Both quaternions must be normalized. /// </summary> @@ -274,16 +280,16 @@ 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) ); } /// <summary> /// Returns the result of the spherical linear interpolation between - /// this quaternion and `to` by amount `weight`, but without + /// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>, but without /// checking if the rotation path is not bigger than 90 degrees. /// </summary> /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param> @@ -305,10 +311,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) ); } @@ -327,7 +333,7 @@ namespace Godot #endif var u = new Vector3(x, y, z); Vector3 uv = u.Cross(v); - return v + ((uv * w) + u.Cross(uv)) * 2; + return v + (((uv * w) + u.Cross(uv)) * 2); } // Constants @@ -338,15 +344,15 @@ namespace Godot /// Equivalent to an identity <see cref="Basis"/> matrix. If a vector is transformed by /// an identity quaternion, it will not change. /// </summary> - /// <value>Equivalent to `new Quaternion(0, 0, 0, 1)`.</value> + /// <value>Equivalent to <c>new Quaternion(0, 0, 0, 1)</c>.</value> public static Quaternion Identity { get { return _identity; } } /// <summary> - /// Constructs a quaternion defined by the given values. + /// Constructs a <see cref="Quaternion"/> defined by the given values. /// </summary> - /// <param name="x">X component of the quaternion (imaginary `i` axis part).</param> - /// <param name="y">Y component of the quaternion (imaginary `j` axis part).</param> - /// <param name="z">Z component of the quaternion (imaginary `k` axis part).</param> + /// <param name="x">X component of the quaternion (imaginary <c>i</c> axis part).</param> + /// <param name="y">Y component of the quaternion (imaginary <c>j</c> axis part).</param> + /// <param name="z">Z component of the quaternion (imaginary <c>k</c> axis part).</param> /// <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) { @@ -357,7 +363,7 @@ namespace Godot } /// <summary> - /// Constructs a quaternion from the given quaternion. + /// Constructs a <see cref="Quaternion"/> from the given <see cref="Quaternion"/>. /// </summary> /// <param name="q">The existing quaternion.</param> public Quaternion(Quaternion q) @@ -366,46 +372,45 @@ namespace Godot } /// <summary> - /// Constructs a quaternion from the given <see cref="Basis"/>. + /// Constructs a <see cref="Quaternion"/> from the given <see cref="Basis"/>. /// </summary> - /// <param name="basis">The basis to construct from.</param> + /// <param name="basis">The <see cref="Basis"/> to construct from.</param> public Quaternion(Basis basis) { this = basis.Quaternion(); } /// <summary> - /// Constructs a quaternion that will perform a rotation specified by - /// Euler angles (in the YXZ convention: when decomposing, - /// first Z, then X, and Y last), + /// Constructs a <see cref="Quaternion"/> that will perform a rotation specified by + /// Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last), /// given in the vector format as (X angle, Y angle, Z angle). /// </summary> - /// <param name="eulerYXZ"></param> + /// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param> public Quaternion(Vector3 eulerYXZ) { - real_t half_a1 = eulerYXZ.y * 0.5f; - real_t half_a2 = eulerYXZ.x * 0.5f; - real_t half_a3 = 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) // a3 is the angle of the first rotation, following the notation in this reference. - real_t cos_a1 = Mathf.Cos(half_a1); - real_t sin_a1 = Mathf.Sin(half_a1); - real_t cos_a2 = Mathf.Cos(half_a2); - real_t sin_a2 = Mathf.Sin(half_a2); - real_t cos_a3 = Mathf.Cos(half_a3); - real_t sin_a3 = Mathf.Sin(half_a3); + real_t cosA1 = Mathf.Cos(halfA1); + real_t sinA1 = Mathf.Sin(halfA1); + real_t cosA2 = Mathf.Cos(halfA2); + real_t sinA2 = Mathf.Sin(halfA2); + real_t cosA3 = Mathf.Cos(halfA3); + real_t sinA3 = Mathf.Sin(halfA3); - x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3; - y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3; - z = cos_a1 * cos_a2 * sin_a3 - sin_a1 * sin_a2 * cos_a3; - w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3; + x = (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3); + y = (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3); + z = (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3); + w = (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3); } /// <summary> - /// Constructs a quaternion that will rotate around the given axis + /// Constructs a <see cref="Quaternion"/> that will rotate around the given axis /// by the specified angle. The axis must be a normalized vector. /// </summary> /// <param name="axis">The axis to rotate around. Must be normalized.</param> @@ -445,10 +450,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) ); } @@ -471,10 +476,10 @@ namespace Godot { return new Quaternion ( - left.w * right.x + left.y * right.z - left.z * right.y, - left.w * right.y + left.z * right.x - left.x * right.z, - left.w * right.z + left.x * right.y - left.y * right.x, - -left.x * right.x - left.y * right.y - left.z * right.z + (left.w * right.x) + (left.y * right.z) - (left.z * right.y), + (left.w * right.y) + (left.z * right.x) - (left.x * right.z), + (left.w * right.z) + (left.x * right.y) - (left.y * right.x), + -(left.x * right.x) - (left.y * right.y) - (left.z * right.z) ); } @@ -482,10 +487,10 @@ namespace Godot { return new Quaternion ( - right.w * left.x + right.y * left.z - right.z * left.y, - right.w * left.y + right.z * left.x - right.x * left.z, - right.w * left.z + right.x * left.y - right.y * left.x, - -right.x * left.x - right.y * left.y - right.z * left.z + (right.w * left.x) + (right.y * left.z) - (right.z * left.y), + (right.w * left.y) + (right.z * left.x) - (right.x * left.z), + (right.w * left.z) + (right.x * left.y) - (right.y * left.x), + -(right.x * left.x) - (right.y * left.y) - (right.z * left.z) ); } @@ -514,6 +519,11 @@ namespace Godot return !left.Equals(right); } + /// <summary> + /// Returns <see langword="true"/> if this quaternion and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the quaternion and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Quaternion) @@ -524,14 +534,19 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this quaternion and <paramref name="other"/> are equal. + /// </summary> + /// <param name="other">The other quaternion to compare.</param> + /// <returns>Whether or not the quaternions are equal.</returns> public bool Equals(Quaternion other) { return x == other.x && y == other.y && z == other.z && w == other.w; } /// <summary> - /// Returns true if this quaternion and `other` are approximately equal, by running - /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. + /// Returns <see langword="true"/> if this quaternion and <paramref name="other"/> are approximately equal, + /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. /// </summary> /// <param name="other">The other quaternion to compare.</param> /// <returns>Whether or not the quaternions are approximately equal.</returns> @@ -540,16 +555,28 @@ namespace Godot return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); } + /// <summary> + /// Serves as the hash function for <see cref="Quaternion"/>. + /// </summary> + /// <returns>A hash code for this quaternion.</returns> public override int GetHashCode() { return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Quaternion"/> to a string. + /// </summary> + /// <returns>A string representation of this quaternion.</returns> public override string ToString() { return $"({x}, {y}, {z}, {w})"; } + /// <summary> + /// Converts this <see cref="Quaternion"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this quaternion.</returns> public string ToString(string format) { return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})"; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs index 94761531b1..1588869ec0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs @@ -3,9 +3,15 @@ using System.Runtime.CompilerServices; namespace Godot { + /// <summary> + /// 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> public sealed partial class RID : IDisposable { - private bool disposed = false; + private bool _disposed = false; internal IntPtr ptr; @@ -14,7 +20,7 @@ namespace Godot if (instance == null) throw new NullReferenceException($"The instance of type {nameof(RID)} is null."); - if (instance.disposed) + if (instance._disposed) throw new ObjectDisposedException(instance.GetType().FullName); return instance.ptr; @@ -25,6 +31,9 @@ namespace Godot Dispose(false); } + /// <summary> + /// Disposes of this <see cref="RID"/>. + /// </summary> public void Dispose() { Dispose(true); @@ -33,7 +42,7 @@ namespace Godot private void Dispose(bool disposing) { - if (disposed) + if (_disposed) return; if (ptr != IntPtr.Zero) @@ -42,7 +51,7 @@ namespace Godot ptr = IntPtr.Zero; } - disposed = true; + _disposed = true; } internal RID(IntPtr ptr) @@ -50,6 +59,9 @@ namespace Godot this.ptr = ptr; } + /// <summary> + /// The pointer to the native instance of this <see cref="RID"/>. + /// </summary> public IntPtr NativeInstance { get { return ptr; } @@ -60,25 +72,36 @@ namespace Godot this.ptr = IntPtr.Zero; } + /// <summary> + /// Constructs a new <see cref="RID"/> for the given <see cref="Object"/> <paramref name="from"/>. + /// </summary> public RID(Object from) { this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from)); } + /// <summary> + /// Returns the ID of the referenced resource. + /// </summary> + /// <returns>The ID of the referenced resource.</returns> public int GetId() { - return godot_icall_RID_get_id(RID.GetPtr(this)); + return godot_icall_RID_get_id(GetPtr(this)); } + /// <summary> + /// Converts this <see cref="RID"/> to a string. + /// </summary> + /// <returns>A string representation of this RID.</returns> public override string ToString() => "[RID]"; [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from); + internal static extern IntPtr godot_icall_RID_Ctor(IntPtr from); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_RID_Dtor(IntPtr ptr); + internal static extern void godot_icall_RID_Dtor(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_RID_get_id(IntPtr ptr); + internal static extern int godot_icall_RID_get_id(IntPtr ptr); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index dec69c7f94..1d001fba2d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -20,7 +20,7 @@ namespace Godot private Vector2 _size; /// <summary> - /// Beginning corner. Typically has values lower than End. + /// Beginning corner. Typically has values lower than <see cref="End"/>. /// </summary> /// <value>Directly uses a private field.</value> public Vector2 Position @@ -30,7 +30,7 @@ namespace Godot } /// <summary> - /// Size from Position to End. Typically all components are positive. + /// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive. /// If the size is negative, you can use <see cref="Abs"/> to fix it. /// </summary> /// <value>Directly uses a private field.</value> @@ -41,10 +41,13 @@ namespace Godot } /// <summary> - /// Ending corner. This is calculated as <see cref="Position"/> plus - /// <see cref="Size"/>. Setting this value will change the size. + /// Ending corner. This is calculated as <see cref="Position"/> plus <see cref="Size"/>. + /// Setting this value will change the size. /// </summary> - /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value> + /// <value> + /// 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 Vector2 End { get { return _position + _size; } @@ -52,7 +55,7 @@ namespace Godot } /// <summary> - /// The area of this Rect2. + /// The area of this <see cref="Rect2"/>. /// </summary> /// <value>Equivalent to <see cref="GetArea()"/>.</value> public real_t Area @@ -61,10 +64,10 @@ namespace Godot } /// <summary> - /// Returns a Rect2 with equivalent position and size, modified so that + /// Returns a <see cref="Rect2"/> 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 Rect2.</returns> + /// <returns>The modified <see cref="Rect2"/>.</returns> public Rect2 Abs() { Vector2 end = End; @@ -73,14 +76,17 @@ namespace Godot } /// <summary> - /// Returns the intersection of this Rect2 and `b`. - /// If the rectangles do not intersect, an empty Rect2 is returned. + /// Returns the intersection of this <see cref="Rect2"/> and <paramref name="b"/>. + /// If the rectangles do not intersect, an empty <see cref="Rect2"/> is returned. /// </summary> - /// <param name="b">The other Rect2.</param> - /// <returns>The intersection of this Rect2 and `b`, or an empty Rect2 if they do not intersect.</returns> + /// <param name="b">The other <see cref="Rect2"/>.</param> + /// <returns> + /// The intersection of this <see cref="Rect2"/> and <paramref name="b"/>, + /// or an empty <see cref="Rect2"/> if they do not intersect. + /// </returns> public Rect2 Intersection(Rect2 b) { - var newRect = b; + Rect2 newRect = b; if (!Intersects(newRect)) { @@ -100,10 +106,12 @@ namespace Godot } /// <summary> - /// Returns true if this Rect2 completely encloses another one. + /// Returns <see langword="true"/> if this <see cref="Rect2"/> completely encloses another one. /// </summary> - /// <param name="b">The other Rect2 that may be enclosed.</param> - /// <returns>A bool for whether or not this Rect2 encloses `b`.</returns> + /// <param name="b">The other <see cref="Rect2"/> that may be enclosed.</param> + /// <returns> + /// A <see langword="bool"/> for whether or not this <see cref="Rect2"/> encloses <paramref name="b"/>. + /// </returns> public bool Encloses(Rect2 b) { return b._position.x >= _position.x && b._position.y >= _position.y && @@ -112,13 +120,13 @@ namespace Godot } /// <summary> - /// Returns this Rect2 expanded to include a given point. + /// Returns this <see cref="Rect2"/> expanded to include a given point. /// </summary> /// <param name="to">The point to include.</param> - /// <returns>The expanded Rect2.</returns> + /// <returns>The expanded <see cref="Rect2"/>.</returns> public Rect2 Expand(Vector2 to) { - var expanded = this; + Rect2 expanded = this; Vector2 begin = expanded._position; Vector2 end = expanded._position + expanded._size; @@ -148,7 +156,7 @@ namespace Godot } /// <summary> - /// Returns the area of the Rect2. + /// Returns the area of the <see cref="Rect2"/>. /// </summary> /// <returns>The area.</returns> public real_t GetArea() @@ -157,13 +165,16 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2 grown by the specified amount on all sides. + /// Returns a copy of the <see cref="Rect2"/> grown by the specified amount + /// on all sides. /// </summary> + /// <seealso cref="GrowIndividual(real_t, real_t, real_t, real_t)"/> + /// <seealso cref="GrowSide(Side, real_t)"/> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown Rect2.</returns> + /// <returns>The grown <see cref="Rect2"/>.</returns> public Rect2 Grow(real_t by) { - var g = this; + Rect2 g = this; g._position.x -= by; g._position.y -= by; @@ -174,16 +185,19 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2 grown by the specified amount on each side individually. + /// Returns a copy of the <see cref="Rect2"/> grown by the specified amount + /// on each side individually. /// </summary> + /// <seealso cref="Grow(real_t)"/> + /// <seealso cref="GrowSide(Side, real_t)"/> /// <param name="left">The amount to grow by on the left side.</param> /// <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 Rect2.</returns> + /// <returns>The grown <see cref="Rect2"/>.</returns> public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom) { - var g = this; + Rect2 g = this; g._position.x -= left; g._position.y -= top; @@ -194,14 +208,17 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2 grown by the specified amount on the specified Side. + /// Returns a copy of the <see cref="Rect2"/> grown by the specified amount + /// on the specified <see cref="Side"/>. /// </summary> + /// <seealso cref="Grow(real_t)"/> + /// <seealso cref="GrowIndividual(real_t, real_t, real_t, real_t)"/> /// <param name="side">The side to grow.</param> /// <param name="by">The amount to grow by.</param> - /// <returns>The grown Rect2.</returns> + /// <returns>The grown <see cref="Rect2"/>.</returns> public Rect2 GrowSide(Side side, real_t by) { - var g = this; + Rect2 g = this; g = g.GrowIndividual(Side.Left == side ? by : 0, Side.Top == side ? by : 0, @@ -212,19 +229,25 @@ namespace Godot } /// <summary> - /// Returns true if the Rect2 is flat or empty, or false otherwise. + /// Returns <see langword="true"/> if the <see cref="Rect2"/> is flat or empty, + /// or <see langword="false"/> otherwise. /// </summary> - /// <returns>A bool for whether or not the Rect2 has area.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> has area. + /// </returns> public bool HasNoArea() { return _size.x <= 0 || _size.y <= 0; } /// <summary> - /// Returns true if the Rect2 contains a point, or false otherwise. + /// Returns <see langword="true"/> if the <see cref="Rect2"/> contains a point, + /// or <see langword="false"/> otherwise. /// </summary> /// <param name="point">The point to check.</param> - /// <returns>A bool for whether or not the Rect2 contains `point`.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> contains <paramref name="point"/>. + /// </returns> public bool HasPoint(Vector2 point) { if (point.x < _position.x) @@ -241,15 +264,16 @@ namespace Godot } /// <summary> - /// Returns true if the Rect2 overlaps with `b` + /// Returns <see langword="true"/> if the <see cref="Rect2"/> overlaps with <paramref name="b"/> /// (i.e. they have at least one point in common). /// - /// If `includeBorders` is true, they will also be considered overlapping - /// if their borders touch, even without intersection. + /// If <paramref name="includeBorders"/> is <see langword="true"/>, + /// they will also be considered overlapping if their borders touch, + /// even without intersection. /// </summary> - /// <param name="b">The other Rect2 to check for intersections with.</param> + /// <param name="b">The other <see cref="Rect2"/> to check for intersections with.</param> /// <param name="includeBorders">Whether or not to consider borders.</param> - /// <returns>A bool for whether or not they are intersecting.</returns> + /// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns> public bool Intersects(Rect2 b, bool includeBorders = false) { if (includeBorders) @@ -295,10 +319,10 @@ namespace Godot } /// <summary> - /// Returns a larger Rect2 that contains this Rect2 and `b`. + /// Returns a larger <see cref="Rect2"/> that contains this <see cref="Rect2"/> and <paramref name="b"/>. /// </summary> - /// <param name="b">The other Rect2.</param> - /// <returns>The merged Rect2.</returns> + /// <param name="b">The other <see cref="Rect2"/>.</param> + /// <returns>The merged <see cref="Rect2"/>.</returns> public Rect2 Merge(Rect2 b) { Rect2 newRect; @@ -315,7 +339,7 @@ namespace Godot } /// <summary> - /// Constructs a Rect2 from a position and size. + /// Constructs a <see cref="Rect2"/> from a position and size. /// </summary> /// <param name="position">The position.</param> /// <param name="size">The size.</param> @@ -326,7 +350,7 @@ namespace Godot } /// <summary> - /// Constructs a Rect2 from a position, width, and height. + /// Constructs a <see cref="Rect2"/> from a position, width, and height. /// </summary> /// <param name="position">The position.</param> /// <param name="width">The width.</param> @@ -338,7 +362,7 @@ namespace Godot } /// <summary> - /// Constructs a Rect2 from x, y, and size. + /// Constructs a <see cref="Rect2"/> from x, y, and size. /// </summary> /// <param name="x">The position's X coordinate.</param> /// <param name="y">The position's Y coordinate.</param> @@ -350,7 +374,7 @@ namespace Godot } /// <summary> - /// Constructs a Rect2 from x, y, width, and height. + /// Constructs a <see cref="Rect2"/> 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> @@ -372,6 +396,11 @@ namespace Godot return !left.Equals(right); } + /// <summary> + /// Returns <see langword="true"/> if this rect and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the rect and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Rect2) @@ -382,32 +411,49 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this rect and <paramref name="other"/> are equal. + /// </summary> + /// <param name="other">The other rect to compare.</param> + /// <returns>Whether or not the rects are equal.</returns> public bool Equals(Rect2 other) { return _position.Equals(other._position) && _size.Equals(other._size); } /// <summary> - /// Returns true if this Rect2 and `other` are approximately equal, by running - /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component. + /// Returns <see langword="true"/> if this rect and <paramref name="other"/> are approximately equal, + /// by running <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component. /// </summary> - /// <param name="other">The other Rect2 to compare.</param> - /// <returns>Whether or not the Rect2s are approximately equal.</returns> + /// <param name="other">The other rect to compare.</param> + /// <returns>Whether or not the rects are approximately equal.</returns> public bool IsEqualApprox(Rect2 other) { return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size); } + /// <summary> + /// Serves as the hash function for <see cref="Rect2"/>. + /// </summary> + /// <returns>A hash code for this rect.</returns> public override int GetHashCode() { return _position.GetHashCode() ^ _size.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Rect2"/> to a string. + /// </summary> + /// <returns>A string representation of this rect.</returns> public override string ToString() { return $"{_position}, {_size}"; } + /// <summary> + /// Converts this <see cref="Rect2"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this rect.</returns> public string ToString(string format) { return $"{_position.ToString(format)}, {_size.ToString(format)}"; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs index 7fb6614d2c..250b0d0baf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs @@ -15,7 +15,7 @@ namespace Godot private Vector2i _size; /// <summary> - /// Beginning corner. Typically has values lower than End. + /// Beginning corner. Typically has values lower than <see cref="End"/>. /// </summary> /// <value>Directly uses a private field.</value> public Vector2i Position @@ -25,7 +25,7 @@ namespace Godot } /// <summary> - /// Size from Position to End. Typically all components are positive. + /// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive. /// If the size is negative, you can use <see cref="Abs"/> to fix it. /// </summary> /// <value>Directly uses a private field.</value> @@ -36,10 +36,13 @@ namespace Godot } /// <summary> - /// Ending corner. This is calculated as <see cref="Position"/> plus - /// <see cref="Size"/>. Setting this value will change the size. + /// Ending corner. This is calculated as <see cref="Position"/> plus <see cref="Size"/>. + /// Setting this value will change the size. /// </summary> - /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value> + /// <value> + /// 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 { get { return _position + _size; } @@ -47,7 +50,7 @@ namespace Godot } /// <summary> - /// The area of this Rect2i. + /// The area of this <see cref="Rect2i"/>. /// </summary> /// <value>Equivalent to <see cref="GetArea()"/>.</value> public int Area @@ -56,10 +59,10 @@ namespace Godot } /// <summary> - /// Returns a 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 Rect2i.</returns> + /// <returns>The modified <see cref="Rect2i"/>.</returns> public Rect2i Abs() { Vector2i end = End; @@ -68,14 +71,17 @@ namespace Godot } /// <summary> - /// Returns the intersection of this Rect2i and `b`. - /// If the rectangles do not intersect, an empty 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 Rect2i.</param> - /// <returns>The intersection of this Rect2i and `b`, or an empty Rect2i if they do not intersect.</returns> + /// <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. + /// </returns> public Rect2i Intersection(Rect2i b) { - var newRect = b; + Rect2i newRect = b; if (!Intersects(newRect)) { @@ -95,10 +101,12 @@ namespace Godot } /// <summary> - /// Returns true if this Rect2i completely encloses another one. + /// Returns <see langword="true"/> if this <see cref="Rect2i"/> completely encloses another one. /// </summary> - /// <param name="b">The other Rect2i that may be enclosed.</param> - /// <returns>A bool for whether or not this Rect2i encloses `b`.</returns> + /// <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"/>. + /// </returns> public bool Encloses(Rect2i b) { return b._position.x >= _position.x && b._position.y >= _position.y && @@ -107,13 +115,13 @@ namespace Godot } /// <summary> - /// Returns this 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 Rect2i.</returns> + /// <returns>The expanded <see cref="Rect2i"/>.</returns> public Rect2i Expand(Vector2i to) { - var expanded = this; + Rect2i expanded = this; Vector2i begin = expanded._position; Vector2i end = expanded._position + expanded._size; @@ -143,7 +151,7 @@ namespace Godot } /// <summary> - /// Returns the area of the Rect2. + /// Returns the area of the <see cref="Rect2"/>. /// </summary> /// <returns>The area.</returns> public int GetArea() @@ -152,13 +160,16 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2i grown by the specified amount on all sides. + /// 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 Rect2i.</returns> + /// <returns>The grown <see cref="Rect2i"/>.</returns> public Rect2i Grow(int by) { - var g = this; + Rect2i g = this; g._position.x -= by; g._position.y -= by; @@ -169,16 +180,19 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2i grown by the specified amount on each side individually. + /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount + /// on each side individually. /// </summary> + /// <seealso cref="Grow(int)"/> + /// <seealso cref="GrowSide(Side, int)"/> /// <param name="left">The amount to grow by on the left side.</param> /// <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 Rect2i.</returns> + /// <returns>The grown <see cref="Rect2i"/>.</returns> public Rect2i GrowIndividual(int left, int top, int right, int bottom) { - var g = this; + Rect2i g = this; g._position.x -= left; g._position.y -= top; @@ -189,14 +203,17 @@ namespace Godot } /// <summary> - /// Returns a copy of the Rect2i grown by the specified amount on the specified Side. + /// 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 Rect2i.</returns> + /// <returns>The grown <see cref="Rect2i"/>.</returns> public Rect2i GrowSide(Side side, int by) { - var g = this; + Rect2i g = this; g = g.GrowIndividual(Side.Left == side ? by : 0, Side.Top == side ? by : 0, @@ -207,19 +224,25 @@ namespace Godot } /// <summary> - /// Returns true if the Rect2i is flat or empty, or false otherwise. + /// Returns <see langword="true"/> if the <see cref="Rect2i"/> is flat or empty, + /// or <see langword="false"/> otherwise. /// </summary> - /// <returns>A bool for whether or not the Rect2i has area.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> has area. + /// </returns> public bool HasNoArea() { return _size.x <= 0 || _size.y <= 0; } /// <summary> - /// Returns true if the Rect2i contains a point, or false otherwise. + /// 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 bool for whether or not the Rect2i contains `point`.</returns> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> contains <paramref name="point"/>. + /// </returns> public bool HasPoint(Vector2i point) { if (point.x < _position.x) @@ -236,15 +259,16 @@ namespace Godot } /// <summary> - /// Returns true if the Rect2i overlaps with `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). /// - /// If `includeBorders` is true, they will also be considered overlapping - /// if their borders touch, even without intersection. + /// If <paramref name="includeBorders"/> is <see langword="true"/>, + /// they will also be considered overlapping if their borders touch, + /// even without intersection. /// </summary> - /// <param name="b">The other Rect2i to check for intersections with.</param> + /// <param name="b">The other <see cref="Rect2i"/> to check for intersections with.</param> /// <param name="includeBorders">Whether or not to consider borders.</param> - /// <returns>A bool for whether or not they are intersecting.</returns> + /// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns> public bool Intersects(Rect2i b, bool includeBorders = false) { if (includeBorders) @@ -274,10 +298,10 @@ namespace Godot } /// <summary> - /// Returns a larger Rect2i that contains this Rect2i and `b`. + /// Returns a larger <see cref="Rect2i"/> that contains this <see cref="Rect2i"/> and <paramref name="b"/>. /// </summary> - /// <param name="b">The other Rect2i.</param> - /// <returns>The merged Rect2i.</returns> + /// <param name="b">The other <see cref="Rect2i"/>.</param> + /// <returns>The merged <see cref="Rect2i"/>.</returns> public Rect2i Merge(Rect2i b) { Rect2i newRect; @@ -294,7 +318,7 @@ namespace Godot } /// <summary> - /// Constructs a 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> @@ -305,7 +329,7 @@ namespace Godot } /// <summary> - /// Constructs a 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> @@ -317,7 +341,7 @@ namespace Godot } /// <summary> - /// Constructs a 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> @@ -329,7 +353,7 @@ namespace Godot } /// <summary> - /// Constructs a 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> @@ -351,16 +375,29 @@ namespace Godot return !left.Equals(right); } + /// <summary> + /// 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) { return new Rect2(value._position, value._size); } + /// <summary> + /// 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) { return new Rect2i((Vector2i)value.Position, (Vector2i)value.Size); } + /// <summary> + /// Returns <see langword="true"/> if this rect and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the rect and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Rect2i) @@ -371,21 +408,38 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this rect and <paramref name="other"/> are equal. + /// </summary> + /// <param name="other">The other rect to compare.</param> + /// <returns>Whether or not the rects are equal.</returns> public bool Equals(Rect2i other) { return _position.Equals(other._position) && _size.Equals(other._size); } + /// <summary> + /// Serves as the hash function for <see cref="Rect2i"/>. + /// </summary> + /// <returns>A hash code for this rect.</returns> public override int GetHashCode() { return _position.GetHashCode() ^ _size.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Rect2i"/> to a string. + /// </summary> + /// <returns>A string representation of this rect.</returns> public override string ToString() { return $"{_position}, {_size}"; } + /// <summary> + /// Converts this <see cref="Rect2i"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this rect.</returns> public string ToString(string format) { return $"{_position.ToString(format)}, {_size.ToString(format)}"; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs index 4dc630238b..2ba0493002 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -5,9 +5,9 @@ namespace Godot { public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]> { - private bool completed; - private object[] result; - private Action action; + private bool _completed; + private object[] _result; + private Action _action; public SignalAwaiter(Object source, StringName signal, Object target) { @@ -15,24 +15,24 @@ namespace Godot } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter); + internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter); public bool IsCompleted { get { - return completed; + return _completed; } } public void OnCompleted(Action action) { - this.action = action; + this._action = action; } public object[] GetResult() { - return result; + return _result; } public IAwaiter<object[]> GetAwaiter() @@ -42,13 +42,9 @@ namespace Godot internal void SignalCallback(object[] args) { - completed = true; - result = args; - - if (action != null) - { - action(); - } + _completed = true; + _result = args; + _action?.Invoke(); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs index dc92de7a61..5680c9d55a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs @@ -1,13 +1,28 @@ namespace Godot { + /// <summary> + /// Represents a signal defined in an object. + /// </summary> public struct SignalInfo { private readonly Object _owner; private readonly StringName _signalName; + /// <summary> + /// Object that contains the signal. + /// </summary> public Object Owner => _owner; + /// <summary> + /// Name of the signal. + /// </summary> public StringName Name => _signalName; + /// <summary> + /// Creates a new <see cref="Signal"/> with the name <paramref name="name"/> + /// in the specified <paramref name="owner"/>. + /// </summary> + /// <param name="owner">Object that contains the signal.</param> + /// <param name="name">Name of the signal.</param> public SignalInfo(Object owner, StringName name) { _owner = owner; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 6ce148d51e..6b3eb09581 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -8,6 +8,9 @@ using System.Text.RegularExpressions; namespace Godot { + /// <summary> + /// Extension methods to manipulate strings. + /// </summary> public static class StringExtensions { private static int GetSliceCount(this string instance, string splitter) @@ -64,6 +67,11 @@ namespace Godot /// <summary> /// If the string is a path to a file, return the path to the file without the extension. /// </summary> + /// <seealso cref="GetExtension(string)"/> + /// <seealso cref="GetBaseDir(string)"/> + /// <seealso cref="GetFile(string)"/> + /// <param name="instance">The path to a file.</param> + /// <returns>The path to the file without the extension.</returns> public static string GetBaseName(this string instance) { int index = instance.LastIndexOf('.'); @@ -75,19 +83,25 @@ namespace Godot } /// <summary> - /// Return <see langword="true"/> if the strings begins with the given string. + /// 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> public static bool BeginsWith(this string instance, string text) { return instance.StartsWith(text); } /// <summary> - /// Return the bigrams (pairs of consecutive letters) of this string. + /// Returns the bigrams (pairs of consecutive letters) of this string. /// </summary> + /// <param name="instance">The string that will be used.</param> + /// <returns>The bigrams of this string.</returns> public static string[] Bigrams(this string instance) { - var b = new string[instance.Length - 1]; + string[] b = new string[instance.Length - 1]; for (int i = 0; i < b.Length; i++) { @@ -99,8 +113,8 @@ namespace Godot /// <summary> /// Converts a string containing a binary number into an integer. - /// Binary strings can either be prefixed with `0b` or not, - /// and they can also start with a `-` before the optional prefix. + /// Binary strings can either be prefixed with <c>0b</c> or not, + /// and they can also start with a <c>-</c> before the optional prefix. /// </summary> /// <param name="instance">The string to convert.</param> /// <returns>The converted string.</returns> @@ -128,8 +142,14 @@ namespace Godot } /// <summary> - /// Return the amount of substrings in string. + /// Returns the amount of substrings <paramref name="what"/> in the string. /// </summary> + /// <param name="instance">The string where the substring will be searched.</param> + /// <param name="what">The substring that will be counted.</param> + /// <param name="caseSensitive">If the search is case sensitive.</param> + /// <param name="from">Index to start searching from.</param> + /// <param name="to">Index to stop searching at.</param> + /// <returns>Amount of substrings in the string.</returns> public static int Count(this string instance, string what, bool caseSensitive = true, int from = 0, int to = 0) { if (what.Length == 0) @@ -188,8 +208,10 @@ namespace Godot } /// <summary> - /// Return a copy of the string with special characters escaped using the C language standard. + /// Returns a copy of the string with special characters escaped using the C language standard. /// </summary> + /// <param name="instance">The string to escape.</param> + /// <returns>The escaped string.</returns> public static string CEscape(this string instance) { var sb = new StringBuilder(string.Copy(instance)); @@ -210,9 +232,11 @@ namespace Godot } /// <summary> - /// Return a copy of the string with escaped characters replaced by their meanings + /// Returns a copy of the string with escaped characters replaced by their meanings /// according to the C language standard. /// </summary> + /// <param name="instance">The string to unescape.</param> + /// <returns>The unescaped string.</returns> public static string CUnescape(this string instance) { var sb = new StringBuilder(string.Copy(instance)); @@ -233,15 +257,17 @@ namespace Godot } /// <summary> - /// Change the case of some letters. Replace underscores with spaces, convert all letters + /// Changes the case of some letters. Replace underscores with spaces, convert all letters /// to lowercase then capitalize first and every letter following the space character. /// For <c>capitalize camelCase mixed_with_underscores</c> it will return /// <c>Capitalize Camelcase Mixed With Underscores</c>. /// </summary> + /// <param name="instance">The string to capitalize.</param> + /// <returns>The capitalized string.</returns> public static string Capitalize(this string instance) { string aux = instance.Replace("_", " ").ToLower(); - var cap = string.Empty; + string cap = string.Empty; for (int i = 0; i < aux.GetSliceCount(" "); i++) { @@ -259,16 +285,27 @@ namespace Godot } /// <summary> - /// Perform a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. + /// Performs a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. /// </summary> + /// <seealso cref="NocasecmpTo(string, string)"/> + /// <seealso cref="CompareTo(string, string, bool)"/> + /// <param name="instance">The string to compare.</param> + /// <param name="to">The other string to compare.</param> + /// <returns>-1 if less, 0 if equal and +1 if greater.</returns> public static int CasecmpTo(this string instance, string to) { return instance.CompareTo(to, caseSensitive: true); } /// <summary> - /// Perform a comparison to another string, return -1 if less, 0 if equal and +1 if greater. + /// Performs a comparison to another string, return -1 if less, 0 if equal and +1 if greater. /// </summary> + /// <param name="instance">The string to compare.</param> + /// <param name="to">The other string to compare.</param> + /// <param name="caseSensitive"> + /// If <see langword="true"/>, the comparison will be case sensitive. + /// </param> + /// <returns>-1 if less, 0 if equal and +1 if greater.</returns> public static int CompareTo(this string instance, string to, bool caseSensitive = true) { if (string.IsNullOrEmpty(instance)) @@ -321,8 +358,12 @@ namespace Godot } /// <summary> - /// Return <see langword="true"/> if the strings ends with the given string. + /// Returns <see langword="true"/> if the strings ends + /// with the given string <paramref name="text"/>. /// </summary> + /// <param name="instance">The string to check.</param> + /// <param name="text">The ending string.</param> + /// <returns>If the string ends with the given string.</returns> public static bool EndsWith(this string instance, string text) { return instance.EndsWith(text); @@ -331,14 +372,36 @@ namespace Godot /// <summary> /// Erase <paramref name="chars"/> characters from the string starting from <paramref name="pos"/>. /// </summary> + /// <param name="instance">The string to modify.</param> + /// <param name="pos">Starting position from which to erase.</param> + /// <param name="chars">Amount of characters to erase.</param> public static void Erase(this StringBuilder instance, int pos, int chars) { instance.Remove(pos, chars); } /// <summary> - /// If the string is a path to a file, return the extension. - /// </summary> + /// Returns the extension without the leading period character (<c>.</c>) + /// if the string is a valid file name or path. If the string does not contain + /// an extension, returns an empty string instead. + /// </summary> + /// <example> + /// <code> + /// GD.Print("/path/to/file.txt".GetExtension()) // "txt" + /// GD.Print("file.txt".GetExtension()) // "txt" + /// GD.Print("file.sample.txt".GetExtension()) // "txt" + /// GD.Print(".txt".GetExtension()) // "txt" + /// GD.Print("file.txt.".GetExtension()) // "" (empty string) + /// GD.Print("file.txt..".GetExtension()) // "" (empty string) + /// GD.Print("txt".GetExtension()) // "" (empty string) + /// GD.Print("".GetExtension()) // "" (empty string) + /// </code> + /// </example> + /// <seealso cref="GetBaseName(string)"/> + /// <seealso cref="GetBaseDir(string)"/> + /// <seealso cref="GetFile(string)"/> + /// <param name="instance">The path to a file.</param> + /// <returns>The extension of the file or an empty string.</returns> public static string GetExtension(this string instance) { int pos = instance.FindLast("."); @@ -352,6 +415,10 @@ namespace Godot /// <summary> /// Find the first occurrence of a substring. Optionally, the search starting position can be passed. /// </summary> + /// <param name="instance">The string that will be searched.</param> + /// <param name="what">The substring to find.</param> + /// <param name="from">The search starting position.</param> + /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param> /// <returns>The starting position of the substring, or -1 if not found.</returns> public static int Find(this string instance, string what, int from = 0, bool caseSensitive = true) { @@ -361,6 +428,14 @@ namespace Godot /// <summary> /// Find the first occurrence of a char. Optionally, the search starting position can be passed. /// </summary> + /// <seealso cref="Find(string, string, int, bool)"/> + /// <seealso cref="FindLast(string, string, bool)"/> + /// <seealso cref="FindLast(string, string, int, bool)"/> + /// <seealso cref="FindN(string, string, int)"/> + /// <param name="instance">The string that will be searched.</param> + /// <param name="what">The substring to find.</param> + /// <param name="from">The search starting position.</param> + /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param> /// <returns>The first instance of the char, or -1 if not found.</returns> public static int Find(this string instance, char what, int from = 0, bool caseSensitive = true) { @@ -370,6 +445,13 @@ namespace Godot } /// <summary>Find the last occurrence of a substring.</summary> + /// <seealso cref="Find(string, string, int, bool)"/> + /// <seealso cref="Find(string, char, int, bool)"/> + /// <seealso cref="FindLast(string, string, int, bool)"/> + /// <seealso cref="FindN(string, string, int)"/> + /// <param name="instance">The string that will be searched.</param> + /// <param name="what">The substring to find.</param> + /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param> /// <returns>The starting position of the substring, or -1 if not found.</returns> public static int FindLast(this string instance, string what, bool caseSensitive = true) { @@ -377,6 +459,14 @@ namespace Godot } /// <summary>Find the last occurrence of a substring specifying the search starting position.</summary> + /// <seealso cref="Find(string, string, int, bool)"/> + /// <seealso cref="Find(string, char, int, bool)"/> + /// <seealso cref="FindLast(string, string, bool)"/> + /// <seealso cref="FindN(string, string, int)"/> + /// <param name="instance">The string that will be searched.</param> + /// <param name="what">The substring to find.</param> + /// <param name="from">The search starting position.</param> + /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param> /// <returns>The starting position of the substring, or -1 if not found.</returns> public static int FindLast(this string instance, string what, int from, bool caseSensitive = true) { @@ -387,6 +477,13 @@ namespace Godot /// Find the first occurrence of a substring but search as case-insensitive. /// Optionally, the search starting position can be passed. /// </summary> + /// <seealso cref="Find(string, string, int, bool)"/> + /// <seealso cref="Find(string, char, int, bool)"/> + /// <seealso cref="FindLast(string, string, bool)"/> + /// <seealso cref="FindLast(string, string, int, bool)"/> + /// <param name="instance">The string that will be searched.</param> + /// <param name="what">The substring to find.</param> + /// <param name="from">The search starting position.</param> /// <returns>The starting position of the substring, or -1 if not found.</returns> public static int FindN(this string instance, string what, int from = 0) { @@ -396,25 +493,30 @@ namespace Godot /// <summary> /// If the string is a path to a file, return the base directory. /// </summary> + /// <seealso cref="GetBaseName(string)"/> + /// <seealso cref="GetExtension(string)"/> + /// <seealso cref="GetFile(string)"/> + /// <param name="instance">The path to a file.</param> + /// <returns>The base directory.</returns> public static string GetBaseDir(this string instance) { int basepos = instance.Find("://"); string rs; - var @base = string.Empty; + string directory = string.Empty; if (basepos != -1) { - var end = basepos + 3; + int end = basepos + 3; rs = instance.Substring(end); - @base = instance.Substring(0, end); + directory = instance.Substring(0, end); } else { if (instance.BeginsWith("/")) { rs = instance.Substring(1); - @base = "/"; + directory = "/"; } else { @@ -425,14 +527,19 @@ namespace Godot int sep = Mathf.Max(rs.FindLast("/"), rs.FindLast("\\")); if (sep == -1) - return @base; + return directory; - return @base + rs.Substr(0, sep); + return directory + rs.Substr(0, sep); } /// <summary> /// If the string is a path to a file, return the file and ignore the base directory. /// </summary> + /// <seealso cref="GetBaseName(string)"/> + /// <seealso cref="GetExtension(string)"/> + /// <seealso cref="GetBaseDir(string)"/> + /// <param name="instance">The path to a file.</param> + /// <returns>The file name.</returns> public static string GetFile(this string instance) { int sep = Mathf.Max(instance.FindLast("/"), instance.FindLast("\\")); @@ -475,6 +582,8 @@ namespace Godot /// <summary> /// Hash the string and return a 32 bits unsigned integer. /// </summary> + /// <param name="instance">The string to hash.</param> + /// <returns>The calculated hash of the string.</returns> public static uint Hash(this string instance) { uint hash = 5381; @@ -494,7 +603,7 @@ namespace Godot /// <returns>The hexadecimal representation of this byte.</returns> internal static string HexEncode(this byte b) { - var ret = string.Empty; + string ret = string.Empty; for (int i = 0; i < 2; i++) { @@ -524,7 +633,7 @@ namespace Godot /// <returns>The hexadecimal representation of this byte array.</returns> public static string HexEncode(this byte[] bytes) { - var ret = string.Empty; + string ret = string.Empty; foreach (byte b in bytes) { @@ -536,8 +645,8 @@ namespace Godot /// <summary> /// Converts a string containing a hexadecimal number into an integer. - /// Hexadecimal strings can either be prefixed with `0x` or not, - /// and they can also start with a `-` before the optional prefix. + /// Hexadecimal strings can either be prefixed with <c>0x</c> or not, + /// and they can also start with a <c>-</c> before the optional prefix. /// </summary> /// <param name="instance">The string to convert.</param> /// <returns>The converted string.</returns> @@ -565,17 +674,29 @@ namespace Godot } /// <summary> - /// Insert a substring at a given position. + /// Inserts a substring at a given position. /// </summary> + /// <param name="instance">The string to modify.</param> + /// <param name="pos">Position at which to insert the substring.</param> + /// <param name="what">Substring to insert.</param> + /// <returns> + /// The string with <paramref name="what"/> inserted at the given + /// position <paramref name="pos"/>. + /// </returns> public static string Insert(this string instance, int pos, string what) { return instance.Insert(pos, what); } /// <summary> - /// If the string is a path to a file or directory, return <see langword="true"/> if the path is absolute. + /// Returns <see langword="true"/> if the string is a path to a file or + /// directory and its startign point is explicitly defined. This includes + /// <c>res://</c>, <c>user://</c>, <c>C:\</c>, <c>/</c>, etc. /// </summary> - public static bool IsAbsPath(this string instance) + /// <seealso cref="IsRelativePath(string)"/> + /// <param name="instance">The string to check.</param> + /// <returns>If the string is an absolute path.</returns> + public static bool IsAbsolutePath(this string instance) { if (string.IsNullOrEmpty(instance)) return false; @@ -586,16 +707,27 @@ namespace Godot } /// <summary> - /// If the string is a path to a file or directory, return <see langword="true"/> if the path is relative. + /// Returns <see langword="true"/> if the string is a path to a file or + /// directory and its starting point is implicitly defined within the + /// context it is being used. The starting point may refer to the current + /// directory (<c>./</c>), or the current <see cref="Node"/>. /// </summary> - public static bool IsRelPath(this string instance) + /// <seealso cref="IsAbsolutePath(string)"/> + /// <param name="instance">The string to check.</param> + /// <returns>If the string is a relative path.</returns> + public static bool IsRelativePath(this string instance) { - return !IsAbsPath(instance); + return !IsAbsolutePath(instance); } /// <summary> /// Check whether this string is a subsequence of the given string. /// </summary> + /// <seealso cref="IsSubsequenceOfI(string, string)"/> + /// <param name="instance">The subsequence to search.</param> + /// <param name="text">The string that contains the subsequence.</param> + /// <param name="caseSensitive">If <see langword="true"/>, the check is case sensitive.</param> + /// <returns>If the string is a subsequence of the given string.</returns> public static bool IsSubsequenceOf(this string instance, string text, bool caseSensitive = true) { int len = instance.Length; @@ -639,6 +771,10 @@ namespace Godot /// <summary> /// Check whether this string is a subsequence of the given string, ignoring case differences. /// </summary> + /// <seealso cref="IsSubsequenceOf(string, string, bool)"/> + /// <param name="instance">The subsequence to search.</param> + /// <param name="text">The string that contains the subsequence.</param> + /// <returns>If the string is a subsequence of the given string.</returns> public static bool IsSubsequenceOfI(this string instance, string text) { return instance.IsSubsequenceOf(text, caseSensitive: false); @@ -647,6 +783,8 @@ namespace Godot /// <summary> /// Check whether the string contains a valid <see langword="float"/>. /// </summary> + /// <param name="instance">The string to check.</param> + /// <returns>If the string contains a valid floating point number.</returns> public static bool IsValidFloat(this string instance) { float f; @@ -656,6 +794,8 @@ namespace Godot /// <summary> /// Check whether the string contains a valid color in HTML notation. /// </summary> + /// <param name="instance">The string to check.</param> + /// <returns>If the string contains a valid HTML color.</returns> public static bool IsValidHtmlColor(this string instance) { return Color.HtmlIsValid(instance); @@ -666,6 +806,8 @@ namespace Godot /// programming languages, a valid identifier may contain only letters, /// digits and underscores (_) and the first character may not be a digit. /// </summary> + /// <param name="instance">The string to check.</param> + /// <returns>If the string contains a valid identifier.</returns> public static bool IsValidIdentifier(this string instance) { int len = instance.Length; @@ -673,18 +815,15 @@ namespace Godot if (len == 0) return false; + if (instance[0] >= '0' && instance[0] <= '9') + return false; // Identifiers cannot start with numbers. + for (int i = 0; i < len; i++) { - if (i == 0) - { - if (instance[0] >= '0' && instance[0] <= '9') - return false; // Don't start with number plz - } - - bool validChar = instance[i] >= '0' && - instance[i] <= '9' || instance[i] >= 'a' && - instance[i] <= 'z' || instance[i] >= 'A' && - instance[i] <= 'Z' || instance[i] == '_'; + bool validChar = instance[i] == '_' || + (instance[i] >= 'a' && instance[i] <= 'z') || + (instance[i] >= 'A' && instance[i] <= 'Z') || + (instance[i] >= '0' && instance[i] <= '9'); if (!validChar) return false; @@ -696,6 +835,8 @@ namespace Godot /// <summary> /// Check whether the string contains a valid integer. /// </summary> + /// <param name="instance">The string to check.</param> + /// <returns>If the string contains a valid integer.</returns> public static bool IsValidInteger(this string instance) { int f; @@ -705,6 +846,8 @@ namespace Godot /// <summary> /// Check whether the string contains a valid IP address. /// </summary> + /// <param name="instance">The string to check.</param> + /// <returns>If the string contains a valid IP address.</returns> public static bool IsValidIPAddress(this string instance) { // TODO: Support IPv6 addresses @@ -728,8 +871,10 @@ namespace Godot } /// <summary> - /// Return a copy of the string with special characters escaped using the JSON standard. + /// Returns a copy of the string with special characters escaped using the JSON standard. /// </summary> + /// <param name="instance">The string to escape.</param> + /// <returns>The escaped string.</returns> public static string JSONEscape(this string instance) { var sb = new StringBuilder(string.Copy(instance)); @@ -747,8 +892,12 @@ namespace Godot } /// <summary> - /// Return an amount of characters from the left of the string. + /// Returns an amount of characters from the left of the string. /// </summary> + /// <seealso cref="Right(string, int)"/> + /// <param name="instance">The original string.</param> + /// <param name="pos">The position in the string where the left side ends.</param> + /// <returns>The left side of the string from the given position.</returns> public static string Left(this string instance, int pos) { if (pos <= 0) @@ -761,8 +910,10 @@ namespace Godot } /// <summary> - /// Return the length of the string in characters. + /// Returns the length of the string in characters. /// </summary> + /// <param name="instance">The string to check.</param> + /// <returns>The length of the string.</returns> public static int Length(this string instance) { return instance.Length; @@ -771,6 +922,7 @@ namespace Godot /// <summary> /// Returns a copy of the string with characters removed from the left. /// </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> @@ -799,6 +951,12 @@ namespace Godot /// Do a simple expression match, where '*' matches zero or more /// arbitrary characters and '?' matches any single character except '.'. /// </summary> + /// <param name="instance">The string to check.</param> + /// <param name="expr">Expression to check.</param> + /// <param name="caseSensitive"> + /// If <see langword="true"/>, the check will be case sensitive. + /// </param> + /// <returns>If the expression has any matches.</returns> private static bool ExprMatch(this string instance, string expr, bool caseSensitive) { // case '\0': @@ -824,6 +982,13 @@ namespace Godot /// Do a simple case sensitive expression match, using ? and * wildcards /// (see <see cref="ExprMatch(string, string, bool)"/>). /// </summary> + /// <seealso cref="MatchN(string, string)"/> + /// <param name="instance">The string to check.</param> + /// <param name="expr">Expression to check.</param> + /// <param name="caseSensitive"> + /// If <see langword="true"/>, the check will be case sensitive. + /// </param> + /// <returns>If the expression has any matches.</returns> public static bool Match(this string instance, string expr, bool caseSensitive = true) { if (instance.Length == 0 || expr.Length == 0) @@ -836,6 +1001,10 @@ namespace Godot /// Do a simple case insensitive expression match, using ? and * wildcards /// (see <see cref="ExprMatch(string, string, bool)"/>). /// </summary> + /// <seealso cref="Match(string, string, bool)"/> + /// <param name="instance">The string to check.</param> + /// <param name="expr">Expression to check.</param> + /// <returns>If the expression has any matches.</returns> public static bool MatchN(this string instance, string expr) { if (instance.Length == 0 || expr.Length == 0) @@ -845,46 +1014,65 @@ namespace Godot } /// <summary> - /// Return the MD5 hash of the string as an array of bytes. + /// Returns the MD5 hash of the string as an array of bytes. /// </summary> + /// <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) { return godot_icall_String_md5_buffer(instance); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_String_md5_buffer(string str); + internal static extern byte[] godot_icall_String_md5_buffer(string str); /// <summary> - /// Return the MD5 hash of the string as a string. + /// Returns the MD5 hash of the string as a string. /// </summary> + /// <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) { return godot_icall_String_md5_text(instance); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_String_md5_text(string str); + internal static extern string godot_icall_String_md5_text(string str); /// <summary> /// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. /// </summary> + /// <seealso cref="CasecmpTo(string, string)"/> + /// <seealso cref="CompareTo(string, string, bool)"/> + /// <param name="instance">The string to compare.</param> + /// <param name="to">The other string to compare.</param> + /// <returns>-1 if less, 0 if equal and +1 if greater.</returns> public static int NocasecmpTo(this string instance, string to) { return instance.CompareTo(to, caseSensitive: false); } /// <summary> - /// Return the character code at position <paramref name="at"/>. + /// Returns the character code at position <paramref name="at"/>. /// </summary> + /// <param name="instance">The string to check.</param> + /// <param name="at">The position int the string for the character to check.</param> + /// <returns>The character code.</returns> public static int OrdAt(this string instance, int at) { return instance[at]; } /// <summary> - /// Format a number to have an exact number of <paramref name="digits"/> after the decimal point. + /// Format a number to have an exact number of <paramref name="digits"/> + /// after the decimal point. /// </summary> + /// <seealso cref="PadZeros(string, int)"/> + /// <param name="instance">The string to pad.</param> + /// <param name="digits">Amount of digits after the decimal point.</param> + /// <returns>The string padded with zeroes.</returns> public static string PadDecimals(this string instance, int digits) { int c = instance.Find("."); @@ -919,8 +1107,13 @@ namespace Godot } /// <summary> - /// Format a number to have an exact number of <paramref name="digits"/> before the decimal point. + /// Format a number to have an exact number of <paramref name="digits"/> + /// before the decimal point. /// </summary> + /// <seealso cref="PadDecimals(string, int)"/> + /// <param name="instance">The string to pad.</param> + /// <param name="digits">Amount of digits before the decimal point.</param> + /// <returns>The string padded with zeroes.</returns> public static string PadZeros(this string instance, int digits) { string s = instance; @@ -952,9 +1145,13 @@ namespace Godot } /// <summary> - /// If the string is a path, this concatenates <paramref name="file"/> at the end of the string as a subpath. + /// If the string is a path, this concatenates <paramref name="file"/> + /// at the end of the string as a subpath. /// E.g. <c>"this/is".PlusFile("path") == "this/is/path"</c>. /// </summary> + /// <param name="instance">The path that will be concatenated.</param> + /// <param name="file">File name to concatenate with the path.</param> + /// <returns>The concatenated path with the given file name.</returns> public static string PlusFile(this string instance, string file) { if (instance.Length > 0 && instance[instance.Length - 1] == '/') @@ -965,6 +1162,11 @@ namespace Godot /// <summary> /// Replace occurrences of a substring for different ones inside the string. /// </summary> + /// <seealso cref="ReplaceN(string, string, string)"/> + /// <param name="instance">The string to modify.</param> + /// <param name="what">The substring to be replaced in the string.</param> + /// <param name="forwhat">The substring that replaces <paramref name="what"/>.</param> + /// <returns>The string with the substring occurrences replaced.</returns> public static string Replace(this string instance, string what, string forwhat) { return instance.Replace(what, forwhat); @@ -973,6 +1175,11 @@ namespace Godot /// <summary> /// Replace occurrences of a substring for different ones inside the string, but search case-insensitive. /// </summary> + /// <seealso cref="Replace(string, string, string)"/> + /// <param name="instance">The string to modify.</param> + /// <param name="what">The substring to be replaced in the string.</param> + /// <param name="forwhat">The substring that replaces <paramref name="what"/>.</param> + /// <returns>The string with the substring occurrences replaced.</returns> public static string ReplaceN(this string instance, string what, string forwhat) { return Regex.Replace(instance, what, forwhat, RegexOptions.IgnoreCase); @@ -981,29 +1188,43 @@ namespace Godot /// <summary> /// Perform a search for a substring, but start from the end of the string instead of the beginning. /// </summary> + /// <seealso cref="RFindN(string, string, int)"/> + /// <param name="instance">The string that will be searched.</param> + /// <param name="what">The substring to search in the string.</param> + /// <param name="from">The position at which to start searching.</param> + /// <returns>The position at which the substring was found, or -1 if not found.</returns> public static int RFind(this string instance, string what, int from = -1) { return godot_icall_String_rfind(instance, what, from); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_String_rfind(string str, string what, int from); + internal static extern int godot_icall_String_rfind(string str, string what, int from); /// <summary> /// Perform a search for a substring, but start from the end of the string instead of the beginning. /// Also search case-insensitive. /// </summary> + /// <seealso cref="RFind(string, string, int)"/> + /// <param name="instance">The string that will be searched.</param> + /// <param name="what">The substring to search in the string.</param> + /// <param name="from">The position at which to start searching.</param> + /// <returns>The position at which the substring was found, or -1 if not found.</returns> public static int RFindN(this string instance, string what, int from = -1) { return godot_icall_String_rfindn(instance, what, from); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static int godot_icall_String_rfindn(string str, string what, int from); + internal static extern int godot_icall_String_rfindn(string str, string what, int from); /// <summary> - /// Return the right side of the string from a given position. + /// Returns the right side of the string from a given position. /// </summary> + /// <seealso cref="Left(string, int)"/> + /// <param name="instance">The original string.</param> + /// <param name="pos">The position in the string from which the right side starts.</param> + /// <returns>The right side of the string from the given position.</returns> public static string Right(this string instance, int pos) { if (pos >= instance.Length) @@ -1018,6 +1239,7 @@ namespace Godot /// <summary> /// Returns a copy of the string with characters removed from the right. /// </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> @@ -1042,29 +1264,41 @@ namespace Godot return instance.Substr(0, end + 1); } + /// <summary> + /// Returns the SHA-256 hash of the string as an array of bytes. + /// </summary> + /// <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) { return godot_icall_String_sha256_buffer(instance); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_String_sha256_buffer(string str); + internal static extern byte[] godot_icall_String_sha256_buffer(string str); /// <summary> - /// Return the SHA-256 hash of the string as a string. + /// Returns the SHA-256 hash of the string as a string. /// </summary> + /// <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) { return godot_icall_String_sha256_text(instance); } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static string godot_icall_String_sha256_text(string str); + internal static extern string godot_icall_String_sha256_text(string str); /// <summary> - /// Return the similarity index of the text compared to this string. + /// Returns the similarity index of the text compared to this string. /// 1 means totally similar and 0 means totally dissimilar. /// </summary> + /// <param name="instance">The string to compare.</param> + /// <param name="text">The other string to compare.</param> + /// <returns>The similarity index.</returns> public static float Similarity(this string instance, string text) { if (instance == text) @@ -1103,9 +1337,27 @@ namespace Godot } /// <summary> + /// Returns a simplified canonical path. + /// </summary> + public static string SimplifyPath(this string instance) + { + return godot_icall_String_simplify_path(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_String_simplify_path(string str); + + /// <summary> /// Split the string by a divisor string, return an array of the substrings. /// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". /// </summary> + /// <seealso cref="SplitFloats(string, string, bool)"/> + /// <param name="instance">The string to split.</param> + /// <param name="divisor">The divisor string that splits the string.</param> + /// <param name="allowEmpty"> + /// If <see langword="true"/>, the array may include empty strings. + /// </param> + /// <returns>The array of strings split from the string.</returns> public static string[] Split(this string instance, string divisor, bool allowEmpty = true) { return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries); @@ -1115,6 +1367,13 @@ namespace Godot /// Split the string in floats by using a divisor string, return an array of the substrings. /// Example "1,2.5,3" will return [1,2.5,3] if split by ",". /// </summary> + /// <seealso cref="Split(string, string, bool)"/> + /// <param name="instance">The string to split.</param> + /// <param name="divisor">The divisor string that splits the string.</param> + /// <param name="allowEmpty"> + /// If <see langword="true"/>, the array may include empty floats. + /// </param> + /// <returns>The array of floats split from the string.</returns> public static float[] SplitFloats(this string instance, string divisor, bool allowEmpty = true) { var ret = new List<float>(); @@ -1137,7 +1396,8 @@ namespace Godot return ret.ToArray(); } - private static readonly char[] _nonPrintable = { + private static readonly char[] _nonPrintable = + { (char)00, (char)01, (char)02, (char)03, (char)04, (char)05, (char)06, (char)07, (char)08, (char)09, (char)10, (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, @@ -1147,9 +1407,13 @@ namespace Godot }; /// <summary> - /// Return a copy of the string stripped of any non-printable character at the beginning and the end. + /// Returns a copy of the string stripped of any non-printable character at the beginning and the end. /// The optional arguments are used to toggle stripping on the left and right edges respectively. /// </summary> + /// <param name="instance">The string to strip.</param> + /// <param name="left">If the left side should be stripped.</param> + /// <param name="right">If the right side should be stripped.</param> + /// <returns>The string stripped of any non-printable characters.</returns> public static string StripEdges(this string instance, bool left = true, bool right = true) { if (left) @@ -1163,8 +1427,14 @@ namespace Godot } /// <summary> - /// Return part of the string from the position <paramref name="from"/>, with length <paramref name="len"/>. + /// Returns part of the string from the position <paramref name="from"/>, with length <paramref name="len"/>. /// </summary> + /// <param name="instance">The string to slice.</param> + /// <param name="from">The position in the string that the part starts from.</param> + /// <param name="len">The length of the returned part.</param> + /// <returns> + /// Part of the string from the position <paramref name="from"/>, with length <paramref name="len"/>. + /// </returns> public static string Substr(this string instance, int from, int len) { int max = instance.Length - from; @@ -1172,52 +1442,70 @@ namespace Godot } /// <summary> - /// Convert the String (which is a character array) to PackedByteArray (which is an array of bytes). + /// Converts the String (which is a character array) to PackedByteArray (which is an array of bytes). /// The conversion is speeded up in comparison to <see cref="ToUTF8(string)"/> with the assumption /// that all the characters the String contains are only ASCII characters. /// </summary> + /// <seealso cref="ToUTF8(string)"/> + /// <param name="instance">The string to convert.</param> + /// <returns>The string as ASCII encoded bytes.</returns> public static byte[] ToAscii(this string instance) { return Encoding.ASCII.GetBytes(instance); } /// <summary> - /// Convert a string, containing a decimal number, into a <see langword="float" />. + /// Converts a string, containing a decimal number, into a <see langword="float" />. /// </summary> + /// <seealso cref="ToInt(string)"/> + /// <param name="instance">The string to convert.</param> + /// <returns>The number representation of the string.</returns> public static float ToFloat(this string instance) { return float.Parse(instance); } /// <summary> - /// Convert a string, containing an integer number, into an <see langword="int" />. + /// Converts a string, containing an integer number, into an <see langword="int" />. /// </summary> + /// <seealso cref="ToFloat(string)"/> + /// <param name="instance">The string to convert.</param> + /// <returns>The number representation of the string.</returns> public static int ToInt(this string instance) { return int.Parse(instance); } /// <summary> - /// Return the string converted to lowercase. + /// Returns the string converted to lowercase. /// </summary> + /// <seealso cref="ToUpper(string)"/> + /// <param name="instance">The string to convert.</param> + /// <returns>The string converted to lowercase.</returns> public static string ToLower(this string instance) { return instance.ToLower(); } /// <summary> - /// Return the string converted to uppercase. + /// Returns the string converted to uppercase. /// </summary> + /// <seealso cref="ToLower(string)"/> + /// <param name="instance">The string to convert.</param> + /// <returns>The string converted to uppercase.</returns> public static string ToUpper(this string instance) { return instance.ToUpper(); } /// <summary> - /// Convert the String (which is an array of characters) to PackedByteArray (which is an array of bytes). + /// Converts the String (which is an array of characters) to PackedByteArray (which is an array of bytes). /// The conversion is a bit slower than <see cref="ToAscii(string)"/>, but supports all UTF-8 characters. /// Therefore, you should prefer this function over <see cref="ToAscii(string)"/>. /// </summary> + /// <seealso cref="ToAscii(string)"/> + /// <param name="instance">The string to convert.</param> + /// <returns>The string as UTF-8 encoded bytes.</returns> public static byte[] ToUTF8(this string instance) { return Encoding.UTF8.GetBytes(instance); @@ -1226,8 +1514,8 @@ namespace Godot /// <summary> /// Decodes a string in URL encoded format. This is meant to /// decode parameters in a URL when receiving an HTTP request. - /// This mostly wraps around `System.Uri.UnescapeDataString()`, - /// but also handles `+`. + /// This mostly wraps around <see cref="Uri.UnescapeDataString"/>, + /// but also handles <c>+</c>. /// See <see cref="URIEncode"/> for encoding. /// </summary> /// <param name="instance">The string to decode.</param> @@ -1240,7 +1528,7 @@ namespace Godot /// <summary> /// Encodes a string to URL friendly format. This is meant to /// encode parameters in a URL when sending an HTTP request. - /// This wraps around `System.Uri.EscapeDataString()`. + /// This wraps around <see cref="Uri.EscapeDataString"/>. /// See <see cref="URIDecode"/> for decoding. /// </summary> /// <param name="instance">The string to encode.</param> @@ -1251,17 +1539,23 @@ namespace Godot } /// <summary> - /// Return a copy of the string with special characters escaped using the XML standard. + /// Returns a copy of the string with special characters escaped using the XML standard. /// </summary> + /// <seealso cref="XMLUnescape(string)"/> + /// <param name="instance">The string to escape.</param> + /// <returns>The escaped string.</returns> public static string XMLEscape(this string instance) { return SecurityElement.Escape(instance); } /// <summary> - /// Return a copy of the string with escaped characters replaced by their meanings + /// Returns a copy of the string with escaped characters replaced by their meanings /// according to the XML standard. /// </summary> + /// <seealso cref="XMLEscape(string)"/> + /// <param name="instance">The string to unescape.</param> + /// <returns>The unescaped string.</returns> public static string XMLUnescape(this string instance) { return SecurityElement.FromString(instance).Text; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index 7700b6d4ed..b1d504410b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -3,6 +3,13 @@ using System.Runtime.CompilerServices; namespace Godot { + /// <summary> + /// StringNames are immutable strings designed for general-purpose representation of unique names. + /// StringName ensures that only one instance of a given name exists (so two StringNames with the + /// same value are the same object). + /// Comparing them is much faster than with regular strings, because only the pointers are compared, + /// not the whole strings. + /// </summary> public sealed partial class StringName : IDisposable { private IntPtr ptr; @@ -23,6 +30,9 @@ namespace Godot Dispose(false); } + /// <summary> + /// Disposes of this <see cref="StringName"/>. + /// </summary> public void Dispose() { Dispose(true); @@ -43,25 +53,48 @@ namespace Godot this.ptr = ptr; } + /// <summary> + /// Constructs an empty <see cref="StringName"/>. + /// </summary> public StringName() { ptr = IntPtr.Zero; } + /// <summary> + /// Constructs a <see cref="StringName"/> from the given <paramref name="path"/> string. + /// </summary> + /// <param name="path">String to construct the <see cref="StringName"/> from.</param> public StringName(string path) { ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path); } + /// <summary> + /// Converts a string to a <see cref="StringName"/>. + /// </summary> + /// <param name="from">The string to convert.</param> public static implicit operator StringName(string from) => new StringName(from); + /// <summary> + /// Converts a <see cref="StringName"/> to a string. + /// </summary> + /// <param name="from">The <see cref="StringName"/> to convert.</param> public static implicit operator string(StringName from) => from.ToString(); + /// <summary> + /// Converts this <see cref="StringName"/> to a string. + /// </summary> + /// <returns>A string representation of this <see cref="StringName"/>.</returns> public override string ToString() { return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this)); } + /// <summary> + /// Check whether this <see cref="StringName"/> is empty. + /// </summary> + /// <returns>If the <see cref="StringName"/> is empty.</returns> public bool IsEmpty() { return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 62a6fe6959..daea09ba72 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -21,18 +21,18 @@ namespace Godot public struct Transform2D : IEquatable<Transform2D> { /// <summary> - /// The basis matrix's X vector (column 0). Equivalent to array index `[0]`. + /// The basis matrix's X vector (column 0). Equivalent to array index <c>[0]</c>. /// </summary> /// <value></value> public Vector2 x; /// <summary> - /// The basis matrix's Y vector (column 1). Equivalent to array index `[1]`. + /// The basis matrix's Y vector (column 1). Equivalent to array index <c>[1]</c>. /// </summary> public Vector2 y; /// <summary> - /// The origin vector (column 2, the third column). Equivalent to array index `[2]`. + /// The origin vector (column 2, the third column). Equivalent to array index <c>[2]</c>. /// The origin vector represents translation. /// </summary> public Vector2 origin; @@ -77,7 +77,8 @@ namespace Godot } /// <summary> - /// Access whole columns in the form of Vector2. The third column is the origin vector. + /// Access whole columns in the form of <see cref="Vector2"/>. + /// The third column is the <see cref="origin"/> vector. /// </summary> /// <param name="column">Which column vector.</param> public Vector2 this[int column] @@ -116,7 +117,8 @@ namespace Godot } /// <summary> - /// Access matrix elements in column-major order. The third column is the origin vector. + /// Access matrix elements in column-major order. + /// 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> @@ -138,6 +140,7 @@ namespace Godot /// Returns the inverse of the transform, under the assumption that /// the transformation is composed of rotation, scaling, and translation. /// </summary> + /// <seealso cref="Inverse"/> /// <returns>The inverse transformation matrix.</returns> public Transform2D AffineInverse() { @@ -146,7 +149,7 @@ namespace Godot if (det == 0) throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); - var inv = this; + Transform2D inv = this; real_t temp = inv[0, 0]; inv[0, 0] = inv[1, 1]; @@ -173,13 +176,14 @@ namespace Godot /// <returns>The determinant of the basis matrix.</returns> private 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 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> /// <returns>The transformed vector.</returns> public Vector2 BasisXform(Vector2 v) @@ -189,11 +193,12 @@ namespace Godot /// <summary> /// Returns a vector transformed (multiplied) by the inverse basis matrix. - /// This method does not account for translation (the 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. /// </summary> + /// <seealso cref="BasisXform(Vector2)"/> /// <param name="v">A vector to inversely transform.</param> /// <returns>The inversely transformed vector.</returns> public Vector2 BasisXformInv(Vector2 v) @@ -202,7 +207,7 @@ namespace Godot } /// <summary> - /// Interpolates this transform to the other `transform` by `weight`. + /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>. /// </summary> /// <param name="transform">The other transform.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> @@ -233,8 +238,8 @@ namespace Godot else { real_t angle = weight * Mathf.Acos(dot); - Vector2 v3 = (v2 - v1 * dot).Normalized(); - v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle); + Vector2 v3 = (v2 - (v1 * dot)).Normalized(); + v = (v1 * Mathf.Cos(angle)) + (v3 * Mathf.Sin(angle)); } // Extract parameters @@ -258,7 +263,7 @@ namespace Godot /// <returns>The inverse matrix.</returns> public Transform2D Inverse() { - var inv = this; + Transform2D inv = this; // Swap real_t temp = inv.x.y; @@ -277,13 +282,13 @@ namespace Godot /// <returns>The orthonormalized transform.</returns> public Transform2D Orthonormalized() { - var on = this; + Transform2D on = this; Vector2 onX = on.x; Vector2 onY = on.y; onX.Normalize(); - onY = onY - onX * onX.Dot(onY); + onY = onY - (onX * onX.Dot(onY)); onY.Normalize(); on.x = onX; @@ -293,7 +298,7 @@ namespace Godot } /// <summary> - /// Rotates the transform by `phi` (in radians), using matrix multiplication. + /// Rotates the transform by <paramref name="phi"/> (in radians), using matrix multiplication. /// </summary> /// <param name="phi">The angle to rotate, in radians.</param> /// <returns>The rotated transformation matrix.</returns> @@ -309,7 +314,7 @@ namespace Godot /// <returns>The scaled transformation matrix.</returns> public Transform2D Scaled(Vector2 scale) { - var copy = this; + Transform2D copy = this; copy.x *= scale; copy.y *= scale; copy.origin *= scale; @@ -326,16 +331,16 @@ namespace Godot private real_t Tdotx(Vector2 with) { - return this[0, 0] * with[0] + this[1, 0] * with[1]; + return (this[0, 0] * with[0]) + (this[1, 0] * with[1]); } private real_t Tdoty(Vector2 with) { - return this[0, 1] * with[0] + this[1, 1] * with[1]; + return (this[0, 1] * with[0]) + (this[1, 1] * with[1]); } /// <summary> - /// Translates the transform by the given `offset`, + /// Translates the transform by the given <paramref name="offset"/>, /// relative to the transform's basis vectors. /// /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>, @@ -345,7 +350,7 @@ namespace Godot /// <returns>The translated matrix.</returns> public Transform2D Translated(Vector2 offset) { - var copy = this; + Transform2D copy = this; copy.origin += copy.BasisXform(offset); return copy; } @@ -353,6 +358,7 @@ namespace Godot /// <summary> /// Returns a vector transformed (multiplied) by this transformation matrix. /// </summary> + /// <seealso cref="XformInv(Vector2)"/> /// <param name="v">A vector to transform.</param> /// <returns>The transformed vector.</returns> public Vector2 Xform(Vector2 v) @@ -363,6 +369,7 @@ namespace Godot /// <summary> /// Returns a vector transformed (multiplied) by the inverse transformation matrix. /// </summary> + /// <seealso cref="Xform(Vector2)"/> /// <param name="v">A vector to inversely transform.</param> /// <returns>The inversely transformed vector.</returns> public Vector2 XformInv(Vector2 v) @@ -378,20 +385,20 @@ namespace Godot /// <summary> /// The identity transform, with no translation, rotation, or scaling applied. - /// This is used as a replacement for `Transform2D()` in GDScript. - /// Do not use `new Transform2D()` with no arguments in C#, because it sets all values to zero. + /// This is used as a replacement for <c>Transform2D()</c> in GDScript. + /// Do not use <c>new Transform2D()</c> with no arguments in C#, because it sets all values to zero. /// </summary> - /// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)`.</value> + /// <value>Equivalent to <c>new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)</c>.</value> public static Transform2D Identity { get { return _identity; } } /// <summary> /// The transform that will flip something along the X axis. /// </summary> - /// <value>Equivalent to `new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)`.</value> + /// <value>Equivalent to <c>new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)</c>.</value> public static Transform2D FlipX { get { return _flipX; } } /// <summary> /// The transform that will flip something along the Y axis. /// </summary> - /// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)`.</value> + /// <value>Equivalent to <c>new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)</c>.</value> public static Transform2D FlipY { get { return _flipY; } } /// <summary> @@ -411,12 +418,12 @@ namespace Godot /// Constructs a transformation matrix from the given components. /// Arguments are named such that xy is equal to calling x.y /// </summary> - /// <param name="xx">The X component of the X column vector, accessed via `t.x.x` or `[0][0]`</param> - /// <param name="xy">The Y component of the X column vector, accessed via `t.x.y` or `[0][1]`</param> - /// <param name="yx">The X component of the Y column vector, accessed via `t.y.x` or `[1][0]`</param> - /// <param name="yy">The Y component of the Y column vector, accessed via `t.y.y` or `[1][1]`</param> - /// <param name="ox">The X component of the origin vector, accessed via `t.origin.x` or `[2][0]`</param> - /// <param name="oy">The Y component of the origin vector, accessed via `t.origin.y` or `[2][1]`</param> + /// <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); @@ -425,16 +432,17 @@ namespace Godot } /// <summary> - /// Constructs a transformation matrix from a rotation value and origin vector. + /// Constructs a transformation matrix from a <paramref name="rotation"/> value and + /// <paramref name="origin"/> vector. /// </summary> - /// <param name="rot">The rotation of the new transform, in radians.</param> - /// <param name="pos">The origin vector, or column index 2.</param> - public Transform2D(real_t rot, Vector2 pos) + /// <param name="rotation">The rotation of the new transform, in radians.</param> + /// <param name="origin">The origin vector, or column index 2.</param> + public Transform2D(real_t rotation, Vector2 origin) { - x.x = y.y = Mathf.Cos(rot); - x.y = y.x = Mathf.Sin(rot); + x.x = y.y = Mathf.Cos(rotation); + x.y = y.x = Mathf.Sin(rotation); y.x *= -1; - origin = pos; + this.origin = origin; } public static Transform2D operator *(Transform2D left, Transform2D right) @@ -464,19 +472,29 @@ namespace Godot return !left.Equals(right); } + /// <summary> + /// Returns <see langword="true"/> if this transform and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the transform and the other object are equal.</returns> public override bool Equals(object obj) { return obj is Transform2D transform2D && Equals(transform2D); } + /// <summary> + /// Returns <see langword="true"/> if this transform and <paramref name="other"/> are equal. + /// </summary> + /// <param name="other">The other transform to compare.</param> + /// <returns>Whether or not the matrices are equal.</returns> public bool Equals(Transform2D other) { return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin); } /// <summary> - /// Returns true if this transform and `other` are approximately equal, by running - /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component. + /// Returns <see langword="true"/> if this transform and <paramref name="other"/> are approximately equal, + /// by running <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component. /// </summary> /// <param name="other">The other transform to compare.</param> /// <returns>Whether or not the matrices are approximately equal.</returns> @@ -485,16 +503,28 @@ namespace Godot return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin); } + /// <summary> + /// Serves as the hash function for <see cref="Transform2D"/>. + /// </summary> + /// <returns>A hash code for this transform.</returns> public override int GetHashCode() { return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Transform2D"/> to a string. + /// </summary> + /// <returns>A string representation of this transform.</returns> public override string ToString() { return $"[X: {x}, Y: {y}, O: {origin}]"; } + /// <summary> + /// Converts this <see cref="Transform2D"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this transform.</returns> public string ToString(string 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 afc6a65a45..7176cd60dc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -28,12 +28,13 @@ namespace Godot public Basis basis; /// <summary> - /// The origin vector (column 3, the fourth column). Equivalent to array index `[3]`. + /// The origin vector (column 3, the fourth column). Equivalent to array index <c>[3]</c>. /// </summary> public Vector3 origin; /// <summary> - /// Access whole columns in the form of Vector3. The fourth column is the origin vector. + /// Access whole columns in the form of <see cref="Vector3"/>. + /// The fourth column is the <see cref="origin"/> vector. /// </summary> /// <param name="column">Which column vector.</param> public Vector3 this[int column] @@ -77,7 +78,8 @@ namespace Godot } /// <summary> - /// Access matrix elements in column-major order. The fourth column is the origin vector. + /// Access matrix elements in column-major order. + /// 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> @@ -106,6 +108,7 @@ namespace Godot /// Returns the inverse of the transform, under the assumption that /// the transformation is composed of rotation, scaling, and translation. /// </summary> + /// <seealso cref="Inverse"/> /// <returns>The inverse transformation matrix.</returns> public Transform3D AffineInverse() { @@ -114,7 +117,7 @@ namespace Godot } /// <summary> - /// Interpolates this transform to the other `transform` by `weight`. + /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>. /// </summary> /// <param name="transform">The other transform.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> @@ -132,7 +135,9 @@ namespace Godot Vector3 destinationLocation = transform.origin; var interpolated = new Transform3D(); - interpolated.basis.SetQuaternionScale(sourceRotation.Slerp(destinationRotation, weight).Normalized(), sourceScale.Lerp(destinationScale, weight)); + Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized(); + Vector3 scale = sourceScale.Lerp(destinationScale, weight); + interpolated.basis.SetQuaternionScale(quaternion, scale); interpolated.origin = sourceLocation.Lerp(destinationLocation, weight); return interpolated; @@ -152,11 +157,11 @@ namespace Godot /// <summary> /// Returns a copy of the transform rotated such that its - /// -Z axis (forward) points towards the target position. + /// -Z axis (forward) points towards the <paramref name="target"/> position. /// - /// The transform will first be rotated around the given up vector, - /// and then fully aligned to the target by a further rotation around - /// an axis perpendicular to both the target and up vectors. + /// The transform will first be rotated around the given <paramref name="up"/> vector, + /// and then fully aligned to the <paramref name="target"/> by a further rotation around + /// an axis perpendicular to both the <paramref name="target"/> and <paramref name="up"/> vectors. /// /// Operations take place in global space. /// </summary> @@ -165,7 +170,7 @@ namespace Godot /// <returns>The resulting transform.</returns> public Transform3D LookingAt(Vector3 target, Vector3 up) { - var t = this; + Transform3D t = this; t.SetLookAt(origin, target, up); return t; } @@ -181,7 +186,7 @@ namespace Godot } /// <summary> - /// Rotates the transform around the given `axis` by `phi` (in radians), + /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="phi"/> (in radians), /// using matrix multiplication. The axis must be a normalized vector. /// </summary> /// <param name="axis">The axis to rotate around. Must be normalized.</param> @@ -226,7 +231,7 @@ namespace Godot } /// <summary> - /// Translates the transform by the given `offset`, + /// Translates the transform by the given <paramref name="offset"/>, /// relative to the transform's basis vectors. /// /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>, @@ -247,6 +252,7 @@ namespace Godot /// <summary> /// Returns a vector transformed (multiplied) by this transformation matrix. /// </summary> + /// <seealso cref="XformInv(Vector3)"/> /// <param name="v">A vector to transform.</param> /// <returns>The transformed vector.</returns> public Vector3 Xform(Vector3 v) @@ -265,6 +271,7 @@ namespace Godot /// Note: This results in a multiplication by the inverse of the /// transformation matrix only if it represents a rotation-reflection. /// </summary> + /// <seealso cref="Xform(Vector3)"/> /// <param name="v">A vector to inversely transform.</param> /// <returns>The inversely transformed vector.</returns> public Vector3 XformInv(Vector3 v) @@ -273,9 +280,9 @@ namespace Godot return new Vector3 ( - basis.Row0[0] * vInv.x + basis.Row1[0] * vInv.y + basis.Row2[0] * vInv.z, - basis.Row0[1] * vInv.x + basis.Row1[1] * vInv.y + basis.Row2[1] * vInv.z, - basis.Row0[2] * vInv.x + basis.Row1[2] * vInv.y + basis.Row2[2] * vInv.z + (basis.Row0[0] * vInv.x) + (basis.Row1[0] * vInv.y) + (basis.Row2[0] * vInv.z), + (basis.Row0[1] * vInv.x) + (basis.Row1[1] * vInv.y) + (basis.Row2[1] * vInv.z), + (basis.Row0[2] * vInv.x) + (basis.Row1[2] * vInv.y) + (basis.Row2[2] * vInv.z) ); } @@ -287,25 +294,25 @@ namespace Godot /// <summary> /// The identity transform, with no translation, rotation, or scaling applied. - /// This is used as a replacement for `Transform()` in GDScript. - /// Do not use `new Transform()` with no arguments in C#, because it sets all values to zero. + /// This is used as a replacement for <c>Transform()</c> in GDScript. + /// Do not use <c>new Transform()</c> with no arguments in C#, because it sets all values to zero. /// </summary> - /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Back, Vector3.Zero)`.</value> + /// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Up, Vector3.Back, Vector3.Zero)</c>.</value> public static Transform3D Identity { get { return _identity; } } /// <summary> /// The transform that will flip something along the X axis. /// </summary> - /// <value>Equivalent to `new Transform(Vector3.Left, Vector3.Up, Vector3.Back, Vector3.Zero)`.</value> + /// <value>Equivalent to <c>new Transform(Vector3.Left, Vector3.Up, Vector3.Back, Vector3.Zero)</c>.</value> public static Transform3D FlipX { get { return _flipX; } } /// <summary> /// The transform that will flip something along the Y axis. /// </summary> - /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Down, Vector3.Back, Vector3.Zero)`.</value> + /// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Down, Vector3.Back, Vector3.Zero)</c>.</value> public static Transform3D FlipY { get { return _flipY; } } /// <summary> /// The transform that will flip something along the Z axis. /// </summary> - /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)`.</value> + /// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)</c>.</value> public static Transform3D FlipZ { get { return _flipZ; } } /// <summary> @@ -322,9 +329,10 @@ namespace Godot } /// <summary> - /// Constructs a transformation matrix from the given quaternion and origin vector. + /// Constructs a transformation matrix from the given <paramref name="quaternion"/> + /// and <paramref name="origin"/> vector. /// </summary> - /// <param name="quaternion">The <see cref="Godot.Quaternion"/> to create the basis from.</param> + /// <param name="quaternion">The <see cref="Quaternion"/> to create the basis from.</param> /// <param name="origin">The origin vector, or column index 3.</param> public Transform3D(Quaternion quaternion, Vector3 origin) { @@ -333,9 +341,10 @@ namespace Godot } /// <summary> - /// Constructs a transformation matrix from the given basis and origin vector. + /// Constructs a transformation matrix from the given <paramref name="basis"/> and + /// <paramref name="origin"/> vector. /// </summary> - /// <param name="basis">The <see cref="Godot.Basis"/> to create the basis from.</param> + /// <param name="basis">The <see cref="Basis"/> to create the basis from.</param> /// <param name="origin">The origin vector, or column index 3.</param> public Transform3D(Basis basis, Vector3 origin) { @@ -360,6 +369,11 @@ namespace Godot return !left.Equals(right); } + /// <summary> + /// Returns <see langword="true"/> if this transform and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the transform and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Transform3D) @@ -370,14 +384,19 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this transform and <paramref name="other"/> are equal. + /// </summary> + /// <param name="other">The other transform to compare.</param> + /// <returns>Whether or not the matrices are equal.</returns> public bool Equals(Transform3D other) { return basis.Equals(other.basis) && origin.Equals(other.origin); } /// <summary> - /// Returns true if this transform and `other` are approximately equal, by running - /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component. + /// Returns <see langword="true"/> if this transform and <paramref name="other"/> are approximately equal, + /// by running <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component. /// </summary> /// <param name="other">The other transform to compare.</param> /// <returns>Whether or not the matrices are approximately equal.</returns> @@ -386,16 +405,28 @@ namespace Godot return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin); } + /// <summary> + /// Serves as the hash function for <see cref="Transform3D"/>. + /// </summary> + /// <returns>A hash code for this transform.</returns> public override int GetHashCode() { return basis.GetHashCode() ^ origin.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Transform3D"/> to a string. + /// </summary> + /// <returns>A string representation of this transform.</returns> public override string ToString() { return $"[X: {basis.x}, Y: {basis.y}, Z: {basis.z}, O: {origin}]"; } + /// <summary> + /// Converts this <see cref="Transform3D"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this transform.</returns> public string ToString(string 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/UnhandledExceptionArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs index be01674568..eae8927ceb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs @@ -8,7 +8,7 @@ namespace Godot public class UnhandledExceptionArgs { /// <summary> - /// Exception object + /// Exception object. /// </summary> public Exception Exception { get; private set; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 8bb5e90a68..fe70d71cce 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -21,23 +21,36 @@ namespace Godot /// </summary> public enum Axis { + /// <summary> + /// The vector's X axis. + /// </summary> X = 0, + /// <summary> + /// The vector's Y axis. + /// </summary> Y } /// <summary> - /// The vector's X component. Also accessible by using the index position `[0]`. + /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> public real_t x; + /// <summary> - /// The vector's Y component. Also accessible by using the index position `[1]`. + /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> public real_t y; /// <summary> /// Access vector components using their index. /// </summary> - /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`.</value> + /// <exception cref="IndexOutOfRangeException"> + /// Thrown when the given the <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"/>. + /// </value> public real_t this[int index] { get @@ -97,7 +110,7 @@ namespace Godot /// 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 `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`. + /// 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 real_t Angle() @@ -126,9 +139,9 @@ namespace Godot } /// <summary> - /// Returns the aspect ratio of this vector, the ratio of `x` to `y`. + /// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>. /// </summary> - /// <returns>The `x` component divided by the `y` component.</returns> + /// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns> public real_t Aspect() { return x / y; @@ -155,7 +168,7 @@ namespace Godot /// <summary> /// Returns a new vector with all components clamped between the - /// components of `min` and `max` using + /// components of <paramref name="min"/> and <paramref name="max"/> using /// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>. /// </summary> /// <param name="min">The vector with minimum allowed values.</param> @@ -171,21 +184,22 @@ namespace Godot } /// <summary> - /// Returns the cross product of this vector and `b`. + /// Returns the cross product of this vector and <paramref name="b"/>. /// </summary> /// <param name="b">The other vector.</param> /// <returns>The cross product value.</returns> public real_t Cross(Vector2 b) { - return x * b.y - y * b.x; + return (x * b.y) - (y * b.x); } /// <summary> - /// Performs a cubic interpolation between vectors `preA`, this vector, `b`, and `postB`, by the given amount `t`. + /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector, + /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>. /// </summary> /// <param name="b">The destination vector.</param> /// <param name="preA">A vector before this vector.</param> - /// <param name="postB">A vector after `b`.</param> + /// <param name="postB">A vector after <paramref name="b"/>.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated vector.</returns> public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t weight) @@ -199,24 +213,26 @@ namespace Godot real_t t2 = t * t; real_t t3 = t2 * t; - return 0.5f * (p1 * 2.0f + - (-p0 + p2) * t + - (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + - (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + return 0.5f * ( + (p1 * 2.0f) + + ((-p0 + p2) * t) + + (((2.0f * p0) - (5.0f * p1) + (4 * p2) - p3) * t2) + + ((-p0 + (3.0f * p1) - (3.0f * p2) + p3) * t3) + ); } /// <summary> - /// Returns the normalized vector pointing from this vector to `b`. + /// Returns the normalized vector pointing from this vector to <paramref name="b"/>. /// </summary> /// <param name="b">The other vector to point towards.</param> - /// <returns>The direction from this vector to `b`.</returns> + /// <returns>The direction from this vector to <paramref name="b"/>.</returns> public Vector2 DirectionTo(Vector2 b) { return new Vector2(b.x - x, b.y - y).Normalized(); } /// <summary> - /// Returns the squared distance between this vector and `to`. + /// Returns the squared distance between this vector and <paramref name="to"/>. /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if /// you need to compare vectors or need the squared distance for some formula. /// </summary> @@ -228,7 +244,7 @@ namespace Godot } /// <summary> - /// Returns the distance between this vector and `to`. + /// Returns the distance between this vector and <paramref name="to"/>. /// </summary> /// <param name="to">The other vector to use.</param> /// <returns>The distance between the two vectors.</returns> @@ -238,13 +254,13 @@ namespace Godot } /// <summary> - /// Returns the dot product of this vector and `with`. + /// Returns the dot product of this vector and <paramref name="with"/>. /// </summary> /// <param name="with">The other vector to use.</param> /// <returns>The dot product of the two vectors.</returns> public real_t Dot(Vector2 with) { - return x * with.x + y * with.y; + return (x * with.x) + (y * with.y); } /// <summary> @@ -257,7 +273,7 @@ namespace Godot } /// <summary> - /// Returns the inverse of this vector. This is the same as `new Vector2(1 / v.x, 1 / v.y)`. + /// 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 Vector2 Inverse() @@ -266,9 +282,9 @@ namespace Godot } /// <summary> - /// Returns true if the vector is normalized, and false otherwise. + /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. /// </summary> - /// <returns>A bool indicating whether or not the vector is normalized.</returns> + /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> public bool IsNormalized() { return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; @@ -277,10 +293,11 @@ namespace Godot /// <summary> /// Returns the length (magnitude) of this vector. /// </summary> + /// <seealso cref="LengthSquared"/> /// <returns>The length of this vector.</returns> public real_t Length() { - return Mathf.Sqrt(x * x + y * y); + return Mathf.Sqrt((x * x) + (y * y)); } /// <summary> @@ -291,12 +308,12 @@ namespace Godot /// <returns>The squared length of this vector.</returns> public real_t LengthSquared() { - return x * x + y * y; + return (x * x) + (y * y); } /// <summary> /// Returns the result of the linear interpolation between - /// this vector and `to` by amount `weight`. + /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>. /// </summary> /// <param name="to">The destination vector for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> @@ -312,10 +329,12 @@ namespace Godot /// <summary> /// Returns the result of the linear interpolation between - /// this vector and `to` by the vector amount `weight`. + /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>. /// </summary> /// <param name="to">The destination vector for interpolation.</param> - /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <param name="weight"> + /// A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation. + /// </param> /// <returns>The resulting vector of the interpolation.</returns> public Vector2 Lerp(Vector2 to, Vector2 weight) { @@ -327,7 +346,7 @@ namespace Godot } /// <summary> - /// Returns the vector with a maximum length by limiting its length to `length`. + /// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>. /// </summary> /// <param name="length">The length to limit to.</param> /// <returns>The vector with its length limited.</returns> @@ -366,35 +385,41 @@ namespace Godot } /// <summary> - /// Moves this vector toward `to` by the fixed `delta` amount. + /// Moves this vector toward <paramref name="to"/> by the fixed <paramref name="delta"/> amount. /// </summary> /// <param name="to">The vector to move towards.</param> /// <param name="delta">The amount to move towards by.</param> /// <returns>The resulting vector.</returns> public Vector2 MoveToward(Vector2 to, real_t delta) { - var v = this; - var vd = to - v; - var len = vd.Length(); - return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; + Vector2 v = this; + Vector2 vd = to - v; + real_t len = vd.Length(); + if (len <= delta || len < Mathf.Epsilon) + return to; + + return v + (vd / len * delta); } /// <summary> - /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`. + /// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>. /// </summary> /// <returns>A normalized version of the vector.</returns> public Vector2 Normalized() { - var v = this; + Vector2 v = this; v.Normalize(); return v; } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `mod`. + /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components + /// and <paramref name="mod"/>. /// </summary> /// <param name="mod">A value representing the divisor of the operation.</param> - /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `mod`.</returns> + /// <returns> + /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>. + /// </returns> public Vector2 PosMod(real_t mod) { Vector2 v; @@ -404,10 +429,13 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `modv`'s components. + /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components + /// and <paramref name="modv"/>'s components. /// </summary> /// <param name="modv">A vector representing the divisors of the operation.</param> - /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `modv`'s components.</returns> + /// <returns> + /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="modv"/>'s components. + /// </returns> public Vector2 PosMod(Vector2 modv) { Vector2 v; @@ -417,7 +445,7 @@ namespace Godot } /// <summary> - /// Returns this vector projected onto another vector `b`. + /// Returns this vector projected onto another vector <paramref name="onNormal"/>. /// </summary> /// <param name="onNormal">The vector to project onto.</param> /// <returns>The projected vector.</returns> @@ -427,7 +455,7 @@ namespace Godot } /// <summary> - /// Returns this vector reflected from a plane defined by the given `normal`. + /// Returns this vector reflected from a plane defined by the given <paramref name="normal"/>. /// </summary> /// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param> /// <returns>The reflected vector.</returns> @@ -439,11 +467,11 @@ namespace Godot throw new ArgumentException("Argument is not normalized", nameof(normal)); } #endif - return 2 * Dot(normal) * normal - this; + return (2 * Dot(normal) * normal) - this; } /// <summary> - /// Rotates this vector by `phi` radians. + /// Rotates this vector by <paramref name="phi"/> radians. /// </summary> /// <param name="phi">The angle to rotate by, in radians.</param> /// <returns>The rotated vector.</returns> @@ -471,7 +499,7 @@ namespace Godot /// on the signs of this vector's components, or zero if the component is zero, /// by calling <see cref="Mathf.Sign(real_t)"/> on each component. /// </summary> - /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns> + /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> public Vector2 Sign() { Vector2 v; @@ -482,7 +510,7 @@ namespace Godot /// <summary> /// Returns the result of the spherical linear interpolation between - /// this vector and `to` by amount `weight`. + /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>. /// /// Note: Both vectors must be normalized. /// </summary> @@ -498,24 +526,24 @@ namespace Godot } if (!to.IsNormalized()) { - throw new InvalidOperationException("Vector2.Slerp: `to` is not normalized."); + throw new InvalidOperationException($"Vector2.Slerp: `{nameof(to)}` is not normalized."); } #endif return Rotated(AngleTo(to) * weight); } /// <summary> - /// Returns this vector slid along a plane defined by the given normal. + /// Returns this vector slid along a plane defined by the given <paramref name="normal"/>. /// </summary> /// <param name="normal">The normal vector defining the plane to slide on.</param> /// <returns>The slid vector.</returns> public Vector2 Slide(Vector2 normal) { - return this - normal * Dot(normal); + return this - (normal * Dot(normal)); } /// <summary> - /// Returns this vector with each component snapped to the nearest multiple of `step`. + /// Returns this vector with each component snapped to the nearest multiple of <paramref name="step"/>. /// This can also be used to round to an arbitrary number of decimals. /// </summary> /// <param name="step">A vector value representing the step size to snap to.</param> @@ -546,40 +574,40 @@ namespace Godot private static readonly Vector2 _left = new Vector2(-1, 0); /// <summary> - /// Zero vector, a vector with all components set to `0`. + /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> - /// <value>Equivalent to `new Vector2(0, 0)`</value> + /// <value>Equivalent to <c>new Vector2(0, 0)</c>.</value> public static Vector2 Zero { get { return _zero; } } /// <summary> - /// One vector, a vector with all components set to `1`. + /// One vector, a vector with all components set to <c>1</c>. /// </summary> - /// <value>Equivalent to `new Vector2(1, 1)`</value> + /// <value>Equivalent to <c>new Vector2(1, 1)</c>.</value> public static Vector2 One { get { return _one; } } /// <summary> - /// Infinity vector, a vector with all components set to `Mathf.Inf`. + /// Infinity vector, a vector with all components set to <see cref="Mathf.Inf"/>. /// </summary> - /// <value>Equivalent to `new Vector2(Mathf.Inf, Mathf.Inf)`</value> + /// <value>Equivalent to <c>new Vector2(Mathf.Inf, Mathf.Inf)</c>.</value> public static Vector2 Inf { get { return _inf; } } /// <summary> /// Up unit vector. Y is down in 2D, so this vector points -Y. /// </summary> - /// <value>Equivalent to `new Vector2(0, -1)`</value> + /// <value>Equivalent to <c>new Vector2(0, -1)</c>.</value> public static Vector2 Up { get { return _up; } } /// <summary> /// Down unit vector. Y is down in 2D, so this vector points +Y. /// </summary> - /// <value>Equivalent to `new Vector2(0, 1)`</value> + /// <value>Equivalent to <c>new Vector2(0, 1)</c>.</value> public static Vector2 Down { get { return _down; } } /// <summary> /// Right unit vector. Represents the direction of right. /// </summary> - /// <value>Equivalent to `new Vector2(1, 0)`</value> + /// <value>Equivalent to <c>new Vector2(1, 0)</c>.</value> public static Vector2 Right { get { return _right; } } /// <summary> /// Left unit vector. Represents the direction of left. /// </summary> - /// <value>Equivalent to `new Vector2(-1, 0)`</value> + /// <value>Equivalent to <c>new Vector2(-1, 0)</c>.</value> public static Vector2 Left { get { return _left; } } /// <summary> @@ -603,6 +631,17 @@ namespace Godot y = v.y; } + /// <summary> + /// Creates a unit Vector2 rotated to the given angle. This is equivalent to doing + /// <c>Vector2(Mathf.Cos(angle), Mathf.Sin(angle))</c> or <c>Vector2.Right.Rotated(angle)</c>. + /// </summary> + /// <param name="angle">Angle of the vector, in radians.</param> + /// <returns>The resulting vector.</returns> + public static Vector2 FromAngle(real_t angle) + { + return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)); + } + public static Vector2 operator +(Vector2 left, Vector2 right) { left.x += right.x; @@ -719,6 +758,11 @@ namespace Godot return left.x >= right.x; } + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the vector and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Vector2) @@ -728,14 +772,19 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are equal. + /// </summary> + /// <param name="other">The other vector to compare.</param> + /// <returns>Whether or not the vectors are equal.</returns> public bool Equals(Vector2 other) { return x == other.x && y == other.y; } /// <summary> - /// Returns true if this vector and `other` are approximately equal, by running - /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. + /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are approximately equal, + /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. /// </summary> /// <param name="other">The other vector to compare.</param> /// <returns>Whether or not the vectors are approximately equal.</returns> @@ -744,16 +793,28 @@ namespace Godot return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y); } + /// <summary> + /// Serves as the hash function for <see cref="Vector2"/>. + /// </summary> + /// <returns>A hash code for this vector.</returns> public override int GetHashCode() { return y.GetHashCode() ^ x.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Vector2"/> to a string. + /// </summary> + /// <returns>A string representation of this vector.</returns> public override string ToString() { return $"({x}, {y})"; } + /// <summary> + /// Converts this <see cref="Vector2"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this vector.</returns> public string ToString(string 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 959f262f52..ca4531d885 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs @@ -21,23 +21,36 @@ namespace Godot /// </summary> public enum Axis { + /// <summary> + /// The vector's X axis. + /// </summary> X = 0, + /// <summary> + /// The vector's Y axis. + /// </summary> Y } /// <summary> - /// The vector's X component. Also accessible by using the index position `[0]`. + /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> public int x; + /// <summary> - /// The vector's Y component. Also accessible by using the index position `[1]`. + /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> public int y; /// <summary> /// Access vector components using their index. /// </summary> - /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`.</value> + /// <exception cref="IndexOutOfRangeException"> + /// Thrown when the given the <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"/>. + /// </value> public int this[int index] { get @@ -81,7 +94,7 @@ namespace Godot /// 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 `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`. + /// 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 real_t Angle() @@ -110,9 +123,9 @@ namespace Godot } /// <summary> - /// Returns the aspect ratio of this vector, the ratio of `x` to `y`. + /// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>. /// </summary> - /// <returns>The `x` component divided by the `y` component.</returns> + /// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns> public real_t Aspect() { return x / (real_t)y; @@ -120,7 +133,7 @@ namespace Godot /// <summary> /// Returns a new vector with all components clamped between the - /// components of `min` and `max` using + /// components of <paramref name="min"/> and <paramref name="max"/> using /// <see cref="Mathf.Clamp(int, int, int)"/>. /// </summary> /// <param name="min">The vector with minimum allowed values.</param> @@ -136,7 +149,7 @@ namespace Godot } /// <summary> - /// Returns the cross product of this vector and `b`. + /// Returns the cross product of this vector and <paramref name="b"/>. /// </summary> /// <param name="b">The other vector.</param> /// <returns>The cross product vector.</returns> @@ -146,7 +159,7 @@ namespace Godot } /// <summary> - /// Returns the squared distance between this vector and `b`. + /// Returns the squared distance between this vector and <paramref name="b"/>. /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if /// you need to compare vectors or need the squared distance for some formula. /// </summary> @@ -158,7 +171,7 @@ namespace Godot } /// <summary> - /// Returns the distance between this vector and `b`. + /// Returns the distance between this vector and <paramref name="b"/>. /// </summary> /// <param name="b">The other vector to use.</param> /// <returns>The distance between the two vectors.</returns> @@ -168,7 +181,7 @@ namespace Godot } /// <summary> - /// Returns the dot product of this vector and `b`. + /// Returns the dot product of this vector and <paramref name="b"/>. /// </summary> /// <param name="b">The other vector to use.</param> /// <returns>The dot product of the two vectors.</returns> @@ -180,6 +193,7 @@ namespace Godot /// <summary> /// Returns the length (magnitude) of this vector. /// </summary> + /// <seealso cref="LengthSquared"/> /// <returns>The length of this vector.</returns> public real_t Length() { @@ -224,10 +238,13 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `mod`. + /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components + /// and <paramref name="mod"/>. /// </summary> /// <param name="mod">A value representing the divisor of the operation.</param> - /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `mod`.</returns> + /// <returns> + /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>. + /// </returns> public Vector2i PosMod(int mod) { Vector2i v = this; @@ -237,10 +254,13 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `modv`'s components. + /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components + /// and <paramref name="modv"/>'s components. /// </summary> /// <param name="modv">A vector representing the divisors of the operation.</param> - /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `modv`'s components.</returns> + /// <returns> + /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components. + /// </returns> public Vector2i PosMod(Vector2i modv) { Vector2i v = this; @@ -254,7 +274,7 @@ namespace Godot /// on the signs of this vector's components, or zero if the component is zero, /// by calling <see cref="Mathf.Sign(int)"/> on each component. /// </summary> - /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns> + /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> public Vector2i Sign() { Vector2i v = this; @@ -283,35 +303,35 @@ namespace Godot private static readonly Vector2i _left = new Vector2i(-1, 0); /// <summary> - /// Zero vector, a vector with all components set to `0`. + /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> - /// <value>Equivalent to `new Vector2i(0, 0)`</value> + /// <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 `1`. + /// One vector, a vector with all components set to <c>1</c>. /// </summary> - /// <value>Equivalent to `new Vector2i(1, 1)`</value> + /// <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 `new Vector2i(0, -1)`</value> + /// <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 `new Vector2i(0, 1)`</value> + /// <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 `new Vector2i(1, 0)`</value> + /// <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 `new Vector2i(-1, 0)`</value> + /// <value>Equivalent to <c>new Vector2i(-1, 0)</c>.</value> public static Vector2i Left { get { return _left; } } /// <summary> @@ -476,16 +496,29 @@ namespace Godot return left.x >= right.x; } + /// <summary> + /// 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) { return new Vector2(value.x, value.y); } + /// <summary> + /// 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) { return new Vector2i(value); } + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the vector and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Vector2i) @@ -496,21 +529,38 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are equal. + /// </summary> + /// <param name="other">The other vector to compare.</param> + /// <returns>Whether or not the vectors are equal.</returns> public bool Equals(Vector2i other) { return x == other.x && y == other.y; } + /// <summary> + /// Serves as the hash function for <see cref="Vector2i"/>. + /// </summary> + /// <returns>A hash code for this vector.</returns> public override int GetHashCode() { return y.GetHashCode() ^ x.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Vector2i"/> to a string. + /// </summary> + /// <returns>A string representation of this vector.</returns> public override string ToString() { return $"({x}, {y})"; } + /// <summary> + /// Converts this <see cref="Vector2i"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this vector.</returns> public string ToString(string 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 bdf64159e9..01e3a71bcb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -21,28 +21,46 @@ namespace Godot /// </summary> public enum Axis { + /// <summary> + /// The vector's X axis. + /// </summary> X = 0, + /// <summary> + /// The vector's Y axis. + /// </summary> Y, + /// <summary> + /// The vector's Z axis. + /// </summary> Z } /// <summary> - /// The vector's X component. Also accessible by using the index position `[0]`. + /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> public real_t x; + /// <summary> - /// The vector's Y component. Also accessible by using the index position `[1]`. + /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> public real_t y; + /// <summary> - /// The vector's Z component. Also accessible by using the index position `[2]`. + /// The vector's Z component. Also accessible by using the index position <c>[2]</c>. /// </summary> public real_t z; /// <summary> /// Access vector components using their index. /// </summary> - /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`.</value> + /// <exception cref="IndexOutOfRangeException"> + /// Thrown when the given the <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"/>. + /// </value> public real_t this[int index] { get @@ -135,7 +153,7 @@ namespace Godot /// <summary> /// Returns a new vector with all components clamped between the - /// components of `min` and `max` using + /// components of <paramref name="min"/> and <paramref name="max"/> using /// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>. /// </summary> /// <param name="min">The vector with minimum allowed values.</param> @@ -152,7 +170,7 @@ namespace Godot } /// <summary> - /// Returns the cross product of this vector and `b`. + /// Returns the cross product of this vector and <paramref name="b"/>. /// </summary> /// <param name="b">The other vector.</param> /// <returns>The cross product vector.</returns> @@ -160,19 +178,19 @@ namespace Godot { return new Vector3 ( - y * b.z - z * b.y, - z * b.x - x * b.z, - x * b.y - y * b.x + (y * b.z) - (z * b.y), + (z * b.x) - (x * b.z), + (x * b.y) - (y * b.x) ); } /// <summary> - /// Performs a cubic interpolation between vectors `preA`, this vector, - /// `b`, and `postB`, by the given amount `t`. + /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector, + /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>. /// </summary> /// <param name="b">The destination vector.</param> /// <param name="preA">A vector before this vector.</param> - /// <param name="postB">A vector after `b`.</param> + /// <param name="postB">A vector after <paramref name="b"/>.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The interpolated vector.</returns> public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t weight) @@ -187,24 +205,24 @@ namespace Godot real_t t3 = t2 * t; return 0.5f * ( - p1 * 2.0f + (-p0 + p2) * t + - (2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 + - (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3 - ); + (p1 * 2.0f) + ((-p0 + p2) * t) + + (((2.0f * p0) - (5.0f * p1) + (4f * p2) - p3) * t2) + + ((-p0 + (3.0f * p1) - (3.0f * p2) + p3) * t3) + ); } /// <summary> - /// Returns the normalized vector pointing from this vector to `b`. + /// Returns the normalized vector pointing from this vector to <paramref name="b"/>. /// </summary> /// <param name="b">The other vector to point towards.</param> - /// <returns>The direction from this vector to `b`.</returns> + /// <returns>The direction from this vector to <paramref name="b"/>.</returns> public Vector3 DirectionTo(Vector3 b) { return new Vector3(b.x - x, b.y - y, b.z - z).Normalized(); } /// <summary> - /// Returns the squared distance between this vector and `b`. + /// Returns the squared distance between this vector and <paramref name="b"/>. /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if /// you need to compare vectors or need the squared distance for some formula. /// </summary> @@ -216,8 +234,9 @@ namespace Godot } /// <summary> - /// Returns the distance between this vector and `b`. + /// Returns the distance between this vector and <paramref name="b"/>. /// </summary> + /// <seealso cref="DistanceSquaredTo(Vector3)"/> /// <param name="b">The other vector to use.</param> /// <returns>The distance between the two vectors.</returns> public real_t DistanceTo(Vector3 b) @@ -226,13 +245,13 @@ namespace Godot } /// <summary> - /// Returns the dot product of this vector and `b`. + /// Returns the dot product of this vector and <paramref name="b"/>. /// </summary> /// <param name="b">The other vector to use.</param> /// <returns>The dot product of the two vectors.</returns> public real_t Dot(Vector3 b) { - return x * b.x + y * b.y + z * b.z; + return (x * b.x) + (y * b.y) + (z * b.z); } /// <summary> @@ -245,7 +264,7 @@ namespace Godot } /// <summary> - /// Returns the inverse of this vector. This is the same as `new Vector3(1 / v.x, 1 / v.y, 1 / v.z)`. + /// 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 Vector3 Inverse() @@ -254,9 +273,9 @@ namespace Godot } /// <summary> - /// Returns true if the vector is normalized, and false otherwise. + /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. /// </summary> - /// <returns>A bool indicating whether or not the vector is normalized.</returns> + /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> public bool IsNormalized() { return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; @@ -265,6 +284,7 @@ namespace Godot /// <summary> /// Returns the length (magnitude) of this vector. /// </summary> + /// <seealso cref="LengthSquared"/> /// <returns>The length of this vector.</returns> public real_t Length() { @@ -292,7 +312,7 @@ namespace Godot /// <summary> /// Returns the result of the linear interpolation between - /// this vector and `to` by amount `weight`. + /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>. /// </summary> /// <param name="to">The destination vector for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> @@ -309,7 +329,7 @@ namespace Godot /// <summary> /// Returns the result of the linear interpolation between - /// this vector and `to` by the vector amount `weight`. + /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>. /// </summary> /// <param name="to">The destination vector for interpolation.</param> /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param> @@ -325,7 +345,7 @@ namespace Godot } /// <summary> - /// Returns the vector with a maximum length by limiting its length to `length`. + /// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>. /// </summary> /// <param name="length">The length to limit to.</param> /// <returns>The vector with its length limited.</returns> @@ -364,32 +384,35 @@ namespace Godot } /// <summary> - /// Moves this vector toward `to` by the fixed `delta` amount. + /// Moves this vector toward <paramref name="to"/> by the fixed <paramref name="delta"/> amount. /// </summary> /// <param name="to">The vector to move towards.</param> /// <param name="delta">The amount to move towards by.</param> /// <returns>The resulting vector.</returns> public Vector3 MoveToward(Vector3 to, real_t delta) { - var v = this; - var vd = to - v; - var len = vd.Length(); - return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; + Vector3 v = this; + Vector3 vd = to - v; + real_t len = vd.Length(); + if (len <= delta || len < Mathf.Epsilon) + return to; + + return v + (vd / len * delta); } /// <summary> - /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`. + /// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>. /// </summary> /// <returns>A normalized version of the vector.</returns> public Vector3 Normalized() { - var v = this; + Vector3 v = this; v.Normalize(); return v; } /// <summary> - /// Returns the outer product with `b`. + /// Returns the outer product with <paramref name="b"/>. /// </summary> /// <param name="b">The other vector.</param> /// <returns>A <see cref="Basis"/> representing the outer product matrix.</returns> @@ -403,10 +426,13 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `mod`. + /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components + /// and <paramref name="mod"/>. /// </summary> /// <param name="mod">A value representing the divisor of the operation.</param> - /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `mod`.</returns> + /// <returns> + /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>. + /// </returns> public Vector3 PosMod(real_t mod) { Vector3 v; @@ -417,10 +443,13 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `modv`'s components. + /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components + /// and <paramref name="modv"/>'s components. /// </summary> /// <param name="modv">A vector representing the divisors of the operation.</param> - /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `modv`'s components.</returns> + /// <returns> + /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="modv"/>'s components. + /// </returns> public Vector3 PosMod(Vector3 modv) { Vector3 v; @@ -431,7 +460,7 @@ namespace Godot } /// <summary> - /// Returns this vector projected onto another vector `b`. + /// Returns this vector projected onto another vector <paramref name="onNormal"/>. /// </summary> /// <param name="onNormal">The vector to project onto.</param> /// <returns>The projected vector.</returns> @@ -441,7 +470,7 @@ namespace Godot } /// <summary> - /// Returns this vector reflected from a plane defined by the given `normal`. + /// Returns this vector reflected from a plane defined by the given <paramref name="normal"/>. /// </summary> /// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param> /// <returns>The reflected vector.</returns> @@ -453,12 +482,12 @@ namespace Godot throw new ArgumentException("Argument is not normalized", nameof(normal)); } #endif - return 2.0f * Dot(normal) * normal - this; + return (2.0f * Dot(normal) * normal) - this; } /// <summary> - /// Rotates this vector around a given `axis` vector by `phi` radians. - /// The `axis` vector must be a normalized vector. + /// Rotates this vector around a given <paramref name="axis"/> vector by <paramref name="phi"/> radians. + /// The <paramref name="axis"/> vector must be a normalized vector. /// </summary> /// <param name="axis">The vector to rotate around. Must be normalized.</param> /// <param name="phi">The angle to rotate by, in radians.</param> @@ -489,7 +518,7 @@ namespace Godot /// on the signs of this vector's components, or zero if the component is zero, /// by calling <see cref="Mathf.Sign(real_t)"/> on each component. /// </summary> - /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns> + /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> public Vector3 Sign() { Vector3 v; @@ -503,7 +532,7 @@ namespace Godot /// Returns the signed angle to the given vector, in radians. /// The sign of the angle is positive in a counter-clockwise /// direction and negative in a clockwise direction when viewed - /// from the side specified by the `axis`. + /// from the side specified by the <paramref name="axis"/>. /// </summary> /// <param name="to">The other vector to compare this vector to.</param> /// <param name="axis">The reference axis to use for the angle sign.</param> @@ -518,7 +547,7 @@ namespace Godot /// <summary> /// Returns the result of the spherical linear interpolation between - /// this vector and `to` by amount `weight`. + /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>. /// /// Note: Both vectors must be normalized. /// </summary> @@ -534,7 +563,7 @@ namespace Godot } if (!to.IsNormalized()) { - throw new InvalidOperationException("Vector3.Slerp: `to` is not normalized."); + throw new InvalidOperationException($"Vector3.Slerp: `{nameof(to)}` is not normalized."); } #endif real_t theta = AngleTo(to); @@ -542,17 +571,17 @@ namespace Godot } /// <summary> - /// Returns this vector slid along a plane defined by the given normal. + /// Returns this vector slid along a plane defined by the given <paramref name="normal"/>. /// </summary> /// <param name="normal">The normal vector defining the plane to slide on.</param> /// <returns>The slid vector.</returns> public Vector3 Slide(Vector3 normal) { - return this - normal * Dot(normal); + return this - (normal * Dot(normal)); } /// <summary> - /// Returns this vector with each component snapped to the nearest multiple of `step`. + /// Returns this vector with each component snapped to the nearest multiple of <paramref name="step"/>. /// This can also be used to round to an arbitrary number of decimals. /// </summary> /// <param name="step">A vector value representing the step size to snap to.</param> @@ -570,10 +599,10 @@ namespace Godot /// <summary> /// Returns a diagonal matrix with the vector as main diagonal. /// - /// This is equivalent to a Basis with no rotation or shearing and + /// This is equivalent to a <see cref="Basis"/> with no rotation or shearing and /// this vector's components set as the scale. /// </summary> - /// <returns>A Basis with the vector as its main diagonal.</returns> + /// <returns>A <see cref="Basis"/> with the vector as its main diagonal.</returns> public Basis ToDiagonalMatrix() { return new Basis( @@ -596,54 +625,54 @@ namespace Godot private static readonly Vector3 _back = new Vector3(0, 0, 1); /// <summary> - /// Zero vector, a vector with all components set to `0`. + /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> - /// <value>Equivalent to `new Vector3(0, 0, 0)`</value> + /// <value>Equivalent to <c>new Vector3(0, 0, 0)</c>.</value> public static Vector3 Zero { get { return _zero; } } /// <summary> - /// One vector, a vector with all components set to `1`. + /// One vector, a vector with all components set to <c>1</c>. /// </summary> - /// <value>Equivalent to `new Vector3(1, 1, 1)`</value> + /// <value>Equivalent to <c>new Vector3(1, 1, 1)</c>.</value> public static Vector3 One { get { return _one; } } /// <summary> - /// Infinity vector, a vector with all components set to `Mathf.Inf`. + /// Infinity vector, a vector with all components set to <see cref="Mathf.Inf"/>. /// </summary> - /// <value>Equivalent to `new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf)`</value> + /// <value>Equivalent to <c>new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf)</c>.</value> public static Vector3 Inf { get { return _inf; } } /// <summary> /// Up unit vector. /// </summary> - /// <value>Equivalent to `new Vector3(0, 1, 0)`</value> + /// <value>Equivalent to <c>new Vector3(0, 1, 0)</c>.</value> public static Vector3 Up { get { return _up; } } /// <summary> /// Down unit vector. /// </summary> - /// <value>Equivalent to `new Vector3(0, -1, 0)`</value> + /// <value>Equivalent to <c>new Vector3(0, -1, 0)</c>.</value> public static Vector3 Down { get { return _down; } } /// <summary> /// Right unit vector. Represents the local direction of right, /// and the global direction of east. /// </summary> - /// <value>Equivalent to `new Vector3(1, 0, 0)`</value> + /// <value>Equivalent to <c>new Vector3(1, 0, 0)</c>.</value> public static Vector3 Right { get { return _right; } } /// <summary> /// Left unit vector. Represents the local direction of left, /// and the global direction of west. /// </summary> - /// <value>Equivalent to `new Vector3(-1, 0, 0)`</value> + /// <value>Equivalent to <c>new Vector3(-1, 0, 0)</c>.</value> public static Vector3 Left { get { return _left; } } /// <summary> /// Forward unit vector. Represents the local direction of forward, /// and the global direction of north. /// </summary> - /// <value>Equivalent to `new Vector3(0, 0, -1)`</value> + /// <value>Equivalent to <c>new Vector3(0, 0, -1)</c>.</value> public static Vector3 Forward { get { return _forward; } } /// <summary> /// Back unit vector. Represents the local direction of back, /// and the global direction of south. /// </summary> - /// <value>Equivalent to `new Vector3(0, 0, 1)`</value> + /// <value>Equivalent to <c>new Vector3(0, 0, 1)</c>.</value> public static Vector3 Back { get { return _back; } } /// <summary> @@ -812,6 +841,11 @@ namespace Godot return left.x > right.x; } + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the vector and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Vector3) @@ -822,14 +856,19 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are equal + /// </summary> + /// <param name="other">The other vector to compare.</param> + /// <returns>Whether or not the vectors are equal.</returns> public bool Equals(Vector3 other) { return x == other.x && y == other.y && z == other.z; } /// <summary> - /// Returns true if this vector and `other` are approximately equal, by running - /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. + /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are approximately equal, + /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. /// </summary> /// <param name="other">The other vector to compare.</param> /// <returns>Whether or not the vectors are approximately equal.</returns> @@ -838,16 +877,28 @@ namespace Godot return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z); } + /// <summary> + /// Serves as the hash function for <see cref="Vector3"/>. + /// </summary> + /// <returns>A hash code for this vector.</returns> public override int GetHashCode() { return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Vector3"/> to a string. + /// </summary> + /// <returns>A string representation of this vector.</returns> public override string ToString() { return $"({x}, {y}, {z})"; } + /// <summary> + /// Converts this <see cref="Vector3"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this vector.</returns> public string ToString(string 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 c96a7cf1b0..2a7771cdfc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs @@ -21,28 +21,46 @@ namespace Godot /// </summary> public enum Axis { + /// <summary> + /// The vector's X axis. + /// </summary> X = 0, + /// <summary> + /// The vector's Y axis. + /// </summary> Y, + /// <summary> + /// The vector's Z axis. + /// </summary> Z } /// <summary> - /// The vector's X component. Also accessible by using the index position `[0]`. + /// The vector's X component. Also accessible by using the index position <c>[0]</c>. /// </summary> public int x; + /// <summary> - /// The vector's Y component. Also accessible by using the index position `[1]`. + /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. /// </summary> public int y; + /// <summary> - /// The vector's Z component. Also accessible by using the index position `[2]`. + /// The vector's Z component. Also accessible by using the index position <c>[2]</c>. /// </summary> public int z; /// <summary> - /// Access vector components using their index. + /// Access vector components using their <paramref name="index"/>. /// </summary> - /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`.</value> + /// <exception cref="IndexOutOfRangeException"> + /// Thrown when the given the <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"/>. + /// </value> public int this[int index] { get @@ -89,7 +107,7 @@ namespace Godot /// <summary> /// Returns a new vector with all components clamped between the - /// components of `min` and `max` using + /// components of <paramref name="min"/> and <paramref name="max"/> using /// <see cref="Mathf.Clamp(int, int, int)"/>. /// </summary> /// <param name="min">The vector with minimum allowed values.</param> @@ -106,7 +124,7 @@ namespace Godot } /// <summary> - /// Returns the squared distance between this vector and `b`. + /// Returns the squared distance between this vector and <paramref name="b"/>. /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if /// you need to compare vectors or need the squared distance for some formula. /// </summary> @@ -118,8 +136,9 @@ namespace Godot } /// <summary> - /// Returns the distance between this vector and `b`. + /// Returns the distance between this vector and <paramref name="b"/>. /// </summary> + /// <seealso cref="DistanceSquaredTo(Vector3i)"/> /// <param name="b">The other vector to use.</param> /// <returns>The distance between the two vectors.</returns> public real_t DistanceTo(Vector3i b) @@ -128,7 +147,7 @@ namespace Godot } /// <summary> - /// Returns the dot product of this vector and `b`. + /// Returns the dot product of this vector and <paramref name="b"/>. /// </summary> /// <param name="b">The other vector to use.</param> /// <returns>The dot product of the two vectors.</returns> @@ -140,6 +159,7 @@ namespace Godot /// <summary> /// Returns the length (magnitude) of this vector. /// </summary> + /// <seealso cref="LengthSquared"/> /// <returns>The length of this vector.</returns> public real_t Length() { @@ -186,10 +206,13 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `mod`. + /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components + /// and <paramref name="mod"/>. /// </summary> /// <param name="mod">A value representing the divisor of the operation.</param> - /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `mod`.</returns> + /// <returns> + /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>. + /// </returns> public Vector3i PosMod(int mod) { Vector3i v = this; @@ -200,10 +223,13 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `modv`'s components. + /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components + /// and <paramref name="modv"/>'s components. /// </summary> /// <param name="modv">A vector representing the divisors of the operation.</param> - /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `modv`'s components.</returns> + /// <returns> + /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components. + /// </returns> public Vector3i PosMod(Vector3i modv) { Vector3i v = this; @@ -218,7 +244,7 @@ namespace Godot /// on the signs of this vector's components, or zero if the component is zero, /// by calling <see cref="Mathf.Sign(int)"/> on each component. /// </summary> - /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns> + /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> public Vector3i Sign() { Vector3i v = this; @@ -240,49 +266,49 @@ namespace Godot private static readonly Vector3i _back = new Vector3i(0, 0, 1); /// <summary> - /// Zero vector, a vector with all components set to `0`. + /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> - /// <value>Equivalent to `new Vector3i(0, 0, 0)`</value> + /// <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 `1`. + /// One vector, a vector with all components set to <c>1</c>. /// </summary> - /// <value>Equivalent to `new Vector3i(1, 1, 1)`</value> + /// <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 `new Vector3i(0, 1, 0)`</value> + /// <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 `new Vector3i(0, -1, 0)`</value> + /// <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 `new Vector3i(1, 0, 0)`</value> + /// <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 `new Vector3i(-1, 0, 0)`</value> + /// <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 `new Vector3i(0, 0, -1)`</value> + /// <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 `new Vector3i(0, 0, 1)`</value> + /// <value>Equivalent to <c>new Vector3i(0, 0, 1)</c>.</value> public static Vector3i Back { get { return _back; } } /// <summary> @@ -479,16 +505,29 @@ namespace Godot return left.x > right.x; } + /// <summary> + /// 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) { return new Vector3(value.x, value.y, value.z); } + /// <summary> + /// 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) { return new Vector3i(value); } + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the vector and the other object are equal.</returns> public override bool Equals(object obj) { if (obj is Vector3i) @@ -499,21 +538,38 @@ namespace Godot return false; } + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are equal + /// </summary> + /// <param name="other">The other vector to compare.</param> + /// <returns>Whether or not the vectors are equal.</returns> public bool Equals(Vector3i other) { return x == other.x && y == other.y && z == other.z; } + /// <summary> + /// Serves as the hash function for <see cref="Vector3i"/>. + /// </summary> + /// <returns>A hash code for this vector.</returns> public override int GetHashCode() { return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode(); } + /// <summary> + /// Converts this <see cref="Vector3i"/> to a string. + /// </summary> + /// <returns>A string representation of this vector.</returns> public override string ToString() { return $"({x}, {y}, {z})"; } + /// <summary> + /// Converts this <see cref="Vector3i"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this vector.</returns> public string ToString(string format) { return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})"; diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp index 18a9221f89..bb80a836ad 100644 --- a/modules/mono/glue/string_glue.cpp +++ b/modules/mono/glue/string_glue.cpp @@ -67,6 +67,11 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) { return GDMonoMarshal::mono_string_from_godot(ret); } +MonoString *godot_icall_String_simplify_path(MonoString *p_str) { + String ret = GDMonoMarshal::mono_string_to_godot(p_str).simplify_path(); + return GDMonoMarshal::mono_string_from_godot(ret); +} + void godot_register_string_icalls() { GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text); @@ -74,6 +79,7 @@ void godot_register_string_icalls() { GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text); + GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 0b36796d98..8b215a66c2 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -141,7 +141,6 @@ void CachedData::clear_godot_api_cache() { class_SignalAttribute = nullptr; class_ToolAttribute = nullptr; class_RemoteAttribute = nullptr; - class_MasterAttribute = nullptr; class_PuppetAttribute = nullptr; class_GodotMethodAttribute = nullptr; field_GodotMethodAttribute_methodName = nullptr; @@ -267,7 +266,6 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute)); CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute)); CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute)); - CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute)); CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 6d49da0af3..fd28bbda14 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -112,7 +112,6 @@ struct CachedData { GDMonoClass *class_SignalAttribute; GDMonoClass *class_ToolAttribute; GDMonoClass *class_RemoteAttribute; - GDMonoClass *class_MasterAttribute; GDMonoClass *class_PuppetAttribute; GDMonoClass *class_GodotMethodAttribute; GDMonoField *field_GodotMethodAttribute_methodName; diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 053618ebe4..2fb5e446da 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -200,7 +200,7 @@ String str_format(const char *p_format, ...) { return res; } -#if defined(MINGW_ENABLED) || defined(_MSC_VER) && _MSC_VER < 1900 +#if defined(MINGW_ENABLED) #define gd_vsnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf_s(m_buffer, m_count, _TRUNCATE, m_format, m_args_copy) #define gd_vscprintf(m_format, m_args_copy) _vscprintf(m_format, m_args_copy) #else diff --git a/modules/msdfgen/SCsub b/modules/msdfgen/SCsub new file mode 100644 index 0000000000..227369f4bd --- /dev/null +++ b/modules/msdfgen/SCsub @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_msdfgen = env_modules.Clone() + +# Thirdparty source files + +thirdparty_obj = [] + +if env["builtin_msdfgen"]: + env_msdfgen.disable_warnings() + + thirdparty_dir = "#thirdparty/msdfgen/" + thirdparty_sources = [ + "core/Contour.cpp", + "core/EdgeHolder.cpp", + "core/MSDFErrorCorrection.cpp", + "core/Projection.cpp", + "core/Scanline.cpp", + "core/Shape.cpp", + "core/SignedDistance.cpp", + "core/Vector2.cpp", + "core/contour-combiners.cpp", + "core/edge-coloring.cpp", + "core/edge-segments.cpp", + "core/edge-selectors.cpp", + "core/equation-solver.cpp", + "core/msdf-error-correction.cpp", + "core/msdfgen.cpp", + "core/rasterization.cpp", + "core/render-sdf.cpp", + "core/sdf-error-estimation.cpp", + "core/shape-description.cpp", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + env_msdfgen.Append(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"]) + + lib = env_msdfgen.add_library("msdfgen_builtin", thirdparty_sources) + thirdparty_obj += lib + + # Needs to be appended to arrive after libscene in the linker call, + # but we don't want it to arrive *after* system libs, so manual hack + # LIBS contains first SCons Library objects ("SCons.Node.FS.File object") + # and then plain strings for system library. We insert between the two. + inserted = False + for idx, linklib in enumerate(env["LIBS"]): + if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object + env["LIBS"].insert(idx, lib) + inserted = True + break + if not inserted: + env.Append(LIBS=[lib]) + + +# Godot source files + +module_obj = [] + +env_msdfgen.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/msdfgen/config.py b/modules/msdfgen/config.py new file mode 100644 index 0000000000..653e466a74 --- /dev/null +++ b/modules/msdfgen/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return env.module_check_dependencies("msdfgen", ["freetype"]) + + +def configure(env): + pass diff --git a/modules/gdnative/xr/register_types.cpp b/modules/msdfgen/register_types.cpp index cb043debc5..ad60a66d2d 100644 --- a/modules/gdnative/xr/register_types.cpp +++ b/modules/msdfgen/register_types.cpp @@ -29,12 +29,7 @@ /*************************************************************************/ #include "register_types.h" -#include "xr_interface_gdnative.h" -void register_xr_types() { - GDREGISTER_CLASS(XRInterfaceGDNative); - ClassDB::add_compatibility_class("ARVRInterfaceGDNative", "XRInterfaceGDNative"); -} +void register_msdfgen_types() {} -void unregister_xr_types() { -} +void unregister_msdfgen_types() {} diff --git a/modules/gdnative/xr/register_types.h b/modules/msdfgen/register_types.h index 4e7469abe9..fb776c14ed 100644 --- a/modules/gdnative/xr/register_types.h +++ b/modules/msdfgen/register_types.h @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef XR_REGISTER_TYPES_H -#define XR_REGISTER_TYPES_H +#ifndef MSDFGEN_REGISTER_TYPES_H +#define MSDFGEN_REGISTER_TYPES_H -void register_xr_types(); -void unregister_xr_types(); +void register_msdfgen_types(); +void unregister_msdfgen_types(); -#endif // XR_REGISTER_TYPES_H +#endif // MSDFGEN_REGISTER_TYPES_H diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 768b419348..3a938200e9 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -32,13 +32,15 @@ #include "core/io/file_access.h" -void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { - ERR_FAIL_COND(!active); +int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { + ERR_FAIL_COND_V(!active, 0); int todo = p_frames; int start_buffer = 0; + int frames_mixed_this_step = p_frames; + while (todo && active) { float *buffer = (float *)p_buffer; if (start_buffer > 0) { @@ -64,6 +66,7 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra // we still have buffer to fill, start from this element in the next iteration. start_buffer = p_frames - todo; } else { + frames_mixed_this_step = p_frames - todo; for (int i = p_frames - todo; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); } @@ -72,6 +75,7 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra } } } + return frames_mixed_this_step; } float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() { diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index 2bd70a2722..756c241d1f 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -52,7 +52,7 @@ class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled { Ref<AudioStreamOGGVorbis> vorbis_stream; protected: - virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override; + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override; virtual float get_stream_sampling_rate() override; public: diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 1fcbb7aaca..6691f86e60 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -38,7 +38,8 @@ def make_icu_data(target, source, env): # Thirdparty source files thirdparty_obj = [] -freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"]) +freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"], True) +msdngen_enabled = env.module_check_dependencies("text_server_adv", ["msdfgen"], True) if env["builtin_harfbuzz"]: env_harfbuzz = env_modules.Clone() @@ -509,6 +510,13 @@ env_text_server_adv.Append( ] ) +if msdngen_enabled: + env_text_server_adv.Append( + CPPPATH=[ + "#thirdparty/msdfgen", + ] + ) + if freetype_enabled: env_text_server_adv.Append( CPPPATH=[ diff --git a/modules/text_server_adv/bitmap_font_adv.cpp b/modules/text_server_adv/bitmap_font_adv.cpp deleted file mode 100644 index 1731fb6223..0000000000 --- a/modules/text_server_adv/bitmap_font_adv.cpp +++ /dev/null @@ -1,585 +0,0 @@ -/*************************************************************************/ -/* bitmap_font_adv.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "bitmap_font_adv.h" - -/*************************************************************************/ -/* hb_bmp_font_t HarfBuzz Bitmap font interface */ -/*************************************************************************/ - -struct hb_bmp_font_t { - BitmapFontDataAdvanced *face = nullptr; - float font_size = 0.0; - bool unref = false; /* Whether to destroy bm_face when done. */ -}; - -static hb_bmp_font_t *_hb_bmp_font_create(BitmapFontDataAdvanced *p_face, float p_font_size, bool p_unref) { - hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(calloc(1, sizeof(hb_bmp_font_t))); - - if (!bm_font) { - return nullptr; - } - - bm_font->face = p_face; - bm_font->font_size = p_font_size; - bm_font->unref = p_unref; - - return bm_font; -} - -static void _hb_bmp_font_destroy(void *data) { - hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(data); - free(bm_font); -} - -static hb_bool_t hb_bmp_get_nominal_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t *glyph, void *user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data); - - if (!bm_font->face) { - return false; - } - - if (!bm_font->face->has_char(unicode)) { - if (bm_font->face->has_char(0xF000u + unicode)) { - *glyph = 0xF000u + unicode; - return true; - } else { - return false; - } - } - - *glyph = unicode; - return true; -} - -static hb_position_t hb_bmp_get_glyph_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data); - - if (!bm_font->face) { - return 0; - } - - if (!bm_font->face->has_char(glyph)) { - return 0; - } - - return bm_font->face->get_advance(glyph, bm_font->font_size).x * 64; -} - -static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data); - - if (!bm_font->face) { - return 0; - } - - if (!bm_font->face->has_char(glyph)) { - return 0; - } - - return -bm_font->face->get_advance(glyph, bm_font->font_size).y * 64; -} - -static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph, void *user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data); - - if (!bm_font->face) { - return 0; - } - - if (!bm_font->face->has_char(left_glyph)) { - return 0; - } - - if (!bm_font->face->has_char(right_glyph)) { - return 0; - } - - return bm_font->face->get_kerning(left_glyph, right_glyph, bm_font->font_size).x * 64; -} - -static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y, void *user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data); - - if (!bm_font->face) { - return false; - } - - if (!bm_font->face->has_char(glyph)) { - return false; - } - - *x = bm_font->face->get_advance(glyph, bm_font->font_size).x * 32; - *y = bm_font->face->get_ascent(bm_font->font_size) * 64; - - return true; -} - -static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_glyph_extents_t *extents, void *user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data); - - if (!bm_font->face) { - return false; - } - - if (!bm_font->face->has_char(glyph)) { - return false; - } - - extents->x_bearing = 0; - extents->y_bearing = 0; - extents->width = bm_font->face->get_size(glyph, bm_font->font_size).x * 64; - extents->height = bm_font->face->get_size(glyph, bm_font->font_size).y * 64; - - return true; -} - -static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *font, void *font_data, hb_font_extents_t *metrics, void *user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(font_data); - - if (!bm_font->face) { - return false; - } - - metrics->ascender = bm_font->face->get_ascent(bm_font->font_size); - metrics->descender = bm_font->face->get_descent(bm_font->font_size); - metrics->line_gap = 0; - - return true; -} - -static hb_font_funcs_t *funcs = nullptr; -void hb_bmp_create_font_funcs() { - funcs = hb_font_funcs_create(); - - hb_font_funcs_set_font_h_extents_func(funcs, hb_bmp_get_font_h_extents, nullptr, nullptr); - //hb_font_funcs_set_font_v_extents_func (funcs, hb_bmp_get_font_v_extents, nullptr, nullptr); - hb_font_funcs_set_nominal_glyph_func(funcs, hb_bmp_get_nominal_glyph, nullptr, nullptr); - //hb_font_funcs_set_variation_glyph_func (funcs, hb_bmp_get_variation_glyph, nullptr, nullptr); - hb_font_funcs_set_glyph_h_advance_func(funcs, hb_bmp_get_glyph_h_advance, nullptr, nullptr); - hb_font_funcs_set_glyph_v_advance_func(funcs, hb_bmp_get_glyph_v_advance, nullptr, nullptr); - //hb_font_funcs_set_glyph_h_origin_func(funcs, hb_bmp_get_glyph_h_origin, nullptr, nullptr); - hb_font_funcs_set_glyph_v_origin_func(funcs, hb_bmp_get_glyph_v_origin, nullptr, nullptr); - hb_font_funcs_set_glyph_h_kerning_func(funcs, hb_bmp_get_glyph_h_kerning, nullptr, nullptr); - //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_bmp_get_glyph_v_kerning, nullptr, nullptr); - hb_font_funcs_set_glyph_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr); - //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_bmp_get_glyph_contour_point, nullptr, nullptr); - //hb_font_funcs_set_glyph_name_func (funcs, hb_bmp_get_glyph_name, nullptr, nullptr); - //hb_font_funcs_set_glyph_from_name_func (funcs, hb_bmp_get_glyph_from_name, nullptr, nullptr); - - hb_font_funcs_make_immutable(funcs); -} - -void hb_bmp_free_font_funcs() { - if (funcs != nullptr) { - hb_font_funcs_destroy(funcs); - funcs = nullptr; - } -} - -static void _hb_bmp_font_set_funcs(hb_font_t *p_font, BitmapFontDataAdvanced *p_face, int p_size, bool p_unref) { - hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_size, p_unref), _hb_bmp_font_destroy); -} - -hb_font_t *hb_bmp_font_create(BitmapFontDataAdvanced *p_face, int p_size, hb_destroy_func_t p_destroy) { - hb_font_t *font; - hb_face_t *face = hb_face_create(nullptr, 0); - - font = hb_font_create(face); - hb_face_destroy(face); - _hb_bmp_font_set_funcs(font, p_face, p_size, false); - return font; -} - -/*************************************************************************/ -/* BitmapFontDataAdvanced */ -/*************************************************************************/ - -Error BitmapFontDataAdvanced::load_from_file(const String &p_filename, int p_base_size) { - _THREAD_SAFE_METHOD_ - //fnt format used by angelcode bmfont - //https://www.angelcode.com/products/bmfont/ - - FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_filename + "."); - - while (true) { - String line = f->get_line(); - - int delimiter = line.find(" "); - String type = line.substr(0, delimiter); - int pos = delimiter + 1; - Map<String, String> keys; - - while (pos < line.size() && line[pos] == ' ') { - pos++; - } - - while (pos < line.size()) { - int eq = line.find("=", pos); - if (eq == -1) { - break; - } - String key = line.substr(pos, eq - pos); - int end = -1; - String value; - if (line[eq + 1] == '"') { - end = line.find("\"", eq + 2); - if (end == -1) { - break; - } - value = line.substr(eq + 2, end - 1 - eq - 1); - pos = end + 1; - } else { - end = line.find(" ", eq + 1); - if (end == -1) { - end = line.size(); - } - value = line.substr(eq + 1, end - eq); - pos = end; - } - - while (pos < line.size() && line[pos] == ' ') { - pos++; - } - - keys[key] = value; - } - - if (type == "info") { - if (keys.has("size")) { - base_size = keys["size"].to_int(); - } - } else if (type == "common") { - if (keys.has("lineHeight")) { - height = keys["lineHeight"].to_int(); - } - if (keys.has("base")) { - ascent = keys["base"].to_int(); - } - } else if (type == "page") { - if (keys.has("file")) { - String base_dir = p_filename.get_base_dir(); - String file = base_dir.plus_file(keys["file"]); - if (RenderingServer::get_singleton() != nullptr) { - Ref<Texture2D> tex = ResourceLoader::load(file); - if (tex.is_null()) { - ERR_PRINT("Can't load font texture!"); - } else { - ERR_FAIL_COND_V_MSG(tex.is_null(), ERR_FILE_CANT_READ, "It's not a reference to a valid Texture object."); - textures.push_back(tex); - } - } - } - } else if (type == "char") { - Character c; - char32_t idx = 0; - if (keys.has("id")) { - idx = keys["id"].to_int(); - } - if (keys.has("x")) { - c.rect.position.x = keys["x"].to_int(); - } - if (keys.has("y")) { - c.rect.position.y = keys["y"].to_int(); - } - if (keys.has("width")) { - c.rect.size.width = keys["width"].to_int(); - } - if (keys.has("height")) { - c.rect.size.height = keys["height"].to_int(); - } - if (keys.has("xoffset")) { - c.align.x = keys["xoffset"].to_int(); - } - if (keys.has("yoffset")) { - c.align.y = keys["yoffset"].to_int(); - } - if (keys.has("page")) { - c.texture_idx = keys["page"].to_int(); - } - if (keys.has("xadvance")) { - c.advance.x = keys["xadvance"].to_int(); - } - if (keys.has("yadvance")) { - c.advance.x = keys["yadvance"].to_int(); - } - if (c.advance.x < 0) { - c.advance.x = c.rect.size.width + 1; - } - if (c.advance.y < 0) { - c.advance.y = c.rect.size.height + 1; - } - char_map[idx] = c; - } else if (type == "kerning") { - KerningPairKey kpk; - float k = 0.0; - if (keys.has("first")) { - kpk.A = keys["first"].to_int(); - } - if (keys.has("second")) { - kpk.B = keys["second"].to_int(); - } - if (keys.has("amount")) { - k = keys["amount"].to_int(); - } - kerning_map[kpk] = k; - } - - if (f->eof_reached()) { - break; - } - } - if (base_size == 0) { - base_size = height; - } - - if (hb_handle) { - hb_font_destroy(hb_handle); - } - hb_handle = hb_bmp_font_create(this, base_size, nullptr); - valid = true; - - memdelete(f); - return OK; -} - -Error BitmapFontDataAdvanced::bitmap_new(float p_height, float p_ascent, int p_base_size) { - height = p_height; - ascent = p_ascent; - - base_size = p_base_size; - if (base_size == 0) { - base_size = height; - } - - char_map.clear(); - textures.clear(); - kerning_map.clear(); - if (hb_handle) { - hb_font_destroy(hb_handle); - } - hb_handle = hb_bmp_font_create(this, base_size, nullptr); - valid = true; - - return OK; -} - -void BitmapFontDataAdvanced::bitmap_add_texture(const Ref<Texture> &p_texture) { - ERR_FAIL_COND(!valid); - ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object."); - - textures.push_back(p_texture); -} - -void BitmapFontDataAdvanced::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - ERR_FAIL_COND(!valid); - - Character chr; - chr.rect = p_rect; - chr.texture_idx = p_texture_idx; - if (p_advance < 0) { - chr.advance.x = chr.rect.size.x; - } else { - chr.advance.x = p_advance; - } - chr.align = p_align; - char_map[p_char] = chr; -} - -void BitmapFontDataAdvanced::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { - ERR_FAIL_COND(!valid); - - KerningPairKey kpk; - kpk.A = p_A; - kpk.B = p_B; - - if (p_kerning == 0 && kerning_map.has(kpk)) { - kerning_map.erase(kpk); - } else { - kerning_map[kpk] = p_kerning; - } -} - -float BitmapFontDataAdvanced::get_height(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return height * (float(p_size) / float(base_size)); -} - -float BitmapFontDataAdvanced::get_ascent(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return ascent * (float(p_size) / float(base_size)); -} - -float BitmapFontDataAdvanced::get_descent(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return (height - ascent) * (float(p_size) / float(base_size)); -} - -float BitmapFontDataAdvanced::get_underline_position(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return 2 * (float(p_size) / float(base_size)); -} - -float BitmapFontDataAdvanced::get_underline_thickness(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return 1 * (float(p_size) / float(base_size)); -} - -void BitmapFontDataAdvanced::set_distance_field_hint(bool p_distance_field) { - distance_field_hint = p_distance_field; -} - -bool BitmapFontDataAdvanced::get_distance_field_hint() const { - return distance_field_hint; -} - -float BitmapFontDataAdvanced::get_base_size() const { - return base_size; -} - -hb_font_t *BitmapFontDataAdvanced::get_hb_handle(int p_size) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, nullptr); - return hb_handle; -} - -bool BitmapFontDataAdvanced::has_char(char32_t p_char) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, false); - return char_map.has(p_char); -} - -String BitmapFontDataAdvanced::get_supported_chars() const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, String()); - String chars; - const uint32_t *k = nullptr; - while ((k = char_map.next(k))) { - chars += char32_t(*k); - } - return chars; -} - -Vector2 BitmapFontDataAdvanced::get_advance(uint32_t p_char, int p_size) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, Vector2()); - const Character *c = char_map.getptr(p_char); - ERR_FAIL_COND_V(c == nullptr, Vector2()); - - return c->advance * (float(p_size) / float(base_size)); -} - -Vector2 BitmapFontDataAdvanced::get_align(uint32_t p_char, int p_size) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, Vector2()); - const Character *c = char_map.getptr(p_char); - ERR_FAIL_COND_V(c == nullptr, Vector2()); - - return c->align * (float(p_size) / float(base_size)); -} - -Vector2 BitmapFontDataAdvanced::get_size(uint32_t p_char, int p_size) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, Vector2()); - const Character *c = char_map.getptr(p_char); - ERR_FAIL_COND_V(c == nullptr, Vector2()); - - return c->rect.size * (float(p_size) / float(base_size)); -} - -float BitmapFontDataAdvanced::get_font_scale(int p_size) const { - return float(p_size) / float(base_size); -} - -Vector2 BitmapFontDataAdvanced::get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, Vector2()); - KerningPairKey kpk; - kpk.A = p_char; - kpk.B = p_next; - - const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk); - if (E) { - return Vector2(-E->get() * (float(p_size) / float(base_size)), 0.f); - } else { - return Vector2(); - } -} - -Vector2 BitmapFontDataAdvanced::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - if (p_index == 0) { - return Vector2(); - } - ERR_FAIL_COND_V(!valid, Vector2()); - const Character *c = char_map.getptr(p_index); - - ERR_FAIL_COND_V(c == nullptr, Vector2()); - ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2()); - if (c->texture_idx != -1) { - Point2i cpos = p_pos; - cpos += (c->align + Vector2(0, -ascent)) * (float(p_size) / float(base_size)); - Size2i csize = c->rect.size * (float(p_size) / float(base_size)); - if (RenderingServer::get_singleton() != nullptr) { - //if (distance_field_hint) { // Not implemented. - // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, true); - //} - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), textures[c->texture_idx]->get_rid(), c->rect, p_color, false, false); - //if (distance_field_hint) { - // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, false); - //} - } - } - - return c->advance * (float(p_size) / float(base_size)); -} - -Vector2 BitmapFontDataAdvanced::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - if (p_index == 0) { - return Vector2(); - } - ERR_FAIL_COND_V(!valid, Vector2()); - const Character *c = char_map.getptr(p_index); - - ERR_FAIL_COND_V(c == nullptr, Vector2()); - ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2()); - - // Not supported, return advance for compatibility. - - return c->advance * (float(p_size) / float(base_size)); -} - -BitmapFontDataAdvanced::~BitmapFontDataAdvanced() { - if (hb_handle) { - hb_font_destroy(hb_handle); - } -} diff --git a/modules/text_server_adv/bitmap_font_adv.h b/modules/text_server_adv/bitmap_font_adv.h deleted file mode 100644 index 7b620021e1..0000000000 --- a/modules/text_server_adv/bitmap_font_adv.h +++ /dev/null @@ -1,123 +0,0 @@ -/*************************************************************************/ -/* bitmap_font_adv.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef BITMAP_FONT_ADV_H -#define BITMAP_FONT_ADV_H - -#include "font_adv.h" - -void hb_bmp_create_font_funcs(); -void hb_bmp_free_font_funcs(); - -struct BitmapFontDataAdvanced : public FontDataAdvanced { - _THREAD_SAFE_CLASS_ - -private: - Vector<Ref<Texture2D>> textures; - - struct Character { - int texture_idx = 0; - Rect2 rect; - Vector2 align; - Vector2 advance = Vector2(-1, -1); - }; - - struct KerningPairKey { - union { - struct { - uint32_t A, B; - }; - - uint64_t pair = 0; - }; - - _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; } - }; - - HashMap<uint32_t, Character> char_map; - Map<KerningPairKey, int> kerning_map; - hb_font_t *hb_handle = nullptr; - - float height = 0.f; - float ascent = 0.f; - int base_size = 0; - bool distance_field_hint = false; - -public: - virtual void clear_cache() override{}; - - virtual Error load_from_file(const String &p_filename, int p_base_size) override; - virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) override; - - virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override; - virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) override; - - virtual float get_height(int p_size) const override; - virtual float get_ascent(int p_size) const override; - virtual float get_descent(int p_size) const override; - - virtual float get_underline_position(int p_size) const override; - virtual float get_underline_thickness(int p_size) const override; - - virtual void set_antialiased(bool p_antialiased) override{}; - virtual bool get_antialiased() const override { return false; }; - - virtual void set_hinting(TextServer::Hinting p_hinting) override{}; - virtual TextServer::Hinting get_hinting() const override { return TextServer::HINTING_NONE; }; - - virtual void set_distance_field_hint(bool p_distance_field) override; - virtual bool get_distance_field_hint() const override; - - virtual void set_force_autohinter(bool p_enabeld) override{}; - virtual bool get_force_autohinter() const override { return false; }; - - virtual bool has_outline() const override { return false; }; - virtual float get_base_size() const override; - virtual float get_font_scale(int p_size) const override; - - virtual hb_font_t *get_hb_handle(int p_size) override; - - virtual bool has_char(char32_t p_char) const override; - virtual String get_supported_chars() const override; - - virtual Vector2 get_advance(uint32_t p_char, int p_size) const override; - Vector2 get_align(uint32_t p_char, int p_size) const; - Vector2 get_size(uint32_t p_char, int p_size) const; - virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const override; - virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const override { return (uint32_t)p_char; }; - - virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override; - virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override; - - virtual ~BitmapFontDataAdvanced(); -}; - -#endif // BITMAP_FONT_ADV_H diff --git a/modules/text_server_adv/dynamic_font_adv.cpp b/modules/text_server_adv/dynamic_font_adv.cpp deleted file mode 100644 index 62eedebb59..0000000000 --- a/modules/text_server_adv/dynamic_font_adv.cpp +++ /dev/null @@ -1,1030 +0,0 @@ -/*************************************************************************/ -/* dynamic_font_adv.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "dynamic_font_adv.h" - -#ifdef MODULE_FREETYPE_ENABLED - -#include FT_STROKER_H -#include FT_ADVANCES_H -#include FT_MULTIPLE_MASTERS_H - -DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size(int p_size, int p_outline_size) { - ERR_FAIL_COND_V(!valid, nullptr); - ERR_FAIL_COND_V(p_size < 0 || p_size > UINT16_MAX, nullptr); - ERR_FAIL_COND_V(p_outline_size < 0 || p_outline_size > UINT16_MAX, nullptr); - - CacheID id; - id.size = p_size; - id.outline_size = p_outline_size; - - DataAtSize *fds = nullptr; - Map<CacheID, DataAtSize *>::Element *E = nullptr; - if (p_outline_size != 0) { - E = size_cache_outline.find(id); - } else { - E = size_cache.find(id); - } - - if (E != nullptr) { - fds = E->get(); - } else { - if (font_mem == nullptr && font_path != String()) { - if (!font_mem_cache.is_empty()) { - font_mem = font_mem_cache.ptr(); - font_mem_size = font_mem_cache.size(); - } else { - FileAccess *f = FileAccess::open(font_path, FileAccess::READ); - if (!f) { - ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'."); - } - - uint64_t len = f->get_length(); - font_mem_cache.resize(len); - f->get_buffer(font_mem_cache.ptrw(), len); - font_mem = font_mem_cache.ptr(); - font_mem_size = len; - f->close(); - } - } - - int error = 0; - fds = memnew(DataAtSize); - if (font_mem) { - memset(&fds->stream, 0, sizeof(FT_StreamRec)); - fds->stream.base = (unsigned char *)font_mem; - fds->stream.size = font_mem_size; - fds->stream.pos = 0; - - FT_Open_Args fargs; - memset(&fargs, 0, sizeof(FT_Open_Args)); - fargs.memory_base = (unsigned char *)font_mem; - fargs.memory_size = font_mem_size; - fargs.flags = FT_OPEN_MEMORY; - fargs.stream = &fds->stream; - error = FT_Open_Face(library, &fargs, 0, &fds->face); - - } else { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "DynamicFont uninitialized."); - } - - if (error == FT_Err_Unknown_File_Format) { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "Unknown font format."); - } else if (error) { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "Error loading font."); - } - - oversampling = TS->font_get_oversampling(); - - if (FT_HAS_COLOR(fds->face) && fds->face->num_fixed_sizes > 0) { - int best_match = 0; - int diff = ABS(p_size - ((int64_t)fds->face->available_sizes[0].width)); - fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[0].width; - for (int i = 1; i < fds->face->num_fixed_sizes; i++) { - int ndiff = ABS(p_size - ((int64_t)fds->face->available_sizes[i].width)); - if (ndiff < diff) { - best_match = i; - diff = ndiff; - fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[i].width; - } - } - FT_Select_Size(fds->face, best_match); - } else { - FT_Set_Pixel_Sizes(fds->face, 0, p_size * oversampling); - } - - fds->size = p_size; - fds->ascent = (fds->face->size->metrics.ascender / 64.0) / oversampling * fds->scale_color_font; - fds->descent = (-fds->face->size->metrics.descender / 64.0) / oversampling * fds->scale_color_font; - fds->underline_position = (-FT_MulFix(fds->face->underline_position, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font; - fds->underline_thickness = (FT_MulFix(fds->face->underline_thickness, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font; - - //Load os2 TTF table - fds->os2 = (TT_OS2 *)FT_Get_Sfnt_Table(fds->face, FT_SFNT_OS2); - - fds->hb_handle = hb_ft_font_create(fds->face, nullptr); - if (fds->hb_handle == nullptr) { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "Error loading HB font."); - } - - if (p_outline_size != 0) { - size_cache_outline[id] = fds; - } else { - size_cache[id] = fds; - } - - // Write variations. - if (fds->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { - FT_MM_Var *amaster; - - FT_Get_MM_Var(fds->face, &amaster); - - Vector<hb_variation_t> hb_vars; - Vector<FT_Fixed> coords; - coords.resize(amaster->num_axis); - - FT_Get_Var_Design_Coordinates(fds->face, coords.size(), coords.ptrw()); - - for (FT_UInt i = 0; i < amaster->num_axis; i++) { - hb_variation_t var; - - // Reset to default. - var.tag = amaster->axis[i].tag; - var.value = (double)amaster->axis[i].def / 65536.f; - coords.write[i] = amaster->axis[i].def; - - if (variations.has(var.tag)) { - var.value = variations[var.tag]; - coords.write[i] = CLAMP(variations[var.tag] * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); - } - - hb_vars.push_back(var); - } - - FT_Set_Var_Design_Coordinates(fds->face, coords.size(), coords.ptrw()); - hb_font_set_variations(fds->hb_handle, hb_vars.is_empty() ? nullptr : &hb_vars[0], hb_vars.size()); - - FT_Done_MM_Var(library, amaster); - } - } - return fds; -} - -Dictionary DynamicFontDataAdvanced::get_variation_list() const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size); - if (fds == nullptr) { - return Dictionary(); - } - - Dictionary ret; - // Read variations. - if (fds->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { - FT_MM_Var *amaster; - - FT_Get_MM_Var(fds->face, &amaster); - - for (FT_UInt i = 0; i < amaster->num_axis; i++) { - ret[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536); - } - - FT_Done_MM_Var(library, amaster); - } - return ret; -} - -void DynamicFontDataAdvanced::set_variation(const String &p_name, double p_value) { - _THREAD_SAFE_METHOD_ - int32_t tag = TS->name_to_tag(p_name); - if (!variations.has(tag) || (variations[tag] != p_value)) { - variations[tag] = p_value; - clear_cache(); - } -} - -double DynamicFontDataAdvanced::get_variation(const String &p_name) const { - _THREAD_SAFE_METHOD_ - int32_t tag = TS->name_to_tag(p_name); - if (!variations.has(tag)) { - return 0.f; - } - return variations[tag]; -} - -Dictionary DynamicFontDataAdvanced::get_feature_list() const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size); - if (fds == nullptr) { - return Dictionary(); - } - - Dictionary out; - // Read feature flags. - unsigned int count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr); - if (count != 0) { - hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t)); - hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags); - for (unsigned int i = 0; i < count; i++) { - out[feature_tags[i]] = 1; - } - memfree(feature_tags); - } - count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr); - if (count != 0) { - hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t)); - hb_ot_layout_table_get_feature_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags); - for (unsigned int i = 0; i < count; i++) { - out[feature_tags[i]] = 1; - } - memfree(feature_tags); - } - - return out; -} - -DynamicFontDataAdvanced::TexturePosition DynamicFontDataAdvanced::find_texture_pos_for_glyph(DynamicFontDataAdvanced::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) { - TexturePosition ret; - ret.index = -1; - - int mw = p_width; - int mh = p_height; - - for (int i = 0; i < p_data->textures.size(); i++) { - const CharTexture &ct = p_data->textures[i]; - - if (RenderingServer::get_singleton() != nullptr) { - if (ct.texture->get_format() != p_image_format) { - continue; - } - } - - if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture - continue; - } - - ret.y = 0x7FFFFFFF; - ret.x = 0; - - for (int j = 0; j < ct.texture_size - mw; j++) { - int max_y = 0; - - for (int k = j; k < j + mw; k++) { - int y = ct.offsets[k]; - if (y > max_y) { - max_y = y; - } - } - - if (max_y < ret.y) { - ret.y = max_y; - ret.x = j; - } - } - - if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size) { - continue; //fail, could not fit it here - } - - ret.index = i; - break; - } - - if (ret.index == -1) { - //could not find texture to fit, create one - ret.x = 0; - ret.y = 0; - - int texsize = MAX(p_data->size * oversampling * 8, 256); - if (mw > texsize) { - texsize = mw; //special case, adapt to it? - } - if (mh > texsize) { - texsize = mh; //special case, adapt to it? - } - - texsize = next_power_of_2(texsize); - - texsize = MIN(texsize, 4096); - - CharTexture tex; - tex.texture_size = texsize; - tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha - - { - //zero texture - uint8_t *w = tex.imgdata.ptrw(); - ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret); - // Initialize the texture to all-white pixels to prevent artifacts when the - // font is displayed at a non-default scale with filtering enabled. - if (p_color_size == 2) { - for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8 - w[i + 0] = 255; - w[i + 1] = 0; - } - } else if (p_color_size == 4) { - for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8 - w[i + 0] = 255; - w[i + 1] = 255; - w[i + 2] = 255; - w[i + 3] = 0; - } - } else { - ERR_FAIL_V(ret); - } - } - tex.offsets.resize(texsize); - for (int i = 0; i < texsize; i++) { //zero offsets - tex.offsets.write[i] = 0; - } - - p_data->textures.push_back(tex); - ret.index = p_data->textures.size() - 1; - } - - return ret; -} - -DynamicFontDataAdvanced::Character DynamicFontDataAdvanced::Character::not_found() { - Character ch; - return ch; -} - -DynamicFontDataAdvanced::Character DynamicFontDataAdvanced::bitmap_to_character(DynamicFontDataAdvanced::DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) { - int w = bitmap.width; - int h = bitmap.rows; - - int mw = w + rect_margin * 2; - int mh = h + rect_margin * 2; - - ERR_FAIL_COND_V(mw > 4096, Character::not_found()); - ERR_FAIL_COND_V(mh > 4096, Character::not_found()); - - int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; - Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; - - TexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh); - ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found()); - - //fit character in char texture - - CharTexture &tex = p_data->textures.write[tex_pos.index]; - - { - uint8_t *wr = tex.imgdata.ptrw(); - - for (int i = 0; i < h; i++) { - for (int j = 0; j < w; j++) { - int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size; - ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found()); - switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: { - int byte = i * bitmap.pitch + (j >> 3); - int bit = 1 << (7 - (j % 8)); - wr[ofs + 0] = 255; //grayscale as 1 - wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0; - } break; - case FT_PIXEL_MODE_GRAY: - wr[ofs + 0] = 255; //grayscale as 1 - wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; - break; - case FT_PIXEL_MODE_BGRA: { - int ofs_color = i * bitmap.pitch + (j << 2); - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; - wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; - } break; - // TODO: FT_PIXEL_MODE_LCD - default: - ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + "."); - break; - } - } - } - } - - //blit to image and texture - { - if (RenderingServer::get_singleton() != nullptr) { - Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata)); - - if (tex.texture.is_null()) { - tex.texture.instantiate(); - tex.texture->create_from_image(img); - } else { - tex.texture->update(img); //update - } - } - } - - // update height array - for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { - tex.offsets.write[k] = tex_pos.y + mh; - } - - Character chr; - chr.align = (Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling).round(); - chr.advance = (advance * p_data->scale_color_font / oversampling).round(); - chr.texture_idx = tex_pos.index; - chr.found = true; - - chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h); - chr.rect = chr.rect_uv; - chr.rect.position /= oversampling; - chr.rect.size *= (p_data->scale_color_font / oversampling); - return chr; -} - -void DynamicFontDataAdvanced::update_glyph(int p_size, uint32_t p_index) { - DataAtSize *fds = get_data_for_size(p_size, false); - ERR_FAIL_COND(fds == nullptr); - - if (fds->glyph_map.has(p_index)) { - return; - } - - Character character = Character::not_found(); - FT_GlyphSlot slot = fds->face->glyph; - - if (p_index == 0) { - fds->glyph_map[p_index] = character; - return; - } - - int ft_hinting; - switch (hinting) { - case TextServer::HINTING_NONE: - ft_hinting = FT_LOAD_NO_HINTING; - break; - case TextServer::HINTING_LIGHT: - ft_hinting = FT_LOAD_TARGET_LIGHT; - break; - default: - ft_hinting = FT_LOAD_TARGET_NORMAL; - break; - } - - FT_Fixed v, h; - FT_Get_Advance(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting, &h); - FT_Get_Advance(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting | FT_LOAD_VERTICAL_LAYOUT, &v); - - int error = FT_Load_Glyph(fds->face, p_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting); - if (error) { - fds->glyph_map[p_index] = character; - return; - } - - error = FT_Render_Glyph(fds->face->glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); - if (!error) { - character = bitmap_to_character(fds, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); - } - - fds->glyph_map[p_index] = character; -} - -void DynamicFontDataAdvanced::update_glyph_outline(int p_size, int p_outline_size, uint32_t p_index) { - DataAtSize *fds = get_data_for_size(p_size, p_outline_size); - ERR_FAIL_COND(fds == nullptr); - - if (fds->glyph_map.has(p_index)) { - return; - } - - Character character = Character::not_found(); - if (p_index == 0) { - fds->glyph_map[p_index] = character; - return; - } - - int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); - if (error) { - fds->glyph_map[p_index] = character; - return; - } - - FT_Stroker stroker; - if (FT_Stroker_New(library, &stroker) != 0) { - fds->glyph_map[p_index] = character; - return; - } - - FT_Stroker_Set(stroker, (int)(p_outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0); - FT_Glyph glyph; - FT_BitmapGlyph glyph_bitmap; - - if (FT_Get_Glyph(fds->face->glyph, &glyph) != 0) { - goto cleanup_stroker; - } - if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { - goto cleanup_glyph; - } - if (FT_Glyph_To_Bitmap(&glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { - goto cleanup_glyph; - } - - glyph_bitmap = (FT_BitmapGlyph)glyph; - character = bitmap_to_character(fds, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); - -cleanup_glyph: - FT_Done_Glyph(glyph); -cleanup_stroker: - FT_Stroker_Done(stroker); - - fds->glyph_map[p_index] = character; -} - -void DynamicFontDataAdvanced::clear_cache() { - _THREAD_SAFE_METHOD_ - for (Map<CacheID, DataAtSize *>::Element *E = size_cache.front(); E; E = E->next()) { - memdelete(E->get()); - } - size_cache.clear(); - for (Map<CacheID, DataAtSize *>::Element *E = size_cache_outline.front(); E; E = E->next()) { - memdelete(E->get()); - } - size_cache_outline.clear(); -} - -Error DynamicFontDataAdvanced::load_from_file(const String &p_filename, int p_base_size) { - _THREAD_SAFE_METHOD_ - if (library == nullptr) { - int error = FT_Init_FreeType(&library); - ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType."); - } - clear_cache(); - - font_path = p_filename; - base_size = p_base_size; - - valid = true; - DataAtSize *fds = get_data_for_size(base_size); // load base size. - if (fds == nullptr) { - valid = false; - ERR_FAIL_V(ERR_CANT_CREATE); - } - - return OK; -} - -Error DynamicFontDataAdvanced::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { - _THREAD_SAFE_METHOD_ - if (library == nullptr) { - int error = FT_Init_FreeType(&library); - ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType."); - } - clear_cache(); - - font_mem = p_data; - font_mem_size = p_size; - base_size = p_base_size; - - valid = true; - DataAtSize *fds = get_data_for_size(base_size); // load base size. - if (fds == nullptr) { - valid = false; - ERR_FAIL_V(ERR_CANT_CREATE); - } - - return OK; -} - -float DynamicFontDataAdvanced::get_height(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->ascent + fds->descent; -} - -float DynamicFontDataAdvanced::get_ascent(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->ascent; -} - -float DynamicFontDataAdvanced::get_descent(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->descent; -} - -float DynamicFontDataAdvanced::get_underline_position(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->underline_position; -} - -float DynamicFontDataAdvanced::get_underline_thickness(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->underline_thickness; -} - -bool DynamicFontDataAdvanced::is_script_supported(uint32_t p_script) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr); - if (count != 0) { - hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t)); - hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags); - for (unsigned int i = 0; i < count; i++) { - if (p_script == script_tags[i]) { - memfree(script_tags); - return true; - } - } - memfree(script_tags); - } - count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr); - if (count != 0) { - hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t)); - hb_ot_layout_table_get_script_tags(hb_font_get_face(fds->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags); - for (unsigned int i = 0; i < count; i++) { - if (p_script == script_tags[i]) { - memfree(script_tags); - return true; - } - } - memfree(script_tags); - } - - if (!fds->os2) { - return false; - } - - switch (p_script) { - case HB_SCRIPT_COMMON: - return (fds->os2->ulUnicodeRange1 & 1L << 4) || (fds->os2->ulUnicodeRange1 & 1L << 5) || (fds->os2->ulUnicodeRange1 & 1L << 6) || (fds->os2->ulUnicodeRange1 & 1L << 31) || (fds->os2->ulUnicodeRange2 & 1L << 0) || (fds->os2->ulUnicodeRange2 & 1L << 1) || (fds->os2->ulUnicodeRange2 & 1L << 2) || (fds->os2->ulUnicodeRange2 & 1L << 3) || (fds->os2->ulUnicodeRange2 & 1L << 4) || (fds->os2->ulUnicodeRange2 & 1L << 5) || (fds->os2->ulUnicodeRange2 & 1L << 6) || (fds->os2->ulUnicodeRange2 & 1L << 7) || (fds->os2->ulUnicodeRange2 & 1L << 8) || (fds->os2->ulUnicodeRange2 & 1L << 9) || (fds->os2->ulUnicodeRange2 & 1L << 10) || (fds->os2->ulUnicodeRange2 & 1L << 11) || (fds->os2->ulUnicodeRange2 & 1L << 12) || (fds->os2->ulUnicodeRange2 & 1L << 13) || (fds->os2->ulUnicodeRange2 & 1L << 14) || (fds->os2->ulUnicodeRange2 & 1L << 15) || (fds->os2->ulUnicodeRange2 & 1L << 30) || (fds->os2->ulUnicodeRange3 & 1L << 0) || (fds->os2->ulUnicodeRange3 & 1L << 1) || (fds->os2->ulUnicodeRange3 & 1L << 2) || (fds->os2->ulUnicodeRange3 & 1L << 4) || (fds->os2->ulUnicodeRange3 & 1L << 5) || (fds->os2->ulUnicodeRange3 & 1L << 18) || (fds->os2->ulUnicodeRange3 & 1L << 24) || (fds->os2->ulUnicodeRange3 & 1L << 25) || (fds->os2->ulUnicodeRange3 & 1L << 26) || (fds->os2->ulUnicodeRange3 & 1L << 27) || (fds->os2->ulUnicodeRange3 & 1L << 28) || (fds->os2->ulUnicodeRange4 & 1L << 3) || (fds->os2->ulUnicodeRange4 & 1L << 6) || (fds->os2->ulUnicodeRange4 & 1L << 15) || (fds->os2->ulUnicodeRange4 & 1L << 23) || (fds->os2->ulUnicodeRange4 & 1L << 24) || (fds->os2->ulUnicodeRange4 & 1L << 26); - case HB_SCRIPT_LATIN: - return (fds->os2->ulUnicodeRange1 & 1L << 0) || (fds->os2->ulUnicodeRange1 & 1L << 1) || (fds->os2->ulUnicodeRange1 & 1L << 2) || (fds->os2->ulUnicodeRange1 & 1L << 3) || (fds->os2->ulUnicodeRange1 & 1L << 29); - case HB_SCRIPT_GREEK: - return (fds->os2->ulUnicodeRange1 & 1L << 7) || (fds->os2->ulUnicodeRange1 & 1L << 30); - case HB_SCRIPT_COPTIC: - return (fds->os2->ulUnicodeRange1 & 1L << 8); - case HB_SCRIPT_CYRILLIC: - return (fds->os2->ulUnicodeRange1 & 1L << 9); - case HB_SCRIPT_ARMENIAN: - return (fds->os2->ulUnicodeRange1 & 1L << 10); - case HB_SCRIPT_HEBREW: - return (fds->os2->ulUnicodeRange1 & 1L << 11); - case HB_SCRIPT_VAI: - return (fds->os2->ulUnicodeRange1 & 1L << 12); - case HB_SCRIPT_ARABIC: - return (fds->os2->ulUnicodeRange1 & 1L << 13) || (fds->os2->ulUnicodeRange2 & 1L << 31) || (fds->os2->ulUnicodeRange3 & 1L << 3); - case HB_SCRIPT_NKO: - return (fds->os2->ulUnicodeRange1 & 1L << 14); - case HB_SCRIPT_DEVANAGARI: - return (fds->os2->ulUnicodeRange1 & 1L << 15); - case HB_SCRIPT_BENGALI: - return (fds->os2->ulUnicodeRange1 & 1L << 16); - case HB_SCRIPT_GURMUKHI: - return (fds->os2->ulUnicodeRange1 & 1L << 17); - case HB_SCRIPT_GUJARATI: - return (fds->os2->ulUnicodeRange1 & 1L << 18); - case HB_SCRIPT_ORIYA: - return (fds->os2->ulUnicodeRange1 & 1L << 19); - case HB_SCRIPT_TAMIL: - return (fds->os2->ulUnicodeRange1 & 1L << 20); - case HB_SCRIPT_TELUGU: - return (fds->os2->ulUnicodeRange1 & 1L << 21); - case HB_SCRIPT_KANNADA: - return (fds->os2->ulUnicodeRange1 & 1L << 22); - case HB_SCRIPT_MALAYALAM: - return (fds->os2->ulUnicodeRange1 & 1L << 23); - case HB_SCRIPT_THAI: - return (fds->os2->ulUnicodeRange1 & 1L << 24); - case HB_SCRIPT_LAO: - return (fds->os2->ulUnicodeRange1 & 1L << 25); - case HB_SCRIPT_GEORGIAN: - return (fds->os2->ulUnicodeRange1 & 1L << 26); - case HB_SCRIPT_BALINESE: - return (fds->os2->ulUnicodeRange1 & 1L << 27); - case HB_SCRIPT_HANGUL: - return (fds->os2->ulUnicodeRange1 & 1L << 28) || (fds->os2->ulUnicodeRange2 & 1L << 20) || (fds->os2->ulUnicodeRange2 & 1L << 24); - case HB_SCRIPT_HAN: - return (fds->os2->ulUnicodeRange2 & 1L << 21) || (fds->os2->ulUnicodeRange2 & 1L << 22) || (fds->os2->ulUnicodeRange2 & 1L << 23) || (fds->os2->ulUnicodeRange2 & 1L << 26) || (fds->os2->ulUnicodeRange2 & 1L << 27) || (fds->os2->ulUnicodeRange2 & 1L << 29); - case HB_SCRIPT_HIRAGANA: - return (fds->os2->ulUnicodeRange2 & 1L << 17); - case HB_SCRIPT_KATAKANA: - return (fds->os2->ulUnicodeRange2 & 1L << 18); - case HB_SCRIPT_BOPOMOFO: - return (fds->os2->ulUnicodeRange2 & 1L << 19); - case HB_SCRIPT_TIBETAN: - return (fds->os2->ulUnicodeRange3 & 1L << 6); - case HB_SCRIPT_SYRIAC: - return (fds->os2->ulUnicodeRange3 & 1L << 7); - case HB_SCRIPT_THAANA: - return (fds->os2->ulUnicodeRange3 & 1L << 8); - case HB_SCRIPT_SINHALA: - return (fds->os2->ulUnicodeRange3 & 1L << 9); - case HB_SCRIPT_MYANMAR: - return (fds->os2->ulUnicodeRange3 & 1L << 10); - case HB_SCRIPT_ETHIOPIC: - return (fds->os2->ulUnicodeRange3 & 1L << 11); - case HB_SCRIPT_CHEROKEE: - return (fds->os2->ulUnicodeRange3 & 1L << 12); - case HB_SCRIPT_CANADIAN_SYLLABICS: - return (fds->os2->ulUnicodeRange3 & 1L << 13); - case HB_SCRIPT_OGHAM: - return (fds->os2->ulUnicodeRange3 & 1L << 14); - case HB_SCRIPT_RUNIC: - return (fds->os2->ulUnicodeRange3 & 1L << 15); - case HB_SCRIPT_KHMER: - return (fds->os2->ulUnicodeRange3 & 1L << 16); - case HB_SCRIPT_MONGOLIAN: - return (fds->os2->ulUnicodeRange3 & 1L << 17); - case HB_SCRIPT_YI: - return (fds->os2->ulUnicodeRange3 & 1L << 19); - case HB_SCRIPT_HANUNOO: - case HB_SCRIPT_TAGBANWA: - case HB_SCRIPT_BUHID: - case HB_SCRIPT_TAGALOG: - return (fds->os2->ulUnicodeRange3 & 1L << 20); - case HB_SCRIPT_OLD_ITALIC: - return (fds->os2->ulUnicodeRange3 & 1L << 21); - case HB_SCRIPT_GOTHIC: - return (fds->os2->ulUnicodeRange3 & 1L << 22); - case HB_SCRIPT_DESERET: - return (fds->os2->ulUnicodeRange3 & 1L << 23); - case HB_SCRIPT_LIMBU: - return (fds->os2->ulUnicodeRange3 & 1L << 29); - case HB_SCRIPT_TAI_LE: - return (fds->os2->ulUnicodeRange3 & 1L << 30); - case HB_SCRIPT_NEW_TAI_LUE: - return (fds->os2->ulUnicodeRange3 & 1L << 31); - case HB_SCRIPT_BUGINESE: - return (fds->os2->ulUnicodeRange4 & 1L << 0); - case HB_SCRIPT_GLAGOLITIC: - return (fds->os2->ulUnicodeRange4 & 1L << 1); - case HB_SCRIPT_TIFINAGH: - return (fds->os2->ulUnicodeRange4 & 1L << 2); - case HB_SCRIPT_SYLOTI_NAGRI: - return (fds->os2->ulUnicodeRange4 & 1L << 4); - case HB_SCRIPT_LINEAR_B: - return (fds->os2->ulUnicodeRange4 & 1L << 5); - case HB_SCRIPT_UGARITIC: - return (fds->os2->ulUnicodeRange4 & 1L << 7); - case HB_SCRIPT_OLD_PERSIAN: - return (fds->os2->ulUnicodeRange4 & 1L << 8); - case HB_SCRIPT_SHAVIAN: - return (fds->os2->ulUnicodeRange4 & 1L << 9); - case HB_SCRIPT_OSMANYA: - return (fds->os2->ulUnicodeRange4 & 1L << 10); - case HB_SCRIPT_CYPRIOT: - return (fds->os2->ulUnicodeRange4 & 1L << 11); - case HB_SCRIPT_KHAROSHTHI: - return (fds->os2->ulUnicodeRange4 & 1L << 12); - case HB_SCRIPT_TAI_VIET: - return (fds->os2->ulUnicodeRange4 & 1L << 13); - case HB_SCRIPT_CUNEIFORM: - return (fds->os2->ulUnicodeRange4 & 1L << 14); - case HB_SCRIPT_SUNDANESE: - return (fds->os2->ulUnicodeRange4 & 1L << 16); - case HB_SCRIPT_LEPCHA: - return (fds->os2->ulUnicodeRange4 & 1L << 17); - case HB_SCRIPT_OL_CHIKI: - return (fds->os2->ulUnicodeRange4 & 1L << 18); - case HB_SCRIPT_SAURASHTRA: - return (fds->os2->ulUnicodeRange4 & 1L << 19); - case HB_SCRIPT_KAYAH_LI: - return (fds->os2->ulUnicodeRange4 & 1L << 20); - case HB_SCRIPT_REJANG: - return (fds->os2->ulUnicodeRange4 & 1L << 21); - case HB_SCRIPT_CHAM: - return (fds->os2->ulUnicodeRange4 & 1L << 22); - case HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: - return (fds->os2->ulUnicodeRange4 & 1L << 25); - default: - return false; - }; -} - -void DynamicFontDataAdvanced::set_antialiased(bool p_antialiased) { - if (antialiased != p_antialiased) { - clear_cache(); - antialiased = p_antialiased; - } -} - -bool DynamicFontDataAdvanced::get_antialiased() const { - return antialiased; -} - -void DynamicFontDataAdvanced::set_force_autohinter(bool p_enabled) { - if (force_autohinter != p_enabled) { - clear_cache(); - force_autohinter = p_enabled; - } -} - -bool DynamicFontDataAdvanced::get_force_autohinter() const { - return force_autohinter; -} - -void DynamicFontDataAdvanced::set_hinting(TextServer::Hinting p_hinting) { - if (hinting != p_hinting) { - clear_cache(); - hinting = p_hinting; - } -} - -TextServer::Hinting DynamicFontDataAdvanced::get_hinting() const { - return hinting; -} - -bool DynamicFontDataAdvanced::has_outline() const { - return true; -} - -float DynamicFontDataAdvanced::get_base_size() const { - return base_size; -} - -String DynamicFontDataAdvanced::get_supported_chars() const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size); - ERR_FAIL_COND_V(fds == nullptr, String()); - - String chars; - - FT_UInt gindex; - FT_ULong charcode = FT_Get_First_Char(fds->face, &gindex); - while (gindex != 0) { - if (charcode != 0) { - chars += char32_t(charcode); - } - charcode = FT_Get_Next_Char(fds->face, charcode, &gindex); - } - - return chars; -} - -float DynamicFontDataAdvanced::get_font_scale(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 1.0f); - - return fds->scale_color_font / oversampling; -} - -bool DynamicFontDataAdvanced::has_char(char32_t p_char) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(base_size, FT_Get_Char_Index(fds->face, p_char)); - Character ch = fds->glyph_map[FT_Get_Char_Index(fds->face, p_char)]; - - return (ch.found); -} - -hb_font_t *DynamicFontDataAdvanced::get_hb_handle(int p_size) { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, nullptr); - - return fds->hb_handle; -} - -uint32_t DynamicFontDataAdvanced::get_glyph_index(char32_t p_char, char32_t p_variation_selector) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(base_size); - ERR_FAIL_COND_V(fds == nullptr, 0); - - if (p_variation_selector == 0x0000) { - return FT_Get_Char_Index(fds->face, p_char); - } else { - return FT_Face_GetCharVariantIndex(fds->face, p_char, p_variation_selector); - } -} - -Vector2 DynamicFontDataAdvanced::get_advance(uint32_t p_index, int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(p_size, p_index); - Character ch = fds->glyph_map[p_index]; - - return ch.advance; -} - -Vector2 DynamicFontDataAdvanced::get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - FT_Vector delta; - FT_Get_Kerning(fds->face, p_char, p_next, FT_KERNING_DEFAULT, &delta); - return Vector2(delta.x, delta.y); -} - -Vector2 DynamicFontDataAdvanced::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast<DynamicFontDataAdvanced *>(this)->update_glyph(p_size, p_index); - Character ch = fds->glyph_map[p_index]; - - Vector2 advance; - if (ch.found) { - ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2()); - - if (ch.texture_idx != -1) { - Point2i cpos = p_pos; - cpos += ch.align; - Color modulate = p_color; - if (FT_HAS_COLOR(fds->face)) { - modulate.r = modulate.g = modulate.b = 1.0; - } - if (RenderingServer::get_singleton() != nullptr) { - RID texture = fds->textures[ch.texture_idx].texture->get_rid(); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false); - } - } - - advance = ch.advance; - } - - return advance; -} - -Vector2 DynamicFontDataAdvanced::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size, p_outline_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast<DynamicFontDataAdvanced *>(this)->update_glyph_outline(p_size, p_outline_size, p_index); - Character ch = fds->glyph_map[p_index]; - - Vector2 advance; - if (ch.found) { - ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2()); - - if (ch.texture_idx != -1) { - Point2i cpos = p_pos; - cpos += ch.align; - Color modulate = p_color; - if (FT_HAS_COLOR(fds->face)) { - modulate.r = modulate.g = modulate.b = 1.0; - } - if (RenderingServer::get_singleton() != nullptr) { - RID texture = fds->textures[ch.texture_idx].texture->get_rid(); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false); - } - } - - advance = ch.advance; - } - - return advance; -} - -bool DynamicFontDataAdvanced::get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataAdvanced *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); - ERR_FAIL_COND_V(error, false); - - r_points.clear(); - r_contours.clear(); - - float h = fds->ascent; - float scale = (1.0 / 64.0) / oversampling * fds->scale_color_font; - for (short i = 0; i < fds->face->glyph->outline.n_points; i++) { - r_points.push_back(Vector3(fds->face->glyph->outline.points[i].x * scale, h - fds->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fds->face->glyph->outline.tags[i]))); - } - for (short i = 0; i < fds->face->glyph->outline.n_contours; i++) { - r_contours.push_back(fds->face->glyph->outline.contours[i]); - } - r_orientation = (FT_Outline_Get_Orientation(&fds->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); - return true; -} - -DynamicFontDataAdvanced::~DynamicFontDataAdvanced() { - clear_cache(); - if (library != nullptr) { - FT_Done_FreeType(library); - } -} - -#endif // MODULE_FREETYPE_ENABLED diff --git a/modules/text_server_adv/dynamic_font_adv.h b/modules/text_server_adv/dynamic_font_adv.h deleted file mode 100644 index b3f97bb029..0000000000 --- a/modules/text_server_adv/dynamic_font_adv.h +++ /dev/null @@ -1,195 +0,0 @@ -/*************************************************************************/ -/* dynamic_font_adv.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef DYNAMIC_FONT_ADV_H -#define DYNAMIC_FONT_ADV_H - -#include "font_adv.h" - -#include "modules/modules_enabled.gen.h" -#ifdef MODULE_FREETYPE_ENABLED - -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_TRUETYPE_TABLES_H - -#include <hb-ft.h> -#include <hb-ot.h> - -struct DynamicFontDataAdvanced : public FontDataAdvanced { - _THREAD_SAFE_CLASS_ - -private: - struct CharTexture { - Vector<uint8_t> imgdata; - int texture_size = 0; - Vector<int> offsets; - Ref<ImageTexture> texture; - }; - - struct Character { - bool found = false; - int texture_idx = 0; - Rect2 rect; - Rect2 rect_uv; - Vector2 align; - Vector2 advance = Vector2(-1, -1); - - static Character not_found(); - }; - - struct TexturePosition { - int index = 0; - int x = 0; - int y = 0; - }; - - struct CacheID { - union { - struct { - uint32_t size : 16; - uint32_t outline_size : 16; - }; - uint32_t key = 0; - }; - bool operator<(CacheID right) const { - return key < right.key; - } - }; - - struct DataAtSize { - FT_Face face = nullptr; - TT_OS2 *os2 = nullptr; - FT_StreamRec stream; - - int size = 0; - float scale_color_font = 1.f; - float ascent = 0.0; - float descent = 0.0; - float underline_position = 0.0; - float underline_thickness = 0.0; - - Vector<CharTexture> textures; - HashMap<uint32_t, Character> glyph_map; - - hb_font_t *hb_handle = nullptr; - ~DataAtSize() { - if (hb_handle != nullptr) { - hb_font_destroy(hb_handle); - } - if (face != nullptr) { - FT_Done_Face(face); - } - } - }; - - FT_Library library = nullptr; - - // Source data. - const uint8_t *font_mem = nullptr; - int font_mem_size = 0; - String font_path; - Vector<uint8_t> font_mem_cache; - - Map<int32_t, double> variations; - - float rect_margin = 1.f; - int base_size = 16; - float oversampling = 1.f; - bool antialiased = true; - bool force_autohinter = false; - TextServer::Hinting hinting = TextServer::HINTING_LIGHT; - - Map<CacheID, DataAtSize *> size_cache; - Map<CacheID, DataAtSize *> size_cache_outline; - - DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0); - - TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height); - Character bitmap_to_character(DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance); - _FORCE_INLINE_ void update_glyph(int p_size, uint32_t p_index); - _FORCE_INLINE_ void update_glyph_outline(int p_size, int p_outline_size, uint32_t p_index); - -public: - virtual void clear_cache() override; - - virtual Error load_from_file(const String &p_filename, int p_base_size) override; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override; - - virtual float get_height(int p_size) const override; - virtual float get_ascent(int p_size) const override; - virtual float get_descent(int p_size) const override; - - virtual Dictionary get_feature_list() const override; - virtual Dictionary get_variation_list() const override; - - virtual void set_variation(const String &p_name, double p_value) override; - virtual double get_variation(const String &p_name) const override; - - virtual float get_underline_position(int p_size) const override; - virtual float get_underline_thickness(int p_size) const override; - - virtual void set_antialiased(bool p_antialiased) override; - virtual bool get_antialiased() const override; - - virtual void set_hinting(TextServer::Hinting p_hinting) override; - virtual TextServer::Hinting get_hinting() const override; - - virtual void set_force_autohinter(bool p_enabled) override; - virtual bool get_force_autohinter() const override; - - virtual void set_distance_field_hint(bool p_distance_field) override{}; - virtual bool get_distance_field_hint() const override { return false; }; - - virtual bool has_outline() const override; - virtual float get_base_size() const override; - - virtual bool is_script_supported(uint32_t p_script) const override; - - virtual bool has_char(char32_t p_char) const override; - virtual String get_supported_chars() const override; - virtual float get_font_scale(int p_size) const override; - - virtual hb_font_t *get_hb_handle(int p_size) override; - virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const override; - virtual Vector2 get_advance(uint32_t p_index, int p_size) const override; - virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const override; - - virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override; - virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override; - - virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; - - virtual ~DynamicFontDataAdvanced() override; -}; - -#endif // MODULE_FREETYPE_ENABLED - -#endif // DYNAMIC_FONT_ADV_H diff --git a/modules/text_server_adv/font_adv.h b/modules/text_server_adv/font_adv.h deleted file mode 100644 index 4fadefc569..0000000000 --- a/modules/text_server_adv/font_adv.h +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************/ -/* font_adv.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FONT_ADV_H -#define FONT_ADV_H - -#include "servers/text_server.h" - -#include <hb.h> - -struct FontDataAdvanced { - Map<String, bool> lang_support_overrides; - Map<String, bool> script_support_overrides; - bool valid = false; - int spacing_space = 0; - int spacing_glyph = 0; - - virtual void clear_cache() = 0; - - virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; }; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; }; - virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; }; - - virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); }; - virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); }; - virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); }; - - virtual float get_height(int p_size) const = 0; - virtual float get_ascent(int p_size) const = 0; - virtual float get_descent(int p_size) const = 0; - - virtual Dictionary get_feature_list() const { return Dictionary(); }; - virtual Dictionary get_variation_list() const { return Dictionary(); }; - - virtual void set_variation(const String &p_name, double p_value){}; - virtual double get_variation(const String &p_name) const { return 0; }; - - virtual float get_underline_position(int p_size) const = 0; - virtual float get_underline_thickness(int p_size) const = 0; - - virtual int get_spacing_space() const { return spacing_space; }; - virtual void set_spacing_space(int p_value) { - spacing_space = p_value; - clear_cache(); - }; - - virtual int get_spacing_glyph() const { return spacing_glyph; }; - virtual void set_spacing_glyph(int p_value) { - spacing_glyph = p_value; - clear_cache(); - }; - - virtual void set_antialiased(bool p_antialiased) = 0; - virtual bool get_antialiased() const = 0; - - virtual void set_hinting(TextServer::Hinting p_hinting) = 0; - virtual TextServer::Hinting get_hinting() const = 0; - - virtual void set_distance_field_hint(bool p_distance_field) = 0; - virtual bool get_distance_field_hint() const = 0; - - virtual void set_force_autohinter(bool p_enabeld) = 0; - virtual bool get_force_autohinter() const = 0; - - virtual bool has_outline() const = 0; - virtual float get_base_size() const = 0; - - virtual bool is_lang_supported(const String &p_lang) const { return true; }; - virtual bool is_script_supported(uint32_t p_script) const { return true; }; - - virtual bool has_char(char32_t p_char) const = 0; - virtual String get_supported_chars() const = 0; - virtual float get_font_scale(int p_size) const { return 1.0f; }; - - virtual hb_font_t *get_hb_handle(int p_size) = 0; - virtual uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector) const = 0; - virtual Vector2 get_advance(uint32_t p_char, int p_size) const = 0; - virtual Vector2 get_kerning(uint32_t p_char, uint32_t p_next, int p_size) const = 0; - - virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0; - virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0; - - virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { return false; }; - - virtual ~FontDataAdvanced(){}; -}; - -#endif // FONT_ADV_H diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 9ecb0de5b8..78a87be971 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -29,15 +29,213 @@ /*************************************************************************/ #include "text_server_adv.h" -#include "bitmap_font_adv.h" -#include "dynamic_font_adv.h" +#include "core/string/print_string.h" #include "core/string/translation.h" #ifdef ICU_STATIC_DATA #include "thirdparty/icu4c/icudata.gen.h" #endif +#ifdef MODULE_MSDFGEN_ENABLED +#include "core/ShapeDistanceFinder.h" +#include "core/contour-combiners.h" +#include "core/edge-selectors.h" +#include "msdfgen.h" +#endif + +/*************************************************************************/ +/* hb_bmp_font_t HarfBuzz Bitmap font interface */ +/*************************************************************************/ + +hb_font_funcs_t *TextServerAdvanced::funcs = nullptr; + +TextServerAdvanced::hb_bmp_font_t *TextServerAdvanced::_hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) { + hb_bmp_font_t *bm_font = memnew(hb_bmp_font_t); + + if (!bm_font) { + return nullptr; + } + + bm_font->face = p_face; + bm_font->unref = p_unref; + + return bm_font; +} + +void TextServerAdvanced::_hb_bmp_font_destroy(void *p_data) { + hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(p_data); + memdelete(bm_font); +} + +hb_bool_t TextServerAdvanced::hb_bmp_get_nominal_glyph(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_unicode, hb_codepoint_t *r_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return false; + } + + if (!bm_font->face->glyph_map.has(p_unicode)) { + if (bm_font->face->glyph_map.has(0xF000u + p_unicode)) { + *r_glyph = 0xF000u + p_unicode; + return true; + } else { + return false; + } + } + + *r_glyph = p_unicode; + return true; +} + +hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return 0; + } + + if (!bm_font->face->glyph_map.has(p_glyph)) { + return 0; + } + + return bm_font->face->glyph_map[p_glyph].advance.x * 64; +} + +hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return 0; + } + + if (!bm_font->face->glyph_map.has(p_glyph)) { + return 0; + } + + return -bm_font->face->glyph_map[p_glyph].advance.y * 64; +} + +hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return 0; + } + + if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) { + return 0; + } + + return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].x * 64; +} + +hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return 0; + } + + if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) { + return 0; + } + + return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].y * 64; +} + +hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return false; + } + + if (!bm_font->face->glyph_map.has(p_glyph)) { + return false; + } + + *r_x = bm_font->face->glyph_map[p_glyph].advance.x * 32; + *r_y = -bm_font->face->ascent * 64; + + return true; +} + +hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return false; + } + + if (!bm_font->face->glyph_map.has(p_glyph)) { + return false; + } + + r_extents->x_bearing = 0; + r_extents->y_bearing = 0; + r_extents->width = bm_font->face->glyph_map[p_glyph].rect.size.x * 64; + r_extents->height = bm_font->face->glyph_map[p_glyph].rect.size.y * 64; + + return true; +} + +hb_bool_t TextServerAdvanced::hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data) { + const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); + + if (!bm_font->face) { + return false; + } + + r_metrics->ascender = bm_font->face->ascent; + r_metrics->descender = bm_font->face->descent; + r_metrics->line_gap = 0; + + return true; +} + +void TextServerAdvanced::hb_bmp_create_font_funcs() { + if (funcs == nullptr) { + funcs = hb_font_funcs_create(); + + hb_font_funcs_set_font_h_extents_func(funcs, hb_bmp_get_font_h_extents, nullptr, nullptr); + hb_font_funcs_set_nominal_glyph_func(funcs, hb_bmp_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advance_func(funcs, hb_bmp_get_glyph_h_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advance_func(funcs, hb_bmp_get_glyph_v_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func(funcs, hb_bmp_get_glyph_v_origin, nullptr, nullptr); + hb_font_funcs_set_glyph_h_kerning_func(funcs, hb_bmp_get_glyph_h_kerning, nullptr, nullptr); + hb_font_funcs_set_glyph_v_kerning_func(funcs, hb_bmp_get_glyph_v_kerning, nullptr, nullptr); + hb_font_funcs_set_glyph_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr); + + hb_font_funcs_make_immutable(funcs); + } +} + +void TextServerAdvanced::hb_bmp_free_font_funcs() { + if (funcs != nullptr) { + hb_font_funcs_destroy(funcs); + funcs = nullptr; + } +} + +void TextServerAdvanced::_hb_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) { + hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_unref), _hb_bmp_font_destroy); +} + +hb_font_t *TextServerAdvanced::hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) { + hb_font_t *font; + hb_face_t *face = hb_face_create(nullptr, 0); + + font = hb_font_create(face); + hb_face_destroy(face); + _hb_bmp_font_set_funcs(font, p_face, false); + return font; +} + +/*************************************************************************/ +/* Character properties. */ +/*************************************************************************/ + _FORCE_INLINE_ bool is_ain(char32_t p_chr) { return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AIN; } @@ -497,7 +695,7 @@ static FeatureInfo feature_set[] = { { 0, String() }, }; -int32_t TextServerAdvanced::name_to_tag(const String &p_name) { +int32_t TextServerAdvanced::name_to_tag(const String &p_name) const { for (int i = 0; feature_set[i].tag != 0; i++) { if (feature_set[i].name == p_name) { return feature_set[i].tag; @@ -508,7 +706,7 @@ int32_t TextServerAdvanced::name_to_tag(const String &p_name) { return hb_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1); } -String TextServerAdvanced::tag_to_name(int32_t p_tag) { +String TextServerAdvanced::tag_to_name(int32_t p_tag) const { for (int i = 0; feature_set[i].tag != 0; i++) { if (feature_set[i].tag == p_tag) { return feature_set[i].name; @@ -523,426 +721,2094 @@ String TextServerAdvanced::tag_to_name(int32_t p_tag) { } /*************************************************************************/ -/* Font interface */ +/* Font Glyph Rendering */ /*************************************************************************/ -RID TextServerAdvanced::create_font_system(const String &p_name, int p_base_size) { - ERR_FAIL_V_MSG(RID(), "System fonts are not supported by this text server."); +_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const { + FontTexturePosition ret; + ret.index = -1; + + int mw = p_width; + int mh = p_height; + + for (int i = 0; i < p_data->textures.size(); i++) { + const FontTexture &ct = p_data->textures[i]; + + if (RenderingServer::get_singleton() != nullptr) { + if (ct.texture->get_format() != p_image_format) { + continue; + } + } + + if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. + continue; + } + + ret.y = 0x7FFFFFFF; + ret.x = 0; + + for (int j = 0; j < ct.texture_w - mw; j++) { + int max_y = 0; + + for (int k = j; k < j + mw; k++) { + int y = ct.offsets[k]; + if (y > max_y) { + max_y = y; + } + } + + if (max_y < ret.y) { + ret.y = max_y; + ret.x = j; + } + } + + if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_h) { + continue; // Fail, could not fit it here. + } + + ret.index = i; + break; + } + + if (ret.index == -1) { + // Could not find texture to fit, create one. + ret.x = 0; + ret.y = 0; + + int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256); + if (mw > texsize) { + texsize = mw; // Special case, adapt to it? + } + if (mh > texsize) { + texsize = mh; // Special case, adapt to it? + } + + texsize = next_power_of_2(texsize); + + texsize = MIN(texsize, 4096); + + FontTexture tex; + tex.texture_w = texsize; + tex.texture_h = texsize; + tex.format = p_image_format; + tex.imgdata.resize(texsize * texsize * p_color_size); + + { + // Zero texture. + uint8_t *w = tex.imgdata.ptrw(); + ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret); + // Initialize the texture to all-white pixels to prevent artifacts when the + // font is displayed at a non-default scale with filtering enabled. + if (p_color_size == 2) { + for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8, BW font. + w[i + 0] = 255; + w[i + 1] = 0; + } + } else if (p_color_size == 4) { + for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8, Color font, Multichannel(+True) SDF. + w[i + 0] = 255; + w[i + 1] = 255; + w[i + 2] = 255; + w[i + 3] = 0; + } + } else { + ERR_FAIL_V(ret); + } + } + tex.offsets.resize(texsize); + for (int i = 0; i < texsize; i++) { // Zero offsets. + tex.offsets.write[i] = 0; + } + + p_data->textures.push_back(tex); + ret.index = p_data->textures.size() - 1; + } + + return ret; } -RID TextServerAdvanced::create_font_resource(const String &p_filename, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = nullptr; - if (p_filename.get_extension() == "fnt" || p_filename.get_extension() == "font") { - fd = memnew(BitmapFontDataAdvanced); +#ifdef MODULE_MSDFGEN_ENABLED + +struct MSContext { + msdfgen::Point2 position; + msdfgen::Shape *shape; + msdfgen::Contour *contour; +}; + +class DistancePixelConversion { + double invRange; + +public: + _FORCE_INLINE_ explicit DistancePixelConversion(double range) : + invRange(1 / range) {} + _FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const { + pixels[0] = float(invRange * distance.r + .5); + pixels[1] = float(invRange * distance.g + .5); + pixels[2] = float(invRange * distance.b + .5); + pixels[3] = float(invRange * distance.a + .5); + } +}; + +struct MSDFThreadData { + msdfgen::Bitmap<float, 4> *output; + msdfgen::Shape *shape; + msdfgen::Projection *projection; + DistancePixelConversion *distancePixelConversion; +}; + +static msdfgen::Point2 ft_point2(const FT_Vector &vector) { + return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f); +} + +static int ft_move_to(const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + if (!(context->contour && context->contour->edges.empty())) { + context->contour = &context->shape->addContour(); + } + context->position = ft_point2(*to); + return 0; +} + +static int ft_line_to(const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + msdfgen::Point2 endpoint = ft_point2(*to); + if (endpoint != context->position) { + context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint)); + context->position = endpoint; + } + return 0; +} + +static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to))); + context->position = ft_point2(*to); + return 0; +} + +static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to))); + context->position = ft_point2(*to); + return 0; +} + +void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const { + MSDFThreadData *td = (MSDFThreadData *)p_td; + + msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape); + int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y; + for (int col = 0; col < td->output->width(); ++col) { + int x = (y % 2) ? td->output->width() - col - 1 : col; + msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5)); + msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p); + td->distancePixelConversion->operator()(td->output->operator()(x, row), distance); + } +} + +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const { + msdfgen::Shape shape; + + shape.contours.clear(); + shape.inverseYAxis = false; + + MSContext context = {}; + context.shape = &shape; + FT_Outline_Funcs ft_functions; + ft_functions.move_to = &ft_move_to; + ft_functions.line_to = &ft_line_to; + ft_functions.conic_to = &ft_conic_to; + ft_functions.cubic_to = &ft_cubic_to; + ft_functions.shift = 0; + ft_functions.delta = 0; + + int error = FT_Outline_Decompose(outline, &ft_functions, &context); + ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'."); + if (!shape.contours.empty() && shape.contours.back().edges.empty()) { + shape.contours.pop_back(); + } + + if (FT_Outline_Get_Orientation(outline) == 1) { + for (int i = 0; i < (int)shape.contours.size(); ++i) { + shape.contours[i].reverse(); + } + } + + shape.inverseYAxis = true; + shape.normalize(); + + msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range); + + FontGlyph chr; + chr.found = true; + chr.advance = advance.round(); + + if (shape.validate() && shape.contours.size() > 0) { + int w = (bounds.r - bounds.l); + int h = (bounds.t - bounds.b); + + int mw = w + p_rect_margin * 2; + int mh = h + p_rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + FontTexture &tex = p_data->textures.write[tex_pos.index]; + + edgeColoringSimple(shape, 3.0); // Max. angle. + msdfgen::Bitmap<float, 4> image(w, h); // Texture size. + //msdfgen::generateMTSDF(image, shape, p_pixel_range, 1.0, msdfgen::Vector2(-bounds.l, -bounds.b)); // Range, scale, translation. + + DistancePixelConversion distancePixelConversion(p_pixel_range); + msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b)); + msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig()); + + MSDFThreadData td; + td.output = ℑ + td.shape = &shape; + td.projection = &projection; + td.distancePixelConversion = &distancePixelConversion; + + if (p_font_data->work_pool.get_thread_count() == 0) { + p_font_data->work_pool.init(); + } + p_font_data->work_pool.do_work(h, this, &TextServerAdvanced::_generateMTSDF_threaded, &td); + + msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config); + + { + uint8_t *wr = tex.imgdata.ptrw(); + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * 4; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph()); + wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f)); + wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f)); + wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f)); + wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f)); + //wr[ofs + 0] = 100; + //wr[ofs + 1] = 100; + //wr[ofs + 2] = 100; + //wr[ofs + 3] = 100; + } + } + } + + // Blit to image and texture. + { + if (RenderingServer::get_singleton() != nullptr) { + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, Image::FORMAT_RGBA8, tex.imgdata)); + if (tex.texture.is_null()) { + tex.texture.instantiate(); + tex.texture->create_from_image(img); + } else { + tex.texture->update(img); + } + } + } + + // Update height array. + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets.write[k] = tex_pos.y + mh; + } + + chr.texture_idx = tex_pos.index; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h); + chr.rect.position = Vector2(bounds.l, -bounds.t); + chr.rect.size = chr.uv_rect.size; + } + return chr; +} +#endif + #ifdef MODULE_FREETYPE_ENABLED - } else if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") { - fd = memnew(DynamicFontDataAdvanced); +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const { + int w = bitmap.width; + int h = bitmap.rows; + + int mw = w + p_rect_margin * 2; + int mh = h + p_rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; + Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + + // Fit character in char texture. + + FontTexture &tex = p_data->textures.write[tex_pos.index]; + + { + uint8_t *wr = tex.imgdata.ptrw(); + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * color_size; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph()); + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: { + int byte = i * bitmap.pitch + (j >> 3); + int bit = 1 << (7 - (j % 8)); + wr[ofs + 0] = 255; //grayscale as 1 + wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0; + } break; + case FT_PIXEL_MODE_GRAY: + wr[ofs + 0] = 255; //grayscale as 1 + wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; + //wr[ofs + 1] = 100; + break; + case FT_PIXEL_MODE_BGRA: { + int ofs_color = i * bitmap.pitch + (j << 2); + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; + } break; + default: + ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + "."); + break; + } + } + } + } + + // Blit to image and texture. + { + if (RenderingServer::get_singleton() != nullptr) { + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, require_format, tex.imgdata)); + + if (tex.texture.is_null()) { + tex.texture.instantiate(); + tex.texture->create_from_image(img); + } else { + tex.texture->update(img); + } + } + } + + // Update height array. + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets.write[k] = tex_pos.y + mh; + } + + FontGlyph chr; + chr.advance = (advance * p_data->scale / p_data->oversampling).round(); + chr.texture_idx = tex_pos.index; + chr.found = true; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h); + chr.rect.position = (Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling).round(); + chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling; + return chr; +} #endif - } else { - return RID(); + +/*************************************************************************/ +/* Font Cache */ +/*************************************************************************/ + +_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false); + + FontDataForSizeAdvanced *fd = p_font_data->cache[p_size]; + if (fd->glyph_map.has(p_glyph)) { + return fd->glyph_map[p_glyph].found; } - Error err = fd->load_from_file(p_filename, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); + if (p_glyph == 0) { // Non graphical or invalid glyph, do not render. + fd->glyph_map[p_glyph] = FontGlyph(); + return true; } - return font_owner.make_rid(fd); +#ifdef MODULE_FREETYPE_ENABLED + FontGlyph gl; + if (fd->face) { + FT_Int32 flags = FT_LOAD_DEFAULT; + + bool outline = p_size.y > 0; + switch (p_font_data->hinting) { + case TextServer::HINTING_NONE: + flags |= FT_LOAD_NO_HINTING; + break; + case TextServer::HINTING_LIGHT: + flags |= FT_LOAD_TARGET_LIGHT; + break; + default: + flags |= FT_LOAD_TARGET_NORMAL; + break; + } + if (p_font_data->force_autohinter) { + flags |= FT_LOAD_FORCE_AUTOHINT; + } + if (outline) { + flags |= FT_LOAD_NO_BITMAP; + } else if (FT_HAS_COLOR(fd->face)) { + flags |= FT_LOAD_COLOR; + } + + FT_Fixed v, h; + FT_Get_Advance(fd->face, p_glyph, flags, &h); + FT_Get_Advance(fd->face, p_glyph, flags | FT_LOAD_VERTICAL_LAYOUT, &v); + + int error = FT_Load_Glyph(fd->face, p_glyph, flags); + if (error) { + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph."); + } + + if (!outline) { + if (!p_font_data->msdf) { + error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + } + FT_GlyphSlot slot = fd->face->glyph; + if (!error) { + if (p_font_data->msdf) { +#ifdef MODULE_MSDFGEN_ENABLED + gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); +#else + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); +#endif + } else { + gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); + } + } + } else { + FT_Stroker stroker; + if (FT_Stroker_New(library, &stroker) != 0) { + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker."); + } + + FT_Stroker_Set(stroker, (int)(fd->size.y * fd->oversampling * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0); + FT_Glyph glyph; + FT_BitmapGlyph glyph_bitmap; + + if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) { + goto cleanup_stroker; + } + if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { + goto cleanup_glyph; + } + if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { + goto cleanup_glyph; + } + glyph_bitmap = (FT_BitmapGlyph)glyph; + gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); + + cleanup_glyph: + FT_Done_Glyph(glyph); + cleanup_stroker: + FT_Stroker_Done(stroker); + } + fd->glyph_map[p_glyph] = gl; + return gl.found; + } +#endif + fd->glyph_map[p_glyph] = FontGlyph(); + return false; } -RID TextServerAdvanced::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = nullptr; - if (p_type == "fnt" || p_type == "font") { - fd = memnew(BitmapFontDataAdvanced); +_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const { + if (p_font_data->cache.has(p_size)) { + return true; + } + + FontDataForSizeAdvanced *fd = memnew(FontDataForSizeAdvanced); + fd->size = p_size; + if (p_font_data->data_ptr) { + // Init dynamic font. #ifdef MODULE_FREETYPE_ENABLED - } else if (p_type == "ttf" || p_type == "otf" || p_type == "woff") { - fd = memnew(DynamicFontDataAdvanced); + int error = 0; + if (!library) { + error = FT_Init_FreeType(&library); + ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'."); + } + + memset(&fd->stream, 0, sizeof(FT_StreamRec)); + fd->stream.base = (unsigned char *)p_font_data->data_ptr; + fd->stream.size = p_font_data->data_size; + fd->stream.pos = 0; + + FT_Open_Args fargs; + memset(&fargs, 0, sizeof(FT_Open_Args)); + fargs.memory_base = (unsigned char *)p_font_data->data_ptr; + fargs.memory_size = p_font_data->data_size; + fargs.flags = FT_OPEN_MEMORY; + fargs.stream = &fd->stream; + error = FT_Open_Face(library, &fargs, 0, &fd->face); + if (error) { + FT_Done_Face(fd->face); + fd->face = nullptr; + ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'."); + } + fd->hb_handle = hb_ft_font_create(fd->face, nullptr); + if (fd->hb_handle == nullptr) { + FT_Done_Face(fd->face); + fd->face = nullptr; + ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating FreeType font object.")); + } + + if (p_font_data->msdf) { + fd->oversampling = 1.0f; + fd->size.x = p_font_data->msdf_source_size; + } else if (p_font_data->oversampling <= 0.0f) { + fd->oversampling = TS->font_get_global_oversampling(); + } else { + fd->oversampling = p_font_data->oversampling; + } + + if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) { + int best_match = 0; + int diff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[0].width)); + fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width; + for (int i = 1; i < fd->face->num_fixed_sizes; i++) { + int ndiff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[i].width)); + if (ndiff < diff) { + best_match = i; + diff = ndiff; + fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width; + } + } + FT_Select_Size(fd->face, best_match); + } else { + FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling); + } + + fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale; + fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale; + fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; + fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; + + if (!p_font_data->face_init) { + // Get supported scripts from OpenType font data. + p_font_data->supported_scripts.clear(); + unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr); + if (count != 0) { + hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t)); + hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags); + for (unsigned int i = 0; i < count; i++) { + p_font_data->supported_scripts.insert(script_tags[i]); + } + memfree(script_tags); + } + count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr); + if (count != 0) { + hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t)); + hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags); + for (unsigned int i = 0; i < count; i++) { + p_font_data->supported_scripts.insert(script_tags[i]); + } + memfree(script_tags); + } + + // Get supported scripts from OS2 table. + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(fd->face, FT_SFNT_OS2); + if (os2) { + if ((os2->ulUnicodeRange1 & 1L << 4) || (os2->ulUnicodeRange1 & 1L << 5) || (os2->ulUnicodeRange1 & 1L << 6) || (os2->ulUnicodeRange1 & 1L << 31) || (os2->ulUnicodeRange2 & 1L << 0) || (os2->ulUnicodeRange2 & 1L << 1) || (os2->ulUnicodeRange2 & 1L << 2) || (os2->ulUnicodeRange2 & 1L << 3) || (os2->ulUnicodeRange2 & 1L << 4) || (os2->ulUnicodeRange2 & 1L << 5) || (os2->ulUnicodeRange2 & 1L << 6) || (os2->ulUnicodeRange2 & 1L << 7) || (os2->ulUnicodeRange2 & 1L << 8) || (os2->ulUnicodeRange2 & 1L << 9) || (os2->ulUnicodeRange2 & 1L << 10) || (os2->ulUnicodeRange2 & 1L << 11) || (os2->ulUnicodeRange2 & 1L << 12) || (os2->ulUnicodeRange2 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 14) || (os2->ulUnicodeRange2 & 1L << 15) || (os2->ulUnicodeRange2 & 1L << 30) || (os2->ulUnicodeRange3 & 1L << 0) || (os2->ulUnicodeRange3 & 1L << 1) || (os2->ulUnicodeRange3 & 1L << 2) || (os2->ulUnicodeRange3 & 1L << 4) || (os2->ulUnicodeRange3 & 1L << 5) || (os2->ulUnicodeRange3 & 1L << 18) || (os2->ulUnicodeRange3 & 1L << 24) || (os2->ulUnicodeRange3 & 1L << 25) || (os2->ulUnicodeRange3 & 1L << 26) || (os2->ulUnicodeRange3 & 1L << 27) || (os2->ulUnicodeRange3 & 1L << 28) || (os2->ulUnicodeRange4 & 1L << 3) || (os2->ulUnicodeRange4 & 1L << 6) || (os2->ulUnicodeRange4 & 1L << 15) || (os2->ulUnicodeRange4 & 1L << 23) || (os2->ulUnicodeRange4 & 1L << 24) || (os2->ulUnicodeRange4 & 1L << 26)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_COMMON); + } + if ((os2->ulUnicodeRange1 & 1L << 0) || (os2->ulUnicodeRange1 & 1L << 1) || (os2->ulUnicodeRange1 & 1L << 2) || (os2->ulUnicodeRange1 & 1L << 3) || (os2->ulUnicodeRange1 & 1L << 29)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LATIN); + } + if ((os2->ulUnicodeRange1 & 1L << 7) || (os2->ulUnicodeRange1 & 1L << 30)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GREEK); + } + if (os2->ulUnicodeRange1 & 1L << 8) { + p_font_data->supported_scripts.insert(HB_SCRIPT_COPTIC); + } + if (os2->ulUnicodeRange1 & 1L << 9) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CYRILLIC); + } + if (os2->ulUnicodeRange1 & 1L << 10) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ARMENIAN); + } + if (os2->ulUnicodeRange1 & 1L << 11) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HEBREW); + } + if (os2->ulUnicodeRange1 & 1L << 12) { + p_font_data->supported_scripts.insert(HB_SCRIPT_VAI); + } + if ((os2->ulUnicodeRange1 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 31) || (os2->ulUnicodeRange3 & 1L << 3)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ARABIC); + } + if (os2->ulUnicodeRange1 & 1L << 14) { + p_font_data->supported_scripts.insert(HB_SCRIPT_NKO); + } + if (os2->ulUnicodeRange1 & 1L << 15) { + p_font_data->supported_scripts.insert(HB_SCRIPT_DEVANAGARI); + } + if (os2->ulUnicodeRange1 & 1L << 16) { + p_font_data->supported_scripts.insert(HB_SCRIPT_BENGALI); + } + if (os2->ulUnicodeRange1 & 1L << 17) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GURMUKHI); + } + if (os2->ulUnicodeRange1 & 1L << 18) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GUJARATI); + } + if (os2->ulUnicodeRange1 & 1L << 19) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ORIYA); + } + if (os2->ulUnicodeRange1 & 1L << 20) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TAMIL); + } + if (os2->ulUnicodeRange1 & 1L << 21) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TELUGU); + } + if (os2->ulUnicodeRange1 & 1L << 22) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KANNADA); + } + if (os2->ulUnicodeRange1 & 1L << 23) { + p_font_data->supported_scripts.insert(HB_SCRIPT_MALAYALAM); + } + if (os2->ulUnicodeRange1 & 1L << 24) { + p_font_data->supported_scripts.insert(HB_SCRIPT_THAI); + } + if (os2->ulUnicodeRange1 & 1L << 25) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LAO); + } + if (os2->ulUnicodeRange1 & 1L << 26) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GEORGIAN); + } + if (os2->ulUnicodeRange1 & 1L << 27) { + p_font_data->supported_scripts.insert(HB_SCRIPT_BALINESE); + } + if ((os2->ulUnicodeRange1 & 1L << 28) || (os2->ulUnicodeRange2 & 1L << 20) || (os2->ulUnicodeRange2 & 1L << 24)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HANGUL); + } + if ((os2->ulUnicodeRange2 & 1L << 21) || (os2->ulUnicodeRange2 & 1L << 22) || (os2->ulUnicodeRange2 & 1L << 23) || (os2->ulUnicodeRange2 & 1L << 26) || (os2->ulUnicodeRange2 & 1L << 27) || (os2->ulUnicodeRange2 & 1L << 29)) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HAN); + } + if (os2->ulUnicodeRange2 & 1L << 17) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HIRAGANA); + } + if (os2->ulUnicodeRange2 & 1L << 18) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KATAKANA); + } + if (os2->ulUnicodeRange2 & 1L << 19) { + p_font_data->supported_scripts.insert(HB_SCRIPT_BOPOMOFO); + } + if (os2->ulUnicodeRange3 & 1L << 6) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TIBETAN); + } + if (os2->ulUnicodeRange3 & 1L << 7) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SYRIAC); + } + if (os2->ulUnicodeRange3 & 1L << 8) { + p_font_data->supported_scripts.insert(HB_SCRIPT_THAANA); + } + if (os2->ulUnicodeRange3 & 1L << 9) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SINHALA); + } + if (os2->ulUnicodeRange3 & 1L << 10) { + p_font_data->supported_scripts.insert(HB_SCRIPT_MYANMAR); + } + if (os2->ulUnicodeRange3 & 1L << 11) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ETHIOPIC); + } + if (os2->ulUnicodeRange3 & 1L << 12) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CHEROKEE); + } + if (os2->ulUnicodeRange3 & 1L << 13) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CANADIAN_SYLLABICS); + } + if (os2->ulUnicodeRange3 & 1L << 14) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OGHAM); + } + if (os2->ulUnicodeRange3 & 1L << 15) { + p_font_data->supported_scripts.insert(HB_SCRIPT_RUNIC); + } + if (os2->ulUnicodeRange3 & 1L << 16) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KHMER); + } + if (os2->ulUnicodeRange3 & 1L << 17) { + p_font_data->supported_scripts.insert(HB_SCRIPT_MONGOLIAN); + } + if (os2->ulUnicodeRange3 & 1L << 19) { + p_font_data->supported_scripts.insert(HB_SCRIPT_YI); + } + if (os2->ulUnicodeRange3 & 1L << 20) { + p_font_data->supported_scripts.insert(HB_SCRIPT_HANUNOO); + p_font_data->supported_scripts.insert(HB_SCRIPT_TAGBANWA); + p_font_data->supported_scripts.insert(HB_SCRIPT_BUHID); + p_font_data->supported_scripts.insert(HB_SCRIPT_TAGALOG); + } + if (os2->ulUnicodeRange3 & 1L << 21) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OLD_ITALIC); + } + if (os2->ulUnicodeRange3 & 1L << 22) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GOTHIC); + } + if (os2->ulUnicodeRange3 & 1L << 23) { + p_font_data->supported_scripts.insert(HB_SCRIPT_DESERET); + } + if (os2->ulUnicodeRange3 & 1L << 29) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LIMBU); + } + if (os2->ulUnicodeRange3 & 1L << 30) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TAI_LE); + } + if (os2->ulUnicodeRange3 & 1L << 31) { + p_font_data->supported_scripts.insert(HB_SCRIPT_NEW_TAI_LUE); + } + if (os2->ulUnicodeRange4 & 1L << 0) { + p_font_data->supported_scripts.insert(HB_SCRIPT_BUGINESE); + } + if (os2->ulUnicodeRange4 & 1L << 1) { + p_font_data->supported_scripts.insert(HB_SCRIPT_GLAGOLITIC); + } + if (os2->ulUnicodeRange4 & 1L << 2) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TIFINAGH); + } + if (os2->ulUnicodeRange4 & 1L << 4) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SYLOTI_NAGRI); + } + if (os2->ulUnicodeRange4 & 1L << 5) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LINEAR_B); + } + if (os2->ulUnicodeRange4 & 1L << 7) { + p_font_data->supported_scripts.insert(HB_SCRIPT_UGARITIC); + } + if (os2->ulUnicodeRange4 & 1L << 8) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OLD_PERSIAN); + } + if (os2->ulUnicodeRange4 & 1L << 9) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SHAVIAN); + } + if (os2->ulUnicodeRange4 & 1L << 10) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OSMANYA); + } + if (os2->ulUnicodeRange4 & 1L << 11) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CYPRIOT); + } + if (os2->ulUnicodeRange4 & 1L << 12) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KHAROSHTHI); + } + if (os2->ulUnicodeRange4 & 1L << 13) { + p_font_data->supported_scripts.insert(HB_SCRIPT_TAI_VIET); + } + if (os2->ulUnicodeRange4 & 1L << 14) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CUNEIFORM); + } + if (os2->ulUnicodeRange4 & 1L << 16) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SUNDANESE); + } + if (os2->ulUnicodeRange4 & 1L << 17) { + p_font_data->supported_scripts.insert(HB_SCRIPT_LEPCHA); + } + if (os2->ulUnicodeRange4 & 1L << 18) { + p_font_data->supported_scripts.insert(HB_SCRIPT_OL_CHIKI); + } + if (os2->ulUnicodeRange4 & 1L << 19) { + p_font_data->supported_scripts.insert(HB_SCRIPT_SAURASHTRA); + } + if (os2->ulUnicodeRange4 & 1L << 20) { + p_font_data->supported_scripts.insert(HB_SCRIPT_KAYAH_LI); + } + if (os2->ulUnicodeRange4 & 1L << 21) { + p_font_data->supported_scripts.insert(HB_SCRIPT_REJANG); + } + if (os2->ulUnicodeRange4 & 1L << 22) { + p_font_data->supported_scripts.insert(HB_SCRIPT_CHAM); + } + if (os2->ulUnicodeRange4 & 1L << 25) { + p_font_data->supported_scripts.insert(HB_SCRIPT_ANATOLIAN_HIEROGLYPHS); + } + } + + // Read OpenType feature tags. + p_font_data->supported_features.clear(); + count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr); + if (count != 0) { + hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t)); + hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags); + for (unsigned int i = 0; i < count; i++) { + p_font_data->supported_features[feature_tags[i]] = 1; + } + memfree(feature_tags); + } + count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr); + if (count != 0) { + hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t)); + hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags); + for (unsigned int i = 0; i < count; i++) { + p_font_data->supported_features[feature_tags[i]] = 1; + } + memfree(feature_tags); + } + + // Read OpenType variations. + p_font_data->supported_varaitions.clear(); + if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + FT_Get_MM_Var(fd->face, &amaster); + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + p_font_data->supported_varaitions[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536); + } + FT_Done_MM_Var(library, amaster); + } + p_font_data->face_init = true; + } + + // Write variations. + if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + + FT_Get_MM_Var(fd->face, &amaster); + + Vector<hb_variation_t> hb_vars; + Vector<FT_Fixed> coords; + coords.resize(amaster->num_axis); + + FT_Get_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw()); + + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + hb_variation_t var; + + // Reset to default. + var.tag = amaster->axis[i].tag; + var.value = (double)amaster->axis[i].def / 65536.f; + coords.write[i] = amaster->axis[i].def; + + if (p_font_data->variation_coordinates.has(var.tag)) { + var.value = p_font_data->variation_coordinates[var.tag]; + coords.write[i] = CLAMP(var.value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + + if (p_font_data->variation_coordinates.has(tag_to_name(var.tag))) { + var.value = p_font_data->variation_coordinates[tag_to_name(var.tag)]; + coords.write[i] = CLAMP(var.value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + + hb_vars.push_back(var); + } + + FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw()); + hb_font_set_variations(fd->hb_handle, hb_vars.is_empty() ? nullptr : &hb_vars[0], hb_vars.size()); + FT_Done_MM_Var(library, amaster); + } +#else + ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } else { - return RID(); + // Init bitmap font. + fd->hb_handle = hb_bmp_font_create(fd, nullptr); + if (!fd->hb_handle) { + ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating bitmap font object.")); + } } + p_font_data->cache[p_size] = fd; + return true; +} - Error err = fd->load_from_memory(p_data, p_size, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); +_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_font_data) { + for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = p_font_data->cache.front(); E; E = E->next()) { + memdelete(E->get()); } + p_font_data->cache.clear(); + p_font_data->face_init = false; + p_font_data->supported_features.clear(); + p_font_data->supported_varaitions.clear(); + p_font_data->supported_scripts.clear(); +} + +hb_font_t *TextServerAdvanced::_font_get_hb_handle(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, nullptr); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), nullptr); + + return fd->cache[size]->hb_handle; +} + +RID TextServerAdvanced::create_font() { + FontDataAdvanced *fd = memnew(FontDataAdvanced); return font_owner.make_rid(fd); } -RID TextServerAdvanced::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = memnew(BitmapFontDataAdvanced); - Error err = fd->bitmap_new(p_height, p_ascent, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); +void TextServerAdvanced::font_set_data(RID p_font_rid, const PackedByteArray &p_data) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + _font_clear_cache(fd); + fd->data = p_data; + fd->data_ptr = fd->data.ptr(); + fd->data_size = fd->data.size(); +} + +void TextServerAdvanced::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + _font_clear_cache(fd); + fd->data.clear(); + fd->data_ptr = p_data_ptr; + fd->data_size = p_data_size; +} + +void TextServerAdvanced::font_set_antialiased(RID p_font_rid, bool p_antialiased) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->antialiased != p_antialiased) { + _font_clear_cache(fd); + fd->antialiased = p_antialiased; } +} - return font_owner.make_rid(fd); +bool TextServerAdvanced::font_is_antialiased(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->antialiased; } -void TextServerAdvanced::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_texture(p_texture); + + MutexLock lock(fd->mutex); + if (fd->msdf != p_msdf) { + _font_clear_cache(fd); + fd->msdf = p_msdf; + } } -void TextServerAdvanced::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +bool TextServerAdvanced::font_is_multichannel_signed_distance_field(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf; +} + +void TextServerAdvanced::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance); + + MutexLock lock(fd->mutex); + if (fd->msdf_range != p_msdf_pixel_range) { + _font_clear_cache(fd); + fd->msdf_range = p_msdf_pixel_range; + } } -void TextServerAdvanced::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +int TextServerAdvanced::font_get_msdf_pixel_range(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf_range; +} + +void TextServerAdvanced::font_set_msdf_size(RID p_font_rid, int p_msdf_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning); + + MutexLock lock(fd->mutex); + if (fd->msdf_source_size != p_msdf_size) { + _font_clear_cache(fd); + fd->msdf_source_size = p_msdf_size; + } } -float TextServerAdvanced::font_get_height(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +int TextServerAdvanced::font_get_msdf_size(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf_source_size; +} + +void TextServerAdvanced::font_set_fixed_size(RID p_font_rid, int p_fixed_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->fixed_size != p_fixed_size) { + fd->fixed_size = p_fixed_size; + } +} + +int TextServerAdvanced::font_get_fixed_size(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->fixed_size; +} + +void TextServerAdvanced::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->force_autohinter != p_force_autohinter) { + _font_clear_cache(fd); + fd->force_autohinter = p_force_autohinter; + } +} + +bool TextServerAdvanced::font_is_force_autohinter(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->force_autohinter; +} + +void TextServerAdvanced::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->hinting != p_hinting) { + _font_clear_cache(fd); + fd->hinting = p_hinting; + } +} + +TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, HINTING_NONE); + + MutexLock lock(fd->mutex); + return fd->hinting; +} + +void TextServerAdvanced::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->variation_coordinates != p_variation_coordinates) { + _font_clear_cache(fd); + fd->variation_coordinates = p_variation_coordinates; + } +} + +Dictionary TextServerAdvanced::font_get_variation_coordinates(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); + + MutexLock lock(fd->mutex); + return fd->variation_coordinates; +} + +void TextServerAdvanced::font_set_oversampling(RID p_font_rid, real_t p_oversampling) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->oversampling != p_oversampling) { + _font_clear_cache(fd); + fd->oversampling = p_oversampling; + } +} + +real_t TextServerAdvanced::font_get_oversampling(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0.f); + + MutexLock lock(fd->mutex); + return fd->oversampling; +} + +Array TextServerAdvanced::font_get_size_cache_list(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Array ret; + for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = fd->cache.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +void TextServerAdvanced::font_clear_size_cache(RID p_font_rid) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = fd->cache.front(); E; E = E->next()) { + memdelete(E->get()); + } + fd->cache.clear(); +} + +void TextServerAdvanced::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->cache.has(p_size)) { + memdelete(fd->cache[p_size]); + fd->cache.erase(p_size); + } +} + +void TextServerAdvanced::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->ascent = p_ascent; +} + +real_t TextServerAdvanced::font_get_ascent(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_height(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->ascent * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->ascent; + } } -float TextServerAdvanced::font_get_ascent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->descent = p_descent; +} + +real_t TextServerAdvanced::font_get_descent(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_ascent(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->descent * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->descent; + } } -float TextServerAdvanced::font_get_descent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->underline_position = p_underline_position; +} + +real_t TextServerAdvanced::font_get_underline_position(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_descent(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->underline_position * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->underline_position; + } } -float TextServerAdvanced::font_get_underline_position(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->underline_thickness = p_underline_thickness; +} + +real_t TextServerAdvanced::font_get_underline_thickness(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_position(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->underline_thickness * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->underline_thickness; + } } -float TextServerAdvanced::font_get_underline_thickness(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->scale = p_scale; +} + +real_t TextServerAdvanced::font_get_scale(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_thickness(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->scale * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->scale / fd->cache[size]->oversampling; + } } -int TextServerAdvanced::font_get_spacing_space(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + switch (p_spacing) { + case TextServer::SPACING_GLYPH: { + fd->cache[size]->spacing_glyph = p_value; + } break; + case TextServer::SPACING_SPACE: { + fd->cache[size]->spacing_space = p_value; + } break; + default: { + ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing)); + } break; + } +} + +int TextServerAdvanced::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0); - return fd->get_spacing_space(); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + + switch (p_spacing) { + case TextServer::SPACING_GLYPH: { + if (fd->msdf) { + return fd->cache[size]->spacing_glyph * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->spacing_glyph; + } + } break; + case TextServer::SPACING_SPACE: { + if (fd->msdf) { + return fd->cache[size]->spacing_space * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->spacing_space; + } + } break; + default: { + ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing)); + } break; + } + return 0; } -void TextServerAdvanced::font_set_spacing_space(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +int TextServerAdvanced::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + + return fd->cache[size]->textures.size(); +} + +void TextServerAdvanced::font_clear_textures(RID p_font_rid, const Vector2i &p_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_spacing_space(p_value); + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->textures.clear(); } -int TextServerAdvanced::font_get_spacing_glyph(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0); - return fd->get_spacing_glyph(); +void TextServerAdvanced::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size()); + + fd->cache[size]->textures.remove(p_texture_index); } -void TextServerAdvanced::font_set_spacing_glyph(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_spacing_glyph(p_value); + ERR_FAIL_COND(p_image.is_null()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_COND(p_texture_index < 0); + if (p_texture_index >= fd->cache[size]->textures.size()) { + fd->cache[size]->textures.resize(p_texture_index + 1); + } + + FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + + tex.imgdata = p_image->get_data(); + tex.texture_w = p_image->get_width(); + tex.texture_h = p_image->get_height(); + tex.format = p_image->get_format(); + + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + tex.texture = Ref<ImageTexture>(); + tex.texture.instantiate(); + tex.texture->create_from_image(img); } -void TextServerAdvanced::font_set_antialiased(RID p_font, bool p_antialiased) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +Ref<Image> TextServerAdvanced::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Ref<Image>()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + + return img; +} + +void TextServerAdvanced::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_antialiased(p_antialiased); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (p_texture_index >= fd->cache[size]->textures.size()) { + fd->cache[size]->textures.resize(p_texture_index + 1); + } + + FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + tex.offsets = p_offset; } -Dictionary TextServerAdvanced::font_get_feature_list(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Dictionary()); - return fd->get_feature_list(); +PackedInt32Array TextServerAdvanced::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, PackedInt32Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + return tex.offsets; } -bool TextServerAdvanced::font_get_antialiased(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_antialiased(); +Array TextServerAdvanced::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + + Array ret; + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + ret.push_back(*E); + } + return ret; } -Dictionary TextServerAdvanced::font_get_variation_list(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Dictionary()); - return fd->get_variation_list(); +void TextServerAdvanced::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + fd->cache[size]->glyph_map.clear(); } -void TextServerAdvanced::font_set_variation(RID p_font, const String &p_name, double p_value) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_variation(p_name, p_value); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + fd->cache[size]->glyph_map.erase(p_glyph); } -double TextServerAdvanced::font_get_variation(RID p_font, const String &p_name) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0); - return fd->get_variation(p_name); +Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].advance * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].advance; + } } -void TextServerAdvanced::font_set_distance_field_hint(RID p_font, bool p_distance_field) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_distance_field_hint(p_distance_field); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].advance = p_advance; + gl[p_glyph].found = true; } -bool TextServerAdvanced::font_get_distance_field_hint(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_distance_field_hint(); +Vector2 TextServerAdvanced::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].rect.position * (real_t)p_size.x / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].rect.position; + } } -void TextServerAdvanced::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_hinting(p_hinting); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.position = p_offset; + gl[p_glyph].found = true; } -TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, TextServer::HINTING_NONE); - return fd->get_hinting(); +Vector2 TextServerAdvanced::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].rect.size * (real_t)p_size.x / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].rect.size; + } } -void TextServerAdvanced::font_set_force_autohinter(RID p_font, bool p_enabeld) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_force_autohinter(p_enabeld); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.size = p_gl_size; + gl[p_glyph].found = true; } -bool TextServerAdvanced::font_get_force_autohinter(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +Rect2 TextServerAdvanced::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Rect2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Rect2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + return gl[p_glyph].uv_rect; +} + +void TextServerAdvanced::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].uv_rect = p_uv_rect; + gl[p_glyph].found = true; +} + +int TextServerAdvanced::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, -1); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); + if (!_ensure_glyph(fd, size, p_glyph)) { + return -1; // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + return gl[p_glyph].texture_idx; +} + +void TextServerAdvanced::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].texture_idx = p_texture_idx; + gl[p_glyph].found = true; +} + +bool TextServerAdvanced::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->get_force_autohinter(); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false); + +#ifdef MODULE_FREETYPE_ENABLED + int error = FT_Load_Glyph(fd->cache[size]->face, p_index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + ERR_FAIL_COND_V(error, false); + + r_points.clear(); + r_contours.clear(); + + real_t h = fd->cache[size]->ascent; + real_t scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale; + if (fd->msdf) { + scale = scale * (real_t)p_size / (real_t)fd->msdf_source_size; + } + for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) { + r_points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i]))); + } + for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) { + r_contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]); + } + r_orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); +#else + return false; +#endif + return true; } -bool TextServerAdvanced::font_has_char(RID p_font, char32_t p_char) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +Array TextServerAdvanced::font_get_kerning_list(RID p_font_rid, int p_size) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + + Array ret; + for (const Map<Vector2i, Vector2>::Element *E = fd->cache[size]->kerning_map.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +void TextServerAdvanced::font_clear_kerning_map(RID p_font_rid, int p_size) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map.clear(); +} + +void TextServerAdvanced::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map.erase(p_glyph_pair); +} + +void TextServerAdvanced::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning; +} + +Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + + const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map; + + if (kern.has(p_glyph_pair)) { + if (fd->msdf) { + return kern[p_glyph_pair] * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return kern[p_glyph_pair]; + } + } else { +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + FT_Vector delta; + FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta); + if (fd->msdf) { + return Vector2(delta.x, delta.y) * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return Vector2(delta.x, delta.y); + } + } +#endif + } + return Vector2(); +} + +int32_t TextServerAdvanced::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + if (p_variation_selector) { + return FT_Face_GetCharVariantIndex(fd->cache[size]->face, p_char, p_variation_selector); + } else { + return FT_Get_Char_Index(fd->cache[size]->face, p_char); + } + } else { + return 0; + } +#else + return (int32_t)p_char; +#endif +} + +bool TextServerAdvanced::font_has_char(RID p_font_rid, char32_t p_char) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->has_char(p_char); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false); + } + FontDataForSizeAdvanced *at_size = fd->cache.front()->get(); + +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + return FT_Get_Char_Index(at_size->face, p_char) != 0; + } +#endif + return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false; } -String TextServerAdvanced::font_get_supported_chars(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +String TextServerAdvanced::font_get_supported_chars(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, String()); - return fd->get_supported_chars(); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String()); + } + FontDataForSizeAdvanced *at_size = fd->cache.front()->get(); + + String chars; +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + FT_UInt gindex; + FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + while (gindex != 0) { + if (charcode != 0) { + chars += char32_t(charcode); + } + charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + } + return chars; + } +#endif + if (at_size) { + const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + chars += char32_t(*E); + } + } + return chars; } -bool TextServerAdvanced::font_has_outline(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->has_outline(); +void TextServerAdvanced::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + for (char32_t i = p_start; i <= p_end; i++) { +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + _ensure_glyph(fd, size, FT_Get_Char_Index(fd->cache[size]->face, i)); + continue; + } +#endif + _ensure_glyph(fd, size, (int32_t)i); + } } -float TextServerAdvanced::font_get_base_size(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_base_size(); +void TextServerAdvanced::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_COND(!_ensure_glyph(fd, size, p_index)); } -bool TextServerAdvanced::font_is_language_supported(RID p_font, const String &p_language) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - if (fd->lang_support_overrides.has(p_language)) { - return fd->lang_support_overrides[p_language]; - } else { - Vector<String> tags = p_language.replace("-", "_").split("_"); - if (tags.size() > 0) { - if (fd->lang_support_overrides.has(tags[0])) { - return fd->lang_support_overrides[tags[0]]; +void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (!_ensure_glyph(fd, size, p_index)) { + return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw. + } + + const FontGlyph &gl = fd->cache[size]->glyph_map[p_index]; + if (gl.found) { + ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + + if (gl.texture_idx != -1) { + Color modulate = p_color; +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } +#endif + if (RenderingServer::get_singleton() != nullptr) { + RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + if (fd->msdf) { + Point2 cpos = p_pos; + cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size; + Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range); + } else { + Point2i cpos = p_pos; + cpos += gl.rect.position; + Size2i csize = gl.rect.size; + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } - return fd->is_lang_supported(p_language); } } -void TextServerAdvanced::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides[p_language] = p_supported; + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size)); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (!_ensure_glyph(fd, size, p_index)) { + return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw. + } + + const FontGlyph &gl = fd->cache[size]->glyph_map[p_index]; + if (gl.found) { + ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + + if (gl.texture_idx != -1) { + Color modulate = p_color; +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } +#endif + if (RenderingServer::get_singleton() != nullptr) { + RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + if (fd->msdf) { + Point2 cpos = p_pos; + cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size; + Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range); + } else { + Point2i cpos = p_pos; + cpos += gl.rect.position; + Size2i csize = gl.rect.size; + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } + } + } + } } -bool TextServerAdvanced::font_get_language_support_override(RID p_font, const String &p_language) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +bool TextServerAdvanced::font_is_language_supported(RID p_font_rid, const String &p_language) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->lang_support_overrides[p_language]; + + MutexLock lock(fd->mutex); + if (fd->language_support_overrides.has(p_language)) { + return fd->language_support_overrides[p_language]; + } else { + return true; + } } -void TextServerAdvanced::font_remove_language_support_override(RID p_font, const String &p_language) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides.erase(p_language); + + MutexLock lock(fd->mutex); + fd->language_support_overrides[p_language] = p_supported; } -Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +bool TextServerAdvanced::font_get_language_support_override(RID p_font_rid, const String &p_language) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->language_support_overrides[p_language]; +} + +void TextServerAdvanced::font_remove_language_support_override(RID p_font_rid, const String &p_language) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + fd->language_support_overrides.erase(p_language); +} + +Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID p_font_rid) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, Vector<String>()); - Vector<String> ret; - for (Map<String, bool>::Element *E = fd->lang_support_overrides.front(); E; E = E->next()) { - ret.push_back(E->key()); + + MutexLock lock(fd->mutex); + Vector<String> out; + for (const Map<String, bool>::Element *E = fd->language_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); } - return ret; + return out; } -bool TextServerAdvanced::font_is_script_supported(RID p_font, const String &p_script) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); +bool TextServerAdvanced::font_is_script_supported(RID p_font_rid, const String &p_script) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); if (fd->script_support_overrides.has(p_script)) { return fd->script_support_overrides[p_script]; } else { - hb_script_t scr = hb_script_from_string(p_script.ascii().get_data(), -1); - return fd->is_script_supported(scr); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false); + return fd->supported_scripts.has(TS->name_to_tag(p_script)); } } -void TextServerAdvanced::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); fd->script_support_overrides[p_script] = p_supported; } -bool TextServerAdvanced::font_get_script_support_override(RID p_font, const String &p_script) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +bool TextServerAdvanced::font_get_script_support_override(RID p_font_rid, const String &p_script) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); return fd->script_support_overrides[p_script]; } -void TextServerAdvanced::font_remove_script_support_override(RID p_font, const String &p_script) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +void TextServerAdvanced::font_remove_script_support_override(RID p_font_rid, const String &p_script) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); fd->script_support_overrides.erase(p_script); } -Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataAdvanced *fd = font_owner.getornull(p_font); +Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font_rid) { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, Vector<String>()); - Vector<String> ret; - for (Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { - ret.push_back(E->key()); - } - return ret; -} -uint32_t TextServerAdvanced::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0); - return fd->get_glyph_index(p_char, p_variation_selector); + MutexLock lock(fd->mutex); + Vector<String> out; + for (const Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); + } + return out; } -Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector2()); - return fd->get_advance(p_index, p_size); -} +Dictionary TextServerAdvanced::font_supported_feature_list(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); -Vector2 TextServerAdvanced::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector2()); - return fd->get_kerning(p_index_a, p_index_b, p_size); + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + return fd->supported_features; } -Vector2 TextServerAdvanced::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector2()); - return fd->draw_glyph(p_canvas, p_size, p_pos, p_index, p_color); -} - -Vector2 TextServerAdvanced::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector2()); - return fd->draw_glyph_outline(p_canvas, p_size, p_outline_size, p_pos, p_index, p_color); -} +Dictionary TextServerAdvanced::font_supported_variation_list(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); -bool TextServerAdvanced::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - const FontDataAdvanced *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_glyph_contours(p_size, p_index, r_points, r_contours, r_orientation); + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + return fd->supported_varaitions; } -float TextServerAdvanced::font_get_oversampling() const { +real_t TextServerAdvanced::font_get_global_oversampling() const { return oversampling; } -void TextServerAdvanced::font_set_oversampling(float p_oversampling) { +void TextServerAdvanced::font_set_global_oversampling(real_t p_oversampling) { _THREAD_SAFE_METHOD_ if (oversampling != p_oversampling) { oversampling = p_oversampling; List<RID> fonts; font_owner.get_owned_list(&fonts); + bool font_cleared = false; for (const RID &E : fonts) { - font_owner.getornull(E)->clear_cache(); + if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) { + font_clear_size_cache(E); + font_cleared = true; + } } - List<RID> text_bufs; - shaped_owner.get_owned_list(&text_bufs); - for (const RID &E : text_bufs) { - invalidate(shaped_owner.getornull(E)); + if (font_cleared) { + List<RID> text_bufs; + shaped_owner.get_owned_list(&text_bufs); + for (const RID &E : text_bufs) { + invalidate(shaped_owner.getornull(E)); + } } } } -Vector<String> TextServerAdvanced::get_system_fonts() const { - return Vector<String>(); -} - /*************************************************************************/ /* Shaped text buffer interface */ /*************************************************************************/ @@ -1029,10 +2895,10 @@ RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, Te } void TextServerAdvanced::shaped_text_clear(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + MutexLock lock(sd->mutex); sd->parent = RID(); sd->start = 0; sd->end = 0; @@ -1044,10 +2910,10 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) { } void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + MutexLock lock(sd->mutex); if (sd->direction != p_direction) { if (sd->parent != RID()) { full_copy(sd); @@ -1058,16 +2924,18 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir } TextServer::Direction TextServerAdvanced::shaped_text_get_direction(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR); + + MutexLock lock(sd->mutex); return sd->direction; } void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + + MutexLock lock(sd->mutex); if (sd->parent != RID()) { full_copy(sd); } @@ -1076,9 +2944,10 @@ void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Vecto } void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + + MutexLock lock(sd->mutex); if (sd->orientation != p_orientation) { if (sd->parent != RID()) { full_copy(sd); @@ -1089,9 +2958,10 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O } void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + + MutexLock lock(sd->mutex); ERR_FAIL_COND(sd->parent != RID()); if (sd->preserve_invalid != p_enabled) { sd->preserve_invalid = p_enabled; @@ -1100,16 +2970,18 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e } bool TextServerAdvanced::shaped_text_get_preserve_invalid(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); return sd->preserve_invalid; } void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + + MutexLock lock(sd->mutex); if (sd->preserve_control != p_enabled) { if (sd->parent != RID()) { full_copy(sd); @@ -1120,25 +2992,31 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e } bool TextServerAdvanced::shaped_text_get_preserve_control(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); return sd->preserve_control; } TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL); + + MutexLock lock(sd->mutex); return sd->orientation; } bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); ERR_FAIL_COND_V(p_size <= 0, false); + MutexLock lock(sd->mutex); + for (int i = 0; i < p_fonts.size(); i++) { + ERR_FAIL_COND_V(!font_owner.getornull(p_fonts[i]), false); + } + if (p_text.is_empty()) { return true; } @@ -1194,9 +3072,10 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con } bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), false); sd->objects[p_key].rect.size = p_size; sd->objects[p_key].inline_align = p_inline_align; @@ -1231,14 +3110,13 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->glyphs.write[i].advance = sd->objects[key].rect.size.y; } } else { - const FontDataAdvanced *fd = font_owner.getornull(gl.font_rid); - if (fd != nullptr) { + if (gl.font_rid.is_valid()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - sd->ascent = MAX(sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off)); - sd->descent = MAX(sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off)); + sd->ascent = MAX(sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off)); + sd->descent = MAX(sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off)); } else { - sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); - sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size)); sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size)); @@ -1257,8 +3135,8 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, } // Align embedded objects to baseline. - float full_ascent = sd->ascent; - float full_descent = sd->descent; + real_t full_ascent = sd->ascent; + real_t full_descent = sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { @@ -1327,9 +3205,10 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, } RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, RID()); + + MutexLock lock(sd->mutex); if (sd->parent != RID()) { return shaped_text_substr(sd->parent, p_start, p_length); } @@ -1363,9 +3242,6 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng int sd_size = sd->glyphs.size(); const Glyph *sd_glyphs = sd->glyphs.ptr(); - const FontDataAdvanced *fd = nullptr; - RID prev_rid = RID(); - for (int ov = 0; ov < sd->bidi_override.size(); ov++) { UErrorCode err = U_ZERO_ERROR; @@ -1423,17 +3299,13 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng new_sd->width += new_sd->objects[key].rect.size.y; } } else { - if (prev_rid != gl.font_rid) { - fd = font_owner.getornull(gl.font_rid); - prev_rid = gl.font_rid; - } - if (fd != nullptr) { + if (gl.font_rid.is_valid()) { if (new_sd->orientation == ORIENTATION_HORIZONTAL) { - new_sd->ascent = MAX(new_sd->ascent, MAX(fd->get_ascent(gl.font_size), -gl.y_off)); - new_sd->descent = MAX(new_sd->descent, MAX(fd->get_descent(gl.font_size), gl.y_off)); + new_sd->ascent = MAX(new_sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off)); + new_sd->descent = MAX(new_sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off)); } else { - new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); - new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + new_sd->ascent = MAX(new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + new_sd->descent = MAX(new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } } else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) { // Glyph not found, replace with hex code box. @@ -1454,8 +3326,8 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng } // Align embedded objects to baseline. - float full_ascent = new_sd->ascent; - float full_descent = new_sd->descent; + real_t full_ascent = new_sd->ascent; + real_t full_descent = new_sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) { if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { @@ -1526,16 +3398,18 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng } RID TextServerAdvanced::shaped_text_get_parent(RID p_shaped) const { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, RID()); + + MutexLock lock(sd->mutex); return sd->parent; } -float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags) { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags) { ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -1572,7 +3446,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, } } - float justification_width; + real_t justification_width; if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) { if (sd->overrun_trim_data.trim_pos >= 0) { start_pos = sd->overrun_trim_data.trim_pos; @@ -1612,7 +3486,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, } if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) { - float delta_width_per_kashida = (p_width - justification_width) / elongation_count; + real_t delta_width_per_kashida = (p_width - justification_width) / elongation_count; for (int i = start_pos; i <= end_pos; i++) { Glyph &gl = sd->glyphs.write[i]; if (gl.count > 0) { @@ -1627,19 +3501,19 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, } } } - float adv_remain = 0; + real_t adv_remain = 0; if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) { - float delta_width_per_space = (p_width - justification_width) / space_count; + real_t delta_width_per_space = (p_width - justification_width) / space_count; for (int i = start_pos; i <= end_pos; i++) { Glyph &gl = sd->glyphs.write[i]; if (gl.count > 0) { if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) { - float old_adv = gl.advance; - float new_advance; + real_t old_adv = gl.advance; + real_t new_advance; if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) { new_advance = MAX(gl.advance + delta_width_per_space, 0.f); } else { - new_advance = MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size); + new_advance = MAX(gl.advance + delta_width_per_space, 0.1 * gl.font_size); } gl.advance = new_advance; adv_remain += (new_advance - gl.advance); @@ -1667,10 +3541,11 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, return sd->width; } -float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) { ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -1679,7 +3554,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float } int tab_index = 0; - float off = 0.f; + real_t off = 0.f; int start, end, delta; if (sd->para_direction == DIRECTION_LTR) { @@ -1696,7 +3571,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float for (int i = start; i != end; i += delta) { if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) { - float tab_off = 0.f; + real_t tab_off = 0.f; while (tab_off <= off) { tab_off += p_tab_stops[tab_index]; tab_index++; @@ -1704,7 +3579,7 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float tab_index = 0; } } - float old_adv = gl[i].advance; + real_t old_adv = gl[i].advance; gl[i].advance = tab_off - off; sd->width += gl[i].advance - old_adv; off = 0; @@ -1716,10 +3591,11 @@ float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<float return 0.f; } -void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_trim_flags) { - _THREAD_SAFE_METHOD_ +void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) { ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped_line); ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid."); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped_line); } @@ -1746,18 +3622,18 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl int sd_size = sd->glyphs.size(); RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; int last_gl_font_size = sd_glyphs[sd_size - 1].font_size; - uint32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.'); - Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, dot_gl_idx, last_gl_font_size); - uint32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' '); - Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, whitespace_gl_idx, last_gl_font_size); + int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, '.'); + Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx); + int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, ' '); + Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx); int ellipsis_width = 0; if (add_ellipsis) { - ellipsis_width = 3 * dot_adv.x + font_get_spacing_glyph(last_gl_font_rid) + (cut_per_word ? whitespace_adv.x : 0); + ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0); } int ell_min_characters = 6; - float width = sd->width; + real_t width = sd->width; bool is_rtl = sd->direction == DIRECTION_RTL || (sd->direction == DIRECTION_AUTO && sd->para_direction == DIRECTION_RTL); @@ -1842,16 +3718,18 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl } TextServer::TrimData TextServerAdvanced::shaped_text_get_trim_data(RID p_shaped) const { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataAdvanced invalid."); + + MutexLock lock(sd->mutex); return sd->overrun_trim_data; } bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped); } @@ -2035,9 +3913,10 @@ _FORCE_INLINE_ int _generate_kashida_justification_opportunies(const String &p_d } bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped); } @@ -2145,8 +4024,8 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { } TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size) { - FontDataAdvanced *fd = font_owner.getornull(p_font); - hb_font_t *hb_font = fd->get_hb_handle(p_font_size); + hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size); + ERR_FAIL_COND_V(hb_font == nullptr, TextServer::Glyph()); hb_buffer_clear_contents(p_sd->hb_buffer); hb_buffer_set_direction(p_sd->hb_buffer, p_direction); @@ -2171,16 +4050,17 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced gl.font_size = p_font_size; if (glyph_count > 0) { + real_t scale = font_get_scale(p_font, p_font_size); if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / fd->get_font_scale(p_font_size))); + gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / scale)); } else { - gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / fd->get_font_scale(p_font_size))); + gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / scale)); } gl.count = 1; gl.index = glyph_info[0].codepoint; - gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / fd->get_font_scale(p_font_size))); - gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / fd->get_font_scale(p_font_size))); + gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / scale)); + gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / scale)); if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) { gl.flags |= GRAPHEME_IS_VALID; @@ -2190,13 +4070,8 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced } void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index) { - FontDataAdvanced *fd = nullptr; - if (p_fb_index < p_fonts.size()) { - fd = font_owner.getornull(p_fonts[p_fb_index]); - } - int fs = p_sd->spans[p_span].font_size; - if (fd == nullptr) { + if (p_fb_index >= p_fonts.size()) { // Add fallback glyphs. for (int i = p_start; i < p_end; i++) { if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) { @@ -2227,7 +4102,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star return; } - hb_font_t *hb_font = fd->get_hb_handle(fs); + RID f = p_fonts[p_fb_index]; + hb_font_t *hb_font = _font_get_hb_handle(f, fs); ERR_FAIL_COND(hb_font == nullptr); hb_buffer_clear_contents(p_sd->hb_buffer); @@ -2307,18 +4183,19 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star gl.index = glyph_info[i].codepoint; if (gl.index != 0) { + real_t scale = font_get_scale(f, fs); if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / fd->get_font_scale(fs))); + gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / scale)); } else { - gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / fd->get_font_scale(fs))); + gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / scale)); } - gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / fd->get_font_scale(fs))); - gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / fd->get_font_scale(fs))); + gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / scale)); + gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale)); } - if (fd->get_spacing_space() && is_whitespace(p_sd->text[glyph_info[i].cluster])) { - gl.advance += fd->get_spacing_space(); + if (font_get_spacing(f, fs, SPACING_SPACE) && is_whitespace(p_sd->text[glyph_info[i].cluster])) { + gl.advance += font_get_spacing(f, fs, SPACING_SPACE); } else { - gl.advance += fd->get_spacing_glyph(); + gl.advance += font_get_spacing(f, fs, SPACING_GLYPH); } if (p_sd->preserve_control) { @@ -2356,8 +4233,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off); p_sd->descent = MAX(p_sd->descent, w[i + j].y_off); } else { - p_sd->ascent = MAX(p_sd->ascent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5)); - p_sd->descent = MAX(p_sd->descent, Math::round(fd->get_advance(w[i + j].index, fs).x * 0.5)); + p_sd->ascent = MAX(p_sd->ascent, Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5)); + p_sd->descent = MAX(p_sd->descent, Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5)); } p_sd->width += w[i + j].advance; p_sd->glyphs.push_back(w[i + j]); @@ -2376,18 +4253,18 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star if (failed_subrun_start != p_end + 1) { _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1); } - p_sd->ascent = MAX(p_sd->ascent, fd->get_ascent(fs)); - p_sd->descent = MAX(p_sd->descent, fd->get_descent(fs)); - p_sd->upos = MAX(p_sd->upos, fd->get_underline_position(fs)); - p_sd->uthk = MAX(p_sd->uthk, fd->get_underline_thickness(fs)); + p_sd->ascent = MAX(p_sd->ascent, font_get_ascent(f, fs)); + p_sd->descent = MAX(p_sd->descent, font_get_descent(f, fs)); + p_sd->upos = MAX(p_sd->upos, font_get_underline_position(f, fs)); + p_sd->uthk = MAX(p_sd->uthk, font_get_underline_thickness(f, fs)); } } bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + MutexLock lock(sd->mutex); if (sd->valid) { return true; } @@ -2546,8 +4423,8 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { } // Align embedded objects to baseline. - float full_ascent = sd->ascent; - float full_descent = sd->descent; + real_t full_ascent = sd->ascent; + real_t full_descent = sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { @@ -2614,16 +4491,18 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { } bool TextServerAdvanced::shaped_text_is_ready(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); return sd->valid; } Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_get_glyphs(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2631,16 +4510,18 @@ Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_get_glyphs(RID p_shape } Vector2i TextServerAdvanced::shaped_text_get_range(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Vector2i()); + + MutexLock lock(sd->mutex); return Vector2(sd->start, sd->end); } Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_sort_logical(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2655,10 +4536,11 @@ Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_sort_logical(RID p_sha } Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const { - _THREAD_SAFE_METHOD_ Array ret; const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, ret); + + MutexLock lock(sd->mutex); for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { ret.push_back(E->key()); } @@ -2667,9 +4549,10 @@ Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const { } Rect2 TextServerAdvanced::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Rect2()); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2()); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); @@ -2678,9 +4561,10 @@ Rect2 TextServerAdvanced::shaped_text_get_object_rect(RID p_shaped, Variant p_ke } Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Size2()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2691,40 +4575,44 @@ Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const { } } -float TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } return sd->ascent; } -float TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } return sd->descent; } -float TextServerAdvanced::shaped_text_get_width(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_get_width(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } return (sd->text_trimmed ? sd->width_trimmed : sd->width); } -float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2732,10 +4620,11 @@ float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const return sd->upos; } -float TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped); } @@ -2762,7 +4651,6 @@ static num_system_data num_systems[]{ }; String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const { - _THREAD_SAFE_METHOD_ String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; @@ -2789,7 +4677,6 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p } String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const { - _THREAD_SAFE_METHOD_ String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; @@ -2819,7 +4706,6 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_ } String TextServerAdvanced::percent_sign(const String &p_language) const { - _THREAD_SAFE_METHOD_ String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; for (int i = 0; num_systems[i].lang != String(); i++) { @@ -2849,6 +4735,9 @@ TextServerAdvanced::TextServerAdvanced() { TextServerAdvanced::~TextServerAdvanced() { hb_bmp_free_font_funcs(); + if (library != nullptr) { + FT_Done_FreeType(library); + } u_cleanup(); #ifndef ICU_STATIC_DATA if (icu_data != nullptr) { diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index d19ba41a1a..5989035800 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -39,6 +39,7 @@ #include "servers/text_server.h" #include "core/templates/rid_owner.h" +#include "core/templates/thread_work_pool.h" #include "scene/resources/texture.h" #include "script_iterator.h" @@ -53,11 +54,24 @@ #include <unicode/ustring.h> #include <unicode/utypes.h> +#include "modules/modules_enabled.gen.h" + +#ifdef MODULE_FREETYPE_ENABLED +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include FT_STROKER_H +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_BBOX_H + +#include <hb-ft.h> +#include <hb-ot.h> +#endif + #include <hb-icu.h> #include <hb.h> -#include "font_adv.h" - class TextServerAdvanced : public TextServer { GDCLASS(TextServerAdvanced, TextServer); _THREAD_SAFE_CLASS_ @@ -65,8 +79,149 @@ class TextServerAdvanced : public TextServer { static String interface_name; static uint32_t interface_features; + // ICU support data. + uint8_t *icu_data = nullptr; + // Font cache data. + +#ifdef MODULE_FREETYPE_ENABLED + mutable FT_Library library = nullptr; +#endif + + const int rect_range = 2; + + struct FontTexture { + Image::Format format; + PackedByteArray imgdata; + int texture_w = 0; + int texture_h = 0; + PackedInt32Array offsets; + Ref<ImageTexture> texture; + }; + + struct FontTexturePosition { + int index = 0; + int x = 0; + int y = 0; + }; + + struct FontGlyph { + bool found = false; + int texture_idx = -1; + Rect2 rect; + Rect2 uv_rect; + Vector2 advance; + }; + + struct FontDataForSizeAdvanced { + real_t ascent = 0.f; + real_t descent = 0.f; + real_t underline_position = 0.f; + real_t underline_thickness = 0.f; + real_t scale = 1.f; + real_t oversampling = 1.f; + + int spacing_glyph = 0; + int spacing_space = 0; + + Vector2i size; + + Vector<FontTexture> textures; + HashMap<int32_t, FontGlyph> glyph_map; + Map<Vector2i, Vector2> kerning_map; + + hb_font_t *hb_handle = nullptr; + +#ifdef MODULE_FREETYPE_ENABLED + FT_Face face = nullptr; + FT_StreamRec stream; +#endif + + ~FontDataForSizeAdvanced() { + if (hb_handle != nullptr) { + hb_font_destroy(hb_handle); + } +#ifdef MODULE_FREETYPE_ENABLED + if (face != nullptr) { + FT_Done_Face(face); + } +#endif + } + }; + + struct FontDataAdvanced { + Mutex mutex; + + bool antialiased = true; + bool msdf = false; + int msdf_range = 14; + int msdf_source_size = 48; + int fixed_size = 0; + bool force_autohinter = false; + TextServer::Hinting hinting = TextServer::HINTING_LIGHT; + Dictionary variation_coordinates; + real_t oversampling = 0.f; + + Map<Vector2i, FontDataForSizeAdvanced *> cache; + + bool face_init = false; + Set<uint32_t> supported_scripts; + Dictionary supported_features; + Dictionary supported_varaitions; + + // Language/script support override. + Map<String, bool> language_support_overrides; + Map<String, bool> script_support_overrides; + + PackedByteArray data; + const uint8_t *data_ptr; + size_t data_size; + mutable ThreadWorkPool work_pool; + + ~FontDataAdvanced() { + work_pool.finish(); + for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = cache.front(); E; E = E->next()) { + memdelete(E->get()); + } + cache.clear(); + } + }; + + _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const; +#ifdef MODULE_MSDFGEN_ENABLED + _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; +#endif +#ifdef MODULE_FREETYPE_ENABLED + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const; +#endif + _FORCE_INLINE_ bool _ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; + _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const; + _FORCE_INLINE_ void _font_clear_cache(FontDataAdvanced *p_font_data); + void _generateMTSDF_threaded(uint32_t y, void *p_td) const; + + _FORCE_INLINE_ Vector2i _get_size(const FontDataAdvanced *p_font_data, int p_size) const { + if (p_font_data->msdf) { + return Vector2i(p_font_data->msdf_source_size, 0); + } else if (p_font_data->fixed_size > 0) { + return Vector2i(p_font_data->fixed_size, 0); + } else { + return Vector2i(p_size, 0); + } + } + + _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataAdvanced *p_font_data, const Vector2i &p_size) const { + if (p_font_data->msdf) { + return Vector2i(p_font_data->msdf_source_size, 0); + } else if (p_font_data->fixed_size > 0) { + return Vector2i(p_font_data->fixed_size, MIN(p_size.y, 1)); + } else { + return p_size; + } + } + + // Shaped text cache data. + struct ShapedTextDataAdvanced : public ShapedTextData { /* Intermediate data */ Char16String utf16; @@ -88,7 +243,9 @@ class TextServerAdvanced : public TextServer { } }; - float oversampling = 1.f; + // Common data. + + real_t oversampling = 1.f; mutable RID_PtrOwner<FontDataAdvanced> font_owner; mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner; @@ -97,6 +254,30 @@ class TextServerAdvanced : public TextServer { void _shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index); TextServer::Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size); + // HarfBuzz bitmap font interface. + + static hb_font_funcs_t *funcs; + + struct hb_bmp_font_t { + TextServerAdvanced::FontDataForSizeAdvanced *face = nullptr; + bool unref = false; /* Whether to destroy bm_face when done. */ + }; + + static hb_bmp_font_t *_hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref); + static void _hb_bmp_font_destroy(void *p_data); + static hb_bool_t hb_bmp_get_nominal_glyph(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_unicode, hb_codepoint_t *r_glyph, void *p_user_data); + static hb_position_t hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data); + static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data); + static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data); + static hb_position_t hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data); + static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data); + static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data); + static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data); + static void hb_bmp_create_font_funcs(); + static void hb_bmp_free_font_funcs(); + static void _hb_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref); + static hb_font_t *hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy); + protected: static void _bind_methods(){}; @@ -119,81 +300,132 @@ public: virtual bool is_locale_right_to_left(const String &p_locale) override; - virtual int32_t name_to_tag(const String &p_name) override; - virtual String tag_to_name(int32_t p_tag) override; + virtual int32_t name_to_tag(const String &p_name) const override; + virtual String tag_to_name(int32_t p_tag) const override; /* Font interface */ - virtual RID create_font_system(const String &p_name, int p_base_size = 16) override; - virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override; - virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override; - virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + virtual RID create_font() override; + + virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override; + virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override; + + virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) const override; + + virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override; + virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override; + + virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override; + virtual int font_get_msdf_pixel_range(RID p_font_rid) const override; + + virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override; + virtual int font_get_msdf_size(RID p_font_rid) const override; + + virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override; + virtual int font_get_fixed_size(RID p_font_rid) const override; + + virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override; + virtual bool font_is_force_autohinter(RID p_font_rid) const override; + + virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override; + virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override; + + virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override; + virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override; + + virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) const override; + + virtual Array font_get_size_cache_list(RID p_font_rid) const override; + virtual void font_clear_size_cache(RID p_font_rid) override; + virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override; + + hb_font_t *_font_get_hb_handle(RID p_font, int p_font_size) const; + + virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override; + virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override; + + virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override; + virtual real_t font_get_descent(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override; + virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override; + virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override; + + virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override; + virtual real_t font_get_scale(RID p_font_rid, int p_size) const override; + + virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override; + virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override; - virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; - virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; + virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) override; - virtual float font_get_height(RID p_font, int p_size) const override; - virtual float font_get_ascent(RID p_font, int p_size) const override; - virtual float font_get_descent(RID p_font, int p_size) const override; + virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override; + virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual float font_get_underline_position(RID p_font, int p_size) const override; - virtual float font_get_underline_thickness(RID p_font, int p_size) const override; + virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override; + virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) override; + virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override; - virtual int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override; - virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) const override; + virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override; - virtual Dictionary font_get_feature_list(RID p_font) const override; - virtual Dictionary font_get_variation_list(RID p_font) const override; + virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) override; - virtual void font_set_variation(RID p_font, const String &p_name, double p_value) override; - virtual double font_get_variation(RID p_font, const String &p_name) const override; + virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override; - virtual void font_set_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) const override; + virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override; - virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override; - virtual bool font_get_distance_field_hint(RID p_font) const override; + virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; - virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override; - virtual bool font_get_force_autohinter(RID p_font) const override; + virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override; + virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override; + virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) override; - virtual bool font_has_char(RID p_font, char32_t p_char) const override; - virtual String font_get_supported_chars(RID p_font) const override; + virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override; + virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override; - virtual bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) const override; + virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) const override; - virtual bool font_is_language_supported(RID p_font, const String &p_language) const override; - virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override; - virtual bool font_get_language_support_override(RID p_font, const String &p_language) override; - virtual void font_remove_language_support_override(RID p_font, const String &p_language) override; - Vector<String> font_get_language_support_overrides(RID p_font) override; + virtual bool font_has_char(RID p_font_rid, char32_t p_char) const override; + virtual String font_get_supported_chars(RID p_font_rid) const override; - virtual bool font_is_script_supported(RID p_font, const String &p_script) const override; - virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override; - virtual bool font_get_script_support_override(RID p_font, const String &p_script) override; - virtual void font_remove_script_support_override(RID p_font, const String &p_script) override; - Vector<String> font_get_script_support_overrides(RID p_font) override; + virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override; + virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) override; - virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override; - virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override; - virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override; + virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; - virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; - virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const override; + virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override; + virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override; + virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override; + virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override; - virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; + virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override; + virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override; + virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override; + virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override; + virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override; - virtual float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) override; + virtual Dictionary font_supported_feature_list(RID p_font_rid) const override; + virtual Dictionary font_supported_variation_list(RID p_font_rid) const override; - virtual Vector<String> get_system_fonts() const override; + virtual real_t font_get_global_oversampling() const override; + virtual void font_set_global_oversampling(real_t p_oversampling) override; /* Shaped text buffer interface */ @@ -222,14 +454,14 @@ public: virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; - virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; - virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override; + virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; + virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override; virtual bool shaped_text_shape(RID p_shaped) override; virtual bool shaped_text_update_breaks(RID p_shaped) override; virtual bool shaped_text_update_justification_ops(RID p_shaped) override; - virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_trim_flags) override; + virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override; virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override; virtual bool shaped_text_is_ready(RID p_shaped) const override; @@ -244,11 +476,11 @@ public: virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override; virtual Size2 shaped_text_get_size(RID p_shaped) const override; - virtual float shaped_text_get_ascent(RID p_shaped) const override; - virtual float shaped_text_get_descent(RID p_shaped) const override; - virtual float shaped_text_get_width(RID p_shaped) const override; - virtual float shaped_text_get_underline_position(RID p_shaped) const override; - virtual float shaped_text_get_underline_thickness(RID p_shaped) const override; + virtual real_t shaped_text_get_ascent(RID p_shaped) const override; + virtual real_t shaped_text_get_descent(RID p_shaped) const override; + virtual real_t shaped_text_get_width(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_position(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override; virtual String format_number(const String &p_string, const String &p_language = "") const override; virtual String parse_number(const String &p_string, const String &p_language = "") const override; diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub index 03eccbe7bd..31d1db6167 100644 --- a/modules/text_server_fb/SCsub +++ b/modules/text_server_fb/SCsub @@ -3,11 +3,23 @@ Import("env") Import("env_modules") +freetype_enabled = env.module_check_dependencies("text_server_fb", ["freetype"], True) +msdngen_enabled = env.module_check_dependencies("text_server_fb", ["msdfgen"], True) + env_text_server_fb = env_modules.Clone() -env_text_server_fb.Append( - CPPPATH=[ - "#thirdparty/freetype/include", - ] -) + +if msdngen_enabled: + env_text_server_fb.Append( + CPPPATH=[ + "#thirdparty/msdfgen", + ] + ) + +if freetype_enabled: + env_text_server_fb.Append( + CPPPATH=[ + "#thirdparty/freetype/include", + ] + ) env_text_server_fb.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/text_server_fb/bitmap_font_fb.cpp b/modules/text_server_fb/bitmap_font_fb.cpp deleted file mode 100644 index 313f170f04..0000000000 --- a/modules/text_server_fb/bitmap_font_fb.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/*************************************************************************/ -/* bitmap_font_fb.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "bitmap_font_fb.h" - -Error BitmapFontDataFallback::load_from_file(const String &p_filename, int p_base_size) { - _THREAD_SAFE_METHOD_ - //fnt format used by angelcode bmfont - //http://www.angelcode.com/products/bmfont/ - - FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_FILE_NOT_FOUND, "Can't open font: " + p_filename + "."); - - while (true) { - String line = f->get_line(); - - int delimiter = line.find(" "); - String type = line.substr(0, delimiter); - int pos = delimiter + 1; - Map<String, String> keys; - - while (pos < line.size() && line[pos] == ' ') { - pos++; - } - - while (pos < line.size()) { - int eq = line.find("=", pos); - if (eq == -1) { - break; - } - String key = line.substr(pos, eq - pos); - int end = -1; - String value; - if (line[eq + 1] == '"') { - end = line.find("\"", eq + 2); - if (end == -1) { - break; - } - value = line.substr(eq + 2, end - 1 - eq - 1); - pos = end + 1; - } else { - end = line.find(" ", eq + 1); - if (end == -1) { - end = line.size(); - } - value = line.substr(eq + 1, end - eq); - pos = end; - } - - while (pos < line.size() && line[pos] == ' ') { - pos++; - } - - keys[key] = value; - } - - if (type == "info") { - if (keys.has("size")) { - base_size = keys["size"].to_int(); - } - } else if (type == "common") { - if (keys.has("lineHeight")) { - height = keys["lineHeight"].to_int(); - } - if (keys.has("base")) { - ascent = keys["base"].to_int(); - } - } else if (type == "page") { - if (keys.has("file")) { - String base_dir = p_filename.get_base_dir(); - String file = base_dir.plus_file(keys["file"]); - if (RenderingServer::get_singleton() != nullptr) { - Ref<Texture2D> tex = ResourceLoader::load(file); - if (tex.is_null()) { - ERR_PRINT("Can't load font texture!"); - } else { - ERR_FAIL_COND_V_MSG(tex.is_null(), ERR_FILE_CANT_READ, "It's not a reference to a valid Texture object."); - textures.push_back(tex); - } - } - } - } else if (type == "char") { - Character c; - char32_t idx = 0; - if (keys.has("id")) { - idx = keys["id"].to_int(); - } - if (keys.has("x")) { - c.rect.position.x = keys["x"].to_int(); - } - if (keys.has("y")) { - c.rect.position.y = keys["y"].to_int(); - } - if (keys.has("width")) { - c.rect.size.width = keys["width"].to_int(); - } - if (keys.has("height")) { - c.rect.size.height = keys["height"].to_int(); - } - if (keys.has("xoffset")) { - c.align.x = keys["xoffset"].to_int(); - } - if (keys.has("yoffset")) { - c.align.y = keys["yoffset"].to_int(); - } - if (keys.has("page")) { - c.texture_idx = keys["page"].to_int(); - } - if (keys.has("xadvance")) { - c.advance.x = keys["xadvance"].to_int(); - } - if (keys.has("yadvance")) { - c.advance.y = keys["yadvance"].to_int(); - } - if (c.advance.x < 0) { - c.advance.x = c.rect.size.width + 1; - } - if (c.advance.y < 0) { - c.advance.y = c.rect.size.height + 1; - } - char_map[idx] = c; - } else if (type == "kerning") { - KerningPairKey kpk; - float k = 0.0; - if (keys.has("first")) { - kpk.A = keys["first"].to_int(); - } - if (keys.has("second")) { - kpk.B = keys["second"].to_int(); - } - if (keys.has("amount")) { - k = keys["amount"].to_int(); - } - kerning_map[kpk] = k; - } - - if (f->eof_reached()) { - break; - } - } - if (base_size == 0) { - base_size = height; - } - - valid = true; - - memdelete(f); - return OK; -} - -Error BitmapFontDataFallback::bitmap_new(float p_height, float p_ascent, int p_base_size) { - height = p_height; - ascent = p_ascent; - - base_size = p_base_size; - if (base_size == 0) { - base_size = height; - } - - char_map.clear(); - textures.clear(); - kerning_map.clear(); - - valid = true; - - return OK; -} - -void BitmapFontDataFallback::bitmap_add_texture(const Ref<Texture> &p_texture) { - ERR_FAIL_COND(!valid); - ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object."); - - textures.push_back(p_texture); -} - -void BitmapFontDataFallback::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - ERR_FAIL_COND(!valid); - - Character chr; - chr.rect = p_rect; - chr.texture_idx = p_texture_idx; - if (p_advance < 0) { - chr.advance.x = chr.rect.size.x; - } else { - chr.advance.x = p_advance; - } - chr.align = p_align; - char_map[p_char] = chr; -} - -void BitmapFontDataFallback::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { - ERR_FAIL_COND(!valid); - - KerningPairKey kpk; - kpk.A = p_A; - kpk.B = p_B; - - if (p_kerning == 0 && kerning_map.has(kpk)) { - kerning_map.erase(kpk); - } else { - kerning_map[kpk] = p_kerning; - } -} - -float BitmapFontDataFallback::get_height(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return height * (float(p_size) / float(base_size)); -} - -float BitmapFontDataFallback::get_ascent(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return ascent * (float(p_size) / float(base_size)); -} - -float BitmapFontDataFallback::get_descent(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return (height - ascent) * (float(p_size) / float(base_size)); -} - -float BitmapFontDataFallback::get_underline_position(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return 2 * (float(p_size) / float(base_size)); -} - -float BitmapFontDataFallback::get_underline_thickness(int p_size) const { - ERR_FAIL_COND_V(!valid, 0.f); - return 1 * (float(p_size) / float(base_size)); -} - -void BitmapFontDataFallback::set_distance_field_hint(bool p_distance_field) { - distance_field_hint = p_distance_field; -} - -bool BitmapFontDataFallback::get_distance_field_hint() const { - return distance_field_hint; -} - -float BitmapFontDataFallback::get_base_size() const { - return base_size; -} - -bool BitmapFontDataFallback::has_char(char32_t p_char) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, false); - return char_map.has(p_char); -} - -String BitmapFontDataFallback::get_supported_chars() const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, String()); - String chars; - const char32_t *k = nullptr; - while ((k = char_map.next(k))) { - chars += char32_t(*k); - } - return chars; -} - -Vector2 BitmapFontDataFallback::get_advance(char32_t p_char, int p_size) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, Vector2()); - const Character *c = char_map.getptr(p_char); - ERR_FAIL_COND_V(c == nullptr, Vector2()); - - return c->advance * (float(p_size) / float(base_size)); -} - -Vector2 BitmapFontDataFallback::get_kerning(char32_t p_char, char32_t p_next, int p_size) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!valid, Vector2()); - KerningPairKey kpk; - kpk.A = p_char; - kpk.B = p_next; - - const Map<KerningPairKey, int>::Element *E = kerning_map.find(kpk); - if (E) { - return Vector2(-E->get() * (float(p_size) / float(base_size)), 0); - } else { - return Vector2(); - } -} - -Vector2 BitmapFontDataFallback::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - if (p_index == 0) { - return Vector2(); - } - ERR_FAIL_COND_V(!valid, Vector2()); - const Character *c = char_map.getptr(p_index); - - ERR_FAIL_COND_V(c == nullptr, Vector2()); - ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2()); - if (c->texture_idx != -1) { - Point2i cpos = p_pos; - cpos += (c->align + Vector2(0, -ascent)) * (float(p_size) / float(base_size)); - Size2i csize = c->rect.size * (float(p_size) / float(base_size)); - if (RenderingServer::get_singleton() != nullptr) { - //if (distance_field_hint) { // Not implemented. - // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, true); - //} - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), textures[c->texture_idx]->get_rid(), c->rect, p_color, false, false); - //if (distance_field_hint) { - // RenderingServer::get_singleton()->canvas_item_set_distance_field_mode(p_canvas, false); - //} - } - } - - return c->advance * (float(p_size) / float(base_size)); -} - -Vector2 BitmapFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - if (p_index == 0) { - return Vector2(); - } - ERR_FAIL_COND_V(!valid, Vector2()); - const Character *c = char_map.getptr(p_index); - - ERR_FAIL_COND_V(c == nullptr, Vector2()); - ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), Vector2()); - - // Not supported, return advance for compatibility. - - return c->advance * (float(p_size) / float(base_size)); -} diff --git a/modules/text_server_fb/bitmap_font_fb.h b/modules/text_server_fb/bitmap_font_fb.h deleted file mode 100644 index 7cd7507ebc..0000000000 --- a/modules/text_server_fb/bitmap_font_fb.h +++ /dev/null @@ -1,111 +0,0 @@ -/*************************************************************************/ -/* bitmap_font_fb.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef BITMAP_FONT_FALLBACK_H -#define BITMAP_FONT_FALLBACK_H - -#include "font_fb.h" - -struct BitmapFontDataFallback : public FontDataFallback { - _THREAD_SAFE_CLASS_ - -private: - Vector<Ref<Texture2D>> textures; - - struct Character { - int texture_idx = 0; - Rect2 rect; - Vector2 align; - Vector2 advance = Vector2(-1, -1); - }; - - struct KerningPairKey { - union { - struct { - uint32_t A, B; - }; - - uint64_t pair = 0; - }; - - _FORCE_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; } - }; - - HashMap<char32_t, Character> char_map; - Map<KerningPairKey, int> kerning_map; - - float height = 0.f; - float ascent = 0.f; - int base_size = 0; - bool distance_field_hint = false; - -public: - virtual void clear_cache() override{}; - - virtual Error load_from_file(const String &p_filename, int p_base_size) override; - virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) override; - - virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override; - virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) override; - - virtual float get_height(int p_size) const override; - virtual float get_ascent(int p_size) const override; - virtual float get_descent(int p_size) const override; - - virtual float get_underline_position(int p_size) const override; - virtual float get_underline_thickness(int p_size) const override; - - virtual void set_antialiased(bool p_antialiased) override{}; - virtual bool get_antialiased() const override { return false; }; - - virtual void set_hinting(TextServer::Hinting p_hinting) override{}; - virtual TextServer::Hinting get_hinting() const override { return TextServer::HINTING_NONE; }; - - virtual void set_distance_field_hint(bool p_distance_field) override; - virtual bool get_distance_field_hint() const override; - - virtual void set_force_autohinter(bool p_enabeld) override{}; - virtual bool get_force_autohinter() const override { return false; }; - - virtual bool has_outline() const override { return false; }; - virtual float get_base_size() const override; - - virtual bool has_char(char32_t p_char) const override; - virtual String get_supported_chars() const override; - - virtual Vector2 get_advance(char32_t p_char, int p_size) const override; - virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const override; - - virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override; - virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override; -}; - -#endif // BITMAP_FONT_FALLBACK_H diff --git a/modules/text_server_fb/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp deleted file mode 100644 index 7e77987074..0000000000 --- a/modules/text_server_fb/dynamic_font_fb.cpp +++ /dev/null @@ -1,713 +0,0 @@ -/*************************************************************************/ -/* dynamic_font_fb.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "dynamic_font_fb.h" - -#ifdef MODULE_FREETYPE_ENABLED - -#include FT_STROKER_H -#include FT_ADVANCES_H - -DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size(int p_size, int p_outline_size) { - ERR_FAIL_COND_V(!valid, nullptr); - ERR_FAIL_COND_V(p_size < 0 || p_size > UINT16_MAX, nullptr); - ERR_FAIL_COND_V(p_outline_size < 0 || p_outline_size > UINT16_MAX, nullptr); - - CacheID id; - id.size = p_size; - id.outline_size = p_outline_size; - - DataAtSize *fds = nullptr; - Map<CacheID, DataAtSize *>::Element *E = nullptr; - if (p_outline_size != 0) { - E = size_cache_outline.find(id); - } else { - E = size_cache.find(id); - } - - if (E != nullptr) { - fds = E->get(); - } else { - if (font_mem == nullptr && font_path != String()) { - if (!font_mem_cache.is_empty()) { - font_mem = font_mem_cache.ptr(); - font_mem_size = font_mem_cache.size(); - } else { - FileAccess *f = FileAccess::open(font_path, FileAccess::READ); - if (!f) { - ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'."); - } - - uint64_t len = f->get_length(); - font_mem_cache.resize(len); - f->get_buffer(font_mem_cache.ptrw(), len); - font_mem = font_mem_cache.ptr(); - font_mem_size = len; - f->close(); - } - } - - int error = 0; - fds = memnew(DataAtSize); - if (font_mem) { - memset(&fds->stream, 0, sizeof(FT_StreamRec)); - fds->stream.base = (unsigned char *)font_mem; - fds->stream.size = font_mem_size; - fds->stream.pos = 0; - - FT_Open_Args fargs; - memset(&fargs, 0, sizeof(FT_Open_Args)); - fargs.memory_base = (unsigned char *)font_mem; - fargs.memory_size = font_mem_size; - fargs.flags = FT_OPEN_MEMORY; - fargs.stream = &fds->stream; - error = FT_Open_Face(library, &fargs, 0, &fds->face); - - } else { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "DynamicFont uninitialized."); - } - - if (error == FT_Err_Unknown_File_Format) { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "Unknown font format."); - - } else if (error) { - memdelete(fds); - ERR_FAIL_V_MSG(nullptr, "Error loading font."); - } - - oversampling = TS->font_get_oversampling(); - - if (FT_HAS_COLOR(fds->face) && fds->face->num_fixed_sizes > 0) { - int best_match = 0; - int diff = ABS(p_size - ((int64_t)fds->face->available_sizes[0].width)); - fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[0].width; - for (int i = 1; i < fds->face->num_fixed_sizes; i++) { - int ndiff = ABS(p_size - ((int64_t)fds->face->available_sizes[i].width)); - if (ndiff < diff) { - best_match = i; - diff = ndiff; - fds->scale_color_font = float(p_size * oversampling) / fds->face->available_sizes[i].width; - } - } - FT_Select_Size(fds->face, best_match); - } else { - FT_Set_Pixel_Sizes(fds->face, 0, p_size * oversampling); - } - - fds->size = p_size; - fds->ascent = (fds->face->size->metrics.ascender / 64.0) / oversampling * fds->scale_color_font; - fds->descent = (-fds->face->size->metrics.descender / 64.0) / oversampling * fds->scale_color_font; - fds->underline_position = (-FT_MulFix(fds->face->underline_position, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font; - fds->underline_thickness = (FT_MulFix(fds->face->underline_thickness, fds->face->size->metrics.y_scale) / 64.0) / oversampling * fds->scale_color_font; - - if (p_outline_size != 0) { - size_cache_outline[id] = fds; - } else { - size_cache[id] = fds; - } - } - - return fds; -} - -DynamicFontDataFallback::TexturePosition DynamicFontDataFallback::find_texture_pos_for_glyph(DynamicFontDataFallback::DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) { - TexturePosition ret; - ret.index = -1; - - int mw = p_width; - int mh = p_height; - - for (int i = 0; i < p_data->textures.size(); i++) { - const CharTexture &ct = p_data->textures[i]; - - if (RenderingServer::get_singleton() != nullptr) { - if (ct.texture->get_format() != p_image_format) { - continue; - } - } - - if (mw > ct.texture_size || mh > ct.texture_size) { //too big for this texture - continue; - } - - ret.y = 0x7FFFFFFF; - ret.x = 0; - - for (int j = 0; j < ct.texture_size - mw; j++) { - int max_y = 0; - - for (int k = j; k < j + mw; k++) { - int y = ct.offsets[k]; - if (y > max_y) { - max_y = y; - } - } - - if (max_y < ret.y) { - ret.y = max_y; - ret.x = j; - } - } - - if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size) { - continue; //fail, could not fit it here - } - - ret.index = i; - break; - } - - if (ret.index == -1) { - //could not find texture to fit, create one - ret.x = 0; - ret.y = 0; - - int texsize = MAX(p_data->size * oversampling * 8, 256); - if (mw > texsize) { - texsize = mw; //special case, adapt to it? - } - if (mh > texsize) { - texsize = mh; //special case, adapt to it? - } - - texsize = next_power_of_2(texsize); - - texsize = MIN(texsize, 4096); - - CharTexture tex; - tex.texture_size = texsize; - tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha - - { - //zero texture - uint8_t *w = tex.imgdata.ptrw(); - ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret); - // Initialize the texture to all-white pixels to prevent artifacts when the - // font is displayed at a non-default scale with filtering enabled. - if (p_color_size == 2) { - for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8 - w[i + 0] = 255; - w[i + 1] = 0; - } - } else if (p_color_size == 4) { - for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8 - w[i + 0] = 255; - w[i + 1] = 255; - w[i + 2] = 255; - w[i + 3] = 0; - } - } else { - ERR_FAIL_V(ret); - } - } - tex.offsets.resize(texsize); - for (int i = 0; i < texsize; i++) { //zero offsets - tex.offsets.write[i] = 0; - } - - p_data->textures.push_back(tex); - ret.index = p_data->textures.size() - 1; - } - - return ret; -} - -DynamicFontDataFallback::Character DynamicFontDataFallback::Character::not_found() { - Character ch; - return ch; -} - -DynamicFontDataFallback::Character DynamicFontDataFallback::bitmap_to_character(DynamicFontDataFallback::DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) { - int w = bitmap.width; - int h = bitmap.rows; - - int mw = w + rect_margin * 2; - int mh = h + rect_margin * 2; - - ERR_FAIL_COND_V(mw > 4096, Character::not_found()); - ERR_FAIL_COND_V(mh > 4096, Character::not_found()); - - int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; - Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; - - TexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh); - ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found()); - - //fit character in char texture - - CharTexture &tex = p_data->textures.write[tex_pos.index]; - - { - uint8_t *wr = tex.imgdata.ptrw(); - - for (int i = 0; i < h; i++) { - for (int j = 0; j < w; j++) { - int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size; - ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found()); - switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: { - int byte = i * bitmap.pitch + (j >> 3); - int bit = 1 << (7 - (j % 8)); - wr[ofs + 0] = 255; //grayscale as 1 - wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0; - } break; - case FT_PIXEL_MODE_GRAY: - wr[ofs + 0] = 255; //grayscale as 1 - wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; - break; - case FT_PIXEL_MODE_BGRA: { - int ofs_color = i * bitmap.pitch + (j << 2); - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; - wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; - wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; - } break; - // TODO: FT_PIXEL_MODE_LCD - default: - ERR_FAIL_V_MSG(Character::not_found(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + "."); - break; - } - } - } - } - - //blit to image and texture - { - if (RenderingServer::get_singleton() != nullptr) { - Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata)); - - if (tex.texture.is_null()) { - tex.texture.instantiate(); - tex.texture->create_from_image(img); - } else { - tex.texture->update(img); //update - } - } - } - - // update height array - for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { - tex.offsets.write[k] = tex_pos.y + mh; - } - - Character chr; - chr.align = (Vector2(xofs, -yofs) * p_data->scale_color_font / oversampling).round(); - chr.advance = (advance * p_data->scale_color_font / oversampling).round(); - chr.texture_idx = tex_pos.index; - chr.found = true; - - chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h); - chr.rect = chr.rect_uv; - chr.rect.position /= oversampling; - chr.rect.size *= (p_data->scale_color_font / oversampling); - return chr; -} - -void DynamicFontDataFallback::update_char(int p_size, char32_t p_char) { - DataAtSize *fds = get_data_for_size(p_size, false); - ERR_FAIL_COND(fds == nullptr); - - if (fds->char_map.has(p_char)) { - return; - } - - Character character = Character::not_found(); - - FT_GlyphSlot slot = fds->face->glyph; - FT_UInt gl_index = FT_Get_Char_Index(fds->face, p_char); - - if (gl_index == 0) { - fds->char_map[p_char] = character; - return; - } - - int ft_hinting; - switch (hinting) { - case TextServer::HINTING_NONE: - ft_hinting = FT_LOAD_NO_HINTING; - break; - case TextServer::HINTING_LIGHT: - ft_hinting = FT_LOAD_TARGET_LIGHT; - break; - default: - ft_hinting = FT_LOAD_TARGET_NORMAL; - break; - } - - FT_Fixed v, h; - FT_Get_Advance(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting, &h); - FT_Get_Advance(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting | FT_LOAD_VERTICAL_LAYOUT, &v); - - int error = FT_Load_Glyph(fds->face, gl_index, FT_HAS_COLOR(fds->face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting); - if (error) { - fds->char_map[p_char] = character; - return; - } - - error = FT_Render_Glyph(fds->face->glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); - if (!error) { - character = bitmap_to_character(fds, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); - } - - fds->char_map[p_char] = character; -} - -void DynamicFontDataFallback::update_char_outline(int p_size, int p_outline_size, char32_t p_char) { - DataAtSize *fds = get_data_for_size(p_size, p_outline_size); - ERR_FAIL_COND(fds == nullptr); - - if (fds->char_map.has(p_char)) { - return; - } - - Character character = Character::not_found(); - FT_UInt gl_index = FT_Get_Char_Index(fds->face, p_char); - - if (gl_index == 0) { - fds->char_map[p_char] = character; - return; - } - - int error = FT_Load_Glyph(fds->face, gl_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); - if (error) { - fds->char_map[p_char] = character; - return; - } - - FT_Stroker stroker; - if (FT_Stroker_New(library, &stroker) != 0) { - fds->char_map[p_char] = character; - return; - } - - FT_Stroker_Set(stroker, (int)(p_outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0); - FT_Glyph glyph; - FT_BitmapGlyph glyph_bitmap; - - if (FT_Get_Glyph(fds->face->glyph, &glyph) != 0) { - goto cleanup_stroker; - } - if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { - goto cleanup_glyph; - } - if (FT_Glyph_To_Bitmap(&glyph, antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { - goto cleanup_glyph; - } - - glyph_bitmap = (FT_BitmapGlyph)glyph; - character = bitmap_to_character(fds, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); - -cleanup_glyph: - FT_Done_Glyph(glyph); -cleanup_stroker: - FT_Stroker_Done(stroker); - - fds->char_map[p_char] = character; -} - -void DynamicFontDataFallback::clear_cache() { - _THREAD_SAFE_METHOD_ - for (Map<CacheID, DataAtSize *>::Element *E = size_cache.front(); E; E = E->next()) { - memdelete(E->get()); - } - size_cache.clear(); - for (Map<CacheID, DataAtSize *>::Element *E = size_cache_outline.front(); E; E = E->next()) { - memdelete(E->get()); - } - size_cache_outline.clear(); -} - -Error DynamicFontDataFallback::load_from_file(const String &p_filename, int p_base_size) { - _THREAD_SAFE_METHOD_ - if (library == nullptr) { - int error = FT_Init_FreeType(&library); - ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType."); - } - clear_cache(); - - font_path = p_filename; - base_size = p_base_size; - - valid = true; - DataAtSize *fds = get_data_for_size(base_size); // load base size. - if (fds == nullptr) { - valid = false; - ERR_FAIL_V(ERR_CANT_CREATE); - } - - return OK; -} - -Error DynamicFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { - _THREAD_SAFE_METHOD_ - if (library == nullptr) { - int error = FT_Init_FreeType(&library); - ERR_FAIL_COND_V_MSG(error != 0, ERR_CANT_CREATE, "Error initializing FreeType."); - } - clear_cache(); - - font_mem = p_data; - font_mem_size = p_size; - base_size = p_base_size; - - valid = true; - DataAtSize *fds = get_data_for_size(base_size); // load base size. - if (fds == nullptr) { - valid = false; - ERR_FAIL_V(ERR_CANT_CREATE); - } - - return OK; -} - -float DynamicFontDataFallback::get_height(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->ascent + fds->descent; -} - -float DynamicFontDataFallback::get_ascent(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->ascent; -} - -float DynamicFontDataFallback::get_descent(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->descent; -} - -float DynamicFontDataFallback::get_underline_position(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->underline_position; -} - -float DynamicFontDataFallback::get_underline_thickness(int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, 0.f); - return fds->underline_thickness; -} - -void DynamicFontDataFallback::set_antialiased(bool p_antialiased) { - if (antialiased != p_antialiased) { - clear_cache(); - antialiased = p_antialiased; - } -} - -bool DynamicFontDataFallback::get_antialiased() const { - return antialiased; -} - -void DynamicFontDataFallback::set_force_autohinter(bool p_enabled) { - if (force_autohinter != p_enabled) { - clear_cache(); - force_autohinter = p_enabled; - } -} - -bool DynamicFontDataFallback::get_force_autohinter() const { - return force_autohinter; -} - -void DynamicFontDataFallback::set_hinting(TextServer::Hinting p_hinting) { - if (hinting != p_hinting) { - clear_cache(); - hinting = p_hinting; - } -} - -TextServer::Hinting DynamicFontDataFallback::get_hinting() const { - return hinting; -} - -bool DynamicFontDataFallback::has_outline() const { - return true; -} - -float DynamicFontDataFallback::get_base_size() const { - return base_size; -} - -bool DynamicFontDataFallback::has_char(char32_t p_char) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(base_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - const_cast<DynamicFontDataFallback *>(this)->update_char(base_size, p_char); - Character ch = fds->char_map[p_char]; - - return (ch.found); -} - -String DynamicFontDataFallback::get_supported_chars() const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(base_size); - ERR_FAIL_COND_V(fds == nullptr, String()); - - String chars; - - FT_UInt gindex; - FT_ULong charcode = FT_Get_First_Char(fds->face, &gindex); - while (gindex != 0) { - if (charcode != 0) { - chars += char32_t(charcode); - } - charcode = FT_Get_Next_Char(fds->face, charcode, &gindex); - } - - return chars; -} - -Vector2 DynamicFontDataFallback::get_advance(char32_t p_char, int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast<DynamicFontDataFallback *>(this)->update_char(p_size, p_char); - Character ch = fds->char_map[p_char]; - - return ch.advance; -} - -Vector2 DynamicFontDataFallback::get_kerning(char32_t p_char, char32_t p_next, int p_size) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - FT_Vector delta; - FT_Get_Kerning(fds->face, FT_Get_Char_Index(fds->face, p_char), FT_Get_Char_Index(fds->face, p_next), FT_KERNING_DEFAULT, &delta); - return Vector2(delta.x, delta.y); -} - -Vector2 DynamicFontDataFallback::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast<DynamicFontDataFallback *>(this)->update_char(p_size, p_index); - Character ch = fds->char_map[p_index]; - - Vector2 advance; - if (ch.found) { - ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2()); - - if (ch.texture_idx != -1) { - Point2i cpos = p_pos; - cpos += ch.align; - - Color modulate = p_color; - if (FT_HAS_COLOR(fds->face)) { - modulate.r = modulate.g = modulate.b = 1.0; - } - if (RenderingServer::get_singleton() != nullptr) { - RID texture = fds->textures[ch.texture_idx].texture->get_rid(); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false); - } - } - - advance = ch.advance; - } - - return advance; -} - -Vector2 DynamicFontDataFallback::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size, p_outline_size); - ERR_FAIL_COND_V(fds == nullptr, Vector2()); - - const_cast<DynamicFontDataFallback *>(this)->update_char_outline(p_size, p_outline_size, p_index); - Character ch = fds->char_map[p_index]; - - Vector2 advance; - if (ch.found) { - ERR_FAIL_COND_V(ch.texture_idx < -1 || ch.texture_idx >= fds->textures.size(), Vector2()); - - if (ch.texture_idx != -1) { - Point2i cpos = p_pos; - cpos += ch.align; - - Color modulate = p_color; - if (FT_HAS_COLOR(fds->face)) { - modulate.r = modulate.g = modulate.b = 1.0; - } - if (RenderingServer::get_singleton() != nullptr) { - RID texture = fds->textures[ch.texture_idx].texture->get_rid(); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, ch.rect.size), texture, ch.rect_uv, modulate, false, false); - } - } - - advance = ch.advance; - } - - return advance; -} - -bool DynamicFontDataFallback::get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - DataAtSize *fds = const_cast<DynamicFontDataFallback *>(this)->get_data_for_size(p_size); - ERR_FAIL_COND_V(fds == nullptr, false); - - int error = FT_Load_Glyph(fds->face, p_index, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); - ERR_FAIL_COND_V(error, false); - - r_points.clear(); - r_contours.clear(); - - float h = fds->ascent; - float scale = (1.0 / 64.0) / oversampling * fds->scale_color_font; - for (short i = 0; i < fds->face->glyph->outline.n_points; i++) { - r_points.push_back(Vector3(fds->face->glyph->outline.points[i].x * scale, h - fds->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fds->face->glyph->outline.tags[i]))); - } - for (short i = 0; i < fds->face->glyph->outline.n_contours; i++) { - r_contours.push_back(fds->face->glyph->outline.contours[i]); - } - r_orientation = (FT_Outline_Get_Orientation(&fds->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); - return true; -} - -DynamicFontDataFallback::~DynamicFontDataFallback() { - clear_cache(); - if (library != nullptr) { - FT_Done_FreeType(library); - } -} - -#endif // MODULE_FREETYPE_ENABLED diff --git a/modules/text_server_fb/dynamic_font_fb.h b/modules/text_server_fb/dynamic_font_fb.h deleted file mode 100644 index 82e59fa607..0000000000 --- a/modules/text_server_fb/dynamic_font_fb.h +++ /dev/null @@ -1,173 +0,0 @@ -/*************************************************************************/ -/* dynamic_font_fb.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef DYNAMIC_FONT_FALLBACK_H -#define DYNAMIC_FONT_FALLBACK_H - -#include "font_fb.h" - -#include "modules/modules_enabled.gen.h" -#ifdef MODULE_FREETYPE_ENABLED - -#include <ft2build.h> -#include FT_FREETYPE_H - -struct DynamicFontDataFallback : public FontDataFallback { - _THREAD_SAFE_CLASS_ - -private: - struct CharTexture { - Vector<uint8_t> imgdata; - int texture_size = 0; - Vector<int> offsets; - Ref<ImageTexture> texture; - }; - - struct Character { - bool found = false; - int texture_idx = 0; - Rect2 rect; - Rect2 rect_uv; - Vector2 align; - Vector2 advance = Vector2(-1, -1); - - static Character not_found(); - }; - - struct TexturePosition { - int index = 0; - int x = 0; - int y = 0; - }; - - struct CacheID { - union { - struct { - uint32_t size : 16; - uint32_t outline_size : 16; - }; - uint32_t key = 0; - }; - bool operator<(CacheID right) const { - return key < right.key; - } - }; - - struct DataAtSize { - FT_Face face = nullptr; - FT_StreamRec stream; - - int size = 0; - float scale_color_font = 1.f; - float ascent = 0.0; - float descent = 0.0; - float underline_position = 0.0; - float underline_thickness = 0.0; - - Vector<CharTexture> textures; - HashMap<char32_t, Character> char_map; - - ~DataAtSize() { - if (face != nullptr) { - FT_Done_Face(face); - } - } - }; - - FT_Library library = nullptr; - - // Source data. - const uint8_t *font_mem = nullptr; - int font_mem_size = 0; - String font_path; - Vector<uint8_t> font_mem_cache; - - float rect_margin = 1.f; - int base_size = 16; - float oversampling = 1.f; - bool antialiased = true; - bool force_autohinter = false; - TextServer::Hinting hinting = TextServer::HINTING_LIGHT; - - Map<CacheID, DataAtSize *> size_cache; - Map<CacheID, DataAtSize *> size_cache_outline; - - DataAtSize *get_data_for_size(int p_size, int p_outline_size = 0); - - TexturePosition find_texture_pos_for_glyph(DataAtSize *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height); - Character bitmap_to_character(DataAtSize *p_data, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance); - _FORCE_INLINE_ void update_char(int p_size, char32_t p_char); - _FORCE_INLINE_ void update_char_outline(int p_size, int p_outline_size, char32_t p_char); - -public: - virtual void clear_cache() override; - - virtual Error load_from_file(const String &p_filename, int p_base_size) override; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override; - - virtual float get_height(int p_size) const override; - virtual float get_ascent(int p_size) const override; - virtual float get_descent(int p_size) const override; - - virtual float get_underline_position(int p_size) const override; - virtual float get_underline_thickness(int p_size) const override; - - virtual void set_antialiased(bool p_antialiased) override; - virtual bool get_antialiased() const override; - - virtual void set_hinting(TextServer::Hinting p_hinting) override; - virtual TextServer::Hinting get_hinting() const override; - - virtual void set_force_autohinter(bool p_enabeld) override; - virtual bool get_force_autohinter() const override; - - virtual void set_distance_field_hint(bool p_distance_field) override{}; - virtual bool get_distance_field_hint() const override { return false; }; - - virtual bool has_outline() const override; - virtual float get_base_size() const override; - - virtual bool has_char(char32_t p_char) const override; - virtual String get_supported_chars() const override; - - virtual Vector2 get_advance(char32_t p_char, int p_size) const override; - virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const override; - - virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override; - virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const override; - - virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; - - virtual ~DynamicFontDataFallback() override; -}; - -#endif // MODULE_FREETYPE_ENABLED - -#endif // DYNAMIC_FONT_FALLBACK_H diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h deleted file mode 100644 index fe9888b7f4..0000000000 --- a/modules/text_server_fb/font_fb.h +++ /dev/null @@ -1,101 +0,0 @@ -/*************************************************************************/ -/* font_fb.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef FONT_FALLBACK_H -#define FONT_FALLBACK_H - -#include "servers/text_server.h" - -struct FontDataFallback { - Map<String, bool> lang_support_overrides; - Map<String, bool> script_support_overrides; - bool valid = false; - int spacing_space = 0; - int spacing_glyph = 0; - - virtual void clear_cache() = 0; - - virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; }; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; }; - virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; }; - - virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); }; - virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); }; - virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); }; - - virtual float get_height(int p_size) const = 0; - virtual float get_ascent(int p_size) const = 0; - virtual float get_descent(int p_size) const = 0; - - virtual float get_underline_position(int p_size) const = 0; - virtual float get_underline_thickness(int p_size) const = 0; - - virtual int get_spacing_space() const { return spacing_space; }; - virtual void set_spacing_space(int p_value) { - spacing_space = p_value; - clear_cache(); - }; - - virtual int get_spacing_glyph() const { return spacing_glyph; }; - virtual void set_spacing_glyph(int p_value) { - spacing_glyph = p_value; - clear_cache(); - }; - - virtual void set_antialiased(bool p_antialiased) = 0; - virtual bool get_antialiased() const = 0; - - virtual void set_hinting(TextServer::Hinting p_hinting) = 0; - virtual TextServer::Hinting get_hinting() const = 0; - - virtual void set_distance_field_hint(bool p_distance_field) = 0; - virtual bool get_distance_field_hint() const = 0; - - virtual void set_force_autohinter(bool p_enabeld) = 0; - virtual bool get_force_autohinter() const = 0; - - virtual bool has_outline() const = 0; - virtual float get_base_size() const = 0; - - virtual bool has_char(char32_t p_char) const = 0; - virtual String get_supported_chars() const = 0; - - virtual Vector2 get_advance(char32_t p_char, int p_size) const = 0; - virtual Vector2 get_kerning(char32_t p_char, char32_t p_next, int p_size) const = 0; - - virtual Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0; - virtual Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const = 0; - - virtual bool get_glyph_contours(int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { return false; }; - - virtual ~FontDataFallback(){}; -}; - -#endif // FONT_FALLBACK_H diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 110194c373..e4e6797f92 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -30,8 +30,18 @@ #include "text_server_fb.h" -#include "bitmap_font_fb.h" -#include "dynamic_font_fb.h" +#include "core/string/print_string.h" + +#ifdef MODULE_MSDFGEN_ENABLED +#include "core/ShapeDistanceFinder.h" +#include "core/contour-combiners.h" +#include "core/edge-selectors.h" +#include "msdfgen.h" +#endif + +/*************************************************************************/ +/* Character properties. */ +/*************************************************************************/ _FORCE_INLINE_ bool is_control(char32_t p_char) { return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F); @@ -100,291 +110,1779 @@ bool TextServerFallback::is_locale_right_to_left(const String &p_locale) { return false; // No RTL support. } +#define OT_TAG(c1, c2, c3, c4) ((int32_t)((((uint32_t)(c1)&0xFF) << 24) | (((uint32_t)(c2)&0xFF) << 16) | (((uint32_t)(c3)&0xFF) << 8) | ((uint32_t)(c4)&0xFF))) + +struct FeatureInfo { + int32_t tag; + String name; +}; + +static FeatureInfo feature_set[] = { + // Registered OpenType variation tags. + { OT_TAG('i', 't', 'a', 'l'), "italic" }, + { OT_TAG('o', 'p', 's', 'z'), "optical_size" }, + { OT_TAG('s', 'l', 'n', 't'), "slant" }, + { OT_TAG('w', 'd', 't', 'h'), "width" }, + { OT_TAG('w', 'g', 'h', 't'), "weight" }, + { 0, String() }, +}; + +_FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) { + char tag[4]; + uint32_t i; + + if (!p_str || !p_len || !*p_str) + return OT_TAG(0, 0, 0, 0); + + if (p_len < 0 || p_len > 4) { + p_len = 4; + } + for (i = 0; i < (uint32_t)p_len && p_str[i]; i++) { + tag[i] = p_str[i]; + } + + for (; i < 4; i++) { + tag[i] = ' '; + } + + return OT_TAG(tag[0], tag[1], tag[2], tag[3]); +} + +int32_t TextServerFallback::name_to_tag(const String &p_name) const { + for (int i = 0; feature_set[i].tag != 0; i++) { + if (feature_set[i].name == p_name) { + return feature_set[i].tag; + } + } + + // No readable name, use tag string. + return ot_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1); +} + +_FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) { + p_buf[0] = (char)(uint8_t)(p_tag >> 24); + p_buf[1] = (char)(uint8_t)(p_tag >> 16); + p_buf[2] = (char)(uint8_t)(p_tag >> 8); + p_buf[3] = (char)(uint8_t)(p_tag >> 0); +} + +String TextServerFallback::tag_to_name(int32_t p_tag) const { + for (int i = 0; feature_set[i].tag != 0; i++) { + if (feature_set[i].tag == p_tag) { + return feature_set[i].name; + } + } + + // No readable name, use tag string. + char name[5]; + memset(name, 0, 5); + ot_tag_to_string(p_tag, name); + return String("custom_") + String(name); +} + /*************************************************************************/ -/* Font interface */ +/* Font Glyph Rendering */ /*************************************************************************/ -RID TextServerFallback::create_font_system(const String &p_name, int p_base_size) { - ERR_FAIL_V_MSG(RID(), "System fonts are not supported by this text server."); +_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const { + FontTexturePosition ret; + ret.index = -1; + + int mw = p_width; + int mh = p_height; + + for (int i = 0; i < p_data->textures.size(); i++) { + const FontTexture &ct = p_data->textures[i]; + + if (RenderingServer::get_singleton() != nullptr) { + if (ct.texture->get_format() != p_image_format) { + continue; + } + } + + if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. + continue; + } + + ret.y = 0x7FFFFFFF; + ret.x = 0; + + for (int j = 0; j < ct.texture_w - mw; j++) { + int max_y = 0; + + for (int k = j; k < j + mw; k++) { + int y = ct.offsets[k]; + if (y > max_y) { + max_y = y; + } + } + + if (max_y < ret.y) { + ret.y = max_y; + ret.x = j; + } + } + + if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_h) { + continue; // Fail, could not fit it here. + } + + ret.index = i; + break; + } + + if (ret.index == -1) { + // Could not find texture to fit, create one. + ret.x = 0; + ret.y = 0; + + int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256); + if (mw > texsize) { + texsize = mw; // Special case, adapt to it? + } + if (mh > texsize) { + texsize = mh; // Special case, adapt to it? + } + + texsize = next_power_of_2(texsize); + + texsize = MIN(texsize, 4096); + + FontTexture tex; + tex.texture_w = texsize; + tex.texture_h = texsize; + tex.format = p_image_format; + tex.imgdata.resize(texsize * texsize * p_color_size); + + { + // Zero texture. + uint8_t *w = tex.imgdata.ptrw(); + ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret); + // Initialize the texture to all-white pixels to prevent artifacts when the + // font is displayed at a non-default scale with filtering enabled. + if (p_color_size == 2) { + for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8, BW font. + w[i + 0] = 255; + w[i + 1] = 0; + } + } else if (p_color_size == 4) { + for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8, Color font, Multichannel(+True) SDF. + w[i + 0] = 255; + w[i + 1] = 255; + w[i + 2] = 255; + w[i + 3] = 0; + } + } else { + ERR_FAIL_V(ret); + } + } + tex.offsets.resize(texsize); + for (int i = 0; i < texsize; i++) { // Zero offsets. + tex.offsets.write[i] = 0; + } + + p_data->textures.push_back(tex); + ret.index = p_data->textures.size() - 1; + } + + return ret; } -RID TextServerFallback::create_font_resource(const String &p_filename, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = nullptr; - if (p_filename.get_extension() == "fnt" || p_filename.get_extension() == "font") { - fd = memnew(BitmapFontDataFallback); +#ifdef MODULE_MSDFGEN_ENABLED + +struct MSContext { + msdfgen::Point2 position; + msdfgen::Shape *shape; + msdfgen::Contour *contour; +}; + +class DistancePixelConversion { + double invRange; + +public: + _FORCE_INLINE_ explicit DistancePixelConversion(double range) : + invRange(1 / range) {} + _FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const { + pixels[0] = float(invRange * distance.r + .5); + pixels[1] = float(invRange * distance.g + .5); + pixels[2] = float(invRange * distance.b + .5); + pixels[3] = float(invRange * distance.a + .5); + } +}; + +struct MSDFThreadData { + msdfgen::Bitmap<float, 4> *output; + msdfgen::Shape *shape; + msdfgen::Projection *projection; + DistancePixelConversion *distancePixelConversion; +}; + +static msdfgen::Point2 ft_point2(const FT_Vector &vector) { + return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f); +} + +static int ft_move_to(const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + if (!(context->contour && context->contour->edges.empty())) { + context->contour = &context->shape->addContour(); + } + context->position = ft_point2(*to); + return 0; +} + +static int ft_line_to(const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + msdfgen::Point2 endpoint = ft_point2(*to); + if (endpoint != context->position) { + context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint)); + context->position = endpoint; + } + return 0; +} + +static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to))); + context->position = ft_point2(*to); + return 0; +} + +static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) { + MSContext *context = reinterpret_cast<MSContext *>(user); + context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to))); + context->position = ft_point2(*to); + return 0; +} + +void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const { + MSDFThreadData *td = (MSDFThreadData *)p_td; + + msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape); + int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y; + for (int col = 0; col < td->output->width(); ++col) { + int x = (y % 2) ? td->output->width() - col - 1 : col; + msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5)); + msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p); + td->distancePixelConversion->operator()(td->output->operator()(x, row), distance); + } +} + +_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const { + msdfgen::Shape shape; + + shape.contours.clear(); + shape.inverseYAxis = false; + + MSContext context = {}; + context.shape = &shape; + FT_Outline_Funcs ft_functions; + ft_functions.move_to = &ft_move_to; + ft_functions.line_to = &ft_line_to; + ft_functions.conic_to = &ft_conic_to; + ft_functions.cubic_to = &ft_cubic_to; + ft_functions.shift = 0; + ft_functions.delta = 0; + + int error = FT_Outline_Decompose(outline, &ft_functions, &context); + ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'."); + if (!shape.contours.empty() && shape.contours.back().edges.empty()) { + shape.contours.pop_back(); + } + + if (FT_Outline_Get_Orientation(outline) == 1) { + for (int i = 0; i < (int)shape.contours.size(); ++i) { + shape.contours[i].reverse(); + } + } + + shape.inverseYAxis = true; + shape.normalize(); + + msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range); + + FontGlyph chr; + chr.found = true; + chr.advance = advance.round(); + + if (shape.validate() && shape.contours.size() > 0) { + int w = (bounds.r - bounds.l); + int h = (bounds.t - bounds.b); + + int mw = w + p_rect_margin * 2; + int mh = h + p_rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + FontTexture &tex = p_data->textures.write[tex_pos.index]; + + edgeColoringSimple(shape, 3.0); // Max. angle. + msdfgen::Bitmap<real_t, 4> image(w, h); // Texture size. + //msdfgen::generateMTSDF(image, shape, p_pixel_range, 1.0, msdfgen::Vector2(-bounds.l, -bounds.b)); // Range, scale, translation. + + DistancePixelConversion distancePixelConversion(p_pixel_range); + msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b)); + msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig()); + + MSDFThreadData td; + td.output = ℑ + td.shape = &shape; + td.projection = &projection; + td.distancePixelConversion = &distancePixelConversion; + + if (p_font_data->work_pool.get_thread_count() == 0) { + p_font_data->work_pool.init(); + } + p_font_data->work_pool.do_work(h, this, &TextServerFallback::_generateMTSDF_threaded, &td); + + msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config); + + { + uint8_t *wr = tex.imgdata.ptrw(); + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * 4; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph()); + wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f)); + wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f)); + wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f)); + wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f)); + } + } + } + + // Blit to image and texture. + { + if (RenderingServer::get_singleton() != nullptr) { + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, Image::FORMAT_RGBA8, tex.imgdata)); + if (tex.texture.is_null()) { + tex.texture.instantiate(); + tex.texture->create_from_image(img); + } else { + tex.texture->update(img); + } + } + } + + // Update height array. + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets.write[k] = tex_pos.y + mh; + } + + chr.texture_idx = tex_pos.index; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h); + chr.rect.position = Vector2(bounds.l, -bounds.t); + chr.rect.size = chr.uv_rect.size; + } + return chr; +} +#endif + #ifdef MODULE_FREETYPE_ENABLED - } else if (p_filename.get_extension() == "ttf" || p_filename.get_extension() == "otf" || p_filename.get_extension() == "woff") { - fd = memnew(DynamicFontDataFallback); +_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const { + int w = bitmap.width; + int h = bitmap.rows; + + int mw = w + p_rect_margin * 2; + int mh = h + p_rect_margin * 2; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; + Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + + // Fit character in char texture. + + FontTexture &tex = p_data->textures.write[tex_pos.index]; + + { + uint8_t *wr = tex.imgdata.ptrw(); + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * color_size; + ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph()); + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: { + int byte = i * bitmap.pitch + (j >> 3); + int bit = 1 << (7 - (j % 8)); + wr[ofs + 0] = 255; //grayscale as 1 + wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0; + } break; + case FT_PIXEL_MODE_GRAY: + wr[ofs + 0] = 255; //grayscale as 1 + wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; + break; + case FT_PIXEL_MODE_BGRA: { + int ofs_color = i * bitmap.pitch + (j << 2); + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; + wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = bitmap.buffer[ofs_color + 3]; + } break; + default: + ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + "."); + break; + } + } + } + } + + // Blit to image and texture. + { + if (RenderingServer::get_singleton() != nullptr) { + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, require_format, tex.imgdata)); + + if (tex.texture.is_null()) { + tex.texture.instantiate(); + tex.texture->create_from_image(img); + } else { + tex.texture->update(img); + } + } + } + + // Update height array. + for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { + tex.offsets.write[k] = tex_pos.y + mh; + } + + FontGlyph chr; + chr.advance = (advance * p_data->scale / p_data->oversampling).round(); + chr.texture_idx = tex_pos.index; + chr.found = true; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h); + chr.rect.position = (Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling).round(); + chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling; + return chr; +} #endif - } else { - return RID(); + +/*************************************************************************/ +/* Font Cache */ +/*************************************************************************/ + +_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false); + + FontDataForSizeFallback *fd = p_font_data->cache[p_size]; + if (fd->glyph_map.has(p_glyph)) { + return fd->glyph_map[p_glyph].found; } - Error err = fd->load_from_file(p_filename, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); + if (p_glyph == 0) { // Non graphical or invalid glyph, do not render. + fd->glyph_map[p_glyph] = FontGlyph(); + return true; } - return font_owner.make_rid(fd); +#ifdef MODULE_FREETYPE_ENABLED + FontGlyph gl; + if (fd->face) { + FT_Int32 flags = FT_LOAD_DEFAULT; + + bool outline = p_size.y > 0; + switch (p_font_data->hinting) { + case TextServer::HINTING_NONE: + flags |= FT_LOAD_NO_HINTING; + break; + case TextServer::HINTING_LIGHT: + flags |= FT_LOAD_TARGET_LIGHT; + break; + default: + flags |= FT_LOAD_TARGET_NORMAL; + break; + } + if (p_font_data->force_autohinter) { + flags |= FT_LOAD_FORCE_AUTOHINT; + } + if (outline) { + flags |= FT_LOAD_NO_BITMAP; + } else if (FT_HAS_COLOR(fd->face)) { + flags |= FT_LOAD_COLOR; + } + + FT_Fixed v, h; + FT_Get_Advance(fd->face, p_glyph, flags, &h); + FT_Get_Advance(fd->face, p_glyph, flags | FT_LOAD_VERTICAL_LAYOUT, &v); + + int error = FT_Load_Glyph(fd->face, p_glyph, flags); + if (error) { + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph."); + } + + if (!outline) { + if (!p_font_data->msdf) { + error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + } + FT_GlyphSlot slot = fd->face->glyph; + if (!error) { + if (p_font_data->msdf) { +#ifdef MODULE_MSDFGEN_ENABLED + gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); +#else + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); +#endif + } else { + gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); + } + } + } else { + FT_Stroker stroker; + if (FT_Stroker_New(library, &stroker) != 0) { + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker."); + } + + FT_Stroker_Set(stroker, (int)(fd->size.y * fd->oversampling * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0); + FT_Glyph glyph; + FT_BitmapGlyph glyph_bitmap; + + if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) { + goto cleanup_stroker; + } + if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) { + goto cleanup_glyph; + } + if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) { + goto cleanup_glyph; + } + glyph_bitmap = (FT_BitmapGlyph)glyph; + gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2()); + + cleanup_glyph: + FT_Done_Glyph(glyph); + cleanup_stroker: + FT_Stroker_Done(stroker); + } + fd->glyph_map[p_glyph] = gl; + return gl.found; + } +#endif + fd->glyph_map[p_glyph] = FontGlyph(); + return false; } -RID TextServerFallback::create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = nullptr; - if (p_type == "fnt" || p_type == "font") { - fd = memnew(BitmapFontDataFallback); +_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const { + if (p_font_data->cache.has(p_size)) { + return true; + } + + FontDataForSizeFallback *fd = memnew(FontDataForSizeFallback); + fd->size = p_size; + if (p_font_data->data_ptr) { + // Init dynamic font. #ifdef MODULE_FREETYPE_ENABLED - } else if (p_type == "ttf" || p_type == "otf" || p_type == "woff") { - fd = memnew(DynamicFontDataFallback); + int error = 0; + if (!library) { + error = FT_Init_FreeType(&library); + ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'."); + } + + memset(&fd->stream, 0, sizeof(FT_StreamRec)); + fd->stream.base = (unsigned char *)p_font_data->data_ptr; + fd->stream.size = p_font_data->data_size; + fd->stream.pos = 0; + + FT_Open_Args fargs; + memset(&fargs, 0, sizeof(FT_Open_Args)); + fargs.memory_base = (unsigned char *)p_font_data->data_ptr; + fargs.memory_size = p_font_data->data_size; + fargs.flags = FT_OPEN_MEMORY; + fargs.stream = &fd->stream; + error = FT_Open_Face(library, &fargs, 0, &fd->face); + if (error) { + FT_Done_Face(fd->face); + fd->face = nullptr; + ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'."); + } + + if (p_font_data->msdf) { + fd->oversampling = 1.0f; + fd->size.x = p_font_data->msdf_source_size; + } else if (p_font_data->oversampling <= 0.0f) { + fd->oversampling = TS->font_get_global_oversampling(); + } else { + fd->oversampling = p_font_data->oversampling; + } + + if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) { + int best_match = 0; + int diff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[0].width)); + fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width; + for (int i = 1; i < fd->face->num_fixed_sizes; i++) { + int ndiff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[i].width)); + if (ndiff < diff) { + best_match = i; + diff = ndiff; + fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width; + } + } + FT_Select_Size(fd->face, best_match); + } else { + FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling); + } + + fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale; + fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale; + fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; + fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; + + if (!p_font_data->face_init) { + // Read OpenType variations. + p_font_data->supported_varaitions.clear(); + if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + FT_Get_MM_Var(fd->face, &amaster); + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + p_font_data->supported_varaitions[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536); + } + FT_Done_MM_Var(library, amaster); + } + p_font_data->face_init = true; + } + + // Write variations. + if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *amaster; + + FT_Get_MM_Var(fd->face, &amaster); + + Vector<FT_Fixed> coords; + coords.resize(amaster->num_axis); + + FT_Get_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw()); + + for (FT_UInt i = 0; i < amaster->num_axis; i++) { + // Reset to default. + int32_t var_tag = amaster->axis[i].tag; + real_t var_value = (double)amaster->axis[i].def / 65536.f; + coords.write[i] = amaster->axis[i].def; + + if (p_font_data->variation_coordinates.has(var_tag)) { + var_value = p_font_data->variation_coordinates[var_tag]; + coords.write[i] = CLAMP(var_value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + + if (p_font_data->variation_coordinates.has(tag_to_name(var_tag))) { + var_value = p_font_data->variation_coordinates[tag_to_name(var_tag)]; + coords.write[i] = CLAMP(var_value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum); + } + } + + FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw()); + FT_Done_MM_Var(library, amaster); + } +#else + ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif - } else { - return RID(); } + p_font_data->cache[p_size] = fd; + return true; +} - Error err = fd->load_from_memory(p_data, p_size, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); +_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_font_data) { + for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = p_font_data->cache.front(); E; E = E->next()) { + memdelete(E->get()); } + p_font_data->cache.clear(); + p_font_data->face_init = false; + p_font_data->supported_varaitions.clear(); +} + +RID TextServerFallback::create_font() { + FontDataFallback *fd = memnew(FontDataFallback); + return font_owner.make_rid(fd); } -RID TextServerFallback::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = memnew(BitmapFontDataFallback); - Error err = fd->bitmap_new(p_height, p_ascent, p_base_size); - if (err != OK) { - memdelete(fd); - return RID(); +void TextServerFallback::font_set_data(RID p_font_rid, const PackedByteArray &p_data) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + _font_clear_cache(fd); + fd->data = p_data; + fd->data_ptr = fd->data.ptr(); + fd->data_size = fd->data.size(); +} + +void TextServerFallback::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + _font_clear_cache(fd); + fd->data.clear(); + fd->data_ptr = p_data_ptr; + fd->data_size = p_data_size; +} + +void TextServerFallback::font_set_antialiased(RID p_font_rid, bool p_antialiased) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->antialiased != p_antialiased) { + _font_clear_cache(fd); + fd->antialiased = p_antialiased; } +} - return font_owner.make_rid(fd); +bool TextServerFallback::font_is_antialiased(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->antialiased; } -void TextServerFallback::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_texture(p_texture); + + MutexLock lock(fd->mutex); + if (fd->msdf != p_msdf) { + _font_clear_cache(fd); + fd->msdf = p_msdf; + } } -void TextServerFallback::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +bool TextServerFallback::font_is_multichannel_signed_distance_field(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf; +} + +void TextServerFallback::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance); + + MutexLock lock(fd->mutex); + if (fd->msdf_range != p_msdf_pixel_range) { + _font_clear_cache(fd); + fd->msdf_range = p_msdf_pixel_range; + } } -void TextServerFallback::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +int TextServerFallback::font_get_msdf_pixel_range(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf_range; +} + +void TextServerFallback::font_set_msdf_size(RID p_font_rid, int p_msdf_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning); + + MutexLock lock(fd->mutex); + if (fd->msdf_source_size != p_msdf_size) { + _font_clear_cache(fd); + fd->msdf_source_size = p_msdf_size; + } } -float TextServerFallback::font_get_height(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +int TextServerFallback::font_get_msdf_size(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->msdf_source_size; +} + +void TextServerFallback::font_set_fixed_size(RID p_font_rid, int p_fixed_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->fixed_size != p_fixed_size) { + fd->fixed_size = p_fixed_size; + } +} + +int TextServerFallback::font_get_fixed_size(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->fixed_size; +} + +void TextServerFallback::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->force_autohinter != p_force_autohinter) { + _font_clear_cache(fd); + fd->force_autohinter = p_force_autohinter; + } +} + +bool TextServerFallback::font_is_force_autohinter(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->force_autohinter; +} + +void TextServerFallback::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->hinting != p_hinting) { + _font_clear_cache(fd); + fd->hinting = p_hinting; + } +} + +TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, HINTING_NONE); + + MutexLock lock(fd->mutex); + return fd->hinting; +} + +void TextServerFallback::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->variation_coordinates != p_variation_coordinates) { + _font_clear_cache(fd); + fd->variation_coordinates = p_variation_coordinates; + } +} + +Dictionary TextServerFallback::font_get_variation_coordinates(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); + + MutexLock lock(fd->mutex); + return fd->variation_coordinates; +} + +void TextServerFallback::font_set_oversampling(RID p_font_rid, real_t p_oversampling) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->oversampling != p_oversampling) { + _font_clear_cache(fd); + fd->oversampling = p_oversampling; + } +} + +real_t TextServerFallback::font_get_oversampling(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_height(p_size); + + MutexLock lock(fd->mutex); + return fd->oversampling; } -float TextServerFallback::font_get_ascent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +Array TextServerFallback::font_get_size_cache_list(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Array ret; + for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = fd->cache.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +void TextServerFallback::font_clear_size_cache(RID p_font_rid) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = fd->cache.front(); E; E = E->next()) { + memdelete(E->get()); + } + fd->cache.clear(); +} + +void TextServerFallback::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + if (fd->cache.has(p_size)) { + memdelete(fd->cache[p_size]); + fd->cache.erase(p_size); + } +} + +void TextServerFallback::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->ascent = p_ascent; +} + +real_t TextServerFallback::font_get_ascent(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_ascent(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->ascent * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->ascent; + } } -float TextServerFallback::font_get_descent(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->descent = p_descent; +} + +real_t TextServerFallback::font_get_descent(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_descent(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->descent * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->descent; + } } -float TextServerFallback::font_get_underline_position(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->underline_position = p_underline_position; +} + +real_t TextServerFallback::font_get_underline_position(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_position(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->underline_position * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->underline_position; + } } -float TextServerFallback::font_get_underline_thickness(RID p_font, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->underline_thickness = p_underline_thickness; +} + +real_t TextServerFallback::font_get_underline_thickness(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_underline_thickness(p_size); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->underline_thickness * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->underline_thickness; + } } -int TextServerFallback::font_get_spacing_space(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0); - return fd->get_spacing_space(); +void TextServerFallback::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->scale = p_scale; } -void TextServerFallback::font_set_spacing_space(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +real_t TextServerFallback::font_get_scale(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0.f); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f); + + if (fd->msdf) { + return fd->cache[size]->scale * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->scale / fd->cache[size]->oversampling; + } +} + +void TextServerFallback::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_spacing_space(p_value); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + switch (p_spacing) { + case TextServer::SPACING_GLYPH: { + fd->cache[size]->spacing_glyph = p_value; + } break; + case TextServer::SPACING_SPACE: { + fd->cache[size]->spacing_space = p_value; + } break; + default: { + ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing)); + } break; + } } -int TextServerFallback::font_get_spacing_glyph(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +int TextServerFallback::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + + switch (p_spacing) { + case TextServer::SPACING_GLYPH: { + if (fd->msdf) { + return fd->cache[size]->spacing_glyph * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->spacing_glyph; + } + } break; + case TextServer::SPACING_SPACE: { + if (fd->msdf) { + return fd->cache[size]->spacing_space * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return fd->cache[size]->spacing_space; + } + } break; + default: { + ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing)); + } break; + } + return 0; +} + +int TextServerFallback::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, 0); - return fd->get_spacing_glyph(); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + + return fd->cache[size]->textures.size(); } -void TextServerFallback::font_set_spacing_glyph(RID p_font, int p_value) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_clear_textures(RID p_font_rid, const Vector2i &p_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_spacing_glyph(p_value); + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->textures.clear(); } -void TextServerFallback::font_set_antialiased(RID p_font, bool p_antialiased) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_antialiased(p_antialiased); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size()); + + fd->cache[size]->textures.remove(p_texture_index); } -bool TextServerFallback::font_get_antialiased(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_antialiased(); +void TextServerFallback::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + ERR_FAIL_COND(p_image.is_null()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_COND(p_texture_index < 0); + if (p_texture_index >= fd->cache[size]->textures.size()) { + fd->cache[size]->textures.resize(p_texture_index + 1); + } + + FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + + tex.imgdata = p_image->get_data(); + tex.texture_w = p_image->get_width(); + tex.texture_h = p_image->get_height(); + tex.format = p_image->get_format(); + + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + tex.texture = Ref<ImageTexture>(); + tex.texture.instantiate(); + tex.texture->create_from_image(img); } -void TextServerFallback::font_set_distance_field_hint(RID p_font, bool p_distance_field) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +Ref<Image> TextServerFallback::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Ref<Image>()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata)); + + return img; +} + +void TextServerFallback::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_distance_field_hint(p_distance_field); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (p_texture_index >= fd->cache[size]->textures.size()) { + fd->cache[size]->textures.resize(p_texture_index + 1); + } + + FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + tex.offsets = p_offset; } -bool TextServerFallback::font_get_distance_field_hint(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_distance_field_hint(); +PackedInt32Array TextServerFallback::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, PackedInt32Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); + ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); + + const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; + return tex.offsets; } -void TextServerFallback::font_set_hinting(RID p_font, TextServer::Hinting p_hinting) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +Array TextServerFallback::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + + Array ret; + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + ret.push_back(*E); + } + return ret; +} + +void TextServerFallback::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_hinting(p_hinting); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + fd->cache[size]->glyph_map.clear(); } -TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, TextServer::HINTING_NONE); - return fd->get_hinting(); +void TextServerFallback::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + fd->cache[size]->glyph_map.erase(p_glyph); } -void TextServerFallback::font_set_force_autohinter(RID p_font, bool p_enabeld) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +Vector2 TextServerFallback::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].advance * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].advance; + } +} + +void TextServerFallback::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->set_force_autohinter(p_enabeld); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].advance = p_advance; + gl[p_glyph].found = true; } -bool TextServerFallback::font_get_force_autohinter(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +Vector2 TextServerFallback::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].rect.position * (real_t)p_size.x / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].rect.position; + } +} + +void TextServerFallback::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.position = p_offset; + gl[p_glyph].found = true; +} + +Vector2 TextServerFallback::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Vector2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + if (fd->msdf) { + return gl[p_glyph].rect.size * (real_t)p_size.x / (real_t)fd->msdf_source_size; + } else { + return gl[p_glyph].rect.size; + } +} + +void TextServerFallback::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].rect.size = p_gl_size; + gl[p_glyph].found = true; +} + +Rect2 TextServerFallback::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Rect2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2()); + if (!_ensure_glyph(fd, size, p_glyph)) { + return Rect2(); // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + return gl[p_glyph].uv_rect; +} + +void TextServerFallback::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].uv_rect = p_uv_rect; + gl[p_glyph].found = true; +} + +int TextServerFallback::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, -1); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1); + if (!_ensure_glyph(fd, size, p_glyph)) { + return -1; // Invalid or non graphicl glyph, do not display errors. + } + + const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + return gl[p_glyph].texture_idx; +} + +void TextServerFallback::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + + HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; + + gl[p_glyph].texture_idx = p_texture_idx; + gl[p_glyph].found = true; +} + +bool TextServerFallback::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->get_force_autohinter(); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false); + +#ifdef MODULE_FREETYPE_ENABLED + int error = FT_Load_Glyph(fd->cache[size]->face, p_index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + ERR_FAIL_COND_V(error, false); + + r_points.clear(); + r_contours.clear(); + + real_t h = fd->cache[size]->ascent; + real_t scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale; + if (fd->msdf) { + scale = scale * (real_t)p_size / (real_t)fd->msdf_source_size; + } + for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) { + r_points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i]))); + } + for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) { + r_contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]); + } + r_orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT); +#else + return false; +#endif + return true; } -bool TextServerFallback::font_has_char(RID p_font, char32_t p_char) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +Array TextServerFallback::font_get_kerning_list(RID p_font_rid, int p_size) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Array()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array()); + + Array ret; + for (const Map<Vector2i, Vector2>::Element *E = fd->cache[size]->kerning_map.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +void TextServerFallback::font_clear_kerning_map(RID p_font_rid, int p_size) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map.clear(); +} + +void TextServerFallback::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map.erase(p_glyph_pair); +} + +void TextServerFallback::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning; +} + +Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Vector2()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2()); + + const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map; + + if (kern.has(p_glyph_pair)) { + if (fd->msdf) { + return kern[p_glyph_pair] * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return kern[p_glyph_pair]; + } + } else { +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + FT_Vector delta; + FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta); + if (fd->msdf) { + return Vector2(delta.x, delta.y) * (real_t)p_size / (real_t)fd->msdf_source_size; + } else { + return Vector2(delta.x, delta.y); + } + } +#endif + } + return Vector2(); +} + +int32_t TextServerFallback::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + if (p_variation_selector) { + return FT_Face_GetCharVariantIndex(fd->cache[size]->face, p_char, p_variation_selector); + } else { + return FT_Get_Char_Index(fd->cache[size]->face, p_char); + } + } else { + return 0; + } +#else + return (int32_t)p_char; +#endif +} + +bool TextServerFallback::font_has_char(RID p_font_rid, char32_t p_char) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->has_char(p_char); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false); + } + FontDataForSizeFallback *at_size = fd->cache.front()->get(); + +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + return FT_Get_Char_Index(at_size->face, p_char) != 0; + } +#endif + return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false; } -String TextServerFallback::font_get_supported_chars(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +String TextServerFallback::font_get_supported_chars(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, String()); - return fd->get_supported_chars(); + + MutexLock lock(fd->mutex); + if (fd->cache.is_empty()) { + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String()); + } + FontDataForSizeFallback *at_size = fd->cache.front()->get(); + + String chars; +#ifdef MODULE_FREETYPE_ENABLED + if (at_size && at_size->face) { + FT_UInt gindex; + FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex); + while (gindex != 0) { + if (charcode != 0) { + chars += char32_t(charcode); + } + charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex); + } + return chars; + } +#endif + if (at_size) { + const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map; + const int32_t *E = nullptr; + while ((E = gl.next(E))) { + chars += char32_t(*E); + } + } + return chars; } -bool TextServerFallback::font_has_outline(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->has_outline(); +void TextServerFallback::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + for (char32_t i = p_start; i <= p_end; i++) { +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face) { + _ensure_glyph(fd, size, FT_Get_Char_Index(fd->cache[size]->face, i)); + continue; + } +#endif + _ensure_glyph(fd, size, (int32_t)i); + } } -float TextServerFallback::font_get_base_size(RID p_font) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, 0.f); - return fd->get_base_size(); +void TextServerFallback::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + ERR_FAIL_COND(!_ensure_glyph(fd, size, p_index)); } -bool TextServerFallback::font_is_language_supported(RID p_font, const String &p_language) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - if (fd->lang_support_overrides.has(p_language)) { - return fd->lang_support_overrides[p_language]; - } else { - Vector<String> tags = p_language.replace("-", "_").split("_"); - if (tags.size() > 0) { - if (fd->lang_support_overrides.has(tags[0])) { - return fd->lang_support_overrides[tags[0]]; +void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, p_size); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (!_ensure_glyph(fd, size, p_index)) { + return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw. + } + + const FontGlyph &gl = fd->cache[size]->glyph_map[p_index]; + if (gl.found) { + ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + + if (gl.texture_idx != -1) { + Color modulate = p_color; +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } +#endif + if (RenderingServer::get_singleton() != nullptr) { + RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + if (fd->msdf) { + Point2 cpos = p_pos; + cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size; + Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range); + } else { + Point2i cpos = p_pos; + cpos += gl.rect.position; + Size2i csize = gl.rect.size; + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } } } - return false; } } -void TextServerFallback::font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides[p_language] = p_supported; + + MutexLock lock(fd->mutex); + Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size)); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); + if (!_ensure_glyph(fd, size, p_index)) { + return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw. + } + + const FontGlyph &gl = fd->cache[size]->glyph_map[p_index]; + if (gl.found) { + ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size()); + + if (gl.texture_idx != -1) { + Color modulate = p_color; +#ifdef MODULE_FREETYPE_ENABLED + if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) { + modulate.r = modulate.g = modulate.b = 1.0; + } +#endif + if (RenderingServer::get_singleton() != nullptr) { + RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid(); + if (fd->msdf) { + Point2 cpos = p_pos; + cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size; + Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size; + RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range); + } else { + Point2i cpos = p_pos; + cpos += gl.rect.position; + Size2i csize = gl.rect.size; + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false); + } + } + } + } } -bool TextServerFallback::font_get_language_support_override(RID p_font, const String &p_language) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +bool TextServerFallback::font_is_language_supported(RID p_font_rid, const String &p_language) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); - return fd->lang_support_overrides[p_language]; + + MutexLock lock(fd->mutex); + if (fd->language_support_overrides.has(p_language)) { + return fd->language_support_overrides[p_language]; + } else { + return true; + } } -void TextServerFallback::font_remove_language_support_override(RID p_font, const String &p_language) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); - fd->lang_support_overrides.erase(p_language); + + MutexLock lock(fd->mutex); + fd->language_support_overrides[p_language] = p_supported; } -Vector<String> TextServerFallback::font_get_language_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +bool TextServerFallback::font_get_language_support_override(RID p_font_rid, const String &p_language) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); + return fd->language_support_overrides[p_language]; +} + +void TextServerFallback::font_remove_language_support_override(RID p_font_rid, const String &p_language) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + fd->language_support_overrides.erase(p_language); +} + +Vector<String> TextServerFallback::font_get_language_support_overrides(RID p_font_rid) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, Vector<String>()); - Vector<String> ret; - for (Map<String, bool>::Element *E = fd->lang_support_overrides.front(); E; E = E->next()) { - ret.push_back(E->key()); + + MutexLock lock(fd->mutex); + Vector<String> out; + for (const Map<String, bool>::Element *E = fd->language_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); } - return ret; + return out; } -bool TextServerFallback::font_is_script_supported(RID p_font, const String &p_script) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); +bool TextServerFallback::font_is_script_supported(RID p_font_rid, const String &p_script) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); if (fd->script_support_overrides.has(p_script)) { return fd->script_support_overrides[p_script]; } else { @@ -392,95 +1890,84 @@ bool TextServerFallback::font_is_script_supported(RID p_font, const String &p_sc } } -void TextServerFallback::font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); fd->script_support_overrides[p_script] = p_supported; } -bool TextServerFallback::font_get_script_support_override(RID p_font, const String &p_script) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +bool TextServerFallback::font_get_script_support_override(RID p_font_rid, const String &p_script) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, false); + + MutexLock lock(fd->mutex); return fd->script_support_overrides[p_script]; } -void TextServerFallback::font_remove_script_support_override(RID p_font, const String &p_script) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +void TextServerFallback::font_remove_script_support_override(RID p_font_rid, const String &p_script) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND(!fd); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND(!_ensure_cache_for_size(fd, size)); fd->script_support_overrides.erase(p_script); } -Vector<String> TextServerFallback::font_get_script_support_overrides(RID p_font) { - _THREAD_SAFE_METHOD_ - FontDataFallback *fd = font_owner.getornull(p_font); +Vector<String> TextServerFallback::font_get_script_support_overrides(RID p_font_rid) { + FontDataFallback *fd = font_owner.getornull(p_font_rid); ERR_FAIL_COND_V(!fd, Vector<String>()); - Vector<String> ret; - for (Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { - ret.push_back(E->key()); - } - return ret; -} -uint32_t TextServerFallback::font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector) const { - return (uint32_t)p_char; -} - -Vector2 TextServerFallback::font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector2()); - return fd->get_advance(p_index, p_size); + MutexLock lock(fd->mutex); + Vector<String> out; + for (const Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) { + out.push_back(E->key()); + } + return out; } -Vector2 TextServerFallback::font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector2()); - return fd->get_kerning(p_index_a, p_index_b, p_size); +Dictionary TextServerFallback::font_supported_feature_list(RID p_font_rid) const { + return Dictionary(); } -Vector2 TextServerFallback::font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector2()); - return fd->draw_glyph(p_canvas, p_size, p_pos, p_index, p_color); -} +Dictionary TextServerFallback::font_supported_variation_list(RID p_font_rid) const { + FontDataFallback *fd = font_owner.getornull(p_font_rid); + ERR_FAIL_COND_V(!fd, Dictionary()); -Vector2 TextServerFallback::font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, Vector2()); - return fd->draw_glyph_outline(p_canvas, p_size, p_outline_size, p_pos, p_index, p_color); + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary()); + return fd->supported_varaitions; } -bool TextServerFallback::font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const { - _THREAD_SAFE_METHOD_ - const FontDataFallback *fd = font_owner.getornull(p_font); - ERR_FAIL_COND_V(!fd, false); - return fd->get_glyph_contours(p_size, p_index, r_points, r_contours, r_orientation); -} - -float TextServerFallback::font_get_oversampling() const { +real_t TextServerFallback::font_get_global_oversampling() const { return oversampling; } -void TextServerFallback::font_set_oversampling(float p_oversampling) { +void TextServerFallback::font_set_global_oversampling(real_t p_oversampling) { _THREAD_SAFE_METHOD_ if (oversampling != p_oversampling) { oversampling = p_oversampling; List<RID> fonts; font_owner.get_owned_list(&fonts); + bool font_cleared = false; for (const RID &E : fonts) { - font_owner.getornull(E)->clear_cache(); + if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) { + font_clear_size_cache(E); + font_cleared = true; + } } - } -} -Vector<String> TextServerFallback::get_system_fonts() const { - return Vector<String>(); + if (font_cleared) { + List<RID> text_bufs; + shaped_owner.get_owned_list(&text_bufs); + for (const RID &E : text_bufs) { + invalidate(shaped_owner.getornull(E)); + } + } + } } /*************************************************************************/ @@ -533,10 +2020,10 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te } void TextServerFallback::shaped_text_clear(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + MutexLock lock(sd->mutex); sd->parent = RID(); sd->start = 0; sd->end = 0; @@ -557,9 +2044,10 @@ TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped } void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + + MutexLock lock(sd->mutex); if (sd->orientation != p_orientation) { if (sd->parent != RID()) { full_copy(sd); @@ -574,15 +2062,17 @@ void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Vecto } TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL); + + MutexLock lock(sd->mutex); return sd->orientation; } void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); + + MutexLock lock(sd->mutex); ERR_FAIL_COND(!sd); if (sd->preserve_invalid != p_enabled) { if (sd->parent != RID()) { @@ -594,16 +2084,18 @@ void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e } bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); return sd->preserve_invalid; } void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND(!sd); + + MutexLock lock(sd->mutex); if (sd->preserve_control != p_enabled) { if (sd->parent != RID()) { full_copy(sd); @@ -614,18 +2106,24 @@ void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_e } bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); return sd->preserve_control; } bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(p_size <= 0, false); + for (int i = 0; i < p_fonts.size(); i++) { + ERR_FAIL_COND_V(!font_owner.getornull(p_fonts[i]), false); + } + if (p_text.is_empty()) { return true; } @@ -637,6 +2135,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te ShapedTextData::Span span; span.start = sd->text.length(); span.end = span.start + p_text.length(); + // Pre-sort fonts, push fonts with the language support first. Vector<RID> fonts_no_match; int font_count = p_fonts.size(); @@ -662,9 +2161,10 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te } bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(p_key == Variant(), false); ERR_FAIL_COND_V(sd->objects.has(p_key), false); @@ -692,9 +2192,10 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con } bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), false); sd->objects[p_key].rect.size = p_size; sd->objects[p_key].inline_align = p_inline_align; @@ -706,8 +2207,6 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->upos = 0; sd->uthk = 0; int sd_size = sd->glyphs.size(); - const FontDataFallback *fd = nullptr; - RID prev_rid = RID(); for (int i = 0; i < sd_size; i++) { Glyph gl = sd->glyphs[i]; @@ -731,17 +2230,13 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->glyphs.write[i].advance = sd->objects[key].rect.size.y; } } else { - if (prev_rid != gl.font_rid) { - fd = font_owner.getornull(gl.font_rid); - prev_rid = gl.font_rid; - } - if (fd != nullptr) { + if (gl.font_rid.is_valid()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - sd->ascent = MAX(sd->ascent, fd->get_ascent(gl.font_size)); - sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size)); + sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size)); + sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size)); } else { - sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); - sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size)); sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size)); @@ -760,8 +2255,8 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, } // Align embedded objects to baseline. - float full_ascent = sd->ascent; - float full_descent = sd->descent; + real_t full_ascent = sd->ascent; + real_t full_descent = sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { @@ -830,9 +2325,10 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, } RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, RID()); + + MutexLock lock(sd->mutex); if (sd->parent != RID()) { return shaped_text_substr(sd->parent, p_start, p_length); } @@ -886,14 +2382,13 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng new_sd->width += new_sd->objects[key].rect.size.y; } } else { - const FontDataFallback *fd = font_owner.getornull(gl.font_rid); - if (fd != nullptr) { + if (gl.font_rid.is_valid()) { if (new_sd->orientation == ORIENTATION_HORIZONTAL) { - new_sd->ascent = MAX(new_sd->ascent, fd->get_ascent(gl.font_size)); - new_sd->descent = MAX(new_sd->descent, fd->get_descent(gl.font_size)); + new_sd->ascent = MAX(new_sd->ascent, font_get_ascent(gl.font_rid, gl.font_size)); + new_sd->descent = MAX(new_sd->descent, font_get_descent(gl.font_rid, gl.font_size)); } else { - new_sd->ascent = MAX(new_sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); - new_sd->descent = MAX(new_sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + new_sd->ascent = MAX(new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + new_sd->descent = MAX(new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } } else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) { // Glyph not found, replace with hex code box. @@ -912,8 +2407,8 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng } // Align embedded objects to baseline. - float full_ascent = new_sd->ascent; - float full_descent = new_sd->descent; + real_t full_ascent = new_sd->ascent; + real_t full_descent = new_sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) { if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { @@ -984,16 +2479,18 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng } RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, RID()); + + MutexLock lock(sd->mutex); return sd->parent; } -float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags) { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags) { ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1053,13 +2550,13 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, } if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) { - float delta_width_per_space = (p_width - sd->width) / space_count; + real_t delta_width_per_space = (p_width - sd->width) / space_count; for (int i = start_pos; i <= end_pos; i++) { Glyph &gl = sd->glyphs.write[i]; if (gl.count > 0) { if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) { - float old_adv = gl.advance; - gl.advance = Math::round(MAX(gl.advance + delta_width_per_space, 0.05 * gl.font_size)); + real_t old_adv = gl.advance; + gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size)); sd->width += (gl.advance - old_adv); } } @@ -1069,10 +2566,11 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, return sd->width; } -float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) { ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1081,7 +2579,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float } int tab_index = 0; - float off = 0.f; + real_t off = 0.f; int start, end, delta; if (sd->para_direction == DIRECTION_LTR) { @@ -1098,7 +2596,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float for (int i = start; i != end; i += delta) { if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) { - float tab_off = 0.f; + real_t tab_off = 0.f; while (tab_off <= off) { tab_off += p_tab_stops[tab_index]; tab_index++; @@ -1106,7 +2604,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float tab_index = 0; } } - float old_adv = gl[i].advance; + real_t old_adv = gl[i].advance; gl[i].advance = tab_off - off; sd->width += gl[i].advance - old_adv; off = 0; @@ -1119,9 +2617,10 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<float } bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped); } @@ -1159,9 +2658,10 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) { } bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped); } @@ -1173,10 +2673,11 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) { return true; } -void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint8_t p_trim_flags) { - _THREAD_SAFE_METHOD_ +void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) { ShapedTextData *sd = shaped_owner.getornull(p_shaped_line); - ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid."); + ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid."); + + MutexLock lock(sd->mutex); if (!sd->valid) { shaped_text_shape(p_shaped_line); } @@ -1203,18 +2704,18 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl int sd_size = sd->glyphs.size(); RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; int last_gl_font_size = sd_glyphs[sd_size - 1].font_size; - uint32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.'); - Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, dot_gl_idx, last_gl_font_size); - uint32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' '); - Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, whitespace_gl_idx, last_gl_font_size); + int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.', 0); + Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx); + int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' ', 0); + Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx); int ellipsis_width = 0; if (add_ellipsis) { - ellipsis_width = 3 * dot_adv.x + font_get_spacing_glyph(last_gl_font_rid) + (cut_per_word ? whitespace_adv.x : 0); + ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, TextServer::SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0); } int ell_min_characters = 6; - float width = sd->width; + real_t width = sd->width; int trim_pos = 0; int ellipsis_pos = (enforce_ellipsis) ? 0 : -1; @@ -1289,16 +2790,18 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl } TextServer::TrimData TextServerFallback::shaped_text_get_trim_data(RID p_shaped) const { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); - ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataAdvanced invalid."); + ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataFallback invalid."); + + MutexLock lock(sd->mutex); return sd->overrun_trim_data; } bool TextServerFallback::shaped_text_shape(RID p_shaped) { - _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); if (sd->valid) { return true; } @@ -1347,14 +2850,12 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } else { // Text span. for (int j = span.start; j < span.end; j++) { - const FontDataFallback *fd = nullptr; - Glyph gl; gl.start = j; gl.end = j + 1; gl.count = 1; gl.font_size = span.font_size; - gl.index = (uint32_t)sd->text[j]; // Use codepoint. + gl.index = (int32_t)sd->text[j]; // Use codepoint. if (gl.index == 0x0009 || gl.index == 0x000b) { gl.index = 0x0020; } @@ -1363,45 +2864,44 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } // Select first font which has character (font are already sorted by span language). for (int k = 0; k < span.fonts.size(); k++) { - fd = font_owner.getornull(span.fonts[k]); - if (fd != nullptr && fd->has_char(gl.index)) { + if (font_has_char(span.fonts[k], gl.index)) { gl.font_rid = span.fonts[k]; break; } } - if (gl.font_rid != RID()) { + if (gl.font_rid.is_valid()) { if (sd->text[j] != 0 && !is_linebreak(sd->text[j])) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - gl.advance = fd->get_advance(gl.index, gl.font_size).x; + gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x; gl.x_off = 0; gl.y_off = 0; - sd->ascent = MAX(sd->ascent, fd->get_ascent(gl.font_size)); - sd->descent = MAX(sd->descent, fd->get_descent(gl.font_size)); + sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size)); + sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size)); } else { - gl.advance = fd->get_advance(gl.index, gl.font_size).y; - gl.x_off = -Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5); - gl.y_off = fd->get_ascent(gl.font_size); - sd->ascent = MAX(sd->ascent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); - sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); + gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y; + gl.x_off = -Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5); + gl.y_off = font_get_ascent(gl.font_rid, gl.font_size); + sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); + sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } } - if (fd->get_spacing_space() && is_whitespace(sd->text[j])) { - gl.advance += fd->get_spacing_space(); + if (font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE) && is_whitespace(sd->text[j])) { + gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE); } else { - gl.advance += fd->get_spacing_glyph(); + gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_GLYPH); } - sd->upos = MAX(sd->upos, fd->get_underline_position(gl.font_size)); - sd->uthk = MAX(sd->uthk, fd->get_underline_thickness(gl.font_size)); + sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size)); + sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size)); // Add kerning to previous glyph. if (sd->glyphs.size() > 0) { Glyph &prev_gl = sd->glyphs.write[sd->glyphs.size() - 1]; if (prev_gl.font_rid == gl.font_rid && prev_gl.font_size == gl.font_size) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - prev_gl.advance += fd->get_kerning(prev_gl.index, gl.index, gl.font_size).x; + prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).x; } else { - prev_gl.advance += fd->get_kerning(prev_gl.index, gl.index, gl.font_size).y; + prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).y; } } } @@ -1424,8 +2924,8 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } // Align embedded objects to baseline. - float full_ascent = sd->ascent; - float full_descent = sd->descent; + real_t full_ascent = sd->ascent; + real_t full_descent = sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { @@ -1492,16 +2992,18 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); + + MutexLock lock(sd->mutex); return sd->valid; } Vector<TextServer::Glyph> TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1509,16 +3011,18 @@ Vector<TextServer::Glyph> TextServerFallback::shaped_text_get_glyphs(RID p_shape } Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Vector2i()); + + MutexLock lock(sd->mutex); return Vector2(sd->start, sd->end); } Vector<TextServer::Glyph> TextServerFallback::shaped_text_sort_logical(RID p_shaped) { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1527,10 +3031,11 @@ Vector<TextServer::Glyph> TextServerFallback::shaped_text_sort_logical(RID p_sha } Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const { - _THREAD_SAFE_METHOD_ Array ret; const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, ret); + + MutexLock lock(sd->mutex); for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { ret.push_back(E->key()); } @@ -1539,9 +3044,10 @@ Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const { } Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Rect2()); + + MutexLock lock(sd->mutex); ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2()); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); @@ -1550,9 +3056,10 @@ Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_ke } Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const { - _THREAD_SAFE_METHOD_ const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, Size2()); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1563,40 +3070,44 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const { } } -float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_get_ascent(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } return sd->ascent; } -float TextServerFallback::shaped_text_get_descent(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_get_descent(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } return sd->descent; } -float TextServerFallback::shaped_text_get_width(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_get_width(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } return sd->width; } -float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1604,10 +3115,11 @@ float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const return sd->upos; } -float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const { - _THREAD_SAFE_METHOD_ +real_t TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const { const ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); + + MutexLock lock(sd->mutex); if (!sd->valid) { const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped); } @@ -1623,3 +3135,11 @@ TextServer *TextServerFallback::create_func(Error &r_error, void *p_user_data) { void TextServerFallback::register_server() { TextServerManager::register_create_function(interface_name, interface_features, create_func, nullptr); } + +TextServerFallback::TextServerFallback(){}; + +TextServerFallback::~TextServerFallback() { + if (library != nullptr) { + FT_Done_FreeType(library); + } +}; diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index d4cab2409a..fde75e7135 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -39,22 +39,165 @@ #include "servers/text_server.h" #include "core/templates/rid_owner.h" - +#include "core/templates/thread_work_pool.h" #include "scene/resources/texture.h" -#include "font_fb.h" +#include "modules/modules_enabled.gen.h" + +#ifdef MODULE_FREETYPE_ENABLED +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include FT_STROKER_H +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_BBOX_H +#endif class TextServerFallback : public TextServer { GDCLASS(TextServerFallback, TextServer); _THREAD_SAFE_CLASS_ - float oversampling = 1.f; - mutable RID_PtrOwner<FontDataFallback> font_owner; - mutable RID_PtrOwner<ShapedTextData> shaped_owner; - static String interface_name; static uint32_t interface_features; + // Font cache data. + +#ifdef MODULE_FREETYPE_ENABLED + mutable FT_Library library = nullptr; +#endif + + const int rect_range = 2; + + struct FontTexture { + Image::Format format; + PackedByteArray imgdata; + int texture_w = 0; + int texture_h = 0; + PackedInt32Array offsets; + Ref<ImageTexture> texture; + }; + + struct FontTexturePosition { + int index = 0; + int x = 0; + int y = 0; + }; + + struct FontGlyph { + bool found = false; + int texture_idx = -1; + Rect2 rect; + Rect2 uv_rect; + Vector2 advance; + }; + + struct FontDataForSizeFallback { + real_t ascent = 0.f; + real_t descent = 0.f; + real_t underline_position = 0.f; + real_t underline_thickness = 0.f; + real_t scale = 1.f; + real_t oversampling = 1.f; + + int spacing_glyph = 0; + int spacing_space = 0; + + Vector2i size; + + Vector<FontTexture> textures; + HashMap<int32_t, FontGlyph> glyph_map; + Map<Vector2i, Vector2> kerning_map; + +#ifdef MODULE_FREETYPE_ENABLED + FT_Face face = nullptr; + FT_StreamRec stream; +#endif + + ~FontDataForSizeFallback() { +#ifdef MODULE_FREETYPE_ENABLED + if (face != nullptr) { + FT_Done_Face(face); + } +#endif + } + }; + + struct FontDataFallback { + Mutex mutex; + + bool antialiased = true; + bool msdf = false; + int msdf_range = 14; + int msdf_source_size = 48; + int fixed_size = 0; + bool force_autohinter = false; + TextServer::Hinting hinting = TextServer::HINTING_LIGHT; + Dictionary variation_coordinates; + real_t oversampling = 0.f; + + Map<Vector2i, FontDataForSizeFallback *> cache; + + bool face_init = false; + Dictionary supported_varaitions; + + // Language/script support override. + Map<String, bool> language_support_overrides; + Map<String, bool> script_support_overrides; + + PackedByteArray data; + const uint8_t *data_ptr; + size_t data_size; + + mutable ThreadWorkPool work_pool; + + ~FontDataFallback() { + work_pool.finish(); + for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = cache.front(); E; E = E->next()) { + memdelete(E->get()); + } + cache.clear(); + } + }; + + _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const; +#ifdef MODULE_MSDFGEN_ENABLED + _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const; +#endif +#ifdef MODULE_FREETYPE_ENABLED + _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const; +#endif + _FORCE_INLINE_ bool _ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const; + _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const; + _FORCE_INLINE_ void _font_clear_cache(FontDataFallback *p_font_data); + void _generateMTSDF_threaded(uint32_t y, void *p_td) const; + + _FORCE_INLINE_ Vector2i _get_size(const FontDataFallback *p_font_data, int p_size) const { + if (p_font_data->msdf) { + return Vector2i(p_font_data->msdf_source_size, 0); + } else if (p_font_data->fixed_size > 0) { + return Vector2i(p_font_data->fixed_size, 0); + } else { + return Vector2i(p_size, 0); + } + } + + _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataFallback *p_font_data, const Vector2i &p_size) const { + if (p_font_data->msdf) { + return Vector2i(p_font_data->msdf_source_size, 0); + } else if (p_font_data->fixed_size > 0) { + return Vector2i(p_font_data->fixed_size, MIN(p_size.y, 1)); + } else { + return p_size; + } + } + + // Common data. + + real_t oversampling = 1.f; + mutable RID_PtrOwner<FontDataFallback> font_owner; + mutable RID_PtrOwner<ShapedTextData> shaped_owner; + protected: static void _bind_methods(){}; @@ -77,72 +220,130 @@ public: virtual bool is_locale_right_to_left(const String &p_locale) override; + virtual int32_t name_to_tag(const String &p_name) const override; + virtual String tag_to_name(int32_t p_tag) const override; + /* Font interface */ - virtual RID create_font_system(const String &p_name, int p_base_size = 16) override; - virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override; - virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override; - virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + virtual RID create_font() override; + + virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override; + virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override; + + virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override; + virtual bool font_is_antialiased(RID p_font_rid) const override; + + virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override; + virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override; + + virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override; + virtual int font_get_msdf_pixel_range(RID p_font_rid) const override; + + virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override; + virtual int font_get_msdf_size(RID p_font_rid) const override; + + virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override; + virtual int font_get_fixed_size(RID p_font_rid) const override; + + virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override; + virtual bool font_is_force_autohinter(RID p_font_rid) const override; + + virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override; + virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override; + + virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override; + virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override; + + virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override; + virtual real_t font_get_oversampling(RID p_font_rid) const override; + + virtual Array font_get_size_cache_list(RID p_font_rid) const override; + virtual void font_clear_size_cache(RID p_font_rid) override; + virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override; + + virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override; + virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override; + + virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override; + virtual real_t font_get_descent(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override; + virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override; + + virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override; + virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override; + + virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override; + virtual real_t font_get_scale(RID p_font_rid, int p_size) const override; + + virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override; + virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override; + + virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) override; + + virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override; + virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; - virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; - virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; + virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override; + virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override; - virtual float font_get_height(RID p_font, int p_size) const override; - virtual float font_get_ascent(RID p_font, int p_size) const override; - virtual float font_get_descent(RID p_font, int p_size) const override; + virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override; + virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override; + virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override; - virtual float font_get_underline_position(RID p_font, int p_size) const override; - virtual float font_get_underline_thickness(RID p_font, int p_size) const override; + virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override; - virtual int font_get_spacing_space(RID p_font) const override; - virtual void font_set_spacing_space(RID p_font, int p_value) override; + virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override; - virtual int font_get_spacing_glyph(RID p_font) const override; - virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) override; - virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; - virtual bool font_get_antialiased(RID p_font) const override; + virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override; - virtual void font_set_hinting(RID p_font, Hinting p_hinting) override; - virtual Hinting font_get_hinting(RID p_font) const override; + virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override; + virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override; - virtual void font_set_force_autohinter(RID p_font, bool p_enabeld) override; - virtual bool font_get_force_autohinter(RID p_font) const override; + virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; - virtual bool font_has_char(RID p_font, char32_t p_char) const override; - virtual String font_get_supported_chars(RID p_font) const override; + virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override; + virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override; + virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) override; - virtual void font_set_distance_field_hint(RID p_font, bool p_distance_field) override; - virtual bool font_get_distance_field_hint(RID p_font) const override; + virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override; + virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override; - virtual bool font_has_outline(RID p_font) const override; - virtual float font_get_base_size(RID p_font) const override; + virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) const override; - virtual bool font_is_language_supported(RID p_font, const String &p_language) const override; - virtual void font_set_language_support_override(RID p_font, const String &p_language, bool p_supported) override; - virtual bool font_get_language_support_override(RID p_font, const String &p_language) override; - virtual void font_remove_language_support_override(RID p_font, const String &p_language) override; - Vector<String> font_get_language_support_overrides(RID p_font) override; + virtual bool font_has_char(RID p_font_rid, char32_t p_char) const override; + virtual String font_get_supported_chars(RID p_font_rid) const override; - virtual bool font_is_script_supported(RID p_font, const String &p_script) const override; - virtual void font_set_script_support_override(RID p_font, const String &p_script, bool p_supported) override; - virtual bool font_get_script_support_override(RID p_font, const String &p_script) override; - virtual void font_remove_script_support_override(RID p_font, const String &p_script) override; - Vector<String> font_get_script_support_overrides(RID p_font) override; + virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override; + virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) override; - virtual uint32_t font_get_glyph_index(RID p_font, char32_t p_char, char32_t p_variation_selector = 0x0000) const override; - virtual Vector2 font_get_glyph_advance(RID p_font, uint32_t p_index, int p_size) const override; - virtual Vector2 font_get_glyph_kerning(RID p_font, uint32_t p_index_a, uint32_t p_index_b, int p_size) const override; + virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; - virtual Vector2 font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; - virtual Vector2 font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const override; + virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const override; + virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override; + virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override; + virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override; + virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override; - virtual bool font_get_glyph_contours(RID p_font, int p_size, uint32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override; + virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override; + virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override; + virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override; + virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override; + virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override; - virtual float font_get_oversampling() const override; - virtual void font_set_oversampling(float p_oversampling) override; + virtual Dictionary font_supported_feature_list(RID p_font_rid) const override; + virtual Dictionary font_supported_variation_list(RID p_font_rid) const override; - virtual Vector<String> get_system_fonts() const override; + virtual real_t font_get_global_oversampling() const override; + virtual void font_set_global_oversampling(real_t p_oversampling) override; /* Shaped text buffer interface */ @@ -171,14 +372,14 @@ public: virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; - virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; - virtual float shaped_text_tab_align(RID p_shaped, const Vector<float> &p_tab_stops) override; + virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; + virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override; virtual bool shaped_text_shape(RID p_shaped) override; virtual bool shaped_text_update_breaks(RID p_shaped) override; virtual bool shaped_text_update_justification_ops(RID p_shaped) override; - virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint8_t p_trim_flags) override; + virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override; virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override; virtual bool shaped_text_is_ready(RID p_shaped) const override; @@ -193,17 +394,17 @@ public: virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override; virtual Size2 shaped_text_get_size(RID p_shaped) const override; - virtual float shaped_text_get_ascent(RID p_shaped) const override; - virtual float shaped_text_get_descent(RID p_shaped) const override; - virtual float shaped_text_get_width(RID p_shaped) const override; - virtual float shaped_text_get_underline_position(RID p_shaped) const override; - virtual float shaped_text_get_underline_thickness(RID p_shaped) const override; + virtual real_t shaped_text_get_ascent(RID p_shaped) const override; + virtual real_t shaped_text_get_descent(RID p_shaped) const override; + virtual real_t shaped_text_get_width(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_position(RID p_shaped) const override; + virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override; static TextServer *create_func(Error &r_error, void *p_user_data); static void register_server(); - TextServerFallback(){}; - ~TextServerFallback(){}; + TextServerFallback(); + ~TextServerFallback(); }; #endif // TEXT_SERVER_FALLBACK_H diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 195d766c1d..55d0b392fa 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -138,81 +138,75 @@ <constant name="MATH_DB2LINEAR" value="40" enum="BuiltinFunc"> Convert the input from decibel volume to linear volume. </constant> - <constant name="MATH_POLAR2CARTESIAN" value="41" enum="BuiltinFunc"> - Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis). + <constant name="MATH_WRAP" value="41" enum="BuiltinFunc"> </constant> - <constant name="MATH_CARTESIAN2POLAR" value="42" enum="BuiltinFunc"> - Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle). + <constant name="MATH_WRAPF" value="42" enum="BuiltinFunc"> </constant> - <constant name="MATH_WRAP" value="43" enum="BuiltinFunc"> - </constant> - <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc"> - </constant> - <constant name="LOGIC_MAX" value="45" enum="BuiltinFunc"> + <constant name="LOGIC_MAX" value="43" enum="BuiltinFunc"> Return the greater of the two numbers, also known as their maximum. </constant> - <constant name="LOGIC_MIN" value="46" enum="BuiltinFunc"> + <constant name="LOGIC_MIN" value="44" enum="BuiltinFunc"> Return the lesser of the two numbers, also known as their minimum. </constant> - <constant name="LOGIC_CLAMP" value="47" enum="BuiltinFunc"> + <constant name="LOGIC_CLAMP" value="45" enum="BuiltinFunc"> Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code]. </constant> - <constant name="LOGIC_NEAREST_PO2" value="48" enum="BuiltinFunc"> + <constant name="LOGIC_NEAREST_PO2" value="46" enum="BuiltinFunc"> Return the nearest power of 2 to the input. </constant> - <constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc"> + <constant name="OBJ_WEAKREF" value="47" enum="BuiltinFunc"> Create a [WeakRef] from the input. </constant> - <constant name="TYPE_CONVERT" value="50" enum="BuiltinFunc"> + <constant name="TYPE_CONVERT" value="48" enum="BuiltinFunc"> Convert between types. </constant> - <constant name="TYPE_OF" value="51" enum="BuiltinFunc"> + <constant name="TYPE_OF" value="49" enum="BuiltinFunc"> Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned. </constant> - <constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc"> + <constant name="TYPE_EXISTS" value="50" enum="BuiltinFunc"> Checks if a type is registered in the [ClassDB]. </constant> - <constant name="TEXT_CHAR" value="53" enum="BuiltinFunc"> + <constant name="TEXT_CHAR" value="51" enum="BuiltinFunc"> Return a character with the given ascii value. </constant> - <constant name="TEXT_STR" value="54" enum="BuiltinFunc"> + <constant name="TEXT_STR" value="52" enum="BuiltinFunc"> Convert the input to a string. </constant> - <constant name="TEXT_PRINT" value="55" enum="BuiltinFunc"> + <constant name="TEXT_PRINT" value="53" enum="BuiltinFunc"> Print the given string to the output window. </constant> - <constant name="TEXT_PRINTERR" value="56" enum="BuiltinFunc"> + <constant name="TEXT_PRINTERR" value="54" enum="BuiltinFunc"> Print the given string to the standard error output. </constant> - <constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc"> + <constant name="TEXT_PRINTRAW" value="55" enum="BuiltinFunc"> Print the given string to the standard output, without adding a newline. </constant> - <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc"> + <constant name="VAR_TO_STR" value="56" enum="BuiltinFunc"> Serialize a [Variant] to a string. </constant> - <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc"> + <constant name="STR_TO_VAR" value="57" enum="BuiltinFunc"> Deserialize a [Variant] from a string serialized using [constant VAR_TO_STR]. </constant> - <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc"> + <constant name="VAR_TO_BYTES" value="58" enum="BuiltinFunc"> Serialize a [Variant] to a [PackedByteArray]. </constant> - <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc"> + <constant name="BYTES_TO_VAR" value="59" enum="BuiltinFunc"> Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES]. </constant> - <constant name="MATH_SMOOTHSTEP" value="62" enum="BuiltinFunc"> + <constant name="MATH_SMOOTHSTEP" value="60" enum="BuiltinFunc"> Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula: [codeblock] var t = clamp((weight - from) / (to - from), 0.0, 1.0) return t * t * (3.0 - 2.0 * t) [/codeblock] </constant> - <constant name="MATH_POSMOD" value="63" enum="BuiltinFunc"> + <constant name="MATH_POSMOD" value="61" enum="BuiltinFunc"> </constant> - <constant name="MATH_LERP_ANGLE" value="64" enum="BuiltinFunc"> + <constant name="MATH_LERP_ANGLE" value="62" enum="BuiltinFunc"> </constant> - <constant name="TEXT_ORD" value="65" enum="BuiltinFunc"> + <constant name="TEXT_ORD" value="63" enum="BuiltinFunc"> </constant> - <constant name="FUNC_MAX" value="66" enum="BuiltinFunc"> + <constant name="FUNC_MAX" value="64" enum="BuiltinFunc"> Represents the size of the [enum BuiltinFunc] enum. </constant> </constants> diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml deleted file mode 100644 index 9ea889c77b..0000000000 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEditor" inherits="Object" version="4.0"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> - <methods> - <method name="add_custom_node"> - <return type="void" /> - <argument index="0" name="name" type="String" /> - <argument index="1" name="category" type="String" /> - <argument index="2" name="script" type="Script" /> - <description> - Add a custom Visual Script node to the editor. It'll be placed under "Custom Nodes" with the [code]category[/code] as the parameter. - </description> - </method> - <method name="remove_custom_node"> - <return type="void" /> - <argument index="0" name="name" type="String" /> - <argument index="1" name="category" type="String" /> - <description> - Remove a custom Visual Script node from the editor. Custom nodes already placed on scripts won't be removed. - </description> - </method> - </methods> - <signals> - <signal name="custom_nodes_updated"> - <description> - Emitted when a custom Visual Script node is added or removed. - </description> - </signal> - </signals> - <constants> - </constants> -</class> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index fce98eb8a0..7fb9707fce 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -43,7 +43,7 @@ VisualScriptLanguage *visual_script_language = nullptr; #ifdef TOOLS_ENABLED -static vs_bind::VisualScriptEditor *vs_editor_singleton = nullptr; +static VisualScriptCustomNodes *vs_custom_nodes_singleton = nullptr; #endif void register_visual_script_types() { @@ -114,10 +114,10 @@ void register_visual_script_types() { #ifdef TOOLS_ENABLED ClassDB::set_current_api(ClassDB::API_EDITOR); - GDREGISTER_CLASS(vs_bind::VisualScriptEditor); + GDREGISTER_CLASS(VisualScriptCustomNodes); ClassDB::set_current_api(ClassDB::API_CORE); - vs_editor_singleton = memnew(vs_bind::VisualScriptEditor); - Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", vs_bind::VisualScriptEditor::get_singleton())); + vs_custom_nodes_singleton = memnew(VisualScriptCustomNodes); + Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", VisualScriptCustomNodes::get_singleton())); VisualScriptEditor::register_editor(); #endif @@ -130,8 +130,8 @@ void unregister_visual_script_types() { #ifdef TOOLS_ENABLED VisualScriptEditor::free_clipboard(); - if (vs_editor_singleton) { - memdelete(vs_editor_singleton); + if (vs_custom_nodes_singleton) { + memdelete(vs_custom_nodes_singleton); } #endif if (visual_script_language) { diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 142419f90a..4d5f3420b8 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -955,7 +955,7 @@ bool VisualScript::are_subnodes_edited() const { } #endif -const Vector<MultiplayerAPI::RPCConfig> VisualScript::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> VisualScript::get_rpc_methods() const { return rpc_functions; } @@ -1022,11 +1022,11 @@ void VisualScript::_set_data(const Dictionary &p_data) { if (functions[E].func_id >= 0 && nodes.has(functions[E].func_id)) { Ref<VisualScriptFunction> vsf = nodes[functions[E].func_id].node; if (vsf.is_valid()) { - if (vsf->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) { - MultiplayerAPI::RPCConfig nd; + if (vsf->get_rpc_mode() != Multiplayer::RPC_MODE_DISABLED) { + Multiplayer::RPCConfig nd; nd.name = E; nd.rpc_mode = vsf->get_rpc_mode(); - nd.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; // TODO + nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; // TODO if (rpc_functions.find(nd) == -1) { rpc_functions.push_back(nd); } @@ -1036,7 +1036,7 @@ void VisualScript::_set_data(const Dictionary &p_data) { } // Sort so we are 100% that they are always the same. - rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>(); + rpc_functions.sort_custom<Multiplayer::SortRPCConfig>(); } Dictionary VisualScript::_get_data() const { @@ -1833,7 +1833,7 @@ Ref<Script> VisualScriptInstance::get_script() const { return script; } -const Vector<MultiplayerAPI::RPCConfig> VisualScriptInstance::get_rpc_methods() const { +const Vector<Multiplayer::RPCConfig> VisualScriptInstance::get_rpc_methods() const { return script->get_rpc_methods(); } diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 932ebeb27f..39cef8f68b 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -234,7 +234,7 @@ private: HashMap<StringName, Function> functions; HashMap<StringName, Variable> variables; Map<StringName, Vector<Argument>> custom_signals; - Vector<MultiplayerAPI::RPCConfig> rpc_functions; + Vector<Multiplayer::RPCConfig> rpc_functions; Map<Object *, VisualScriptInstance *> instances; @@ -362,7 +362,7 @@ public: virtual int get_member_line(const StringName &p_member) const override; - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override; #ifdef TOOLS_ENABLED virtual bool are_subnodes_edited() const; @@ -443,7 +443,7 @@ public: virtual ScriptLanguage *get_language(); - virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const; + virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const; VisualScriptInstance(); ~VisualScriptInstance(); diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index c61c3ae272..2bd7220d15 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -79,8 +79,6 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "rad2deg", "linear2db", "db2linear", - "polar2cartesian", - "cartesian2polar", "wrapi", "wrapf", "max", @@ -194,8 +192,6 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case MATH_SNAPPED: case MATH_RANDF_RANGE: case MATH_RANDI_RANGE: - case MATH_POLAR2CARTESIAN: - case MATH_CARTESIAN2POLAR: case LOGIC_MAX: case LOGIC_MIN: case TYPE_CONVERT: @@ -381,20 +377,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const case MATH_DB2LINEAR: { return PropertyInfo(Variant::FLOAT, "db"); } break; - case MATH_POLAR2CARTESIAN: { - if (p_idx == 0) { - return PropertyInfo(Variant::FLOAT, "r"); - } else { - return PropertyInfo(Variant::FLOAT, "th"); - } - } break; - case MATH_CARTESIAN2POLAR: { - if (p_idx == 0) { - return PropertyInfo(Variant::FLOAT, "x"); - } else { - return PropertyInfo(Variant::FLOAT, "y"); - } - } break; case MATH_WRAP: { if (p_idx == 0) { return PropertyInfo(Variant::INT, "value"); @@ -553,10 +535,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_DB2LINEAR: { t = Variant::FLOAT; } break; - case MATH_POLAR2CARTESIAN: - case MATH_CARTESIAN2POLAR: { - t = Variant::VECTOR2; - } break; case MATH_WRAP: { t = Variant::INT; } break; @@ -874,20 +852,6 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(0); *r_return = Math::db2linear((double)*p_inputs[0]); } break; - case VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double r = *p_inputs[0]; - double th = *p_inputs[1]; - *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); - } break; - case VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double x = *p_inputs[0]; - double y = *p_inputs[1]; - *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); - } break; case VisualScriptBuiltinFunc::MATH_WRAP: { VALIDATE_ARG_NUM(0); VALIDATE_ARG_NUM(1); @@ -1229,8 +1193,6 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(MATH_RAD2DEG); BIND_ENUM_CONSTANT(MATH_LINEAR2DB); BIND_ENUM_CONSTANT(MATH_DB2LINEAR); - BIND_ENUM_CONSTANT(MATH_POLAR2CARTESIAN); - BIND_ENUM_CONSTANT(MATH_CARTESIAN2POLAR); BIND_ENUM_CONSTANT(MATH_WRAP); BIND_ENUM_CONSTANT(MATH_WRAPF); BIND_ENUM_CONSTANT(LOGIC_MAX); @@ -1320,8 +1282,6 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>); - VisualScriptLanguage::singleton->add_register_func("functions/built_in/polar2cartesian", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN>); - VisualScriptLanguage::singleton->add_register_func("functions/built_in/cartesian2polar", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAPF>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index f59a7a0f0c..26abc1e479 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -79,8 +79,6 @@ public: MATH_RAD2DEG, MATH_LINEAR2DB, MATH_DB2LINEAR, - MATH_POLAR2CARTESIAN, - MATH_CARTESIAN2POLAR, MATH_WRAP, MATH_WRAPF, LOGIC_MAX, diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index ded716cf18..eee9e8f32b 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -4522,46 +4522,44 @@ void VisualScriptEditor::register_editor() { void VisualScriptEditor::validate() { } -namespace vs_bind { +// VisualScriptCustomNodes -Ref<VisualScriptNode> VisualScriptEditor::create_node_custom(const String &p_name) { +Ref<VisualScriptNode> VisualScriptCustomNodes::create_node_custom(const String &p_name) { Ref<VisualScriptCustomNode> node; node.instantiate(); node->set_script(singleton->custom_nodes[p_name]); return node; } -VisualScriptEditor *VisualScriptEditor::singleton = nullptr; -Map<String, REF> VisualScriptEditor::custom_nodes; +VisualScriptCustomNodes *VisualScriptCustomNodes::singleton = nullptr; +Map<String, REF> VisualScriptCustomNodes::custom_nodes; -VisualScriptEditor::VisualScriptEditor() { +VisualScriptCustomNodes::VisualScriptCustomNodes() { singleton = this; } -VisualScriptEditor::~VisualScriptEditor() { +VisualScriptCustomNodes::~VisualScriptCustomNodes() { custom_nodes.clear(); } -void VisualScriptEditor::add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script) { +void VisualScriptCustomNodes::add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script) { String node_name = "custom/" + p_category + "/" + p_name; custom_nodes.insert(node_name, p_script); - VisualScriptLanguage::singleton->add_register_func(node_name, &VisualScriptEditor::create_node_custom); + VisualScriptLanguage::singleton->add_register_func(node_name, &VisualScriptCustomNodes::create_node_custom); emit_signal(SNAME("custom_nodes_updated")); } -void VisualScriptEditor::remove_custom_node(const String &p_name, const String &p_category) { +void VisualScriptCustomNodes::remove_custom_node(const String &p_name, const String &p_category) { String node_name = "custom/" + p_category + "/" + p_name; custom_nodes.erase(node_name); VisualScriptLanguage::singleton->remove_register_func(node_name); emit_signal(SNAME("custom_nodes_updated")); } -void VisualScriptEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_custom_node", "name", "category", "script"), &VisualScriptEditor::add_custom_node); - ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &VisualScriptEditor::remove_custom_node); +void VisualScriptCustomNodes::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_custom_node", "name", "category", "script"), &VisualScriptCustomNodes::add_custom_node); + ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &VisualScriptCustomNodes::remove_custom_node); ADD_SIGNAL(MethodInfo("custom_nodes_updated")); } -} // namespace vs_bind - #endif diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 4fa8ffca67..ab32aae7aa 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -46,6 +46,8 @@ class VisualScriptEditorVariableEdit; // TODO: Maybe this class should be refactored. // See https://github.com/godotengine/godot/issues/51913 class VisualScriptEditor : public ScriptEditorBase { + GDCLASS(VisualScriptEditor, ScriptEditorBase); + enum { TYPE_SEQUENCE = 1000, INDEX_BASE_SEQUENCE = 1024 @@ -309,6 +311,8 @@ public: virtual void tag_saved_version() override; virtual void reload(bool p_soft) override; virtual Array get_breakpoints() override; + virtual void set_breakpoint(int p_line, bool p_enable) override{}; + virtual void clear_breakpoints() override{}; virtual void add_callback(const String &p_function, PackedStringArray p_args) override; virtual void update_settings() override; virtual bool show_members_overview() override; @@ -330,33 +334,29 @@ public: ~VisualScriptEditor(); }; -namespace vs_bind { - // Singleton -class VisualScriptEditor : public Object { - GDCLASS(VisualScriptEditor, Object); +class VisualScriptCustomNodes : public Object { + GDCLASS(VisualScriptCustomNodes, Object); friend class VisualScriptLanguage; protected: static void _bind_methods(); - static VisualScriptEditor *singleton; + static VisualScriptCustomNodes *singleton; static Map<String, REF> custom_nodes; static Ref<VisualScriptNode> create_node_custom(const String &p_name); public: - static VisualScriptEditor *get_singleton() { return singleton; } + static VisualScriptCustomNodes *get_singleton() { return singleton; } void add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script); void remove_custom_node(const String &p_name, const String &p_category); - VisualScriptEditor(); - ~VisualScriptEditor(); + VisualScriptCustomNodes(); + ~VisualScriptCustomNodes(); }; -} // namespace vs_bind - #endif #endif // VISUALSCRIPT_EDITOR_H diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index c9e426fa6c..ef77c0cef3 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -90,7 +90,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value } if (p_name == "rpc/mode") { - rpc_mode = MultiplayerAPI::RPCMode(int(p_value)); + rpc_mode = Multiplayer::RPCMode(int(p_value)); return true; } @@ -163,7 +163,7 @@ void VisualScriptFunction::_get_property_list(List<PropertyInfo> *p_list) const p_list->push_back(PropertyInfo(Variant::INT, "stack/size", PROPERTY_HINT_RANGE, "1,100000")); } p_list->push_back(PropertyInfo(Variant::BOOL, "stack/stackless")); - p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Remote,Master,Puppet,Remote Sync,Master Sync,Puppet Sync")); + p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Any,Authority")); } int VisualScriptFunction::get_output_sequence_port_count() const { @@ -261,11 +261,11 @@ int VisualScriptFunction::get_argument_count() const { return arguments.size(); } -void VisualScriptFunction::set_rpc_mode(MultiplayerAPI::RPCMode p_mode) { +void VisualScriptFunction::set_rpc_mode(Multiplayer::RPCMode p_mode) { rpc_mode = p_mode; } -MultiplayerAPI::RPCMode VisualScriptFunction::get_rpc_mode() const { +Multiplayer::RPCMode VisualScriptFunction::get_rpc_mode() const { return rpc_mode; } @@ -311,14 +311,14 @@ void VisualScriptFunction::reset_state() { stack_size = 256; stack_less = false; sequenced = true; - rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; + rpc_mode = Multiplayer::RPC_MODE_DISABLED; } VisualScriptFunction::VisualScriptFunction() { stack_size = 256; stack_less = false; sequenced = true; - rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; + rpc_mode = Multiplayer::RPC_MODE_DISABLED; } void VisualScriptFunction::set_stack_less(bool p_enable) { @@ -2976,7 +2976,7 @@ public: virtual int get_working_memory_size() const { return work_mem_size; } virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) { - if (GDVIRTUAL_IS_OVERRIDEN_PTR(node, _step)) { + if (GDVIRTUAL_IS_OVERRIDDEN_PTR(node, _step)) { Array in_values; Array out_values; Array work_mem; diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index 35d9b0b4fe..bf2d8e9683 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -49,7 +49,7 @@ class VisualScriptFunction : public VisualScriptNode { bool stack_less; int stack_size; - MultiplayerAPI::RPCMode rpc_mode; + Multiplayer::RPCMode rpc_mode; bool sequenced; protected: @@ -96,8 +96,8 @@ public: void set_return_type(Variant::Type p_type); Variant::Type get_return_type() const; - void set_rpc_mode(MultiplayerAPI::RPCMode p_mode); - MultiplayerAPI::RPCMode get_rpc_mode() const; + void set_rpc_mode(Multiplayer::RPCMode p_mode); + Multiplayer::RPCMode get_rpc_mode() const; virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override; diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml index c53af22ae1..3f137ab7b3 100644 --- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml +++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml @@ -56,7 +56,7 @@ Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647). If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted. If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal MultiplayerPeer.peer_connected] signals until a peer with id [constant MultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal MultiplayerPeer.connection_succeeded]. After that the signal [signal MultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal MultiplayerPeer.server_disconnected] will be emitted and state will become [constant MultiplayerPeer.CONNECTION_CONNECTED]. - You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel). + You can optionally specify a [code]channels_config[/code] array of [enum TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel). </description> </method> <method name="remove_peer"> @@ -69,7 +69,7 @@ </methods> <members> <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> - <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" /> + <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" /> </members> <constants> </constants> diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp index 95c8c13449..d60d694df1 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.cpp +++ b/modules/webrtc/webrtc_multiplayer_peer.cpp @@ -51,11 +51,11 @@ int WebRTCMultiplayerPeer::get_transfer_channel() const { return transfer_channel; } -void WebRTCMultiplayerPeer::set_transfer_mode(TransferMode p_mode) { +void WebRTCMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { transfer_mode = p_mode; } -MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_transfer_mode() const { +Multiplayer::TransferMode WebRTCMultiplayerPeer::get_transfer_mode() const { return transfer_mode; } @@ -204,7 +204,7 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER); channels_config.clear(); for (int i = 0; i < p_channels_config.size(); i++) { - ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'"); + ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'"); int mode = p_channels_config[i].operator int(); // Initialize data channel configurations. Dictionary cfg; @@ -213,17 +213,17 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr cfg["ordered"] = true; switch (mode) { - case TRANSFER_MODE_UNRELIABLE_ORDERED: + case Multiplayer::TRANSFER_MODE_ORDERED: cfg["maxPacketLifetime"] = 1; break; - case TRANSFER_MODE_UNRELIABLE: + case Multiplayer::TRANSFER_MODE_UNRELIABLE: cfg["maxPacketLifetime"] = 1; cfg["ordered"] = false; break; - case TRANSFER_MODE_RELIABLE: + case Multiplayer::TRANSFER_MODE_RELIABLE: break; default: - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'. Got: %d", mode)); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'. Got: %d", mode)); } channels_config.push_back(cfg); } @@ -355,13 +355,13 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si int ch = transfer_channel; if (ch == 0) { switch (transfer_mode) { - case TRANSFER_MODE_RELIABLE: + case Multiplayer::TRANSFER_MODE_RELIABLE: ch = CH_RELIABLE; break; - case TRANSFER_MODE_UNRELIABLE_ORDERED: + case Multiplayer::TRANSFER_MODE_ORDERED: ch = CH_ORDERED; break; - case TRANSFER_MODE_UNRELIABLE: + case Multiplayer::TRANSFER_MODE_UNRELIABLE: ch = CH_UNRELIABLE; break; } diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h index ef4fe1678c..80a6491492 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.h +++ b/modules/webrtc/webrtc_multiplayer_peer.h @@ -31,7 +31,7 @@ #ifndef WEBRTC_MULTIPLAYER_H #define WEBRTC_MULTIPLAYER_H -#include "core/io/multiplayer_peer.h" +#include "core/multiplayer/multiplayer_peer.h" #include "webrtc_peer_connection.h" class WebRTCMultiplayerPeer : public MultiplayerPeer { @@ -68,7 +68,7 @@ private: bool refuse_connections = false; ConnectionStatus connection_status = CONNECTION_DISCONNECTED; int transfer_channel = 0; - TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; + Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; int next_packet_peer = 0; bool server_compat = false; @@ -99,8 +99,8 @@ public: // MultiplayerPeer void set_transfer_channel(int p_channel) override; int get_transfer_channel() const override; - void set_transfer_mode(TransferMode p_mode) override; - TransferMode get_transfer_mode() const override; + void set_transfer_mode(Multiplayer::TransferMode p_mode) override; + Multiplayer::TransferMode get_transfer_mode() const override; void set_target_peer(int p_peer_id) override; int get_unique_id() const override; diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index cd41e9a1fb..ac63379d71 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -32,7 +32,7 @@ </methods> <members> <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> - <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" /> + <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" /> </members> <signals> <signal name="peer_packet"> diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 163cc7706b..7464cf2bf5 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -113,13 +113,13 @@ int WebSocketMultiplayerPeer::get_transfer_channel() const { return 0; } -void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) { +void WebSocketMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { // Websocket uses TCP, reliable } -MultiplayerPeer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const { +Multiplayer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const { // Websocket uses TCP, reliable - return TRANSFER_MODE_RELIABLE; + return Multiplayer::TRANSFER_MODE_RELIABLE; } void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) { diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h index 0fee196f41..d97a599fe9 100644 --- a/modules/websocket/websocket_multiplayer_peer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -32,7 +32,7 @@ #define WEBSOCKET_MULTIPLAYER_PEER_H #include "core/error/error_list.h" -#include "core/io/multiplayer_peer.h" +#include "core/multiplayer/multiplayer_peer.h" #include "core/templates/list.h" #include "websocket_peer.h" @@ -80,8 +80,8 @@ public: /* MultiplayerPeer */ void set_transfer_channel(int p_channel) override; int get_transfer_channel() const override; - void set_transfer_mode(TransferMode p_mode) override; - TransferMode get_transfer_mode() const override; + void set_transfer_mode(Multiplayer::TransferMode p_mode) override; + Multiplayer::TransferMode get_transfer_mode() const override; void set_target_peer(int p_target_peer) override; int get_packet_peer() const override; int get_unique_id() const override; diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index 49997b42d3..26c0176ea4 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -161,22 +161,28 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); _peer = Ref<WSLPeer>(memnew(WSLPeer)); - IPAddress addr; - if (!p_host.is_valid_ip_address()) { - addr = IP::get_singleton()->resolve_hostname(p_host); + if (p_host.is_valid_ip_address()) { + ip_candidates.clear(); + ip_candidates.push_back(IPAddress(p_host)); } else { - addr = p_host; + ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host); } - ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER); String port = ""; if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) { port = ":" + itos(p_port); } - Error err = _tcp->connect_to_host(addr, p_port); + Error err = ERR_BUG; // Should be at least one entry. + while (ip_candidates.size() > 0) { + err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port); + if (err == OK) { + break; + } + } if (err != OK) { _tcp->disconnect_from_host(); _on_error(); @@ -185,6 +191,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, _connection = _tcp; _use_ssl = p_ssl; _host = p_host; + _port = p_port; // Strip edges from protocols. _protocols.resize(p_protocols.size()); String *pw = _protocols.ptrw(); @@ -244,6 +251,7 @@ void WSLClient::poll() { _on_error(); break; case StreamPeerTCP::STATUS_CONNECTED: { + ip_candidates.clear(); Ref<StreamPeerSSL> ssl; if (_use_ssl) { if (_connection == _tcp) { @@ -274,6 +282,12 @@ void WSLClient::poll() { _do_handshake(); } break; case StreamPeerTCP::STATUS_ERROR: + while (ip_candidates.size() > 0) { + _tcp->disconnect_from_host(); + if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) { + return; + } + } disconnect_from_host(); _on_error(); break; @@ -315,6 +329,8 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) { memset(_resp_buf, 0, sizeof(_resp_buf)); _resp_pos = 0; + + ip_candidates.clear(); } IPAddress WSLClient::get_connected_host() const { diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h index 849639ee8b..3972977910 100644 --- a/modules/websocket/wsl_client.h +++ b/modules/websocket/wsl_client.h @@ -63,6 +63,8 @@ private: String _key; String _host; + int _port; + Array ip_candidates; Vector<String> _protocols; bool _use_ssl = false; diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h index 41a690f473..7aac0a6508 100644 --- a/modules/webxr/godot_webxr.h +++ b/modules/webxr/godot_webxr.h @@ -62,7 +62,7 @@ extern void godot_webxr_initialize( extern void godot_webxr_uninitialize(); extern int godot_webxr_get_view_count(); -extern int *godot_webxr_get_render_targetsize(); +extern int *godot_webxr_get_render_target_size(); extern float *godot_webxr_get_transform_for_eye(int p_eye); extern float *godot_webxr_get_projection_for_eye(int p_eye); extern int godot_webxr_get_external_texture_for_eye(int p_eye); diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js index 6e19a8ac6e..c4b21defce 100644 --- a/modules/webxr/native/library_godot_webxr.js +++ b/modules/webxr/native/library_godot_webxr.js @@ -406,9 +406,9 @@ const GodotWebXR = { return GodotWebXR.pose.views.length; }, - godot_webxr_get_render_targetsize__proxy: 'sync', - godot_webxr_get_render_targetsize__sig: 'i', - godot_webxr_get_render_targetsize: function () { + godot_webxr_get_render_target_size__proxy: 'sync', + godot_webxr_get_render_target_size__sig: 'i', + godot_webxr_get_render_target_size: function () { if (!GodotWebXR.session || !GodotWebXR.pose) { return 0; } diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 099e769303..2d699961ae 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -199,7 +199,7 @@ StringName WebXRInterfaceJS::get_name() const { return "WebXR"; }; -int WebXRInterfaceJS::get_capabilities() const { +uint32_t WebXRInterfaceJS::get_capabilities() const { return XRInterface::XR_STEREO | XRInterface::XR_MONO; }; @@ -254,9 +254,9 @@ bool WebXRInterfaceJS::initialize() { void WebXRInterfaceJS::uninitialize() { if (initialized) { XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr) { + if (xr_server != nullptr && xr_server->get_primary_interface() == this) { // no longer our primary interface - xr_server->clear_primary_interface_if(this); + xr_server->set_primary_interface(nullptr); } godot_webxr_uninitialize(); @@ -285,12 +285,12 @@ Transform3D WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) { return transform; } -Size2 WebXRInterfaceJS::get_render_targetsize() { +Size2 WebXRInterfaceJS::get_render_target_size() { if (render_targetsize.width != 0 && render_targetsize.height != 0) { return render_targetsize; } - int *js_size = godot_webxr_get_render_targetsize(); + int *js_size = godot_webxr_get_render_target_size(); if (!initialized || js_size == nullptr) { // As a temporary default (until WebXR is fully initialized), use half the window size. Size2 temp = DisplayServer::get_singleton()->window_get_size(); @@ -365,20 +365,6 @@ CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, real_t p return eye; } -unsigned int WebXRInterfaceJS::get_external_texture_for_eye(XRInterface::Eyes p_eye) { - if (!initialized) { - return 0; - } - return godot_webxr_get_external_texture_for_eye(p_eye); -} - -void WebXRInterfaceJS::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { - if (!initialized) { - return; - } - godot_webxr_commit_for_eye(p_eye); -} - Vector<BlitToScreen> WebXRInterfaceJS::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { Vector<BlitToScreen> blit_to_screen; @@ -474,10 +460,6 @@ void WebXRInterfaceJS::_on_controller_changed() { } } -void WebXRInterfaceJS::notification(int p_what) { - // Nothing to do here. -} - WebXRInterfaceJS::WebXRInterfaceJS() { initialized = false; session_mode = "inline"; diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h index f9368582b7..82307190db 100644 --- a/modules/webxr/webxr_interface_js.h +++ b/modules/webxr/webxr_interface_js.h @@ -76,23 +76,20 @@ public: virtual PackedVector3Array get_bounds_geometry() const override; virtual StringName get_name() const override; - virtual int get_capabilities() const override; + virtual uint32_t get_capabilities() const override; virtual bool is_initialized() const override; virtual bool initialize() override; virtual void uninitialize() override; - virtual Size2 get_render_targetsize() override; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override; virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; - virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; - virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; virtual void process() override; - virtual void notification(int p_what) override; void _on_controller_changed(); |