diff options
Diffstat (limited to 'modules')
225 files changed, 3296 insertions, 3024 deletions
diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index 9a13406900..d2105d7c5d 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -203,7 +203,7 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) { format = basist::transcoder_texture_format::cTFETC2; // get this from renderer imgfmt = Image::FORMAT_ETC2_RGBA8; } else { - //gles2 most likely + //opengl most likely format = basist::transcoder_texture_format::cTFRGBA4444; // get this from renderer imgfmt = Image::FORMAT_RGBA4444; } @@ -216,7 +216,7 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) { format = basist::transcoder_texture_format::cTFETC2; // get this from renderer imgfmt = Image::FORMAT_ETC2_RGBA8; } else { - //gles2 most likely, bad for normal maps, nothing to do about this. + //opengl most likely, bad for normal maps, nothing to do about this. format = basist::transcoder_texture_format::cTFRGBA32; imgfmt = Image::FORMAT_RGBA8; } diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index 171895ed24..3d47055247 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -91,11 +91,13 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, // the data width in case of 8/4/1 bit images const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes; const uint8_t *line = p_buffer + (line_width * (height - 1)); + const uint8_t *end_buffer = p_buffer + p_header.bmp_file_header.bmp_file_size - p_header.bmp_file_header.bmp_file_offset; for (uint64_t i = 0; i < height; i++) { const uint8_t *line_ptr = line; for (unsigned int j = 0; j < w; j++) { + ERR_FAIL_COND_V(line_ptr >= end_buffer, ERR_FILE_CORRUPT); switch (bits_per_pixel) { case 1: { uint8_t color_index = *line_ptr; @@ -246,8 +248,7 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, } // Don't rely on sizeof(bmp_file_header) as structure padding // adds 2 bytes offset leading to misaligned color table reading - uint32_t ct_offset = BITMAP_FILE_HEADER_SIZE + - bmp_header.bmp_info_header.bmp_header_size; + uint32_t ct_offset = BITMAP_FILE_HEADER_SIZE + bmp_header.bmp_info_header.bmp_header_size; f->seek(ct_offset); uint32_t color_table_size = 0; @@ -269,8 +270,7 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, f->seek(bmp_header.bmp_file_header.bmp_file_offset); - uint32_t bmp_buffer_size = (bmp_header.bmp_file_header.bmp_file_size - - bmp_header.bmp_file_header.bmp_file_offset); + uint32_t bmp_buffer_size = (bmp_header.bmp_file_header.bmp_file_size - bmp_header.bmp_file_header.bmp_file_offset); Vector<uint8_t> bmp_buffer; err = bmp_buffer.resize(bmp_buffer_size); diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index c3bd84c329..b5a7da0c38 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -94,10 +94,9 @@ void AreaBullet::dispatch_callbacks() { void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status) { InOutEventCallback &event = eventsCallbacks[static_cast<int>(p_otherObject->getType())]; - Object *areaGodoObject = ObjectDB::get_instance(event.event_callback_id); - if (!areaGodoObject) { - event.event_callback_id = ObjectID(); + if (!event.event_callback.is_valid()) { + event.event_callback = Callable(); return; } @@ -108,7 +107,8 @@ void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3 call_event_res[4] = 0; // self_shape ID Callable::CallError outResp; - areaGodoObject->call(event.event_callback_method, (const Variant **)call_event_res_ptr, 5, outResp); + Variant ret; + event.event_callback.call((const Variant **)call_event_res, 5, ret, outResp); } void AreaBullet::scratch() { @@ -267,13 +267,12 @@ Variant AreaBullet::get_param(PhysicsServer3D::AreaParameter p_param) const { } } -void AreaBullet::set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method) { +void AreaBullet::set_event_callback(Type p_callbackObjectType, const Callable &p_callback) { InOutEventCallback &ev = eventsCallbacks[static_cast<int>(p_callbackObjectType)]; - ev.event_callback_id = p_id; - ev.event_callback_method = p_method; + ev.event_callback = p_callback; /// Set if monitoring - if (eventsCallbacks[0].event_callback_id.is_valid() || eventsCallbacks[1].event_callback_id.is_valid()) { + if (!eventsCallbacks[0].event_callback.is_null() || !eventsCallbacks[1].event_callback.is_null()) { set_godot_object_flags(get_godot_object_flags() | GOF_IS_MONITORING_AREA); } else { set_godot_object_flags(get_godot_object_flags() & (~GOF_IS_MONITORING_AREA)); @@ -281,7 +280,7 @@ void AreaBullet::set_event_callback(Type p_callbackObjectType, ObjectID p_id, co } bool AreaBullet::has_event_callback(Type p_callbackObjectType) { - return eventsCallbacks[static_cast<int>(p_callbackObjectType)].event_callback_id.is_valid(); + return !eventsCallbacks[static_cast<int>(p_callbackObjectType)].event_callback.is_null(); } void AreaBullet::on_enter_area(AreaBullet *p_area) { diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h index 7cf666c119..d96c550082 100644 --- a/modules/bullet/area_bullet.h +++ b/modules/bullet/area_bullet.h @@ -47,8 +47,7 @@ class AreaBullet : public RigidCollisionObjectBullet { public: struct InOutEventCallback { - ObjectID event_callback_id; - StringName event_callback_method; + Callable event_callback; InOutEventCallback() {} }; @@ -145,7 +144,6 @@ public: virtual void dispatch_callbacks(); void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status); - void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant()); void scratch(); void clear_overlaps(bool p_notify); @@ -163,7 +161,7 @@ public: void set_param(PhysicsServer3D::AreaParameter p_param, const Variant &p_value); Variant get_param(PhysicsServer3D::AreaParameter p_param) const; - void set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method); + void set_event_callback(Type p_callbackObjectType, const Callable &p_callback); bool has_event_callback(Type p_callbackObjectType); virtual void on_enter_area(AreaBullet *p_area); diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index 6b2b0c4ad0..610d2145cd 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -413,18 +413,18 @@ void BulletPhysicsServer3D::area_set_monitorable(RID p_area, bool p_monitorable) area->set_monitorable(p_monitorable); } -void BulletPhysicsServer3D::area_set_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) { +void BulletPhysicsServer3D::area_set_monitor_callback(RID p_area, const Callable &p_callback) { AreaBullet *area = area_owner.get_or_null(p_area); ERR_FAIL_COND(!area); - area->set_event_callback(CollisionObjectBullet::TYPE_RIGID_BODY, p_receiver ? p_receiver->get_instance_id() : ObjectID(), p_method); + area->set_event_callback(CollisionObjectBullet::TYPE_RIGID_BODY, p_callback.is_valid() ? p_callback : Callable()); } -void BulletPhysicsServer3D::area_set_area_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) { +void BulletPhysicsServer3D::area_set_area_monitor_callback(RID p_area, const Callable &p_callback) { AreaBullet *area = area_owner.get_or_null(p_area); ERR_FAIL_COND(!area); - area->set_event_callback(CollisionObjectBullet::TYPE_AREA, p_receiver ? p_receiver->get_instance_id() : ObjectID(), p_method); + area->set_event_callback(CollisionObjectBullet::TYPE_AREA, p_callback.is_valid() ? p_callback : Callable()); } void BulletPhysicsServer3D::area_set_ray_pickable(RID p_area, bool p_enable) { @@ -903,7 +903,7 @@ RID BulletPhysicsServer3D::soft_body_get_space(RID p_body) const { return space->get_self(); } -void BulletPhysicsServer3D::soft_body_set_mesh(RID p_body, const REF &p_mesh) { +void BulletPhysicsServer3D::soft_body_set_mesh(RID p_body, RID p_mesh) { SoftBodyBullet *body = soft_body_owner.get_or_null(p_body); ERR_FAIL_COND(!body); diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 7f0934e679..94635b5bfc 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -160,8 +160,8 @@ public: virtual void area_set_collision_layer(RID p_area, uint32_t p_layer) override; virtual void area_set_monitorable(RID p_area, bool p_monitorable) override; - virtual void area_set_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) override; - virtual void area_set_area_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) override; + virtual void area_set_monitor_callback(RID p_area, const Callable &p_callback) override; + virtual void area_set_area_monitor_callback(RID p_area, const Callable &p_callback) override; virtual void area_set_ray_pickable(RID p_area, bool p_enable) override; /* RIGID BODY API */ @@ -265,7 +265,7 @@ public: virtual void soft_body_set_space(RID p_body, RID p_space) override; virtual RID soft_body_get_space(RID p_body) const override; - virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) override; + virtual void soft_body_set_mesh(RID p_body, RID p_mesh) override; virtual AABB soft_body_get_bounds(RID p_body) const override; diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index bbbb0e7851..3a2370ff31 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -32,9 +32,10 @@ #include "bullet_types_converter.h" #include "bullet_utilities.h" -#include "scene/3d/soft_body_3d.h" #include "space_bullet.h" +#include "servers/rendering_server.h" + SoftBodyBullet::SoftBodyBullet() : CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY) {} @@ -70,7 +71,7 @@ void SoftBodyBullet::update_rendering_server(RenderingServerHandler *p_rendering return; } - /// Update visual server vertices + /// Update rendering server vertices const btSoftBody::tNodeArray &nodes(bt_soft_body->m_nodes); const int nodes_count = nodes.size(); @@ -105,24 +106,27 @@ void SoftBodyBullet::update_rendering_server(RenderingServerHandler *p_rendering p_rendering_server_handler->set_aabb(aabb); } -void SoftBodyBullet::set_soft_mesh(const Ref<Mesh> &p_mesh) { - if (p_mesh.is_null()) { - soft_mesh.unref(); - } else { - soft_mesh = p_mesh; - } +void SoftBodyBullet::set_soft_mesh(RID p_mesh) { + destroy_soft_body(); + + soft_mesh = p_mesh; if (soft_mesh.is_null()) { - destroy_soft_body(); return; } - Array arrays = soft_mesh->surface_get_arrays(0); - ERR_FAIL_COND(!(soft_mesh->surface_get_format(0) & RS::ARRAY_FORMAT_INDEX)); - set_trimesh_body_shape(arrays[RS::ARRAY_INDEX], arrays[RS::ARRAY_VERTEX]); + Array arrays = RenderingServer::get_singleton()->mesh_surface_get_arrays(soft_mesh, 0); + ERR_FAIL_COND(arrays.is_empty()); + + bool success = set_trimesh_body_shape(arrays[RS::ARRAY_INDEX], arrays[RS::ARRAY_VERTEX]); + if (!success) { + destroy_soft_body(); + } } void SoftBodyBullet::destroy_soft_body() { + soft_mesh = RID(); + if (!bt_soft_body) { return; } @@ -187,22 +191,24 @@ void SoftBodyBullet::get_node_position(int p_node_index, Vector3 &r_position) co } } -void SoftBodyBullet::set_node_mass(int node_index, btScalar p_mass) { +void SoftBodyBullet::set_node_mass(int p_node_index, btScalar p_mass) { if (0 >= p_mass) { - pin_node(node_index); + pin_node(p_node_index); } else { - unpin_node(node_index); + unpin_node(p_node_index); } if (bt_soft_body) { - bt_soft_body->setMass(node_index, p_mass); + ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size()); + bt_soft_body->setMass(p_node_index, p_mass); } } -btScalar SoftBodyBullet::get_node_mass(int node_index) const { +btScalar SoftBodyBullet::get_node_mass(int p_node_index) const { if (bt_soft_body) { - return bt_soft_body->getMass(node_index); + ERR_FAIL_INDEX_V(p_node_index, bt_soft_body->m_nodes.size(), 1); + return bt_soft_body->getMass(p_node_index); } else { - return -1 == search_node_pinned(node_index) ? 1 : 0; + return -1 == search_node_pinned(p_node_index) ? 1 : 0; } } @@ -289,15 +295,15 @@ void SoftBodyBullet::set_drag_coefficient(real_t p_val) { } } -void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector3> p_vertices) { - /// Assert the current soft body is destroyed - destroy_soft_body(); +bool SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector3> p_vertices) { + ERR_FAIL_COND_V(p_indices.is_empty(), false); + ERR_FAIL_COND_V(p_vertices.is_empty(), false); - /// Parse visual server indices to physical indices. - /// Merge all overlapping vertices and create a map of physical vertices to visual server + /// Parse rendering server indices to physical indices. + /// Merge all overlapping vertices and create a map of physical vertices to rendering server { - /// This is the map of visual server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map + /// This is the map of rendering server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map Vector<int> vs_indices_to_physics_table; { // Map vertices @@ -363,6 +369,8 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(fake_world_info, &bt_vertices[0], &bt_triangles[0], triangles_size, false); setup_soft_body(); } + + return true; } void SoftBodyBullet::setup_soft_body() { @@ -413,17 +421,25 @@ void SoftBodyBullet::setup_soft_body() { // Set pinned nodes for (int i = pinned_nodes.size() - 1; 0 <= i; --i) { - bt_soft_body->setMass(pinned_nodes[i], 0); + const int node_index = pinned_nodes[i]; + ERR_CONTINUE(0 > node_index || bt_soft_body->m_nodes.size() <= node_index); + bt_soft_body->setMass(node_index, 0); } } void SoftBodyBullet::pin_node(int p_node_index) { + if (bt_soft_body) { + ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size()); + } if (-1 == search_node_pinned(p_node_index)) { pinned_nodes.push_back(p_node_index); } } void SoftBodyBullet::unpin_node(int p_node_index) { + if (bt_soft_body) { + ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size()); + } const int id = search_node_pinned(p_node_index); if (-1 != id) { pinned_nodes.remove(id); diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h index 63708b57a7..84da56ae69 100644 --- a/modules/bullet/soft_body_bullet.h +++ b/modules/bullet/soft_body_bullet.h @@ -32,7 +32,6 @@ #define SOFT_BODY_BULLET_H #include "collision_object_bullet.h" -#include "scene/resources/material.h" // TODO remove this please #ifdef None /// This is required to remove the macro None defined by x11 compiler because this word "None" is used internally by Bullet @@ -42,7 +41,6 @@ #include "BulletSoftBody/btSoftBodyHelpers.h" #include "collision_object_bullet.h" -#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" #ifdef x11_None @@ -64,7 +62,7 @@ private: btSoftBody::Material *mat0 = nullptr; // This is just a copy of pointer managed by btSoftBody bool isScratched = false; - Ref<Mesh> soft_mesh; + RID soft_mesh; int simulation_precision = 5; real_t total_mass = 1.; @@ -100,7 +98,7 @@ public: void update_rendering_server(RenderingServerHandler *p_rendering_server_handler); - void set_soft_mesh(const Ref<Mesh> &p_mesh); + void set_soft_mesh(RID p_mesh); void destroy_soft_body(); // Special function. This function has bad performance @@ -139,7 +137,7 @@ public: _FORCE_INLINE_ real_t get_drag_coefficient() const { return drag_coefficient; } private: - void set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector3> p_vertices); + bool set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector3> p_vertices); void setup_soft_body(); void pin_node(int p_node_index); diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_osx.mm index 02f7287d1b..6def813e5c 100644 --- a/modules/camera/camera_osx.mm +++ b/modules/camera/camera_osx.mm @@ -181,7 +181,7 @@ uint8_t *w = img_data[1].ptrw(); memcpy(w, dataCbCr, 2 * new_width * new_height); - ///TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion + ///TODO OpenGL doesn't support FORMAT_RG8, need to do some form of conversion img[1].instantiate(); img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]); } diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 53694035dc..a7742ef415 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -156,8 +156,8 @@ inline bool is_point_in_triangle(const Vector3 &p_point, const Vector3 p_vertice inline static bool is_triangle_degenerate(const Vector2 p_vertices[3], real_t p_vertex_snap2) { real_t det = p_vertices[0].x * p_vertices[1].y - p_vertices[0].x * p_vertices[2].y + - p_vertices[0].y * p_vertices[2].x - p_vertices[0].y * p_vertices[1].x + - p_vertices[1].x * p_vertices[2].y - p_vertices[1].y * p_vertices[2].x; + p_vertices[0].y * p_vertices[2].x - p_vertices[0].y * p_vertices[1].x + + p_vertices[1].x * p_vertices[2].y - p_vertices[1].y * p_vertices[2].x; return det < p_vertex_snap2; } @@ -408,7 +408,7 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_b } break; - case OPERATION_SUBSTRACTION: { + case OPERATION_SUBTRACTION: { int face_count = 0; for (int i = 0; i < mesh_merge.faces.size(); i++) { diff --git a/modules/csg/csg.h b/modules/csg/csg.h index c872860486..b1fe933268 100644 --- a/modules/csg/csg.h +++ b/modules/csg/csg.h @@ -67,7 +67,7 @@ struct CSGBrushOperation { enum Operation { OPERATION_UNION, OPERATION_INTERSECTION, - OPERATION_SUBSTRACTION, + OPERATION_SUBTRACTION, }; void merge_brushes(Operation p_operation, const CSGBrush &p_brush_a, const CSGBrush &p_brush_b, CSGBrush &r_merged_brush, float p_vertex_snap); diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index e4297a593e..b9be7535dc 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -192,7 +192,7 @@ CSGBrush *CSGShape3D::_get_brush() { bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap); break; case CSGShape3D::OPERATION_SUBTRACTION: - bop.merge_brushes(CSGBrushOperation::OPERATION_SUBSTRACTION, *n, *nn2, *nn, snap); + bop.merge_brushes(CSGBrushOperation::OPERATION_SUBTRACTION, *n, *nn2, *nn, snap); break; } memdelete(n); @@ -572,10 +572,11 @@ void CSGShape3D::_validate_property(PropertyInfo &property) const { bool is_collision_prefixed = property.name.begins_with("collision_"); if ((is_collision_prefixed || property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) { //hide collision if not root - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } else if (is_collision_prefixed && !bool(get("use_collision"))) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } + GeometryInstance3D::_validate_property(property); } Array CSGShape3D::get_meshes() const { diff --git a/modules/denoise/config.py b/modules/denoise/config.py index 49a1f036ed..3aa840acb0 100644 --- a/modules/denoise/config.py +++ b/modules/denoise/config.py @@ -1,11 +1,18 @@ def can_build(env, platform): # Thirdparty dependency OpenImage Denoise includes oneDNN library - # which only supports 64-bit architectures. + # and the version we use only supports x86_64. # It's also only relevant for tools build and desktop platforms, # as doing lightmap generation and denoising on Android or HTML5 # would be a bit far-fetched. desktop_platforms = ["linuxbsd", "osx", "windows"] - return env["tools"] and platform in desktop_platforms and env["bits"] == "64" and env["arch"] != "arm64" + supported_arch = env["bits"] == "64" + if env["arch"] == "arm64": + supported_arch = False + if env["arch"].startswith("ppc"): + supported_arch = False + if env["arch"].startswith("rv"): + supported_arch = False + return env["tools"] and platform in desktop_platforms and supported_arch def configure(env): diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml index 00469ab44c..fcdf282a7d 100644 --- a/modules/enet/doc_classes/ENetConnection.xml +++ b/modules/enet/doc_classes/ENetConnection.xml @@ -51,7 +51,7 @@ <argument index="3" name="data" type="int" default="0" /> <description> Initiates a connection to a foreign [code]address[/code] using the specified [code]port[/code] and allocting the requested [code]channels[/code]. Optional [code]data[/code] can be passed during connection in the form of a 32 bit integer. - Note: You must call either [method create_host] or [method create_host_bound] before calling this method. + [b]Note:[/b] You must call either [method create_host] or [method create_host_bound] before calling this method. </description> </method> <method name="create_host"> @@ -121,7 +121,7 @@ <return type="Array" /> <description> Returns the list of peers associated with this host. - Note: This list might include some peers that are not fully connected or are still being disconnected. + [b]Note:[/b] This list might include some peers that are not fully connected or are still being disconnected. </description> </method> <method name="pop_statistic"> @@ -136,7 +136,7 @@ <argument index="0" name="refuse" type="bool" /> <description> Configures the DTLS server to automatically drop new connections. - Note: This method is only relevant after calling [method dtls_server_setup]. + [b]Note:[/b] This method is only relevant after calling [method dtls_server_setup]. </description> </method> <method name="service"> diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index f9659c092a..22136c3944 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -18,7 +18,7 @@ <argument index="1" name="host" type="ENetConnection" /> <description> Add a new remote peer with the given [code]peer_id[/code] connected to the given [code]host[/code]. - Note: The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state. + [b]Note:[/b] The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state. </description> </method> <method name="close_connection"> diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index 52eb46f070..2cfae60ad2 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -444,7 +444,7 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size packet_flags = ENET_PACKET_FLAG_UNSEQUENCED; channel = SYSCH_UNRELIABLE; } break; - case Multiplayer::TRANSFER_MODE_ORDERED: { + case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED: { packet_flags = 0; channel = SYSCH_UNRELIABLE; } break; diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp index dcea476275..e1eacc68b3 100644 --- a/modules/fbx/data/fbx_mesh_data.cpp +++ b/modules/fbx/data/fbx_mesh_data.cpp @@ -31,6 +31,7 @@ #include "fbx_mesh_data.h" #include "core/templates/local_vector.h" +#include "scene/resources/importer_mesh.h" #include "scene/resources/mesh.h" #include "scene/resources/surface_tool.h" @@ -101,7 +102,7 @@ HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2>> *p_data, Hash return collection; } -EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) { +ImporterMeshInstance3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) { mesh_geometry = p_mesh_geometry; // todo: make this just use a uint64_t FBX ID this is a copy of our original materials unfortunately. const std::vector<const FBXDocParser::Material *> &material_lookup = model->GetMaterials(); @@ -344,7 +345,7 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s } // Phase 6. Compose the mesh and return it. - Ref<EditorSceneImporterMesh> mesh; + Ref<ImporterMesh> mesh; mesh.instantiate(); // Add blend shape info. @@ -356,7 +357,6 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); // Add surfaces. - int in_mesh_surface_id = 0; for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) { SurfaceData *surface = surfaces.getptr(*surface_id); @@ -376,11 +376,9 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s } else { mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, mesh_array, blend_shapes); } - - in_mesh_surface_id += 1; } - EditorSceneImporterMeshNode3D *godot_mesh = memnew(EditorSceneImporterMeshNode3D); + ImporterMeshInstance3D *godot_mesh = memnew(ImporterMeshInstance3D); godot_mesh->set_mesh(mesh); const String name = ImportUtils::FBXNodeToName(model->Name()); godot_mesh->set_name(name); // hurry up compiling >.< diff --git a/modules/fbx/data/fbx_mesh_data.h b/modules/fbx/data/fbx_mesh_data.h index 24db4a5469..eec7f38cd6 100644 --- a/modules/fbx/data/fbx_mesh_data.h +++ b/modules/fbx/data/fbx_mesh_data.h @@ -35,7 +35,7 @@ #include "core/templates/local_vector.h" #include "core/templates/ordered_hash_map.h" #include "editor/import/resource_importer_scene.h" -#include "editor/import/scene_importer_mesh_node_3d.h" +#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/resources/surface_tool.h" @@ -98,7 +98,7 @@ struct FBXMeshData : RefCounted { // translate fbx mesh data from document context to FBX Mesh Geometry Context bool valid_weight_indexes = false; - EditorSceneImporterMeshNode3D *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression); + ImporterMeshInstance3D *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression); void gen_weight_info(Ref<SurfaceTool> st, int vertex_id) const; @@ -107,7 +107,7 @@ struct FBXMeshData : RefCounted { int max_weight_count = 0; uint64_t armature_id = 0; bool valid_armature_id = false; - EditorSceneImporterMeshNode3D *godot_mesh_instance = nullptr; + ImporterMeshInstance3D *godot_mesh_instance = nullptr; private: void sanitize_vertex_weights(const ImportState &state); diff --git a/modules/fbx/data/fbx_skeleton.cpp b/modules/fbx/data/fbx_skeleton.cpp index 3dc163964c..11eed2576f 100644 --- a/modules/fbx/data/fbx_skeleton.cpp +++ b/modules/fbx/data/fbx_skeleton.cpp @@ -104,6 +104,13 @@ void FBXSkeleton::init_skeleton(const ImportState &state) { print_verbose("working on bone: " + itos(bone_index) + " bone name:" + bone->bone_name); skeleton->set_bone_rest(bone->godot_bone_id, get_unscaled_transform(bone->node->pivot_transform->LocalTransform, state.scale)); + { + Transform3D base_xform = bone->node->pivot_transform->LocalTransform; + + skeleton->set_bone_pose_position(bone_index, base_xform.origin); + skeleton->set_bone_pose_rotation(bone_index, base_xform.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(bone_index, base_xform.basis.get_scale()); + } // lookup parent ID if (bone->valid_parent && state.fbx_bone_map.has(bone->parent_bone_id)) { diff --git a/modules/fbx/doc_classes/EditorSceneImporterFBX.xml b/modules/fbx/doc_classes/EditorSceneImporterFBX.xml deleted file mode 100644 index 6f83871772..0000000000 --- a/modules/fbx/doc_classes/EditorSceneImporterFBX.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSceneImporterFBX" inherits="EditorSceneImporter" version="4.0"> - <brief_description> - FBX 3D asset importer. - </brief_description> - <description> - This is an FBX 3D asset importer with full support for most FBX features. - If exporting a FBX scene from Autodesk Maya, use these FBX export settings: - [codeblock] - - Smoothing Groups - - Smooth Mesh - - Triangluate (for meshes with blend shapes) - - Bake Animation - - Resample All - - Deformed Models - - Skins - - Blend Shapes - - Curve Filters - - Constant Key Reducer - - Auto Tangents Only - - *Do not check* Constraints (as it will break the file) - - Can check Embed Media (embeds textures into the exported FBX file) - - Note that when importing embedded media, the texture and mesh will be a single immutable file. - - You will have to re-export then re-import the FBX if the texture has changed. - - Units: Centimeters - - Up Axis: Y - - Binary format in FBX 2017 - [/codeblock] - </description> - <tutorials> - </tutorials> -</class> diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp index e0663ab49d..b11c145599 100644 --- a/modules/fbx/editor_scene_importer_fbx.cpp +++ b/modules/fbx/editor_scene_importer_fbx.cpp @@ -40,9 +40,9 @@ #include "editor/editor_log.h" #include "editor/editor_node.h" #include "editor/import/resource_importer_scene.h" -#include "editor/import/scene_importer_mesh_node_3d.h" #include "scene/3d/bone_attachment_3d.h" #include "scene/3d/camera_3d.h" +#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/light_3d.h" #include "scene/main/node.h" #include "scene/resources/material.h" @@ -56,7 +56,7 @@ #include <string> -void EditorSceneImporterFBX::get_extensions(List<String> *r_extensions) const { +void EditorSceneFormatImporterFBX::get_extensions(List<String> *r_extensions) const { // register FBX as the one and only format for FBX importing const String import_setting_string = "filesystem/import/fbx/"; const String fbx_str = "fbx"; @@ -65,7 +65,7 @@ void EditorSceneImporterFBX::get_extensions(List<String> *r_extensions) const { _register_project_setting_import(fbx_str, import_setting_string, exts, r_extensions, true); } -void EditorSceneImporterFBX::_register_project_setting_import(const String generic, +void EditorSceneFormatImporterFBX::_register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, @@ -79,11 +79,11 @@ void EditorSceneImporterFBX::_register_project_setting_import(const String gener } } -uint32_t EditorSceneImporterFBX::get_import_flags() const { +uint32_t EditorSceneFormatImporterFBX::get_import_flags() const { return IMPORT_SCENE; } -Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, +Node3D *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { // done for performance when re-importing lots of files when testing importer in verbose only! if (OS::get_singleton()->is_stdout_verbose()) { @@ -232,7 +232,7 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl } template <class T> -struct EditorSceneImporterAssetImportInterpolate { +struct EditorSceneFormatImporterAssetImportInterpolate { T lerp(const T &a, const T &b, float c) const { return a + (b - a) * c; } @@ -258,7 +258,7 @@ struct EditorSceneImporterAssetImportInterpolate { //thank you for existing, partial specialization template <> -struct EditorSceneImporterAssetImportInterpolate<Quaternion> { +struct EditorSceneFormatImporterAssetImportInterpolate<Quaternion> { Quaternion lerp(const Quaternion &a, const Quaternion &b, float c) const { ERR_FAIL_COND_V(!a.is_normalized(), Quaternion()); ERR_FAIL_COND_V(!b.is_normalized(), Quaternion()); @@ -282,7 +282,7 @@ struct EditorSceneImporterAssetImportInterpolate<Quaternion> { }; template <class T> -T EditorSceneImporterFBX::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, +T EditorSceneFormatImporterFBX::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp) { //could use binary search, worth it? int idx = -1; @@ -293,7 +293,7 @@ T EditorSceneImporterFBX::_interpolate_track(const Vector<float> &p_times, const idx++; } - EditorSceneImporterAssetImportInterpolate<T> interp; + EditorSceneFormatImporterAssetImportInterpolate<T> interp; switch (p_interp) { case AssetImportAnimation::INTERP_LINEAR: { @@ -352,7 +352,7 @@ T EditorSceneImporterFBX::_interpolate_track(const Vector<float> &p_times, const ERR_FAIL_V(p_values[0]); } -Node3D *EditorSceneImporterFBX::_generate_scene( +Node3D *EditorSceneFormatImporterFBX::_generate_scene( const String &p_path, const FBXDocParser::Document *p_document, const uint32_t p_flags, @@ -627,7 +627,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( node_element; node_element = node_element->next()) { Ref<FBXNode> fbx_node = node_element->get(); - EditorSceneImporterMeshNode3D *mesh_node = nullptr; + ImporterMeshInstance3D *mesh_node = nullptr; Ref<FBXMeshData> mesh_data_precached; // check for valid geometry @@ -768,7 +768,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( for (KeyValue<uint64_t, Ref<FBXMeshData>> &mesh_data : state.renderer_mesh_data) { Ref<FBXMeshData> mesh = mesh_data.value; const uint64_t mesh_id = mesh_data.key; - EditorSceneImporterMeshNode3D *mesh_instance = mesh->godot_mesh_instance; + ImporterMeshInstance3D *mesh_instance = mesh->godot_mesh_instance; const int mesh_weights = mesh->max_weight_count; Ref<FBXSkeleton> skeleton; const bool valid_armature = mesh->valid_armature_id; @@ -843,7 +843,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( if (state.animation_player == nullptr) { print_verbose("Creating animation player"); state.animation_player = memnew(AnimationPlayer); - state.root->add_child(state.animation_player); + state.root->add_child(state.animation_player, true); state.animation_player->set_owner(state.root_owner); } @@ -1011,9 +1011,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( // track count is 5. // next track id is 5. const uint64_t target_id = track.key; - int track_idx = animation->add_track(Animation::TYPE_TRANSFORM3D); - // animation->track_set_path(track_idx, node_path); Ref<FBXBone> bone; // note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad. @@ -1037,22 +1035,21 @@ Node3D *EditorSceneImporterFBX::_generate_scene( // if this is a skeleton mapped track we can just set the path for the track. // todo: implement node paths here at some + NodePath track_path; if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) { if (bone->fbx_skeleton.is_valid() && bone.is_valid()) { Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton; String bone_path = state.root->get_path_to(fbx_skeleton->skeleton); bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id); print_verbose("[doc] track bone path: " + bone_path); - NodePath path = bone_path; - animation->track_set_path(track_idx, path); + track_path = bone_path; } } else if (state.fbx_target_map.has(target_id)) { //print_verbose("[doc] we have a valid target for a node animation"); Ref<FBXNode> target_node = state.fbx_target_map[target_id]; if (target_node.is_valid() && target_node->godot_node != nullptr) { String node_path = state.root->get_path_to(target_node->godot_node); - NodePath path = node_path; - animation->track_set_path(track_idx, path); + track_path = node_path; //print_verbose("[doc] node animation path: " + node_path); } } else { @@ -1186,6 +1183,30 @@ Node3D *EditorSceneImporterFBX::_generate_scene( const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale(); print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")"); + int position_idx = -1; + if (pos_values.size()) { + position_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_POSITION_3D); + animation->track_set_path(position_idx, track_path); + animation->track_set_imported(position_idx, true); + } + + int rotation_idx = -1; + if (pos_values.size()) { + rotation_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_ROTATION_3D); + animation->track_set_path(rotation_idx, track_path); + animation->track_set_imported(rotation_idx, true); + } + + int scale_idx = -1; + if (pos_values.size()) { + scale_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_SCALE_3D); + animation->track_set_path(scale_idx, track_path); + animation->track_set_imported(scale_idx, true); + } + while (true) { Vector3 pos = def_pos; Quaternion rot = def_rot; @@ -1206,21 +1227,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene( AssetImportAnimation::INTERP_LINEAR); } - // node animations must also include pivots - if (skeleton_bone >= 0) { - Transform3D xform = Transform3D(); - xform.basis.set_quaternion_scale(rot, scale); - xform.origin = pos; - const Transform3D t = bone_rest.affine_inverse() * xform; - - // populate this again - rot = t.basis.get_rotation_quaternion(); - rot.normalize(); - scale = t.basis.get_scale(); - pos = t.origin; + if (position_idx >= 0) { + animation->position_track_insert_key(position_idx, time, pos); + } + if (rotation_idx >= 0) { + animation->rotation_track_insert_key(rotation_idx, time, rot); + } + if (scale_idx >= 0) { + animation->scale_track_insert_key(scale_idx, time, scale); } - - animation->transform_track_insert_key(track_idx, time, pos, rot, scale); if (last) { break; @@ -1279,7 +1294,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( return scene_root; } -void EditorSceneImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone, +void EditorSceneFormatImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone, ImportState &state, const FBXDocParser::Document *p_doc, uint64_t p_id) { const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(p_id, "Model"); @@ -1368,7 +1383,7 @@ void EditorSceneImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone, } } -void EditorSceneImporterFBX::BuildDocumentNodes( +void EditorSceneFormatImporterFBX::BuildDocumentNodes( Ref<PivotTransform> parent_transform, ImportState &state, const FBXDocParser::Document *p_doc, diff --git a/modules/fbx/editor_scene_importer_fbx.h b/modules/fbx/editor_scene_importer_fbx.h index 4a3b78480b..7845e079c2 100644 --- a/modules/fbx/editor_scene_importer_fbx.h +++ b/modules/fbx/editor_scene_importer_fbx.h @@ -57,9 +57,9 @@ #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL -class EditorSceneImporterFBX : public EditorSceneImporter { +class EditorSceneFormatImporterFBX : public EditorSceneFormatImporter { private: - GDCLASS(EditorSceneImporterFBX, EditorSceneImporter); + GDCLASS(EditorSceneFormatImporterFBX, EditorSceneFormatImporter); struct AssetImportAnimation { enum Interpolation { @@ -122,8 +122,8 @@ private: void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const; public: - EditorSceneImporterFBX() {} - ~EditorSceneImporterFBX() {} + EditorSceneFormatImporterFBX() {} + ~EditorSceneFormatImporterFBX() {} virtual void get_extensions(List<String> *r_extensions) const override; virtual uint32_t get_import_flags() const override; diff --git a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp index 1eee10b251..d6abcbb00a 100644 --- a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp +++ b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp @@ -82,46 +82,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <stdint.h> namespace FBXDocParser { -//enum Flag -//{ -// e_unknown_0 = 1 << 0, -// e_unknown_1 = 1 << 1, -// e_unknown_2 = 1 << 2, -// e_unknown_3 = 1 << 3, -// e_unknown_4 = 1 << 4, -// e_unknown_5 = 1 << 5, -// e_unknown_6 = 1 << 6, -// e_unknown_7 = 1 << 7, -// e_unknown_8 = 1 << 8, -// e_unknown_9 = 1 << 9, -// e_unknown_10 = 1 << 10, -// e_unknown_11 = 1 << 11, -// e_unknown_12 = 1 << 12, -// e_unknown_13 = 1 << 13, -// e_unknown_14 = 1 << 14, -// e_unknown_15 = 1 << 15, -// e_unknown_16 = 1 << 16, -// e_unknown_17 = 1 << 17, -// e_unknown_18 = 1 << 18, -// e_unknown_19 = 1 << 19, -// e_unknown_20 = 1 << 20, -// e_unknown_21 = 1 << 21, -// e_unknown_22 = 1 << 22, -// e_unknown_23 = 1 << 23, -// e_flag_field_size_64_bit = 1 << 24, // Not sure what is -// e_unknown_25 = 1 << 25, -// e_unknown_26 = 1 << 26, -// e_unknown_27 = 1 << 27, -// e_unknown_28 = 1 << 28, -// e_unknown_29 = 1 << 29, -// e_unknown_30 = 1 << 30, -// e_unknown_31 = 1 << 31 -//}; -// -//bool check_flag(uint32_t flags, Flag to_check) -//{ -// return (flags & to_check) != 0; -//} // ------------------------------------------------------------------------------------------------ Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset) : sbegin(sbegin), @@ -458,12 +418,6 @@ void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, //TokenizeError("file is too short",0); } - //uint32_t offset = 0x15; - /* const char* cursor = input + 0x15; - const uint32_t flags = ReadWord(input, cursor, input + length); - const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused - const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/ - if (strncmp(input, "Kaydara FBX Binary", 18)) { TokenizeError("magic bytes not found", 0); } diff --git a/modules/fbx/fbx_parser/FBXCommon.h b/modules/fbx/fbx_parser/FBXCommon.h index 641d6d351e..611bf22d3b 100644 --- a/modules/fbx/fbx_parser/FBXCommon.h +++ b/modules/fbx/fbx_parser/FBXCommon.h @@ -70,8 +70,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file FBXCommon.h -* Some useful constants and enums for dealing with FBX files. -*/ + * Some useful constants and enums for dealing with FBX files. + */ #ifndef FBX_COMMON_H #define FBX_COMMON_H diff --git a/modules/fbx/fbx_parser/FBXDocument.h b/modules/fbx/fbx_parser/FBXDocument.h index 885ff8fca4..539d633331 100644 --- a/modules/fbx/fbx_parser/FBXDocument.h +++ b/modules/fbx/fbx_parser/FBXDocument.h @@ -706,13 +706,13 @@ public: virtual ~AnimationCurve(); /** get list of keyframe positions (time). - * Invariant: |GetKeys()| > 0 */ + * Invariant: |GetKeys()| > 0 */ const KeyTimeList &GetKeys() const { return keys; } /** get list of keyframe values. - * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/ + * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/ const KeyValueList &GetValues() const { return values; } @@ -750,8 +750,8 @@ typedef std::weak_ptr<AnimationCurveNode> AnimationCurveNodeWeakPtr; class AnimationCurveNode : public Object { public: /* the optional white list specifies a list of property names for which the caller - wants animations for. If the curve node does not match one of these, std::range_error - will be thrown. */ + wants animations for. If the curve node does not match one of these, std::range_error + will be thrown. */ AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc, const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0); @@ -760,8 +760,8 @@ public: const AnimationMap &Curves() const; /** Object the curve is assigned to, this can be nullptr if the - * target object has no DOM representation or could not - * be read for other reasons.*/ + * target object has no DOM representation or could not + * be read for other reasons.*/ Object *Target() const { return target; } @@ -799,8 +799,8 @@ public: virtual ~AnimationLayer(); /* the optional white list specifies a list of property names for which the caller - wants animations for. Curves not matching this list will not be added to the - animation layer. */ + wants animations for. Curves not matching this list will not be added to the + animation layer. */ const AnimationCurveNodeList Nodes(const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0) const; private: @@ -891,15 +891,15 @@ public: virtual ~Cluster(); /** get the list of deformer weights associated with this cluster. - * Use #GetIndices() to get the associated vertices. Both arrays - * have the same size (and may also be empty). */ + * Use #GetIndices() to get the associated vertices. Both arrays + * have the same size (and may also be empty). */ const std::vector<float> &GetWeights() const { return weights; } /** get indices into the vertex data of the geometry associated - * with this cluster. Use #GetWeights() to get the associated weights. - * Both arrays have the same size (and may also be empty). */ + * with this cluster. Use #GetWeights() to get the associated weights. + * Both arrays have the same size (and may also be empty). */ const std::vector<unsigned int> &GetIndices() const { return indices; } @@ -998,7 +998,7 @@ public: LazyObject *LazyDestinationObject() const; /** return the name of the property the connection is attached to. - * this is an empty string for object to object (OO) connections. */ + * this is an empty string for object to object (OO) connections. */ const std::string &PropertyName() const { return prop; } diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.h b/modules/fbx/fbx_parser/FBXDocumentUtil.h index ba86191c4a..0489ce10ce 100644 --- a/modules/fbx/fbx_parser/FBXDocumentUtil.h +++ b/modules/fbx/fbx_parser/FBXDocumentUtil.h @@ -107,12 +107,12 @@ const T *ProcessSimpleConnection(const Connection &con, const char **propNameOut = nullptr) { if (is_object_property_conn && !con.PropertyName().length()) { DOMWarning("expected incoming " + std::string(name) + - " link to be an object-object connection, ignoring", + " link to be an object-object connection, ignoring", element); return nullptr; } else if (!is_object_property_conn && con.PropertyName().length()) { DOMWarning("expected incoming " + std::string(name) + - " link to be an object-property connection, ignoring", + " link to be an object-property connection, ignoring", element); return nullptr; } diff --git a/modules/fbx/fbx_parser/FBXImportSettings.h b/modules/fbx/fbx_parser/FBXImportSettings.h index b016db174b..bc22386957 100644 --- a/modules/fbx/fbx_parser/FBXImportSettings.h +++ b/modules/fbx/fbx_parser/FBXImportSettings.h @@ -81,29 +81,29 @@ namespace FBXDocParser { /** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */ struct ImportSettings { /** enable strict mode: - * - only accept fbx 2012, 2013 files - * - on the slightest error, give up. - * - * Basically, strict mode means that the fbx file will actually - * be validated.*/ + * - only accept fbx 2012, 2013 files + * - on the slightest error, give up. + * + * Basically, strict mode means that the fbx file will actually + * be validated.*/ bool strictMode = true; /** specifies whether all geometry layers are read and scanned for - * usable data channels. The FBX spec indicates that many readers - * will only read the first channel and that this is in some way - * the recommended way- in reality, however, it happens a lot that - * vertex data is spread among multiple layers.*/ + * usable data channels. The FBX spec indicates that many readers + * will only read the first channel and that this is in some way + * the recommended way- in reality, however, it happens a lot that + * vertex data is spread among multiple layers.*/ bool readAllLayers = true; /** specifies whether all materials are read, or only those that - * are referenced by at least one mesh. Reading all materials - * may make FBX reading a lot slower since all objects - * need to be processed. - * This bit is ignored unless readMaterials=true.*/ + * are referenced by at least one mesh. Reading all materials + * may make FBX reading a lot slower since all objects + * need to be processed. + * This bit is ignored unless readMaterials=true.*/ bool readAllMaterials = true; /** import materials (true) or skip them and assign a default - * material.*/ + * material.*/ bool readMaterials = true; /** import embedded textures?*/ @@ -116,35 +116,35 @@ struct ImportSettings { bool readLights = true; /** import animations (i.e. animation curves, the node - * skeleton is always imported).*/ + * skeleton is always imported).*/ bool readAnimations = true; /** read bones (vertex weights and deform info).*/ bool readWeights = true; /** preserve transformation pivots and offsets. Since these can - * not directly be represented in assimp, additional dummy - * nodes will be generated. Note that settings this to false - * can make animation import a lot slower. - * - * The naming scheme for the generated nodes is: - * <OriginalName>_$AssimpFbx$_<TransformName> - * - * where <TransformName> is one of - * RotationPivot - * RotationOffset - * PreRotation - * PostRotation - * ScalingPivot - * ScalingOffset - * Translation - * Scaling - * Rotation - **/ + * not directly be represented in assimp, additional dummy + * nodes will be generated. Note that settings this to false + * can make animation import a lot slower. + * + * The naming scheme for the generated nodes is: + * <OriginalName>_$AssimpFbx$_<TransformName> + * + * where <TransformName> is one of + * RotationPivot + * RotationOffset + * PreRotation + * PostRotation + * ScalingPivot + * ScalingOffset + * Translation + * Scaling + * Rotation + **/ bool preservePivots = true; /** do not import animation curves that specify a constant - * values matching the corresponding node transformation.*/ + * values matching the corresponding node transformation.*/ bool optimizeEmptyAnimationCurves = true; /** use legacy naming for embedded textures eg: (*0, *1, *2).*/ diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.h b/modules/fbx/fbx_parser/FBXMeshGeometry.h index c9b25f008d..26fc1914d1 100644 --- a/modules/fbx/fbx_parser/FBXMeshGeometry.h +++ b/modules/fbx/fbx_parser/FBXMeshGeometry.h @@ -226,7 +226,7 @@ public: const std::vector<Vector3> &GetVertices() const; /** Get a list of all vertex normals or an empty array if - * no normals are specified. */ + * no normals are specified. */ const std::vector<Vector3> &GetNormals() const; /** Return list of vertex indices. */ @@ -238,8 +238,8 @@ private: std::vector<unsigned int> m_indices; }; /** -* DOM class for FBX geometry of type "Line" -*/ + * DOM class for FBX geometry of type "Line" + */ class LineGeometry : public Geometry { public: /** The class constructor */ diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp index d9ef025a16..dbc9a0e46d 100644 --- a/modules/fbx/fbx_parser/FBXParser.cpp +++ b/modules/fbx/fbx_parser/FBXParser.cpp @@ -660,13 +660,6 @@ void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el) { static_cast<real_t>(d[1]), static_cast<real_t>(d[2]))); } - // for debugging - /*for ( size_t i = 0; i < out.size(); i++ ) { - aiVector3D vec3( out[ i ] ); - std::stringstream stream; - stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl; - DefaultLogger::get()->info( stream.str() ); - }*/ } else if (type == 'f') { const float *f = reinterpret_cast<const float *>(&buff[0]); for (unsigned int i = 0; i < count3; ++i, f += 3) { diff --git a/modules/fbx/fbx_parser/FBXParser.h b/modules/fbx/fbx_parser/FBXParser.h index 93836c2205..27db18bf8a 100644 --- a/modules/fbx/fbx_parser/FBXParser.h +++ b/modules/fbx/fbx_parser/FBXParser.h @@ -187,7 +187,7 @@ private: class Parser { public: /** Parse given a token list. Does not take ownership of the tokens - - * the objects must persist during the entire parser lifetime */ + * the objects must persist during the entire parser lifetime */ Parser(const TokenList &tokens, bool is_binary); ~Parser(); diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp index 37717e9109..b8c0f685ac 100644 --- a/modules/fbx/fbx_parser/FBXProperties.cpp +++ b/modules/fbx/fbx_parser/FBXProperties.cpp @@ -114,12 +114,12 @@ PropertyPtr ReadTypedProperty(const ElementPtr element) { } else if (!strcmp(cs, "KTime")) { return new TypedProperty<int64_t>(ParseTokenAsInt64(tok[4])); } else if (!strcmp(cs, "Vector3D") || - !strcmp(cs, "ColorRGB") || - !strcmp(cs, "Vector") || - !strcmp(cs, "Color") || - !strcmp(cs, "Lcl Translation") || - !strcmp(cs, "Lcl Rotation") || - !strcmp(cs, "Lcl Scaling")) { + !strcmp(cs, "ColorRGB") || + !strcmp(cs, "Vector") || + !strcmp(cs, "Color") || + !strcmp(cs, "Lcl Translation") || + !strcmp(cs, "Lcl Rotation") || + !strcmp(cs, "Lcl Scaling")) { return new TypedProperty<Vector3>(Vector3( ParseTokenAsFloat(tok[4]), ParseTokenAsFloat(tok[5]), diff --git a/modules/fbx/fbx_parser/FBXUtil.cpp b/modules/fbx/fbx_parser/FBXUtil.cpp index 1f14a69099..df46bd85c7 100644 --- a/modules/fbx/fbx_parser/FBXUtil.cpp +++ b/modules/fbx/fbx_parser/FBXUtil.cpp @@ -169,10 +169,10 @@ char EncodeBase64(char byte) { } /** Encodes a block of 4 bytes to base64 encoding -* @param bytes Bytes to encode. -* @param out_string String to write encoded values to. -* @param string_pos Position in out_string. -*/ + * @param bytes Bytes to encode. + * @param out_string String to write encoded values to. + * @param string_pos Position in out_string. + */ void EncodeByteBlock(const char *bytes, std::string &out_string, size_t string_pos) { char b0 = (bytes[0] & 0xFC) >> 2; char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4); diff --git a/modules/fbx/fbx_parser/FBXUtil.h b/modules/fbx/fbx_parser/FBXUtil.h index efc131831b..dab2a4201e 100644 --- a/modules/fbx/fbx_parser/FBXUtil.h +++ b/modules/fbx/fbx_parser/FBXUtil.h @@ -87,34 +87,34 @@ namespace Util { const char *TokenTypeString(TokenType t); /** Decode a single Base64-encoded character. -* -* @param ch Character to decode (from base64 to binary). -* @return decoded byte value*/ + * + * @param ch Character to decode (from base64 to binary). + * @return decoded byte value*/ uint8_t DecodeBase64(char ch); /** Compute decoded size of a Base64-encoded string -* -* @param in Characters to decode. -* @param inLength Number of characters to decode. -* @return size of the decoded data (number of bytes)*/ + * + * @param in Characters to decode. + * @param inLength Number of characters to decode. + * @return size of the decoded data (number of bytes)*/ size_t ComputeDecodedSizeBase64(const char *in, size_t inLength); /** Decode a Base64-encoded string -* -* @param in Characters to decode. -* @param inLength Number of characters to decode. -* @param out Pointer where we will store the decoded data. -* @param maxOutLength Size of output buffer. -* @return size of the decoded data (number of bytes)*/ + * + * @param in Characters to decode. + * @param inLength Number of characters to decode. + * @param out Pointer where we will store the decoded data. + * @param maxOutLength Size of output buffer. + * @return size of the decoded data (number of bytes)*/ size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength); char EncodeBase64(char byte); /** Encode bytes in base64-encoding -* -* @param data Binary data to encode. -* @param inLength Number of bytes to encode. -* @return base64-encoded string*/ + * + * @param data Binary data to encode. + * @param inLength Number of bytes to encode. + * @return base64-encoded string*/ std::string EncodeBase64(const char *data, size_t length); } // namespace Util } // namespace FBXDocParser diff --git a/modules/fbx/register_types.cpp b/modules/fbx/register_types.cpp index a75da8f3a9..d5e520a060 100644 --- a/modules/fbx/register_types.cpp +++ b/modules/fbx/register_types.cpp @@ -35,7 +35,7 @@ #ifdef TOOLS_ENABLED static void _editor_init() { - Ref<EditorSceneImporterFBX> import_fbx; + Ref<EditorSceneFormatImporterFBX> import_fbx; import_fbx.instantiate(); ResourceImporterScene::get_singleton()->add_importer(import_fbx); } @@ -46,7 +46,7 @@ void register_fbx_types() { ClassDB::APIType prev_api = ClassDB::get_current_api(); ClassDB::set_current_api(ClassDB::API_EDITOR); - GDREGISTER_CLASS(EditorSceneImporterFBX); + GDREGISTER_CLASS(EditorSceneFormatImporterFBX); ClassDB::set_current_api(prev_api); diff --git a/modules/fbx/tools/import_utils.cpp b/modules/fbx/tools/import_utils.cpp index 66b0153308..bb95d120af 100644 --- a/modules/fbx/tools/import_utils.cpp +++ b/modules/fbx/tools/import_utils.cpp @@ -45,27 +45,27 @@ Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector // by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf switch (mode) { case FBXDocParser::Model::RotOrder_EulerXYZ: - ret.set_euler_zyx(p_rotation); + ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ); break; case FBXDocParser::Model::RotOrder_EulerXZY: - ret.set_euler_yzx(p_rotation); + ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY); break; case FBXDocParser::Model::RotOrder_EulerYZX: - ret.set_euler_xzy(p_rotation); + ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX); break; case FBXDocParser::Model::RotOrder_EulerYXZ: - ret.set_euler_zxy(p_rotation); + ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ); break; case FBXDocParser::Model::RotOrder_EulerZXY: - ret.set_euler_yxz(p_rotation); + ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY); break; case FBXDocParser::Model::RotOrder_EulerZYX: - ret.set_euler_xyz(p_rotation); + ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX); break; case FBXDocParser::Model::RotOrder_SphericXYZ: @@ -89,22 +89,22 @@ Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basi // by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf switch (mode) { case FBXDocParser::Model::RotOrder_EulerXYZ: - return p_rotation.get_euler_zyx(); + return p_rotation.get_euler(Basis::EULER_ORDER_XYZ); case FBXDocParser::Model::RotOrder_EulerXZY: - return p_rotation.get_euler_yzx(); + return p_rotation.get_euler(Basis::EULER_ORDER_XZY); case FBXDocParser::Model::RotOrder_EulerYZX: - return p_rotation.get_euler_xzy(); + return p_rotation.get_euler(Basis::EULER_ORDER_YZX); case FBXDocParser::Model::RotOrder_EulerYXZ: - return p_rotation.get_euler_zxy(); + return p_rotation.get_euler(Basis::EULER_ORDER_YXZ); case FBXDocParser::Model::RotOrder_EulerZXY: - return p_rotation.get_euler_yxz(); + return p_rotation.get_euler(Basis::EULER_ORDER_ZXY); case FBXDocParser::Model::RotOrder_EulerZYX: - return p_rotation.get_euler_xyz(); + return p_rotation.get_euler(Basis::EULER_ORDER_ZYX); case FBXDocParser::Model::RotOrder_SphericXYZ: // TODO diff --git a/modules/fbx/tools/import_utils.h b/modules/fbx/tools/import_utils.h index fbe7dbd82f..88c71fb87e 100644 --- a/modules/fbx/tools/import_utils.h +++ b/modules/fbx/tools/import_utils.h @@ -43,7 +43,7 @@ /** * Import Utils * Conversion tools / glue code to convert from FBX to Godot -*/ + */ class ImportUtils { public: /// Convert a vector from degrees to radians. @@ -201,7 +201,7 @@ public: }; /** Get fbx fps for time mode meta data - */ + */ static float get_fbx_fps(int32_t time_mode) { switch (time_mode) { case AssetImportFbx::TIME_MODE_DEFAULT: @@ -258,13 +258,13 @@ public: } /** - * Find hardcoded textures from assimp which could be in many different directories - */ + * Find hardcoded textures from assimp which could be in many different directories + */ /** - * set_texture_mapping_mode - * Helper to check the mapping mode of the texture (repeat, clamp and mirror) - */ + * set_texture_mapping_mode + * Helper to check the mapping mode of the texture (repeat, clamp and mirror) + */ // static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) { // ERR_FAIL_COND(texture.is_null()); // ERR_FAIL_COND(map_mode == nullptr); @@ -282,9 +282,9 @@ public: // } /** - * Load or load from cache image :) - * We need to upgrade this in the later version :) should not be hard - */ + * Load or load from cache image :) + * We need to upgrade this in the later version :) should not be hard + */ //static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path){ // Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path); diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index 3654870b09..6b3bd714b9 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -7,8 +7,8 @@ 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> - <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link> + <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/scripting/gdnative/gdnative_c_example.html</link> + <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/scripting/gdnative/gdnative_cpp_example.html</link> </tutorials> <methods> <method name="get_current_dependencies" qualifiers="const"> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index 9d34e89f02..221374a7a4 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -38,7 +38,7 @@ <return type="Variant" /> <description> Constructs a new object of the base type with a script of this type already attached. - [i]Note[/i]: Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension. + [b]Note:[/b] Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension. </description> </method> </methods> diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp index b84ce2d192..e0de1a0505 100644 --- a/modules/gdnative/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative/gdnative.cpp @@ -129,13 +129,13 @@ void GDAPI godot_free(void *p_ptr) { // Helper print functions. void GDAPI godot_print_error(const char *p_description, const char *p_function, const char *p_file, int p_line) { - _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR); + _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_ERROR); } void GDAPI godot_print_warning(const char *p_description, const char *p_function, const char *p_file, int p_line) { - _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING); + _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_WARNING); } void GDAPI godot_print_script_error(const char *p_description, const char *p_function, const char *p_file, int p_line) { - _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT); + _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_SCRIPT); } void _gdnative_report_version_mismatch(const godot_object *p_library, const char *p_ext, godot_gdnative_api_version p_want, godot_gdnative_api_version p_have) { diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index 09eac2492f..bc53a4001d 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -39,7 +39,7 @@ extern "C" { typedef enum { GODOT_METHOD_RPC_MODE_DISABLED, - GODOT_METHOD_RPC_MODE_ANY, + GODOT_METHOD_RPC_MODE_ANY_PEER, GODOT_METHOD_RPC_MODE_AUTHORITY, } godot_nativescript_method_rpc_mode; @@ -102,7 +102,7 @@ typedef enum { GODOT_PROPERTY_USAGE_DEFAULT = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_EDITOR | GODOT_PROPERTY_USAGE_NETWORK, GODOT_PROPERTY_USAGE_DEFAULT_INTL = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_EDITOR | GODOT_PROPERTY_USAGE_NETWORK | GODOT_PROPERTY_USAGE_INTERNATIONALIZED, - GODOT_PROPERTY_USAGE_NOEDITOR = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_NETWORK, + GODOT_PROPERTY_USAGE_NO_EDITOR = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_NETWORK, } godot_nativescript_property_usage_flags; typedef struct { diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index fb46bafb3c..368eb67fa6 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -1139,15 +1139,12 @@ void NativeScriptLanguage::profiling_start() { MutexLock lock(mutex); profile_data.clear(); - profiling = true; #endif } void NativeScriptLanguage::profiling_stop() { #ifdef DEBUG_ENABLED MutexLock lock(mutex); - - profiling = false; #endif } diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index a7647e8c59..2364c6c0f6 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -269,7 +269,6 @@ private: }; Map<StringName, ProfileData> profile_data; - bool profiling = false; public: // These two maps must only be touched on the main thread @@ -295,8 +294,6 @@ public: return singleton; } - void _hacky_api_anchor(); - _FORCE_INLINE_ void set_language_index(int p_idx) { lang_idx = p_idx; } #ifndef NO_THREADS diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h index bdae265db2..81e711bafc 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.h +++ b/modules/gdnative/pluginscript/pluginscript_instance.h @@ -69,8 +69,6 @@ public: virtual ScriptLanguage *get_language(); - void set_path(const String &p_path); - virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const; virtual void refcount_incremented(); diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 2b4ceda49d..04a293ddbd 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -361,13 +361,6 @@ Error PluginScript::reload(bool p_keep_state) { _properties_default_values[pi.name] = v["default_value"]; } -#ifdef TOOLS_ENABLED -/*for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) { - - _update_placeholder(E->get()); - }*/ -#endif - FREE_SCRIPT_MANIFEST(manifest); return OK; #undef FREE_SCRIPT_MANIFEST diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp index 4c9bfb395d..e249363016 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp +++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp @@ -341,7 +341,7 @@ void VideoStreamGDNative::_bind_methods() { ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamGDNative::set_file); ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamGDNative::get_file); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); } void VideoStreamGDNative::set_audio_track(int p_track) { diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 631ee4d895..5b476cda01 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -98,6 +98,7 @@ [codeblock] [{function:bar, line:12, source:res://script.gd}, {function:foo, line:9, source:res://script.gd}, {function:_ready, line:6, source:res://script.gd}] [/codeblock] + [b]Note:[/b] Not supported for calling from threads. Instead, this will return an empty array. </description> </method> <method name="inst2dict"> @@ -160,17 +161,24 @@ <method name="print_debug" qualifiers="vararg"> <return type="void" /> <description> - Like [method @GlobalScope.print], but prints only when used in debug mode. + Like [method @GlobalScope.print], but includes the current stack frame when running with the debugger turned on. + Output in the console would look something like this: + [codeblock] + Test print + At: res://test.gd:15:_process() + [/codeblock] + [b]Note:[/b] Not supported for calling from threads. Instead of the stack frame, this will print the thread ID. </description> </method> <method name="print_stack"> <return type="void" /> <description> - Prints a stack track at code location, only works when running with debugger turned on. + Prints a stack trace at the current code location. Only works when running with debugger turned on. Output in the console would look something like this: [codeblock] Frame 0 - res://test.gd:16 in function '_process' [/codeblock] + [b]Note:[/b] Not supported for calling from threads. Instead of the stack trace, this will print the thread ID. </description> </method> <method name="range" qualifiers="vararg"> diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index d45202bd40..0a448ed88c 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - <link title="GDScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> + <link title="GDScript documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/index.html</link> </tutorials> <methods> <method name="get_as_byte_code" qualifiers="const"> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 2bae838543..e4d3ed118c 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -545,7 +545,7 @@ void GDScript::_update_doc() { for (int i = 0; i < signals.size(); i++) { DocData::MethodDoc signal_doc; if (doc_signals.has(signals[i].name)) { - DocData::signal_doc_from_methodinfo(signal_doc, signals[i], signals[i].name); + DocData::signal_doc_from_methodinfo(signal_doc, signals[i], doc_signals[signals[i].name]); } else { DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String()); } @@ -835,7 +835,7 @@ Error GDScript::reload(bool p_keep_state) { GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); } // TODO: Show all error messages. - _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); ERR_FAIL_V(ERR_PARSE_ERROR); } @@ -849,7 +849,7 @@ Error GDScript::reload(bool p_keep_state) { const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front(); while (e != nullptr) { - _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); e = e->next(); } ERR_FAIL_V(ERR_PARSE_ERROR); @@ -869,7 +869,7 @@ Error GDScript::reload(bool p_keep_state) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error()); } - _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT); ERR_FAIL_V(ERR_COMPILATION_FAILED); } else { return err; @@ -879,7 +879,7 @@ Error GDScript::reload(bool p_keep_state) { for (const GDScriptWarning &warning : parser.get_warnings()) { if (EngineDebugger::is_active()) { Vector<ScriptLanguage::StackInfo> si; - EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si); + EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), false, ERR_HANDLER_WARNING, si); } } #endif @@ -979,7 +979,7 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) { } void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const { - p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } void GDScript::_bind_methods() { @@ -1257,6 +1257,8 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { call(member->setter, &val, 1, err); if (err.error == Callable::CallError::CALL_OK) { return true; //function exists, call was successful + } else { + return false; } } else { if (member->data_type.has_type) { @@ -2040,15 +2042,15 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const { return p_keyword == "break" || - p_keyword == "continue" || - p_keyword == "elif" || - p_keyword == "else" || - p_keyword == "if" || - p_keyword == "for" || - p_keyword == "match" || - p_keyword == "pass" || - p_keyword == "return" || - p_keyword == "while"; + p_keyword == "continue" || + p_keyword == "elif" || + p_keyword == "else" || + p_keyword == "if" || + p_keyword == "for" || + p_keyword == "match" || + p_keyword == "pass" || + p_keyword == "return" || + p_keyword == "while"; } bool GDScriptLanguage::handles_global_class_type(const String &p_type) const { diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index c35af9ca5b..cd8fd361c5 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -244,6 +244,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, class_type.kind = GDScriptParser::DataType::CLASS; class_type.class_type = p_class; class_type.script_path = parser->script_path; + class_type.builtin_type = Variant::OBJECT; p_class->set_datatype(class_type); if (!p_class->extends_used) { @@ -469,6 +470,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type GDScriptParser::DataType container_type = resolve_datatype(p_type->container_type); if (container_type.kind != GDScriptParser::DataType::VARIANT) { + container_type.is_meta_type = false; + container_type.is_constant = false; result.set_container_element_type(container_type); } } @@ -874,18 +877,34 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { GDScriptParser::ClassNode *previous_class = parser->current_class; parser->current_class = p_class; - // Do functions now. + // Do functions and properties now. for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; - if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) { - continue; - } + if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) { + resolve_function_body(member.function); + + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : member.function->annotations) { + E->apply(parser, member.function); + } + } else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) { + if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) { + if (member.variable->getter != nullptr) { + member.variable->getter->set_datatype(member.variable->datatype); + + resolve_function_body(member.variable->getter); + } + if (member.variable->setter != nullptr) { + resolve_function_signature(member.variable->setter); - resolve_function_body(member.function); + if (member.variable->setter->parameters.size() > 0) { + member.variable->setter->parameters[0]->datatype_specifier = member.variable->datatype_specifier; + member.variable->setter->parameters[0]->set_datatype(member.get_datatype()); + } - // Apply annotations. - for (GDScriptParser::AnnotationNode *&E : member.function->annotations) { - E->apply(parser, member.function); + resolve_function_body(member.variable->setter); + } + } } } @@ -901,17 +920,80 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { resolve_class_body(member.m_class); } - // Check unused variables. + // Check unused variables and datatypes of property getters and setters. for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; - if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) { - continue; - } + if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) { #ifdef DEBUG_ENABLED - if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) { - parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name); - } + if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) { + parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name); + } +#endif + + if (member.variable->property == GDScriptParser::VariableNode::PROP_SETGET) { + GDScriptParser::FunctionNode *getter_function = nullptr; + GDScriptParser::FunctionNode *setter_function = nullptr; + + bool has_valid_getter = false; + bool has_valid_setter = false; + + if (member.variable->getter_pointer != nullptr) { + if (p_class->has_function(member.variable->getter_pointer->name)) { + getter_function = p_class->get_member(member.variable->getter_pointer->name).function; + } + + if (getter_function == nullptr) { + push_error(vformat(R"(Getter "%s" not found.)", member.variable->getter_pointer->name), member.variable); + + } else if (getter_function->parameters.size() != 0 || getter_function->datatype.has_no_type()) { + push_error(vformat(R"(Function "%s" cannot be used as getter because of its signature.)", getter_function->identifier->name), member.variable); + + } else if (!is_type_compatible(member.variable->datatype, getter_function->datatype, true)) { + push_error(vformat(R"(Function with return type "%s" cannot be used as getter for a property of type "%s".)", getter_function->datatype.to_string(), member.variable->datatype.to_string()), member.variable); + + } else { + has_valid_getter = true; + +#ifdef DEBUG_ENABLED + if (member.variable->datatype.builtin_type == Variant::INT && getter_function->datatype.builtin_type == Variant::FLOAT) { + parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION); + } +#endif + } + } + + if (member.variable->setter_pointer != nullptr) { + if (p_class->has_function(member.variable->setter_pointer->name)) { + setter_function = p_class->get_member(member.variable->setter_pointer->name).function; + } + + if (setter_function == nullptr) { + push_error(vformat(R"(Setter "%s" not found.)", member.variable->setter_pointer->name), member.variable); + + } else if (setter_function->parameters.size() != 1) { + push_error(vformat(R"(Function "%s" cannot be used as setter because of its signature.)", setter_function->identifier->name), member.variable); + + } else if (!is_type_compatible(member.variable->datatype, setter_function->parameters[0]->datatype, true)) { + push_error(vformat(R"(Function with argument type "%s" cannot be used as setter for a property of type "%s".)", setter_function->parameters[0]->datatype.to_string(), member.variable->datatype.to_string()), member.variable); + + } else { + has_valid_setter = true; + +#ifdef DEBUG_ENABLED + if (member.variable->datatype.builtin_type == Variant::FLOAT && setter_function->parameters[0]->datatype.builtin_type == Variant::INT) { + parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION); + } #endif + } + } + + if (member.variable->datatype.is_variant() && has_valid_getter && has_valid_setter) { + if (!is_type_compatible(getter_function->datatype, setter_function->parameters[0]->datatype, true)) { + push_error(vformat(R"(Getter with type "%s" cannot be used along with setter of type "%s".)", getter_function->datatype.to_string(), setter_function->parameters[0]->datatype.to_string()), member.variable); + } + } + } + } } } @@ -988,7 +1070,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) { case GDScriptParser::Node::SUBSCRIPT: case GDScriptParser::Node::TERNARY_OPERATOR: case GDScriptParser::Node::UNARY_OPERATOR: - reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node)); + reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node), true); break; case GDScriptParser::Node::BREAK: case GDScriptParser::Node::BREAKPOINT: @@ -1035,7 +1117,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * return_type.is_meta_type = false; p_function->set_datatype(return_type); if (p_function->return_type) { - push_error("Constructor cannot have an explicit return type.", p_function->return_type); + GDScriptParser::DataType declared_return = resolve_datatype(p_function->return_type); + if (declared_return.kind != GDScriptParser::DataType::BUILTIN || declared_return.builtin_type != Variant::NIL) { + push_error("Constructor cannot have an explicit return type.", p_function->return_type); + } } } else { GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type); @@ -1198,7 +1283,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { variable_type.kind = GDScriptParser::DataType::BUILTIN; variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else? p_for->variable->set_datatype(variable_type); - } else { + } else if (p_for->list) { resolve_node(p_for->list); if (p_for->list->datatype.has_container_element_type()) { variable_type = p_for->list->datatype.get_container_element_type(); @@ -1213,7 +1298,9 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { variable_type.kind = GDScriptParser::DataType::VARIANT; } } - p_for->variable->set_datatype(variable_type); + if (p_for->variable) { + p_for->variable->set_datatype(variable_type); + } resolve_suite(p_for->loop); p_for->set_datatype(p_for->loop->get_datatype()); @@ -1523,12 +1610,20 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { GDScriptParser::DataType result; + GDScriptParser::DataType expected_type; + bool has_expected_type = false; + + if (parser->current_function != nullptr) { + expected_type = parser->current_function->get_datatype(); + has_expected_type = true; + } + if (p_return->return_value != nullptr) { reduce_expression(p_return->return_value); if (p_return->return_value->type == GDScriptParser::Node::ARRAY) { // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. - if (parser->current_function->get_datatype().has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) { - update_array_literal_element_type(parser->current_function->get_datatype(), static_cast<GDScriptParser::ArrayNode *>(p_return->return_value)); + if (has_expected_type && expected_type.has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) { + update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value)); } } result = p_return->return_value->get_datatype(); @@ -1540,30 +1635,31 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { result.is_constant = true; } - GDScriptParser::DataType function_type = parser->current_function->get_datatype(); - function_type.is_meta_type = false; - if (function_type.is_hard_type()) { - if (!is_type_compatible(function_type, result)) { - // Try other way. Okay but not safe. - if (!is_type_compatible(result, function_type)) { - push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), function_type.to_string()), p_return); - } else { - // TODO: Add warning. - mark_node_unsafe(p_return); - } + if (has_expected_type) { + expected_type.is_meta_type = false; + if (expected_type.is_hard_type()) { + if (!is_type_compatible(expected_type, result)) { + // Try other way. Okay but not safe. + if (!is_type_compatible(result, expected_type)) { + push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return); + } else { + // TODO: Add warning. + mark_node_unsafe(p_return); + } #ifdef DEBUG_ENABLED - } else if (function_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) { - parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION); - } else if (result.is_variant()) { - mark_node_unsafe(p_return); + } else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) { + parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION); + } else if (result.is_variant()) { + mark_node_unsafe(p_return); #endif + } } } p_return->set_datatype(result); } -void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expression) { +void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expression, bool p_is_root) { // This one makes some magic happen. if (p_expression == nullptr) { @@ -1591,7 +1687,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre reduce_binary_op(static_cast<GDScriptParser::BinaryOpNode *>(p_expression)); break; case GDScriptParser::Node::CALL: - reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression)); + reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression), p_is_root); break; case GDScriptParser::Node::CAST: reduce_cast(static_cast<GDScriptParser::CastNode *>(p_expression)); @@ -1707,6 +1803,7 @@ void GDScriptAnalyzer::update_array_literal_element_type(const GDScriptParser::D } } if (all_same_type) { + element_type.is_constant = false; array_type.set_container_element_type(element_type); } else if (all_have_type) { push_error(vformat(R"(Variant array is not compatible with an array of type "%s".)", p_base_type.get_container_element_type().to_string()), p_array_literal); @@ -1737,7 +1834,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig push_error("Cannot assign a new value to a constant.", p_assignment->assignee); } - if (!assignee_type.is_variant() && !assigned_value_type.is_variant()) { + if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) { bool compatible = true; GDScriptParser::DataType op_type = assigned_value_type; if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { @@ -1789,27 +1886,24 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: { GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype(); if (!id_type.is_hard_type()) { - id_type = assigned_value_type; - id_type.type_source = GDScriptParser::DataType::INFERRED; - id_type.is_constant = false; + id_type.kind = GDScriptParser::DataType::VARIANT; + id_type.type_source = GDScriptParser::DataType::UNDETECTED; identifier->parameter_source->set_datatype(id_type); } } break; case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: { GDScriptParser::DataType id_type = identifier->variable_source->get_datatype(); if (!id_type.is_hard_type()) { - id_type = assigned_value_type; - id_type.type_source = GDScriptParser::DataType::INFERRED; - id_type.is_constant = false; + id_type.kind = GDScriptParser::DataType::VARIANT; + id_type.type_source = GDScriptParser::DataType::UNDETECTED; identifier->variable_source->set_datatype(id_type); } } break; case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: { GDScriptParser::DataType id_type = identifier->bind_source->get_datatype(); if (!id_type.is_hard_type()) { - id_type = assigned_value_type; - id_type.type_source = GDScriptParser::DataType::INFERRED; - id_type.is_constant = false; + id_type.kind = GDScriptParser::DataType::VARIANT; + id_type.type_source = GDScriptParser::DataType::UNDETECTED; identifier->variable_source->set_datatype(id_type); } } break; @@ -1835,16 +1929,25 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) { p_await->set_datatype(await_type); return; } + + GDScriptParser::DataType awaiting_type; + if (p_await->to_await->type == GDScriptParser::Node::CALL) { reduce_call(static_cast<GDScriptParser::CallNode *>(p_await->to_await), true); + awaiting_type = p_await->to_await->get_datatype(); } else { reduce_expression(p_await->to_await); } - p_await->is_constant = p_await->to_await->is_constant; - p_await->reduced_value = p_await->to_await->reduced_value; + if (p_await->to_await->is_constant) { + p_await->is_constant = p_await->to_await->is_constant; + p_await->reduced_value = p_await->to_await->reduced_value; - GDScriptParser::DataType awaiting_type = p_await->to_await->get_datatype(); + awaiting_type = p_await->to_await->get_datatype(); + } else { + awaiting_type.kind = GDScriptParser::DataType::VARIANT; + awaiting_type.type_source = GDScriptParser::DataType::UNDETECTED; + } p_await->set_datatype(awaiting_type); @@ -1964,7 +2067,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o p_binary_op->set_datatype(result); } -void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_await) { +void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) { bool all_is_constant = true; Map<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing. for (int i = 0; i < p_call->arguments.size(); i++) { @@ -2323,7 +2426,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa } } - if (call_type.is_coroutine && !is_await) { + if (call_type.is_coroutine && !p_is_await && !p_is_root) { push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call->callee); } @@ -2591,6 +2694,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->is_constant = false; return; } break; + case GDScriptParser::ClassNode::Member::CLASS: { + resolve_class_interface(member.m_class); + p_identifier->set_datatype(member.m_class->get_datatype()); + return; + } break; default: break; } @@ -2605,10 +2713,13 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod const StringName &native = base.native_type; if (class_exists(native)) { - PropertyInfo prop_info; MethodInfo method_info; - if (ClassDB::get_property_info(native, name, &prop_info)) { - p_identifier->set_datatype(type_from_property(prop_info)); + if (ClassDB::has_property(native, name)) { + StringName getter_name = ClassDB::get_property_getter(native, name); + MethodBind *getter = ClassDB::get_method(native, getter_name); + if (getter != nullptr) { + p_identifier->set_datatype(type_from_property(getter->get_return_info())); + } return; } if (ClassDB::get_method_info(native, name, &method_info)) { @@ -2936,7 +3047,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } else { GDScriptParser::DataType base_type = p_subscript->base->get_datatype(); - if (base_type.is_variant()) { + if (base_type.is_variant() || !base_type.is_hard_type()) { result_type.kind = GDScriptParser::DataType::VARIANT; mark_node_unsafe(p_subscript); } else { @@ -3059,6 +3170,9 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri result_type.kind = GDScriptParser::DataType::BUILTIN; result_type.type_source = base_type.is_hard_type() ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED; + if (base_type.kind != GDScriptParser::DataType::BUILTIN) { + base_type.builtin_type = Variant::OBJECT; + } switch (base_type.builtin_type) { // Can't index at all. case Variant::RID: @@ -3119,6 +3233,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri case Variant::PLANE: case Variant::COLOR: case Variant::DICTIONARY: + case Variant::OBJECT: result_type.kind = GDScriptParser::DataType::VARIANT; result_type.type_source = GDScriptParser::DataType::UNDETECTED; break; @@ -3133,7 +3248,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } break; // Here for completeness. - case Variant::OBJECT: case Variant::VARIANT_MAX: break; } @@ -3300,7 +3414,13 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va current = current->_owner; } - Ref<GDScriptParserRef> ref = get_parser_for(current->path); + Ref<GDScriptParserRef> ref = get_parser_for(current->get_path()); + if (ref.is_null()) { + push_error("Could not find script in path.", p_source); + GDScriptParser::DataType error_type; + error_type.kind = GDScriptParser::DataType::VARIANT; + return error_type; + } ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); GDScriptParser::ClassNode *found = ref->get_parser()->head; @@ -3380,6 +3500,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo } else { ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed array."); } + elem_type.is_constant = false; result.set_container_element_type(elem_type); } } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 2e17e15452..ce4525190b 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -78,12 +78,12 @@ class GDScriptAnalyzer { void resolve_return(GDScriptParser::ReturnNode *p_return); // Reduction functions. - void reduce_expression(GDScriptParser::ExpressionNode *p_expression); + void reduce_expression(GDScriptParser::ExpressionNode *p_expression, bool p_is_root = false); void reduce_array(GDScriptParser::ArrayNode *p_array); void reduce_assignment(GDScriptParser::AssignmentNode *p_assignment); void reduce_await(GDScriptParser::AwaitNode *p_await); void reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_op); - void reduce_call(GDScriptParser::CallNode *p_call, bool is_await = false); + void reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await = false, bool p_is_root = false); void reduce_cast(GDScriptParser::CastNode *p_cast); void reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary); void reduce_get_node(GDScriptParser::GetNodeNode *p_get_node); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index b8300cd872..6a7e4278d2 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -692,7 +692,8 @@ void GDScriptByteCodeGenerator::write_end_ternary() { void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) { if (HAS_BUILTIN_TYPE(p_target)) { - if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type)) { + if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type) && + IS_BUILTIN_TYPE(p_source, Variant::get_indexed_element_type(p_target.type.builtin_type))) { // Use indexed setter instead. Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(p_target.type.builtin_type); append(GDScriptFunction::OPCODE_SET_INDEXED_VALIDATED, 3); @@ -746,7 +747,8 @@ void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address } void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) { - if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name)) { + if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name) && + IS_BUILTIN_TYPE(p_source, Variant::get_member_type(p_target.type.builtin_type, p_name))) { Variant::ValidatedSetter setter = Variant::get_member_validated_setter(p_target.type.builtin_type, p_name); append(GDScriptFunction::OPCODE_SET_NAMED_VALIDATED, 2); append(p_target); diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 8121053245..bb0d9e9e9b 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -58,27 +58,27 @@ Error GDScriptParserRef::raise_status(Status p_new_status) { while (p_new_status > status) { switch (status) { case EMPTY: - result = parser->parse(GDScriptCache::get_source_code(path), path, false); status = PARSED; + result = parser->parse(GDScriptCache::get_source_code(path), path, false); break; case PARSED: { analyzer = memnew(GDScriptAnalyzer(parser)); - Error inheritance_result = analyzer->resolve_inheritance(); status = INHERITANCE_SOLVED; + Error inheritance_result = analyzer->resolve_inheritance(); if (result == OK) { result = inheritance_result; } } break; case INHERITANCE_SOLVED: { - Error interface_result = analyzer->resolve_interface(); status = INTERFACE_SOLVED; + Error interface_result = analyzer->resolve_interface(); if (result == OK) { result = interface_result; } } break; case INTERFACE_SOLVED: { - Error body_result = analyzer->resolve_body(); status = FULLY_SOLVED; + Error body_result = analyzer->resolve_body(); if (result == OK) { result = body_result; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 2c02291795..ab0fe5c37d 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -109,7 +109,9 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D // Locate class by constructing the path to it and following that path GDScriptParser::ClassNode *class_type = p_datatype.class_type; if (class_type) { - if ((!main_script->path.is_empty() && class_type->fqcn.begins_with(main_script->path)) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) { + const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->path); + const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->name); + if (is_inner_by_path || is_inner_by_name) { // Local class. List<StringName> names; while (class_type->outer) { @@ -486,6 +488,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression); GDScriptDataType type = _gdtype_from_datatype(call->get_datatype()); GDScriptCodeGenerator::Address result = codegen.add_temporary(type); + GDScriptCodeGenerator::Address nil = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL); + + GDScriptCodeGenerator::Address return_addr = p_root ? nil : result; Vector<GDScriptCodeGenerator::Address> arguments; for (int i = 0; i < call->arguments.size(); i++) { @@ -536,13 +541,13 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (within_await) { gen->write_call_async(result, self, call->function_name, arguments); } else { - gen->write_call(result, self, call->function_name, arguments); + gen->write_call(return_addr, self, call->function_name, arguments); } } else { if (within_await) { gen->write_call_self_async(result, call->function_name, arguments); } else { - gen->write_call_self(result, call->function_name, arguments); + gen->write_call_self(return_addr, call->function_name, arguments); } } } else if (callee->type == GDScriptParser::Node::SUBSCRIPT) { @@ -577,12 +582,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code gen->write_call_method_bind(result, base, method, arguments); } } else { - gen->write_call(result, base, call->function_name, arguments); + gen->write_call(return_addr, base, call->function_name, arguments); } } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) { gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments); } else { - gen->write_call(result, base, call->function_name, arguments); + gen->write_call(return_addr, base, call->function_name, arguments); } if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) { gen->pop_temporary(); @@ -1019,25 +1024,32 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) { // Assignment to member property. - GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value); + GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value); if (r_error) { return GDScriptCodeGenerator::Address(); } - GDScriptCodeGenerator::Address assign_temp = assigned; + + GDScriptCodeGenerator::Address to_assign = assigned_value; + bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE; StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name; - if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { + if (has_operation) { + GDScriptCodeGenerator::Address op_result = codegen.add_temporary(); GDScriptCodeGenerator::Address member = codegen.add_temporary(); gen->write_get_member(member, name); - gen->write_binary_operator(assigned, assignment->variant_op, member, assigned); - gen->pop_temporary(); + gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value); + gen->pop_temporary(); // Pop member temp. + to_assign = op_result; } - gen->write_set_member(assigned, name); + gen->write_set_member(to_assign, name); - if (assign_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); + if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop the assigned expression or the temp result if it has operation. + } + if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop the assigned expression if not done before. } } else { // Regular assignment. @@ -2080,77 +2092,18 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) { Error error = OK; - CodeGen codegen; - codegen.generator = memnew(GDScriptByteCodeGenerator); - - codegen.class_node = p_class; - codegen.script = p_script; - StringName func_name; + GDScriptParser::FunctionNode *function; if (p_is_setter) { - func_name = "@" + p_variable->identifier->name + "_setter"; + function = p_variable->setter; } else { - func_name = "@" + p_variable->identifier->name + "_getter"; + function = p_variable->getter; } - codegen.function_name = func_name; - - GDScriptDataType return_type; - if (p_is_setter) { - return_type.has_type = true; - return_type.kind = GDScriptDataType::BUILTIN; - return_type.builtin_type = Variant::NIL; - } else { - return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script); - } - - 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())); - codegen.parameters[p_variable->setter_parameter->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, _gdtype_from_datatype(p_variable->get_datatype())); - } + _parse_function(error, p_script, p_class, function); - error = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter); - if (error) { - memdelete(codegen.generator); - return error; - } - - GDScriptFunction *gd_function = codegen.generator->write_end(); - - p_script->member_functions[func_name] = gd_function; - -#ifdef DEBUG_ENABLED - if (EngineDebugger::is_active()) { - String signature; - //path - if (p_script->get_path() != String()) { - signature += p_script->get_path(); - } - //loc - signature += "::" + itos(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line); - - //function and class - - if (p_class->identifier) { - signature += "::" + String(p_class->identifier->name) + "." + String(func_name); - } else { - signature += "::" + String(func_name); - } - - codegen.generator->set_signature(signature); - } -#endif - codegen.generator->set_initial_line(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line); - -#ifdef TOOLS_ENABLED - p_script->member_lines[func_name] = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line; -#endif - memdelete(codegen.generator); - - return OK; + return error; } Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 83805f626a..71d2699c2e 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1089,6 +1089,15 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool kwa++; } + List<StringName> utility_func_names; + Variant::get_utility_function_list(&utility_func_names); + + for (List<StringName>::Element *E = utility_func_names.front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_FUNCTION); + option.insert_text += "("; + r_result.insert(option.display, option); + } + 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) { @@ -2222,8 +2231,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c if (obj) { List<String> options; obj->get_argument_options(p_method, p_argidx, &options); - for (const String &F : options) { - ScriptCodeCompletionOption option(F, ScriptCodeCompletionOption::KIND_FUNCTION); + for (String &opt : options) { + if (opt.is_quoted()) { + opt = opt.unquote().quote(quote_style); // Handle user preference. + } + ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_FUNCTION); r_result.insert(option.display, option); } } @@ -2322,7 +2334,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c GDScriptCompletionIdentifier connect_base; - if (GDScriptUtilityFunctions::function_exists(call->function_name)) { + if (Variant::has_utility_function(call->function_name)) { + MethodInfo info = Variant::get_utility_function_info(call->function_name); + r_arghint = _make_arguments_hint(info, p_argidx); + return; + } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) { MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name); r_arghint = _make_arguments_hint(info, p_argidx); return; @@ -2643,23 +2659,26 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } } break; case GDScriptParser::COMPLETION_GET_NODE: { + // Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically. if (p_owner) { List<String> opts; p_owner->get_argument_options("get_node", 0, &opts); for (const String &E : opts) { + r_forced = true; String opt = E.strip_edges(); if (opt.is_quoted()) { - r_forced = true; - String idopt = opt.unquote(); - if (idopt.replace("/", "_").is_valid_identifier()) { - ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH); - options.insert(option.display, option); - } else { - ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH); - options.insert(option.display, option); - } + // Remove quotes so that we can handle user preferred quote style, + // or handle NodePaths which are valid identifiers and don't need quotes. + opt = opt.unquote(); + } + // The path needs quotes if it's not a valid identifier (with an exception + // for "/" as path separator, which also doesn't require quotes). + if (!opt.replace("/", "_").is_valid_identifier()) { + opt = opt.quote(quote_style); // Handle user preference. } + ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH); + options.insert(option.display, option); } // Get autoloads. diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 4e71eb32e9..b96139ac51 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -947,7 +947,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var void GDScriptParser::parse_property_setter(VariableNode *p_variable) { switch (p_variable->property) { - case VariableNode::PROP_INLINE: + case VariableNode::PROP_INLINE: { consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)"); if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) { p_variable->setter_parameter = parse_identifier(); @@ -955,9 +955,32 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) { consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*"); consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*"); - p_variable->setter = parse_suite("setter definition"); - break; + IdentifierNode *identifier = alloc_node<IdentifierNode>(); + identifier->name = "@" + p_variable->identifier->name + "_setter"; + + FunctionNode *function = alloc_node<FunctionNode>(); + function->identifier = identifier; + + FunctionNode *previous_function = current_function; + current_function = function; + + ParameterNode *parameter = alloc_node<ParameterNode>(); + parameter->identifier = p_variable->setter_parameter; + if (parameter->identifier != nullptr) { + function->parameters_indices[parameter->identifier->name] = 0; + function->parameters.push_back(parameter); + + SuiteNode *body = alloc_node<SuiteNode>(); + body->add_local(parameter, function); + + function->body = parse_suite("setter declaration", body); + p_variable->setter = function; + } + + current_function = previous_function; + break; + } case VariableNode::PROP_SETGET: consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "set")"); make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable); @@ -972,11 +995,25 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) { void GDScriptParser::parse_property_getter(VariableNode *p_variable) { switch (p_variable->property) { - case VariableNode::PROP_INLINE: + case VariableNode::PROP_INLINE: { consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)"); - p_variable->getter = parse_suite("getter definition"); + IdentifierNode *identifier = alloc_node<IdentifierNode>(); + identifier->name = "@" + p_variable->identifier->name + "_getter"; + + FunctionNode *function = alloc_node<FunctionNode>(); + function->identifier = identifier; + + FunctionNode *previous_function = current_function; + current_function = function; + + SuiteNode *body = alloc_node<SuiteNode>(); + function->body = parse_suite("getter declaration", body); + + p_variable->getter = function; + current_function = previous_function; break; + } case VariableNode::PROP_SETGET: consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "get")"); make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable); @@ -1620,6 +1657,10 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() { n_for->list = parse_expression(false); + if (!n_for->list) { + push_error(R"(Expected a list or range after "in".)"); + } + consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "for" condition.)"); // Save break/continue state. @@ -3027,9 +3068,9 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & } else { /* Syntax: - @tutorial ( The Title Here ) : https://the.url/ - ^ open ^ close ^ colon ^ url - */ + * @tutorial ( The Title Here ) : https://the.url/ + * ^ open ^ close ^ colon ^ url + */ int open_bracket_pos = begin_scan, close_bracket_pos = 0; while (open_bracket_pos < striped_line.length() && (striped_line[open_bracket_pos] == ' ' || striped_line[open_bracket_pos] == '\t')) { open_bracket_pos++; @@ -3419,7 +3460,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node String enum_hint_string; for (const Map<StringName, int>::Element *E = export_type.enum_values.front(); E; E = E->next()) { - enum_hint_string += E->key().operator String().camelcase_to_underscore(true).capitalize().xml_escape(); + enum_hint_string += E->key().operator String().capitalize().xml_escape(); enum_hint_string += ":"; enum_hint_string += String::num_int64(E->get()).xml_escape(); @@ -3481,22 +3522,22 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod } for (int i = last; i >= 0; i--) { String mode = p_annotation->resolved_arguments[i].operator String(); - if (mode == "any") { - rpc_config.rpc_mode = Multiplayer::RPC_MODE_ANY; - } else if (mode == "auth") { + if (mode == "any_peer") { + rpc_config.rpc_mode = Multiplayer::RPC_MODE_ANY_PEER; + } else if (mode == "authority") { rpc_config.rpc_mode = Multiplayer::RPC_MODE_AUTHORITY; - } else if (mode == "sync") { - rpc_config.sync = true; - } else if (mode == "nosync") { - rpc_config.sync = false; + } else if (mode == "call_local") { + rpc_config.call_local = true; + } else if (mode == "call_remote") { + rpc_config.call_local = false; } else if (mode == "reliable") { rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; } else if (mode == "unreliable") { rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE; - } else if (mode == "ordered") { - rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_ORDERED; + } else if (mode == "unreliable_ordered") { + rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED; } else { - push_error(R"(Invalid RPC argument. Must be one of: 'sync'/'nosync' (local calls), 'any'/'auth' (permission), 'reliable'/'unreliable'/'ordered' (transfer mode).)", p_annotation); + push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation); } } } @@ -4433,7 +4474,7 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) { if (p_variable->property == VariableNode::PROP_INLINE) { push_line(":"); increase_indent(); - print_suite(p_variable->getter); + print_suite(p_variable->getter->body); decrease_indent(); } else { push_line(" ="); @@ -4453,7 +4494,7 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) { } push_line("):"); increase_indent(); - print_suite(p_variable->setter); + print_suite(p_variable->setter->body); decrease_indent(); } else { push_line(" ="); @@ -4492,7 +4533,7 @@ void GDScriptParser::TreePrinter::print_tree(const GDScriptParser &p_parser) { } print_class(p_parser.get_tree()); - print_line(printed); + print_line(String(printed)); } #endif // DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 593fb0cc5e..af9b973ada 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1109,12 +1109,12 @@ public: PropertyStyle property = PROP_NONE; union { - SuiteNode *setter = nullptr; + FunctionNode *setter = nullptr; IdentifierNode *setter_pointer; }; IdentifierNode *setter_parameter = nullptr; union { - SuiteNode *getter = nullptr; + FunctionNode *getter = nullptr; IdentifierNode *getter_pointer; }; diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index d4a098811a..cd247d1d26 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -1064,7 +1064,8 @@ void GDScriptTokenizer::check_indent() { // First time indenting, choose character now. indent_char = current_indent_char; } else if (current_indent_char != indent_char) { - Token error = make_error(vformat("Used \"%s\" for indentation instead \"%s\" as used before in the file.", String(¤t_indent_char, 1).c_escape(), String(&indent_char, 1).c_escape())); + Token error = make_error(vformat("Used %s character for indentation instead of %s as used before in the file.", + _get_indent_char_name(current_indent_char), _get_indent_char_name(indent_char))); error.start_line = line; error.start_column = 1; error.leftmost_column = 1; @@ -1114,6 +1115,12 @@ void GDScriptTokenizer::check_indent() { } } +String GDScriptTokenizer::_get_indent_char_name(char32_t ch) { + ERR_FAIL_COND_V(ch != ' ' && ch != '\t', String(&ch, 1).c_escape()); + + return ch == ' ' ? "space" : "tab"; +} + void GDScriptTokenizer::_skip_whitespace() { if (pending_indents != 0) { // Still have some indent/dedent tokens to give. diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 84b82c07f0..b4ee11fd9a 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -233,6 +233,7 @@ private: bool has_error() const { return !error_stack.is_empty(); } Token pop_error(); char32_t _advance(); + String _get_indent_char_name(char32_t ch); void _skip_whitespace(); void check_indent(); diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp index f1b0079536..e997d3a51e 100644 --- a/modules/gdscript/gdscript_utility_functions.cpp +++ b/modules/gdscript/gdscript_utility_functions.cpp @@ -437,9 +437,13 @@ struct GDScriptUtilityFunctionsDefinitions { str += p_args[i]->operator String(); } - ScriptLanguage *script = GDScriptLanguage::get_singleton(); - if (script->debug_get_stack_level_count() > 0) { - str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; + if (Thread::get_caller_id() == Thread::get_main_id()) { + ScriptLanguage *script = GDScriptLanguage::get_singleton(); + if (script->debug_get_stack_level_count() > 0) { + str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; + } + } else { + str += "\n At: Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()); } print_line(str); @@ -448,15 +452,24 @@ struct GDScriptUtilityFunctionsDefinitions { static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { VALIDATE_ARG_COUNT(0); + if (Thread::get_caller_id() != Thread::get_main_id()) { + print_line("Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id())); + return; + } ScriptLanguage *script = GDScriptLanguage::get_singleton(); for (int i = 0; i < script->debug_get_stack_level_count(); i++) { print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'"); }; + *r_ret = Variant(); } static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { VALIDATE_ARG_COUNT(0); + if (Thread::get_caller_id() != Thread::get_main_id()) { + *r_ret = Array(); + return; + } ScriptLanguage *script = GDScriptLanguage::get_singleton(); Array ret; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index c89cf2f25c..a1cc2246d6 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -1110,7 +1110,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else { #ifdef DEBUG_ENABLED err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + - "' to a variable of type '" + Variant::get_type_name(var_type) + "'."; + "' to a variable of type '" + Variant::get_type_name(var_type) + "'."; OPCODE_BREAK; } } else { @@ -1132,7 +1132,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (src->get_type() != Variant::ARRAY) { #ifdef DEBUG_ENABLED err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + - "' to a variable of type '" + +"'."; + "' to a variable of type '" + +"'."; #endif OPCODE_BREAK; } @@ -1158,14 +1158,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!nc); if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) { err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + - "' to a variable of type '" + nc->get_name() + "'."; + "' to a variable of type '" + nc->get_name() + "'."; OPCODE_BREAK; } Object *src_obj = src->operator Object *(); if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) { err_text = "Trying to assign value of type '" + src_obj->get_class_name() + - "' to a variable of type '" + nc->get_name() + "'."; + "' to a variable of type '" + nc->get_name() + "'."; OPCODE_BREAK; } #endif // DEBUG_ENABLED @@ -1195,7 +1195,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a ScriptInstance *scr_inst = src->operator Object *()->get_script_instance(); if (!scr_inst) { err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() + - "' to a variable of type '" + base_type->get_path().get_file() + "'."; + "' to a variable of type '" + base_type->get_path().get_file() + "'."; OPCODE_BREAK; } @@ -1212,7 +1212,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (!valid) { err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() + - "' to a variable of type '" + base_type->get_path().get_file() + "'."; + "' to a variable of type '" + base_type->get_path().get_file() + "'."; OPCODE_BREAK; } } @@ -1719,6 +1719,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #define OPCODE_CALL_PTR(m_type) \ OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \ CHECK_SPACE(3 + instr_arg_count); \ + ip += instr_arg_count; \ int argc = _code_ptr[ip + 1]; \ GET_INSTRUCTION_ARG(base, argc); \ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \ @@ -2097,8 +2098,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } if (result.get_type() != Variant::SIGNAL) { + // Not async, return immediately using the target from OPCODE_AWAIT_RESUME. + GET_VARIANT_PTR(target, 3); + *target = result; ip += 4; // Skip OPCODE_AWAIT_RESUME and its data. - // The stack pointer should be the same, so we don't need to set a return value. is_signal = false; } else { sig = result; @@ -3312,7 +3315,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) { // debugger break did not happen - _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), false, ERR_HANDLER_SCRIPT); } #endif diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index d106b3b541..80f4721e2d 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -165,7 +165,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p case ClassNode::Member::VARIABLE: { lsp::DocumentSymbol symbol; symbol.name = m.variable->identifier->name; - symbol.kind = m.variable->property == VariableNode::PropertyStyle::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property; + symbol.kind = m.variable->property == VariableNode::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property; symbol.deprecated = false; symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.variable->start_line); symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.variable->start_column); @@ -269,7 +269,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p if (j > 0) { symbol.detail += ", "; } - symbol.detail += m.signal->parameters[i]->identifier->name; + symbol.detail += m.signal->parameters[j]->identifier->name; } symbol.detail += ")"; @@ -491,7 +491,7 @@ String ExtendGDScriptParser::get_text_for_completion(const lsp::Position &p_curs return longthing; } -String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol, bool p_func_requred) const { +String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol, bool p_func_required) const { String longthing; int len = lines.size(); for (int i = 0; i < len; i++) { @@ -513,7 +513,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c longthing += first_part; longthing += String::chr(0xFFFF); //not unicode, represents the cursor - if (p_func_requred) { + if (p_func_required) { longthing += "("; // tell the parser this is a function call } longthing += last_part; @@ -532,6 +532,9 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const { ERR_FAIL_INDEX_V(p_position.line, lines.size(), ""); String line = lines[p_position.line]; + if (line.is_empty()) { + return ""; + } ERR_FAIL_INDEX_V(p_position.character, line.size(), ""); int start_pos = p_position.character; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index 28b9b3c82a..5d7b16765b 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -85,7 +85,7 @@ public: Error get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const; String get_text_for_completion(const lsp::Position &p_cursor) const; - String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_requred = false) const; + String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_required = false) const; String get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const; String get_uri() const; diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h index 85a44a8cc1..f1413f0133 100644 --- a/modules/gdscript/language_server/gdscript_language_server.h +++ b/modules/gdscript/language_server/gdscript_language_server.h @@ -50,10 +50,8 @@ class GDScriptLanguageServer : public EditorPlugin { private: void _notification(int p_what); - void _iteration(); public: - Error parse_script_file(const String &p_path); GDScriptLanguageServer(); void start(); void stop(); diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index d2e033d7de..92ce71f395 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -428,6 +428,9 @@ GDScriptTextDocument::~GDScriptTextDocument() { void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) { String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path); + if (!path.begins_with("res://")) { + return; + } GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); EditorFileSystem::get_singleton()->update_file(path); diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 371e3de419..932bfb2caa 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -338,7 +338,9 @@ Error GDScriptWorkspace::initialize() { } Vector<DocData::MethodDoc> methods_signals; + methods_signals.append_array(class_data.constructors); methods_signals.append_array(class_data.methods); + methods_signals.append_array(class_data.operators); const int signal_start_idx = methods_signals.size(); methods_signals.append_array(class_data.signals); diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 3710a84a28..b12d1f5f3b 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -358,21 +358,21 @@ struct Command { namespace TextDocumentSyncKind { /** - * Documents should not be synced at all. - */ + * Documents should not be synced at all. + */ static const int None = 0; /** - * Documents are synced by always sending the full content - * of the document. - */ + * Documents are synced by always sending the full content + * of the document. + */ static const int Full = 1; /** - * Documents are synced by sending the full content on open. - * After that only incremental updates to the document are - * send. - */ + * Documents are synced by sending the full content on open. + * After that only incremental updates to the document are + * send. + */ static const int Incremental = 2; }; // namespace TextDocumentSyncKind @@ -667,20 +667,20 @@ struct TextDocumentContentChangeEvent { // Use namespace instead of enumeration to follow the LSP specifications namespace DiagnosticSeverity { /** - * Reports an error. - */ + * Reports an error. + */ static const int Error = 1; /** - * Reports a warning. - */ + * Reports a warning. + */ static const int Warning = 2; /** - * Reports an information. - */ + * Reports an information. + */ static const int Information = 3; /** - * Reports a hint. - */ + * Reports a hint. + */ static const int Hint = 4; }; // namespace DiagnosticSeverity @@ -871,18 +871,18 @@ static const int TypeParameter = 25; */ namespace InsertTextFormat { /** - * The primary text to be inserted is treated as a plain string. - */ + * The primary text to be inserted is treated as a plain string. + */ static const int PlainText = 1; /** - * The primary text to be inserted is treated as a snippet. - * - * A snippet can define tab stops and placeholders with `$1`, `$2` - * and `${3:foo}`. `$0` defines the final tab stop, it defaults to - * the end of the snippet. Placeholders with equal identifiers are linked, - * that is typing in one will update others too. - */ + * The primary text to be inserted is treated as a snippet. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Placeholders with equal identifiers are linked, + * that is typing in one will update others too. + */ static const int Snippet = 2; }; // namespace InsertTextFormat @@ -1359,16 +1359,16 @@ struct NativeSymbolInspectParams { */ namespace FoldingRangeKind { /** - * Folding range for a comment - */ + * Folding range for a comment + */ static const String Comment = "comment"; /** - * Folding range for a imports or includes - */ + * Folding range for a imports or includes + */ static const String Imports = "imports"; /** - * Folding range for a region (e.g. `#region`) - */ + * Folding range for a region (e.g. `#region`) + */ static const String Region = "region"; } // namespace FoldingRangeKind @@ -1419,20 +1419,20 @@ struct FoldingRange { */ namespace CompletionTriggerKind { /** - * Completion was triggered by typing an identifier (24x7 code - * complete), manual invocation (e.g Ctrl+Space) or via API. - */ + * Completion was triggered by typing an identifier (24x7 code + * complete), manual invocation (e.g Ctrl+Space) or via API. + */ static const int Invoked = 1; /** - * Completion was triggered by a trigger character specified by - * the `triggerCharacters` properties of the `CompletionRegistrationOptions`. - */ + * Completion was triggered by a trigger character specified by + * the `triggerCharacters` properties of the `CompletionRegistrationOptions`. + */ static const int TriggerCharacter = 2; /** - * Completion was re-triggered as the current completion list is incomplete. - */ + * Completion was re-triggered as the current completion list is incomplete. + */ static const int TriggerForIncompleteCompletions = 3; } // namespace CompletionTriggerKind @@ -1441,8 +1441,8 @@ static const int TriggerForIncompleteCompletions = 3; */ struct CompletionContext { /** - * How the completion was triggered. - */ + * How the completion was triggered. + */ int triggerKind = CompletionTriggerKind::TriggerCharacter; /** @@ -1906,7 +1906,7 @@ struct GodotNativeClassInfo { struct GodotCapabilities { /** * Native class list - */ + */ List<GodotNativeClassInfo> native_classes; Dictionary to_json() { diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index c383830c82..d2e71efee7 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -133,13 +133,12 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l if (do_init_languages) { init_language(p_source_dir); - - // Enable all warnings for GDScript, so we can test them. - ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true); - for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { - String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); - ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true); - } + } + // Enable all warnings for GDScript, so we can test them. + ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true); + for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { + String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); + ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true); } // Enable printing to show results @@ -335,7 +334,7 @@ void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_e result->output += p_message + "\n"; } -void GDScriptTest::error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, ErrorHandlerType p_type) { +void GDScriptTest::error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, bool p_editor_notify, ErrorHandlerType p_type) { ErrorHandlerData *data = (ErrorHandlerData *)p_this; GDScriptTest *self = data->self; TestResult *result = data->result; diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h index 9b2d14a371..98c57dc97c 100644 --- a/modules/gdscript/tests/gdscript_test_runner.h +++ b/modules/gdscript/tests/gdscript_test_runner.h @@ -87,7 +87,7 @@ private: public: static void print_handler(void *p_this, const String &p_message, bool p_error); - static void error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, ErrorHandlerType p_type); + static void error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, bool p_editor_notify, ErrorHandlerType p_type); TestResult run_test(); bool generate_output(); diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd new file mode 100644 index 0000000000..f1be6aaa0c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd @@ -0,0 +1,11 @@ +var _prop : int + +# Getter function has wrong return type. +var prop : String: + get = get_prop + +func get_prop(): + return _prop + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out new file mode 100644 index 0000000000..29eec51ef2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Function with return type "int" cannot be used as getter for a property of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd new file mode 100644 index 0000000000..dd190157a1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd @@ -0,0 +1,11 @@ +var _prop : int + +# Setter function has wrong argument type. +var prop : String: + set = set_prop + +func set_prop(value : int): + _prop = value + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out new file mode 100644 index 0000000000..7a25280d55 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Function with argument type "int" cannot be used as setter for a property of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd new file mode 100644 index 0000000000..7f2b29222a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd @@ -0,0 +1,9 @@ +var _prop : int + +# Inline getter returns int instead of String. +var prop : String: + get: + return _prop + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out new file mode 100644 index 0000000000..e0adef1bf8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot return value of type "int" because the function return type is "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd new file mode 100644 index 0000000000..0ce239dbbd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd @@ -0,0 +1,9 @@ +var _prop : int + +# Inline setter assigns String to int. +var prop : String: + set(value): + _prop = value + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out new file mode 100644 index 0000000000..bbadf1ce27 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a value of type "String" to a target of type "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd new file mode 100644 index 0000000000..3bbee5f5f7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd @@ -0,0 +1,8 @@ +var with_setter := 0: + set(val): + var x: String = val + with_setter = val + +func test(): + with_setter = 1 + print(with_setter) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out new file mode 100644 index 0000000000..9eb2a42ccd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Value of type "int" cannot be assigned to a variable of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out index 0e9f482af4..481016138a 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out +++ b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out @@ -1,2 +1,6 @@ GDTEST_OK +>> WARNING +>> Line: 6 +>> UNSAFE_METHOD_ACCESS +>> The method 'free' is not present on the inferred type 'Variant' (but may be present on a subtype). Ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd new file mode 100644 index 0000000000..30e7deb05a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd @@ -0,0 +1,19 @@ +class A: + var x = 3 + +class B: + var x = 4 + +class C: + var x = 5 + +class Test: + var a = A.new() + var b: B = B.new() + var c := C.new() + +func test(): + var test_instance := Test.new() + prints(test_instance.a.x) + prints(test_instance.b.x) + prints(test_instance.c.x) diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out new file mode 100644 index 0000000000..a078e62cc7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out @@ -0,0 +1,4 @@ +GDTEST_OK +3 +4 +5 diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd new file mode 100644 index 0000000000..630b20c282 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd @@ -0,0 +1,11 @@ +# https://github.com/godotengine/godot/issues/43503 + +var test_var = null + + +func test(): + print(test_var.x) + + +func _init(): + test_var = Vector3() diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out new file mode 100644 index 0000000000..94e2ec2af8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out @@ -0,0 +1,2 @@ +GDTEST_OK +0 diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd new file mode 100644 index 0000000000..b3784dffa3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd @@ -0,0 +1,14 @@ +# https://github.com/godotengine/godot/issues/41064 +var x = true + +func test(): + var int_var: int = 0 + var dyn_var = 2 + + if x: + dyn_var = 5 + else: + dyn_var = Node.new() + + int_var = dyn_var + print(int_var) diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out new file mode 100644 index 0000000000..952029f665 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out @@ -0,0 +1,2 @@ +GDTEST_OK +5 diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd b/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd new file mode 100644 index 0000000000..1706087f82 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd @@ -0,0 +1,16 @@ +var _prop = 1 +var prop: + get = get_prop, set = set_prop + +func get_prop(): + return _prop + +func set_prop(value): + _prop = value + +func test(): + print(prop) + + prop = 2 + + print(prop) diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_functions.out b/modules/gdscript/tests/scripts/analyzer/features/property_functions.out new file mode 100644 index 0000000000..f1253ca57e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/property_functions.out @@ -0,0 +1,3 @@ +GDTEST_OK +1 +2 diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd new file mode 100644 index 0000000000..23eb011b23 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd @@ -0,0 +1,46 @@ +# Untyped inline property +var prop1: + get: + return prop1 + set(value): + prop1 = value + +# Typed inline property +var prop2 : int: + get: + return prop2 + set(value): + prop2 = value + +# Typed inline property with default value +var prop3 : int = 1: + get: + return prop3 + set(value): + prop3 = value + +# Typed inline property with backing variable +var _prop4 : int = 2 +var prop4: int: + get: + return _prop4 + set(value): + _prop4 = value + +func test(): + print(prop1) + print(prop2) + print(prop3) + print(prop4) + + print() + + prop1 = 1 + prop2 = 2 + prop3 = 3 + prop4 = 4 + + print(prop1) + print(prop2) + print(prop3) + print(prop4) diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.out b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out new file mode 100644 index 0000000000..5482592e90 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out @@ -0,0 +1,10 @@ +GDTEST_OK +null +0 +1 +2 + +1 +2 +3 +4 diff --git a/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd new file mode 100644 index 0000000000..f9a8b23b92 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd @@ -0,0 +1,8 @@ +# https://github.com/godotengine/godot/issues/43221 +extends Node + +func test(): + name = "Node" + print(self["name"]) + self["name"] = "Changed" + print(name) diff --git a/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out new file mode 100644 index 0000000000..6417f4f8da --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out @@ -0,0 +1,3 @@ +GDTEST_OK +Node +Changed diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.gd new file mode 100644 index 0000000000..55c40cb971 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.gd @@ -0,0 +1,6 @@ +# https://github.com/godotengine/godot/issues/53640 + +func test(): + var arr := [0] + arr[0] = 1 + print(arr[0]) diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.out new file mode 100644 index 0000000000..a7f1357bb2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.out @@ -0,0 +1,2 @@ +GDTEST_OK +1 diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd new file mode 100644 index 0000000000..9502f6e196 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd @@ -0,0 +1,10 @@ +class Inner: + var prop = "Inner" + + +var array: Array[Inner] = [Inner.new()] + + +func test(): + var element: Inner = array[0] + print(element.prop) diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out new file mode 100644 index 0000000000..8f250d2632 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out @@ -0,0 +1,2 @@ +GDTEST_OK +Inner diff --git a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out index 6390de9788..31bed2dbc7 100644 --- a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out +++ b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -Used "\t" for indentation instead " " as used before in the file. +Used tab character for indentation instead of space as used before in the file. diff --git a/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd new file mode 100644 index 0000000000..18174eae67 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd @@ -0,0 +1,32 @@ +# https://github.com/godotengine/godot/issues/48121 + +func test(): + var x := [] + var y := [] + x.push_back(y) + print("TEST ARRAY ADD TO SELF: " + str(len(y))) + x.clear() + + x = Array() + y = Array() + x.push_back(y) + print("TEST ARRAY ADD TO SELF: " + str(len(y))) + x.clear() + + x = Array().duplicate() + y = Array().duplicate() + x.push_back(y) + print("TEST ARRAY ADD TO SELF: " + str(len(y))) + x.clear() + + x = [].duplicate() + y = [].duplicate() + x.push_back(y) + print("TEST ARRAY ADD TO SELF: " + str(len(y))) + x.clear() + + x = Array() + y = Array() + x.push_back(y) + print("TEST ARRAY ADD TO SELF: " + str(len(y))) + x.clear() diff --git a/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out new file mode 100644 index 0000000000..f6b7d3cc39 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out @@ -0,0 +1,6 @@ +GDTEST_OK +TEST ARRAY ADD TO SELF: 0 +TEST ARRAY ADD TO SELF: 0 +TEST ARRAY ADD TO SELF: 0 +TEST ARRAY ADD TO SELF: 0 +TEST ARRAY ADD TO SELF: 0 diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd new file mode 100644 index 0000000000..f6526aefb4 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd @@ -0,0 +1,13 @@ +extends Node + +func test(): + process_priority = 10 + var change = 20 + + print(process_priority) + print(change) + + process_priority += change + + print(process_priority) + print(change) diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out new file mode 100644 index 0000000000..c9e6b34c77 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out @@ -0,0 +1,5 @@ +GDTEST_OK +10 +20 +30 +20 diff --git a/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.gd b/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.gd new file mode 100644 index 0000000000..9da61ab184 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.gd @@ -0,0 +1,8 @@ +# https://github.com/godotengine/godot/issues/50894 + +func test(): + print(await not_coroutine()) + + +func not_coroutine(): + return "awaited" diff --git a/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.out b/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.out new file mode 100644 index 0000000000..c2ac488e9b --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.out @@ -0,0 +1,6 @@ +GDTEST_OK +>> WARNING +>> Line: 4 +>> REDUNDANT_AWAIT +>> "await" keyword not needed in this case, because the expression isn't a coroutine nor a signal. +awaited diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd new file mode 100644 index 0000000000..d5a5f8de64 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd @@ -0,0 +1,19 @@ +# https://github.com/godotengine/godot/issues/48121 + +func test(): + var x := Dictionary() + var y := Dictionary() + y[0]=1 + y[1]=1 + y[2]=1 + print("TEST OTHER DICTIONARY: " + str(len(x))) + x.clear() + + x = Dictionary().duplicate() + y = Dictionary().duplicate() + y[0]=1 + y[1]=1 + y[2]=1 + print("TEST OTHER DICTIONARY: " + str(len(x))) + x.clear() + return diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out new file mode 100644 index 0000000000..0bf49f5934 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out @@ -0,0 +1,3 @@ +GDTEST_OK +TEST OTHER DICTIONARY: 0 +TEST OTHER DICTIONARY: 0 diff --git a/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd b/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd new file mode 100644 index 0000000000..c9b5f8481e --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd @@ -0,0 +1,4 @@ +func test(): + var dict = {} + dict.test = 1 + print(dict.test) diff --git a/modules/gdscript/tests/scripts/runtime/features/lua_assign.out b/modules/gdscript/tests/scripts/runtime/features/lua_assign.out new file mode 100644 index 0000000000..a7f1357bb2 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lua_assign.out @@ -0,0 +1,2 @@ +GDTEST_OK +1 diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd new file mode 100644 index 0000000000..fead2df854 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd @@ -0,0 +1,42 @@ +func test(): + print(true, false) + print(-1, 0, 1) + print(-1.25, 0.25, 1.25) + print("hello world") + + print(Vector2(0.25, 0.25)) + print(Vector2i(0, 0)) + + print(Rect2(0.25, 0.25, 0.5, 0.5)) + print(Rect2i(0, 0, 0, 0)) + + print(Vector3(0.25, 0.25, 0.25)) + print(Vector3i(0, 0, 0)) + + print(Transform2D.IDENTITY) + print(Plane(1, 2, 3, 4)) + print(Quaternion(1, 2, 3, 4)) + print(AABB(Vector3.ZERO, Vector3.ONE)) + print(Basis.from_euler(Vector3(0, 0, 0))) + print(Transform3D.IDENTITY) + + print(Color(1, 2, 3, 4)) + print(StringName("hello")) + print(NodePath("hello/world")) + var node := Node.new() + print(RID(node)) + print(node.get_name) + print(node.property_list_changed) + node.free() + print({"hello":123}) + print(["hello", 123]) + + print(PackedByteArray([-1, 0, 1])) + print(PackedInt32Array([-1, 0, 1])) + print(PackedInt64Array([-1, 0, 1])) + print(PackedFloat32Array([-1, 0, 1])) + print(PackedFloat64Array([-1, 0, 1])) + print(PackedStringArray(["hello", "world"])) + print(PackedVector2Array([Vector2.ONE, Vector2.ZERO])) + print(PackedVector3Array([Vector3.ONE, Vector3.ZERO])) + print(PackedColorArray([Color.RED, Color.BLUE, Color.GREEN])) diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out new file mode 100644 index 0000000000..7670fc0128 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out @@ -0,0 +1,34 @@ +GDTEST_OK +truefalse +-101 +-1.250.251.25 +hello world +(0.25, 0.25) +(0, 0) +[P: (0.25, 0.25), S: (0.5, 0.5)] +[P: (0, 0), S: (0, 0)] +(0.25, 0.25, 0.25) +(0, 0, 0) +[X: (1, 0), Y: (0, 1), O: (0, 0)] +[N: (1, 2, 3), D: 4] +(1, 2, 3, 4) +[P: (0, 0, 0), S: (1, 1, 1)] +[X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1)] +[X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1), O: (0, 0, 0)] +(1, 2, 3, 4) +hello +hello/world +RID(0) +Node::get_name +Node::[signal]property_list_changed +{hello:123} +[hello, 123] +[255, 0, 1] +[-1, 0, 1] +[-1, 0, 1] +[-1, 0, 1] +[-1, 0, 1] +[hello, world] +[(1, 1), (0, 0)] +[(1, 1, 1), (0, 0, 0)] +[(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)] diff --git a/modules/gltf/config.py b/modules/gltf/config.py index 52a97c93aa..a4736321fa 100644 --- a/modules/gltf/config.py +++ b/modules/gltf/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return env["tools"] and not env["disable_3d"] + return not env["disable_3d"] def configure(env): @@ -22,6 +22,8 @@ def get_doc_classes(): "GLTFSpecGloss", "GLTFState", "GLTFTexture", + "GLTFDocumentExtension", + "GLTFDocumentExtensionConvertImporterMesh", ] diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 16e649f390..8d8e25e8b3 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -30,4 +30,8 @@ </description> </method> </methods> + <members> + <member name="extensions" type="GLTFDocumentExtension[]" setter="set_extensions" getter="get_extensions" default="[]"> + </member> + </members> </class> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml new file mode 100644 index 0000000000..390bd3b30b --- /dev/null +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GLTFDocumentExtension" inherits="Resource" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="export_post"> + <return type="int" enum="Error" /> + <argument index="0" name="document" type="GLTFDocument" /> + <description> + </description> + </method> + <method name="export_preflight"> + <return type="int" enum="Error" /> + <argument index="0" name="document" type="GLTFDocument" /> + <argument index="1" name="node" type="Node" /> + <description> + </description> + </method> + <method name="get_export_setting" qualifiers="const"> + <return type="Variant" /> + <argument index="0" name="key" type="StringName" /> + <description> + </description> + </method> + <method name="get_export_setting_keys" qualifiers="const"> + <return type="Array" /> + <description> + </description> + </method> + <method name="get_import_setting" qualifiers="const"> + <return type="Variant" /> + <argument index="0" name="key" type="StringName" /> + <description> + </description> + </method> + <method name="get_import_setting_keys" qualifiers="const"> + <return type="Array" /> + <description> + </description> + </method> + <method name="import_post"> + <return type="int" enum="Error" /> + <argument index="0" name="document" type="GLTFDocument" /> + <argument index="1" name="node" type="Node" /> + <description> + </description> + </method> + <method name="import_preflight"> + <return type="int" enum="Error" /> + <argument index="0" name="document" type="GLTFDocument" /> + <description> + </description> + </method> + <method name="set_export_setting"> + <return type="void" /> + <argument index="0" name="key" type="StringName" /> + <argument index="1" name="value" type="Variant" /> + <description> + </description> + </method> + <method name="set_import_setting"> + <return type="void" /> + <argument index="0" name="key" type="StringName" /> + <argument index="1" name="value" type="Variant" /> + <description> + </description> + </method> + </methods> +</class> diff --git a/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml index c85fce7b9d..452eec5f4f 100644 --- a/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSceneImporterGLTF" inherits="EditorSceneImporter" version="4.0"> +<class name="GLTFDocumentExtensionConvertImporterMesh" inherits="GLTFDocumentExtension" version="4.0"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml index 51e9fc032a..58853217e2 100644 --- a/modules/gltf/doc_classes/GLTFMesh.xml +++ b/modules/gltf/doc_classes/GLTFMesh.xml @@ -9,7 +9,9 @@ <members> <member name="blend_weights" type="PackedFloat32Array" setter="set_blend_weights" getter="get_blend_weights" default="PackedFloat32Array()"> </member> - <member name="mesh" type="EditorSceneImporterMesh" setter="set_mesh" getter="get_mesh"> + <member name="instance_materials" type="Array" setter="set_instance_materials" getter="get_instance_materials" default="[]"> + </member> + <member name="mesh" type="ImporterMesh" setter="set_mesh" getter="get_mesh"> </member> </members> </class> diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp index 3ce5ea23dc..25fda7ef3b 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#if TOOLS_ENABLED #include "editor_scene_exporter_gltf_plugin.h" #include "core/config/project_settings.h" #include "core/error/error_list.h" @@ -93,3 +94,5 @@ void SceneExporterGLTFPlugin::convert_scene_to_gltf2() { file_export_lib->set_current_file(filename + String(".gltf")); file_export_lib->popup_centered_ratio(); } + +#endif // TOOLS_ENABLED diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor_scene_exporter_gltf_plugin.h index c4f277fca2..89a8e27053 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.h +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.h @@ -31,7 +31,9 @@ #ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H #define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H +#if TOOLS_ENABLED #include "editor/editor_plugin.h" + #include "editor_scene_importer_gltf.h" class SceneExporterGLTFPlugin : public EditorPlugin { @@ -47,5 +49,5 @@ public: bool has_main_screen() const override; SceneExporterGLTFPlugin(class EditorNode *p_node); }; - +#endif // TOOLS_ENABLED #endif // EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H diff --git a/modules/gltf/editor_scene_importer_gltf.cpp b/modules/gltf/editor_scene_importer_gltf.cpp index 12796c41d7..1a172877a0 100644 --- a/modules/gltf/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor_scene_importer_gltf.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#if TOOLS_ENABLED #include "editor_scene_importer_gltf.h" #include "gltf_document.h" @@ -37,16 +38,16 @@ #include "scene/animation/animation_player.h" #include "scene/resources/animation.h" -uint32_t EditorSceneImporterGLTF::get_import_flags() const { +uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const { return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION; } -void EditorSceneImporterGLTF::get_extensions(List<String> *r_extensions) const { +void EditorSceneFormatImporterGLTF::get_extensions(List<String> *r_extensions) const { r_extensions->push_back("gltf"); r_extensions->push_back("glb"); } -Node *EditorSceneImporterGLTF::import_scene(const String &p_path, +Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { @@ -55,8 +56,10 @@ Node *EditorSceneImporterGLTF::import_scene(const String &p_path, return doc->import_scene_gltf(p_path, p_flags, p_bake_fps, Ref<GLTFState>(), r_missing_deps, r_err); } -Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path, +Ref<Animation> EditorSceneFormatImporterGLTF::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { return Ref<Animation>(); } + +#endif // TOOLS_ENABLED diff --git a/modules/gltf/editor_scene_importer_gltf.h b/modules/gltf/editor_scene_importer_gltf.h index eb8775b137..28963adc28 100644 --- a/modules/gltf/editor_scene_importer_gltf.h +++ b/modules/gltf/editor_scene_importer_gltf.h @@ -30,18 +30,19 @@ #ifndef EDITOR_SCENE_IMPORTER_GLTF_H #define EDITOR_SCENE_IMPORTER_GLTF_H - +#ifdef TOOLS_ENABLED #include "gltf_state.h" +#include "gltf_document_extension.h" + #include "editor/import/resource_importer_scene.h" #include "scene/main/node.h" #include "scene/resources/packed_scene.h" class Animation; -#ifdef TOOLS_ENABLED -class EditorSceneImporterGLTF : public EditorSceneImporter { - GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter); +class EditorSceneFormatImporterGLTF : public EditorSceneFormatImporter { + GDCLASS(EditorSceneFormatImporterGLTF, EditorSceneFormatImporter); public: virtual uint32_t get_import_flags() const override; @@ -50,5 +51,5 @@ public: virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) override; }; -#endif +#endif // TOOLS_ENABLED #endif // EDITOR_SCENE_IMPORTER_GLTF_H diff --git a/modules/gltf/gltf_accessor.cpp b/modules/gltf/gltf_accessor.cpp index daeb084916..85cec3fec4 100644 --- a/modules/gltf/gltf_accessor.cpp +++ b/modules/gltf/gltf_accessor.cpp @@ -30,6 +30,8 @@ #include "gltf_accessor.h" +#include "gltf_document_extension.h" + void GLTFAccessor::_bind_methods() { ClassDB::bind_method(D_METHOD("get_buffer_view"), &GLTFAccessor::get_buffer_view); ClassDB::bind_method(D_METHOD("set_buffer_view", "buffer_view"), &GLTFAccessor::set_buffer_view); diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/gltf_accessor.h index 57aea1026c..bec511f974 100644 --- a/modules/gltf/gltf_accessor.h +++ b/modules/gltf/gltf_accessor.h @@ -32,7 +32,9 @@ #define GLTF_ACCESSOR_H #include "core/io/resource.h" + #include "gltf_document.h" +#include "gltf_document_extension.h" struct GLTFAccessor : public Resource { GDCLASS(GLTFAccessor, Resource); diff --git a/modules/gltf/gltf_buffer_view.cpp b/modules/gltf/gltf_buffer_view.cpp index ba38a11c4c..d00e0f040f 100644 --- a/modules/gltf/gltf_buffer_view.cpp +++ b/modules/gltf/gltf_buffer_view.cpp @@ -30,6 +30,8 @@ #include "gltf_buffer_view.h" +#include "gltf_document_extension.h" + void GLTFBufferView::_bind_methods() { ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer); ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &GLTFBufferView::set_buffer); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 3f1f218e78..ced80e26b5 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -33,6 +33,8 @@ #include "gltf_accessor.h" #include "gltf_animation.h" #include "gltf_camera.h" +#include "gltf_document_extension.h" +#include "gltf_document_extension_convert_importer_mesh.h" #include "gltf_light.h" #include "gltf_mesh.h" #include "gltf_node.h" @@ -49,6 +51,7 @@ #include "core/io/json.h" #include "core/math/disjoint_set.h" #include "core/math/vector2.h" +#include "core/variant/dictionary.h" #include "core/variant/typed_array.h" #include "core/variant/variant.h" #include "core/version.h" @@ -57,8 +60,12 @@ #include "editor/import/resource_importer_scene.h" #include "scene/2d/node_2d.h" #include "scene/3d/camera_3d.h" +#include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" #include "scene/animation/animation_player.h" +#include "scene/resources/importer_mesh.h" +#include "scene/resources/mesh.h" +#include "scene/resources/multimesh.h" #include "scene/resources/surface_tool.h" #include "modules/modules_enabled.gen.h" @@ -113,26 +120,26 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String & return Error::FAILED; } - /* STEP 7 SERIALIZE IMAGES */ - err = _serialize_images(state, p_path); + /* STEP 7 SERIALIZE ANIMATIONS */ + err = _serialize_animations(state); if (err != OK) { return Error::FAILED; } - /* STEP 8 SERIALIZE TEXTURES */ - err = _serialize_textures(state); + /* STEP 8 SERIALIZE ACCESSORS */ + err = _encode_accessors(state); if (err != OK) { return Error::FAILED; } - // /* STEP 9 SERIALIZE ANIMATIONS */ - err = _serialize_animations(state); + /* STEP 9 SERIALIZE IMAGES */ + err = _serialize_images(state, p_path); if (err != OK) { return Error::FAILED; } - /* STEP 10 SERIALIZE ACCESSORS */ - err = _encode_accessors(state); + /* STEP 10 SERIALIZE TEXTURES */ + err = _serialize_textures(state); if (err != OK) { return Error::FAILED; } @@ -240,7 +247,7 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) { JSON json; err = json.parse(text); if (err != OK) { - _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT); return err; } state->json = json.get_data(); @@ -275,7 +282,7 @@ Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) { JSON json; err = json.parse(text); if (err != OK) { - _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT); return err; } @@ -788,6 +795,9 @@ Error GLTFDocument::_encode_buffer_views(Ref<GLTFState> state) { buffers.push_back(d); } print_verbose("glTF: Total buffer views: " + itos(state->buffer_views.size())); + if (!buffers.size()) { + return OK; + } state->json["bufferViews"] = buffers; return OK; } @@ -877,6 +887,9 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> state) { accessors.push_back(d); } + if (!accessors.size()) { + return OK; + } state->json["accessors"] = accessors; ERR_FAIL_COND_V(!state->json.has("accessors"), ERR_FILE_CORRUPT); print_verbose("glTF: Total accessors: " + itos(state->accessors.size())); @@ -2101,10 +2114,11 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { Array meshes; for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < state->meshes.size(); gltf_mesh_i++) { print_verbose("glTF: Serializing mesh: " + itos(gltf_mesh_i)); - Ref<EditorSceneImporterMesh> import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh(); + Ref<ImporterMesh> import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh(); if (import_mesh.is_null()) { continue; } + Array instance_materials = state->meshes.write[gltf_mesh_i]->get_instance_materials(); Array primitives; Dictionary gltf_mesh; Array target_names; @@ -2424,7 +2438,14 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { } } - Ref<BaseMaterial3D> mat = import_mesh->get_surface_material(surface_i); + Variant v; + if (surface_i < instance_materials.size()) { + v = instance_materials.get(surface_i); + } + Ref<BaseMaterial3D> mat = v; + if (!mat.is_valid()) { + mat = import_mesh->get_surface_material(surface_i); + } if (mat.is_valid()) { Map<Ref<BaseMaterial3D>, GLTFMaterialIndex>::Element *material_cache_i = state->material_cache.find(mat); if (material_cache_i && material_cache_i->get() != -1) { @@ -2468,6 +2489,9 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { meshes.push_back(gltf_mesh); } + if (!meshes.size()) { + return OK; + } state->json["meshes"] = meshes; print_verbose("glTF: Total meshes: " + itos(meshes.size())); @@ -2491,9 +2515,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { ERR_FAIL_COND_V(!d.has("primitives"), ERR_PARSE_ERROR); Array primitives = d["primitives"]; - const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : - Dictionary(); - Ref<EditorSceneImporterMesh> import_mesh; + const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary(); + Ref<ImporterMesh> import_mesh; import_mesh.instantiate(); String mesh_name = "mesh"; if (d.has("name") && !String(d["name"]).is_empty()) { @@ -2732,17 +2755,18 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL")); + Ref<SurfaceTool> mesh_surface_tool; + mesh_surface_tool.instantiate(); + mesh_surface_tool->create_from_triangle_arrays(array); + if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + mesh_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); + } + mesh_surface_tool->index(); if (generate_tangents) { //must generate mikktspace tangents.. ergh.. - Ref<SurfaceTool> st; - st.instantiate(); - st->create_from_triangle_arrays(array); - if (a.has("JOINTS_0") && a.has("JOINTS_1")) { - st->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); - } - st->generate_tangents(); - array = st->commit_to_arrays(); + mesh_surface_tool->generate_tangents(); } + array = mesh_surface_tool->commit_to_arrays(); Array morphs; //blend shapes @@ -2772,8 +2796,6 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { array_copy[l] = array[l]; } - array_copy[Mesh::ARRAY_INDEX] = Variant(); - if (t.has("POSITION")) { Vector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true); const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX]; @@ -2852,17 +2874,17 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { array_copy[Mesh::ARRAY_TANGENT] = tangents_v4; } + Ref<SurfaceTool> blend_surface_tool; + blend_surface_tool.instantiate(); + blend_surface_tool->create_from_triangle_arrays(array_copy); + if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + blend_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); + } + blend_surface_tool->index(); if (generate_tangents) { - Ref<SurfaceTool> st; - st.instantiate(); - st->create_from_triangle_arrays(array_copy); - if (a.has("JOINTS_0") && a.has("JOINTS_1")) { - st->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); - } - st->deindex(); - st->generate_tangents(); - array_copy = st->commit_to_arrays(); + blend_surface_tool->generate_tangents(); } + array_copy = blend_surface_tool->commit_to_arrays(); morphs.push_back(array_copy); } @@ -2875,19 +2897,23 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { const int material = p["material"]; ERR_FAIL_INDEX_V(material, state->materials.size(), ERR_FILE_CORRUPT); Ref<BaseMaterial3D> mat3d = state->materials[material]; + ERR_FAIL_NULL_V(mat3d, ERR_FILE_CORRUPT); if (has_vertex_color) { mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); } mat = mat3d; - } else if (has_vertex_color) { + } else { Ref<StandardMaterial3D> mat3d; mat3d.instantiate(); - mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + if (has_vertex_color) { + mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + } mat = mat3d; } - - import_mesh->add_surface(primitive, array, morphs, Dictionary(), mat, mat.is_valid() ? mat->get_name() : String(), flags); + ERR_FAIL_NULL_V(mat, ERR_FILE_CORRUPT); + import_mesh->add_surface(primitive, array, morphs, + Dictionary(), mat, mat->get_name(), flags); } Vector<float> blend_weights; @@ -3445,6 +3471,9 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { } materials.push_back(d); } + if (!materials.size()) { + return OK; + } state->json["materials"] = materials; print_verbose("Total materials: " + itos(state->materials.size())); @@ -3610,7 +3639,6 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) { material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); } } - if (d.has("alphaMode")) { const String &am = d["alphaMode"]; if (am == "BLEND") { @@ -4310,6 +4338,9 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) { skeleton->add_bone(node->get_name()); skeleton->set_bone_rest(bone_index, node->xform); + skeleton->set_bone_pose_position(bone_index, node->position); + skeleton->set_bone_pose_rotation(bone_index, node->rotation.normalized()); + skeleton->set_bone_pose_scale(bone_index, node->scale); if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) { const int bone_parent = skeleton->find_bone(state->nodes[node->parent]->get_name()); @@ -4357,6 +4388,10 @@ Error GLTFDocument::_serialize_skins(Ref<GLTFState> state) { json_skin["name"] = gltf_skin->get_name(); json_skins.push_back(json_skin); } + if (!state->skins.size()) { + return OK; + } + state->json["skins"] = json_skins; return OK; } @@ -4822,6 +4857,9 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) { } } + if (!animations.size()) { + return OK; + } state->json["animations"] = animations; print_verbose("glTF: Total animations '" + itos(state->animations.size()) + "'."); @@ -5007,72 +5045,77 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInst if (p_mesh_instance->get_mesh().is_null()) { return -1; } - Ref<EditorSceneImporterMesh> import_mesh; - import_mesh.instantiate(); - Ref<Mesh> godot_mesh = p_mesh_instance->get_mesh(); - if (godot_mesh.is_null()) { - return -1; - } + Ref<ImporterMesh> current_mesh; + current_mesh.instantiate(); Vector<float> blend_weights; - Vector<String> blend_names; - int32_t blend_count = godot_mesh->get_blend_shape_count(); - blend_names.resize(blend_count); - blend_weights.resize(blend_count); - for (int32_t blend_i = 0; blend_i < godot_mesh->get_blend_shape_count(); blend_i++) { - String blend_name = godot_mesh->get_blend_shape_name(blend_i); - blend_names.write[blend_i] = blend_name; - import_mesh->add_blend_shape(blend_name); - } - for (int32_t surface_i = 0; surface_i < godot_mesh->get_surface_count(); surface_i++) { - Mesh::PrimitiveType primitive_type = godot_mesh->surface_get_primitive_type(surface_i); - Array arrays = godot_mesh->surface_get_arrays(surface_i); - Array blend_shape_arrays = godot_mesh->surface_get_blend_shape_arrays(surface_i); - Ref<Material> mat = godot_mesh->surface_get_material(surface_i); - Ref<ArrayMesh> godot_array_mesh = godot_mesh; - String surface_name; - if (godot_array_mesh.is_valid()) { - surface_name = godot_array_mesh->surface_get_name(surface_i); + { + Ref<Mesh> import_mesh = p_mesh_instance->get_mesh(); + Ref<ArrayMesh> import_array_mesh = p_mesh_instance->get_mesh(); + if (import_mesh->get_blend_shape_count()) { + ArrayMesh::BlendShapeMode shape_mode = ArrayMesh::BLEND_SHAPE_MODE_NORMALIZED; + if (import_array_mesh.is_valid()) { + shape_mode = import_array_mesh->get_blend_shape_mode(); + } + current_mesh->set_blend_shape_mode(shape_mode); + for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) { + current_mesh->add_blend_shape(import_mesh->get_blend_shape_name(morph_i)); + } + } + for (int32_t surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) { + Array array = import_mesh->surface_get_arrays(surface_i); + Ref<Material> mat = import_mesh->surface_get_material(surface_i); + String mat_name; + if (mat.is_valid()) { + mat_name = mat->get_name(); + } + current_mesh->add_surface(import_mesh->surface_get_primitive_type(surface_i), + array, import_mesh->surface_get_blend_shape_arrays(surface_i), import_mesh->surface_get_lods(surface_i), mat, + mat_name, import_mesh->surface_get_format(surface_i)); } + int32_t blend_count = import_mesh->get_blend_shape_count(); + blend_weights.resize(blend_count); + for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) { + blend_weights.write[blend_i] = 0.0f; + } + } + Ref<GLTFMesh> gltf_mesh; + gltf_mesh.instantiate(); + Array instance_materials; + for (int32_t surface_i = 0; surface_i < current_mesh->get_surface_count(); surface_i++) { + Ref<Material> mat = current_mesh->get_surface_material(surface_i); if (p_mesh_instance->get_surface_override_material(surface_i).is_valid()) { mat = p_mesh_instance->get_surface_override_material(surface_i); } if (p_mesh_instance->get_material_override().is_valid()) { mat = p_mesh_instance->get_material_override(); } - import_mesh->add_surface(primitive_type, arrays, blend_shape_arrays, Dictionary(), mat, surface_name, godot_mesh->surface_get_format(surface_i)); + instance_materials.append(mat); } - for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) { - blend_weights.write[blend_i] = 0.0f; - } - Ref<GLTFMesh> gltf_mesh; - gltf_mesh.instantiate(); - gltf_mesh->set_mesh(import_mesh); + gltf_mesh->set_instance_materials(instance_materials); + gltf_mesh->set_mesh(current_mesh); gltf_mesh->set_blend_weights(blend_weights); GLTFMeshIndex mesh_i = state->meshes.size(); state->meshes.push_back(gltf_mesh); return mesh_i; } -EditorSceneImporterMeshNode3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { +ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index) { Ref<GLTFNode> gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->mesh, state->meshes.size(), nullptr); - EditorSceneImporterMeshNode3D *mi = memnew(EditorSceneImporterMeshNode3D); + ImporterMeshInstance3D *mi = memnew(ImporterMeshInstance3D); print_verbose("glTF: Creating mesh for: " + gltf_node->get_name()); Ref<GLTFMesh> mesh = state->meshes.write[gltf_node->mesh]; if (mesh.is_null()) { return mi; } - Ref<EditorSceneImporterMesh> import_mesh = mesh->get_mesh(); + Ref<ImporterMesh> import_mesh = mesh->get_mesh(); if (import_mesh.is_null()) { return mi; } mi->set_mesh(import_mesh); - for (int i = 0; i < mesh->get_blend_weights().size(); i++) { - mi->set("blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i), mesh->get_blend_weights()[i]); - } return mi; } @@ -5241,7 +5284,7 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, co return; } else if (cast_to<MultiMeshInstance3D>(p_current)) { MultiMeshInstance3D *multi = cast_to<MultiMeshInstance3D>(p_current); - _convert_mult_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state); + _convert_multi_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state); #ifdef MODULE_CSG_ENABLED } else if (cast_to<CSGShape3D>(p_current)) { CSGShape3D *shape = cast_to<CSGShape3D>(p_current); @@ -5292,13 +5335,8 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd } Ref<GLTFMesh> gltf_mesh; gltf_mesh.instantiate(); - Ref<EditorSceneImporterMesh> import_mesh; - import_mesh.instantiate(); - Ref<ArrayMesh> array_mesh = csg->get_meshes()[1]; - for (int32_t surface_i = 0; surface_i < array_mesh->get_surface_count(); surface_i++) { - import_mesh->add_surface(Mesh::PrimitiveType::PRIMITIVE_TRIANGLES, array_mesh->surface_get_arrays(surface_i), Array(), Dictionary(), mat, array_mesh->surface_get_name(surface_i)); - } - gltf_mesh->set_mesh(import_mesh); + Ref<ImporterMesh> array_mesh = csg->get_meshes()[1]; + gltf_mesh->set_mesh(array_mesh); GLTFMeshIndex mesh_i = state->meshes.size(); state->meshes.push_back(gltf_mesh); gltf_node->mesh = mesh_i; @@ -5364,7 +5402,7 @@ void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex Vector3 cell_location = cells[k]; int32_t cell = p_grid_map->get_cell_item( Vector3(cell_location.x, cell_location.y, cell_location.z)); - EditorSceneImporterMeshNode3D *import_mesh_node = memnew(EditorSceneImporterMeshNode3D); + ImporterMeshInstance3D *import_mesh_node = memnew(ImporterMeshInstance3D); import_mesh_node->set_mesh(p_grid_map->get_mesh_library()->get_item_mesh(cell)); Transform3D cell_xform; cell_xform.basis.set_orthogonal_index( @@ -5386,50 +5424,73 @@ void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex } #endif // MODULE_GRIDMAP_ENABLED -void GLTFDocument::_convert_mult_mesh_instance_to_gltf(MultiMeshInstance3D *p_multi_mesh_instance, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) { +void GLTFDocument::_convert_multi_mesh_instance_to_gltf( + MultiMeshInstance3D *p_multi_mesh_instance, + GLTFNodeIndex p_parent_node_index, + GLTFNodeIndex p_root_node_index, + Ref<GLTFNode> gltf_node, Ref<GLTFState> state) { + ERR_FAIL_COND(!p_multi_mesh_instance); Ref<MultiMesh> multi_mesh = p_multi_mesh_instance->get_multimesh(); - if (multi_mesh.is_valid()) { - for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count(); - instance_i++) { - GLTFNode *new_gltf_node = memnew(GLTFNode); - Transform3D transform; - if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_2D) { - Transform2D xform_2d = multi_mesh->get_instance_transform_2d(instance_i); - transform.origin = - Vector3(xform_2d.get_origin().x, 0, xform_2d.get_origin().y); - real_t rotation = xform_2d.get_rotation(); - Quaternion quaternion(Vector3(0, 1, 0), rotation); - Size2 scale = xform_2d.get_scale(); - transform.basis.set_quaternion_scale(quaternion, - Vector3(scale.x, 0, scale.y)); - transform = - p_multi_mesh_instance->get_transform() * transform; - } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) { - transform = p_multi_mesh_instance->get_transform() * - multi_mesh->get_instance_transform(instance_i); - } - Ref<ArrayMesh> mm = multi_mesh->get_mesh(); - if (mm.is_valid()) { - Ref<EditorSceneImporterMesh> mesh; - mesh.instantiate(); - for (int32_t surface_i = 0; surface_i < mm->get_surface_count(); surface_i++) { - Array surface = mm->surface_get_arrays(surface_i); - mesh->add_surface(mm->surface_get_primitive_type(surface_i), surface, Array(), Dictionary(), - mm->surface_get_material(surface_i), mm->get_name()); - } - Ref<GLTFMesh> gltf_mesh; - gltf_mesh.instantiate(); - gltf_mesh->set_name(multi_mesh->get_name()); - gltf_mesh->set_mesh(mesh); - new_gltf_node->mesh = state->meshes.size(); - state->meshes.push_back(gltf_mesh); - } - new_gltf_node->xform = transform; - new_gltf_node->set_name(_gen_unique_name(state, p_multi_mesh_instance->get_name())); - gltf_node->children.push_back(state->nodes.size()); - state->nodes.push_back(new_gltf_node); + if (multi_mesh.is_null()) { + return; + } + Ref<GLTFMesh> gltf_mesh; + gltf_mesh.instantiate(); + Ref<Mesh> mesh = multi_mesh->get_mesh(); + if (mesh.is_null()) { + return; + } + gltf_mesh->set_name(multi_mesh->get_name()); + Ref<ImporterMesh> importer_mesh; + importer_mesh.instantiate(); + Ref<ArrayMesh> array_mesh = multi_mesh->get_mesh(); + if (array_mesh.is_valid()) { + importer_mesh->set_blend_shape_mode(array_mesh->get_blend_shape_mode()); + for (int32_t blend_i = 0; blend_i < array_mesh->get_blend_shape_count(); blend_i++) { + importer_mesh->add_blend_shape(array_mesh->get_blend_shape_name(blend_i)); } } + for (int32_t surface_i = 0; surface_i < mesh->get_surface_count(); surface_i++) { + Ref<Material> mat = mesh->surface_get_material(surface_i); + String material_name; + if (mat.is_valid()) { + material_name = mat->get_name(); + } + Array blend_arrays; + if (array_mesh.is_valid()) { + blend_arrays = array_mesh->surface_get_blend_shape_arrays(surface_i); + } + importer_mesh->add_surface(mesh->surface_get_primitive_type(surface_i), mesh->surface_get_arrays(surface_i), + blend_arrays, mesh->surface_get_lods(surface_i), mat, material_name, mesh->surface_get_format(surface_i)); + } + gltf_mesh->set_mesh(importer_mesh); + GLTFMeshIndex mesh_index = state->meshes.size(); + state->meshes.push_back(gltf_mesh); + for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count(); + instance_i++) { + Transform3D transform; + if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_2D) { + Transform2D xform_2d = multi_mesh->get_instance_transform_2d(instance_i); + transform.origin = + Vector3(xform_2d.get_origin().x, 0, xform_2d.get_origin().y); + real_t rotation = xform_2d.get_rotation(); + Quaternion quaternion(Vector3(0, 1, 0), rotation); + Size2 scale = xform_2d.get_scale(); + transform.basis.set_quaternion_scale(quaternion, + Vector3(scale.x, 0, scale.y)); + transform = p_multi_mesh_instance->get_transform() * transform; + } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) { + transform = p_multi_mesh_instance->get_transform() * + multi_mesh->get_instance_transform(instance_i); + } + Ref<GLTFNode> new_gltf_node; + new_gltf_node.instantiate(); + new_gltf_node->mesh = mesh_index; + new_gltf_node->xform = transform; + new_gltf_node->set_name(_gen_unique_name(state, p_multi_mesh_instance->get_name())); + gltf_node->children.push_back(state->nodes.size()); + state->nodes.push_back(new_gltf_node); + } } void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFState> state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node) { @@ -5450,7 +5511,7 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFS // Note that we cannot use _gen_unique_bone_name here, because glTF spec requires all node // names to be unique regardless of whether or not they are used as joints. joint_node->set_name(_gen_unique_name(state, skeleton->get_bone_name(bone_i))); - Transform3D xform = skeleton->get_bone_rest(bone_i) * skeleton->get_bone_pose(bone_i); + Transform3D xform = skeleton->get_bone_pose(bone_i); joint_node->scale = xform.basis.get_scale(); joint_node->rotation = xform.basis.get_rotation_quaternion(); joint_node->position = xform.origin; @@ -5544,7 +5605,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent // Bone Attachment - Parent Case BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent); - scene_parent->add_child(bone_attachment); + scene_parent->add_child(bone_attachment, true); bone_attachment->set_owner(scene_root); // There is no gltf_node that represent this, so just directly create a unique name @@ -5567,7 +5628,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent current_node = _generate_spatial(state, scene_parent, node_index); } - scene_parent->add_child(current_node); + scene_parent->add_child(current_node, true); if (current_node != scene_root) { current_node->set_owner(scene_root); } @@ -5597,7 +5658,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen // Bone Attachment - Direct Parented Skeleton Case BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent); - scene_parent->add_child(bone_attachment); + scene_parent->add_child(bone_attachment, true); bone_attachment->set_owner(scene_root); // There is no gltf_node that represent this, so just directly create a unique name @@ -5611,7 +5672,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen // Add it to the scene if it has not already been added if (skeleton->get_parent() == nullptr) { - scene_parent->add_child(skeleton); + scene_parent->add_child(skeleton, true); skeleton->set_owner(scene_root); } } @@ -5625,7 +5686,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen // Bone Attachment - Same Node Case BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, node_index); - scene_parent->add_child(bone_attachment); + scene_parent->add_child(bone_attachment, true); bone_attachment->set_owner(scene_root); // There is no gltf_node that represent this, so just directly create a unique name @@ -5645,7 +5706,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen current_node = _generate_light(state, scene_parent, node_index); } - scene_parent->add_child(current_node); + scene_parent->add_child(current_node, true); if (current_node != scene_root) { current_node->set_owner(scene_root); } @@ -5661,7 +5722,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen } template <class T> -struct EditorSceneImporterGLTFInterpolate { +struct EditorSceneFormatImporterGLTFInterpolate { T lerp(const T &a, const T &b, float c) const { return a + (b - a) * c; } @@ -5687,7 +5748,7 @@ struct EditorSceneImporterGLTFInterpolate { // thank you for existing, partial specialization template <> -struct EditorSceneImporterGLTFInterpolate<Quaternion> { +struct EditorSceneFormatImporterGLTFInterpolate<Quaternion> { Quaternion lerp(const Quaternion &a, const Quaternion &b, const float c) const { ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quaternion(), "The quaternion \"a\" must be normalized."); ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quaternion(), "The quaternion \"b\" must be normalized."); @@ -5713,7 +5774,7 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> { template <class T> T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { ERR_FAIL_COND_V(!p_values.size(), T()); - if (p_times.size() != p_values.size()) { + if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) { ERR_PRINT_ONCE("The interpolated values are not corresponding to its times."); return p_values[0]; } @@ -5726,7 +5787,7 @@ T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> idx++; } - EditorSceneImporterGLTFInterpolate<T> interp; + EditorSceneFormatImporterGLTFInterpolate<T> interp; switch (p_interp) { case GLTFAnimation::INTERP_LINEAR: { @@ -5848,9 +5909,67 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, const bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0; if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) { //make transform track - int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_TRANSFORM3D); - animation->track_set_path(track_idx, transform_node_path); + int base_idx = animation->get_track_count(); + int position_idx = -1; + int rotation_idx = -1; + int scale_idx = -1; + + if (track.position_track.values.size()) { + Vector3 base_pos = state->nodes[track_i.key]->position; + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.position_track.times.size(); i++) { + Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; + if (!value.is_equal_approx(base_pos)) { + not_default = true; + break; + } + } + if (not_default) { + position_idx = base_idx; + animation->add_track(Animation::TYPE_POSITION_3D); + animation->track_set_path(position_idx, transform_node_path); + animation->track_set_imported(position_idx, true); //helps merging later + + base_idx++; + } + } + if (track.rotation_track.values.size()) { + Quaternion base_rot = state->nodes[track_i.key]->rotation.normalized(); + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.rotation_track.times.size(); i++) { + Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized(); + if (!value.is_equal_approx(base_rot)) { + not_default = true; + break; + } + } + if (not_default) { + rotation_idx = base_idx; + animation->add_track(Animation::TYPE_ROTATION_3D); + animation->track_set_path(rotation_idx, transform_node_path); + animation->track_set_imported(rotation_idx, true); //helps merging later + base_idx++; + } + } + if (track.scale_track.values.size()) { + Vector3 base_scale = state->nodes[track_i.key]->scale; + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.scale_track.times.size(); i++) { + Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; + if (!value.is_equal_approx(base_scale)) { + not_default = true; + break; + } + } + if (not_default) { + scale_idx = base_idx; + animation->add_track(Animation::TYPE_SCALE_3D); + animation->track_set_path(scale_idx, transform_node_path); + animation->track_set_imported(scale_idx, true); //helps merging later + base_idx++; + } + } + //first determine animation length const double increment = 1.0 / bake_fps; @@ -5860,15 +5979,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, Quaternion base_rot; Vector3 base_scale = Vector3(1, 1, 1); - if (!track.rotation_track.values.size()) { + if (rotation_idx == -1) { base_rot = state->nodes[track_i.key]->rotation.normalized(); } - if (!track.position_track.values.size()) { + if (position_idx == -1) { base_pos = state->nodes[track_i.key]->position; } - if (!track.scale_track.values.size()) { + if (scale_idx == -1) { base_scale = state->nodes[track_i.key]->scale; } @@ -5878,35 +5997,21 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, Quaternion rot = base_rot; Vector3 scale = base_scale; - if (track.position_track.times.size()) { + if (position_idx >= 0) { pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation); + animation->position_track_insert_key(position_idx, time, pos); } - if (track.rotation_track.times.size()) { + if (rotation_idx >= 0) { rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation); + animation->rotation_track_insert_key(rotation_idx, time, rot); } - if (track.scale_track.times.size()) { + if (scale_idx >= 0) { scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation); + animation->scale_track_insert_key(scale_idx, time, scale); } - if (gltf_node->skeleton >= 0) { - Transform3D xform; - xform.basis.set_quaternion_scale(rot, scale); - xform.origin = pos; - - const Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton; - const int bone_idx = skeleton->find_bone(gltf_node->get_name()); - xform = skeleton->get_bone_rest(bone_idx).affine_inverse() * xform; - - rot = xform.basis.get_rotation_quaternion(); - rot.normalize(); - scale = xform.basis.get_scale(); - pos = xform.origin; - } - - animation->transform_track_insert_key(track_idx, time, pos, rot, scale); - if (last) { break; } @@ -5924,12 +6029,11 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, ERR_CONTINUE(mesh.is_null()); ERR_CONTINUE(mesh->get_mesh().is_null()); ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null()); - const String prop = "blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i); - const String blend_path = String(node_path) + ":" + prop; + const String blend_path = String(node_path) + ":" + String(mesh->get_mesh()->get_blend_shape_name(i)); const int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_VALUE); + animation->add_track(Animation::TYPE_BLEND_SHAPE); animation->track_set_path(track_idx, blend_path); // Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation, @@ -5940,7 +6044,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { const float t = track.weight_tracks[i].times[j]; const float attribs = track.weight_tracks[i].values[j]; - animation->track_insert_key(track_idx, t, attribs); + animation->blend_shape_track_insert_key(track_idx, t, attribs); } } else { // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies. @@ -5948,7 +6052,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, double time = 0.0; bool last = false; while (true) { - _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp); + float blend = _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp); + animation->blend_shape_track_insert_key(track_idx, time, blend); if (last) { break; } @@ -6022,7 +6127,7 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) { } else { if (skin.is_null()) { // Note that gltf_skin_key should remain null, so these can share a reference. - skin = skeleton->register_skin(nullptr)->get_skin(); + skin = skeleton->create_skin_from_rest_transforms(); } gltf_skin.instantiate(); gltf_skin->godot_skin = skin; @@ -6106,8 +6211,8 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(node_i); ERR_CONTINUE_MSG(mi_element == nullptr, vformat("Unable to find node %d", node_i)); - EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(mi_element->get()); - ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to EditorSceneImporterMeshNode3D", node_i, mi_element->get()->get_class_name())); + ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(mi_element->get()); + ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->get()->get_class_name())); const GLTFSkeletonIndex skel_i = state->skins.write[node->skin]->skeleton; Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i]; @@ -6115,7 +6220,7 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo ERR_CONTINUE_MSG(skeleton == nullptr, vformat("Unable to find Skeleton for node %d skin %d", node_i, skin_i)); mi->get_parent()->remove_child(mi); - skeleton->add_child(mi); + skeleton->add_child(mi, true); mi->set_owner(skeleton->get_owner()); mi->set_skin(state->skins.write[skin_i]->godot_skin); @@ -6125,7 +6230,7 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo } } -GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, Transform3D p_bone_rest, int32_t p_track_i, GLTFNodeIndex p_node_i) { +GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, int32_t p_track_i, GLTFNodeIndex p_node_i) { Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i); GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR; @@ -6144,33 +6249,35 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state for (int32_t key_i = 0; key_i < key_count; key_i++) { times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i); } - if (track_type == Animation::TYPE_TRANSFORM3D) { - p_track.position_track.times = times; - p_track.position_track.interpolation = gltf_interpolation; - p_track.rotation_track.times = times; - p_track.rotation_track.interpolation = gltf_interpolation; + if (track_type == Animation::TYPE_SCALE_3D) { p_track.scale_track.times = times; p_track.scale_track.interpolation = gltf_interpolation; - p_track.scale_track.values.resize(key_count); - p_track.scale_track.interpolation = gltf_interpolation; + for (int32_t key_i = 0; key_i < key_count; key_i++) { + Vector3 scale; + Error err = p_animation->scale_track_get_key(p_track_i, key_i, &scale); + ERR_CONTINUE(err != OK); + p_track.scale_track.values.write[key_i] = scale; + } + } else if (track_type == Animation::TYPE_POSITION_3D) { + p_track.position_track.times = times; p_track.position_track.values.resize(key_count); p_track.position_track.interpolation = gltf_interpolation; - p_track.rotation_track.values.resize(key_count); - p_track.rotation_track.interpolation = gltf_interpolation; for (int32_t key_i = 0; key_i < key_count; key_i++) { Vector3 position; + Error err = p_animation->position_track_get_key(p_track_i, key_i, &position); + ERR_CONTINUE(err != OK); + p_track.position_track.values.write[key_i] = position; + } + } else if (track_type == Animation::TYPE_ROTATION_3D) { + p_track.rotation_track.times = times; + p_track.rotation_track.interpolation = gltf_interpolation; + p_track.rotation_track.values.resize(key_count); + for (int32_t key_i = 0; key_i < key_count; key_i++) { Quaternion rotation; - Vector3 scale; - Error err = p_animation->transform_track_get_key(p_track_i, key_i, &position, &rotation, &scale); + Error err = p_animation->rotation_track_get_key(p_track_i, key_i, &rotation); ERR_CONTINUE(err != OK); - Transform3D xform; - xform.basis.set_quaternion_scale(rotation, scale); - xform.origin = position; - xform = p_bone_rest * xform; - p_track.position_track.values.write[key_i] = xform.get_origin(); - p_track.rotation_track.values.write[key_i] = xform.basis.get_rotation_quaternion(); - p_track.scale_track.values.write[key_i] = xform.basis.get_scale(); + p_track.rotation_track.values.write[key_i] = rotation; } } else if (path.find(":transform") != -1) { p_track.position_track.times = times; @@ -6262,13 +6369,10 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state Vector3 bezier_track = p_track.scale_track.values[key_i]; if (path.find("/scale:x") != -1) { bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); - bezier_track.x = p_bone_rest.affine_inverse().basis.get_scale().x * bezier_track.x; } else if (path.find("/scale:y") != -1) { bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); - bezier_track.y = p_bone_rest.affine_inverse().basis.get_scale().y * bezier_track.y; } else if (path.find("/scale:z") != -1) { bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); - bezier_track.z = p_bone_rest.affine_inverse().basis.get_scale().z * bezier_track.z; } p_track.scale_track.values.write[key_i] = bezier_track; } @@ -6291,19 +6395,15 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state Vector3 bezier_track = p_track.position_track.values[key_i]; if (path.find("/position:x") != -1) { bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); - bezier_track.x = p_bone_rest.affine_inverse().origin.x * bezier_track.x; } else if (path.find("/position:y") != -1) { bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); - bezier_track.y = p_bone_rest.affine_inverse().origin.y * bezier_track.y; } else if (path.find("/position:z") != -1) { bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS); - bezier_track.z = p_bone_rest.affine_inverse().origin.z * bezier_track.z; } p_track.position_track.values.write[key_i] = bezier_track; } } } - return p_track; } @@ -6330,7 +6430,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, if (position_track_i) { track = position_track_i->get(); } - track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index); + track = _convert_animation_track(state, track, animation, track_i, node_index); gltf_animation->get_tracks().insert(node_index, track); } } @@ -6346,7 +6446,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, if (rotation_degree_track_i) { track = rotation_degree_track_i->get(); } - track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index); + track = _convert_animation_track(state, track, animation, track_i, node_index); gltf_animation->get_tracks().insert(node_index, track); } } @@ -6362,7 +6462,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, if (scale_track_i) { track = scale_track_i->get(); } - track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index); + track = _convert_animation_track(state, track, animation, track_i, node_index); gltf_animation->get_tracks().insert(node_index, track); } } @@ -6373,7 +6473,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, for (const KeyValue<GLTFNodeIndex, Node *> &transform_track_i : state->scene_nodes) { if (transform_track_i.value == node) { GLTFAnimation::Track track; - track = _convert_animation_track(state, track, animation, Transform3D(), track_i, transform_track_i.key); + track = _convert_animation_track(state, track, animation, track_i, transform_track_i.key); gltf_animation->get_tracks().insert(transform_track_i.key, track); } } @@ -6397,8 +6497,8 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, if (!tracks.has(mesh_index)) { for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) { String shape_name = mesh->get_blend_shape_name(shape_i); - NodePath shape_path = String(path) + ":blend_shapes/" + shape_name; - int32_t shape_track_i = animation->find_track(shape_path); + NodePath shape_path = String(path) + ":" + shape_name; + int32_t shape_track_i = animation->find_track(shape_path, Animation::TYPE_BLEND_SHAPE); if (shape_track_i == -1) { GLTFAnimation::Channel<float> weight; weight.interpolation = GLTFAnimation::INTERP_LINEAR; @@ -6450,7 +6550,6 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, Ref<GLTFSkeleton> skeleton_gltf = state->skeletons[skeleton_gltf_i]; int32_t bone = skeleton->find_bone(suffix); ERR_CONTINUE(bone == -1); - Transform3D xform = skeleton->get_bone_rest(bone); if (!skeleton_gltf->godot_bone_node.has(bone)) { continue; } @@ -6460,27 +6559,24 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, if (property_track_i) { track = property_track_i->get(); } - track = _convert_animation_track(state, track, animation, xform, track_i, node_i); + track = _convert_animation_track(state, track, animation, track_i, node_i); gltf_animation->get_tracks()[node_i] = track; } } } else if (String(orig_track_path).find(":") == -1) { ERR_CONTINUE(!ap->get_parent()); - for (int32_t node_i = 0; node_i < ap->get_parent()->get_child_count(); node_i++) { - const Node *child = ap->get_parent()->get_child(node_i); - const Node *node = child->get_node_or_null(orig_track_path); - for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : state->scene_nodes) { - if (scene_node_i.value == node) { - GLTFNodeIndex node_index = scene_node_i.key; - Map<int, GLTFAnimation::Track>::Element *node_track_i = gltf_animation->get_tracks().find(node_index); - GLTFAnimation::Track track; - if (node_track_i) { - track = node_track_i->get(); - } - track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index); - gltf_animation->get_tracks().insert(node_index, track); - break; + Node *godot_node = ap->get_parent()->get_node_or_null(orig_track_path); + for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : state->scene_nodes) { + if (scene_node_i.value == godot_node) { + GLTFNodeIndex node_i = scene_node_i.key; + Map<int, GLTFAnimation::Track>::Element *node_track_i = gltf_animation->get_tracks().find(node_i); + GLTFAnimation::Track track; + if (node_track_i) { + track = node_track_i->get(); } + track = _convert_animation_track(state, track, animation, track_i, node_i); + gltf_animation->get_tracks()[node_i] = track; + break; } } } @@ -6698,33 +6794,36 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) { const uint32_t magic = 0x46546C67; // GLTF const int32_t header_size = 12; const int32_t chunk_header_size = 8; - - for (int32_t pad_i = 0; pad_i < (chunk_header_size + json.utf8().length()) % 4; pad_i++) { - json += " "; - } CharString cs = json.utf8(); - const uint32_t text_chunk_length = cs.length(); - + const uint32_t text_data_length = cs.length(); + const uint32_t text_chunk_length = ((text_data_length + 3) & (~3)); const uint32_t text_chunk_type = 0x4E4F534A; //JSON - int32_t binary_data_length = 0; + + uint32_t binary_data_length = 0; if (state->buffers.size()) { binary_data_length = state->buffers[0].size(); } - const int32_t binary_chunk_length = binary_data_length; - const int32_t binary_chunk_type = 0x004E4942; //BIN + const uint32_t binary_chunk_length = ((binary_data_length + 3) & (~3)); + const uint32_t binary_chunk_type = 0x004E4942; //BIN f->create(FileAccess::ACCESS_RESOURCES); f->store_32(magic); f->store_32(state->major_version); // version - f->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_data_length); // length + f->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_chunk_length); // length f->store_32(text_chunk_length); f->store_32(text_chunk_type); f->store_buffer((uint8_t *)&cs[0], cs.length()); + for (uint32_t pad_i = text_data_length; pad_i < text_chunk_length; pad_i++) { + f->store_8(' '); + } if (binary_chunk_length) { f->store_32(binary_chunk_length); f->store_32(binary_chunk_type); f->store_buffer(state->buffers[0].ptr(), binary_data_length); } + for (uint32_t pad_i = binary_data_length; pad_i < binary_chunk_length; pad_i++) { + f->store_8(0); + } f->close(); } else { @@ -6748,10 +6847,25 @@ Error GLTFDocument::save_scene(Node *p_node, const String &p_path, Ref<GLTFDocument> gltf_document; gltf_document.instantiate(); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + Error err = ext->export_preflight(this, p_node); + ERR_FAIL_COND_V(err != OK, err); + } + if (r_state == Ref<GLTFState>()) { r_state.instantiate(); } - return gltf_document->serialize(r_state, p_node, p_path); + Error err = gltf_document->serialize(r_state, p_node, p_path); + ERR_FAIL_COND_V(err != OK, err); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->export_post(this); + ERR_FAIL_COND_V(err != OK, err); + } + return OK; } Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state, List<String> *r_missing_deps, Error *r_err) { @@ -6760,10 +6874,19 @@ Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, in r_state.instantiate(); } r_state->use_named_skin_binds = - p_flags & EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS; + p_flags & EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; Ref<GLTFDocument> gltf_document; gltf_document.instantiate(); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + Error err = ext->import_preflight(this); + if (r_err) { + *r_err = err; + } + ERR_FAIL_COND_V(err != OK, nullptr); + } Error err = gltf_document->parse(r_state, p_path); if (r_err) { *r_err = err; @@ -6777,13 +6900,21 @@ Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, in gltf_document->_process_mesh_instances(r_state, root); if (r_state->animations.size()) { AnimationPlayer *ap = memnew(AnimationPlayer); - root->add_child(ap); + root->add_child(ap, true); ap->set_owner(root); for (int i = 0; i < r_state->animations.size(); i++) { gltf_document->_import_animation(r_state, ap, i, p_bake_fps); } } - + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_post(this, root); + if (r_err) { + *r_err = err; + } + ERR_FAIL_COND_V(err != OK, nullptr); + } return root; } @@ -6792,6 +6923,14 @@ void GLTFDocument::_bind_methods() { &GLTFDocument::save_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>())); ClassDB::bind_method(D_METHOD("import_scene", "path", "flags", "bake_fps", "state"), &GLTFDocument::import_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>())); + ClassDB::bind_method(D_METHOD("set_extensions", "extensions"), + &GLTFDocument::set_extensions); + ClassDB::bind_method(D_METHOD("get_extensions"), + &GLTFDocument::get_extensions); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "extensions", PROPERTY_HINT_ARRAY_TYPE, + vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "GLTFDocumentExtension"), + PROPERTY_USAGE_DEFAULT), + "set_extensions", "get_extensions"); } void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> state) { @@ -6817,3 +6956,21 @@ Node *GLTFDocument::import_scene(const String &p_path, uint32_t p_flags, int32_t } return node; } + +void GLTFDocument::set_extensions(TypedArray<GLTFDocumentExtension> p_extensions) { + document_extensions = p_extensions; +} + +TypedArray<GLTFDocumentExtension> GLTFDocument::get_extensions() const { + return document_extensions; +} + +GLTFDocument::GLTFDocument() { + bool is_editor = ::Engine::get_singleton()->is_editor_hint(); + if (is_editor) { + return; + } + Ref<GLTFDocumentExtensionConvertImporterMesh> extension_editor; + extension_editor.instantiate(); + document_extensions.push_back(extension_editor); +} diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 18aeb81bc0..f2f0b439a5 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -33,8 +33,11 @@ #include "gltf_animation.h" -#include "editor/import/scene_importer_mesh_node_3d.h" +#include "core/variant/dictionary.h" +#include "core/variant/variant.h" +#include "gltf_document_extension_convert_importer_mesh.h" #include "scene/3d/bone_attachment_3d.h" +#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/node_3d.h" @@ -54,6 +57,7 @@ class GLTFSkeleton; class CSGShape3D; class GridMap; class MultiMeshInstance3D; +class GLTFDocumentExtension; using GLTFAccessorIndex = int; using GLTFAnimationIndex = int; @@ -74,11 +78,13 @@ class GLTFDocument : public Resource { friend class GLTFState; friend class GLTFSkin; friend class GLTFSkeleton; + TypedArray<GLTFDocumentExtension> document_extensions; private: const float BAKE_FPS = 30.0f; public: + GLTFDocument(); const int32_t JOINT_GROUP_SIZE = 4; enum GLTFType { TYPE_SCALAR, @@ -118,6 +124,8 @@ public: Error save_scene(Node *p_node, const String &p_path, const String &p_src_path, uint32_t p_flags, float p_bake_fps, Ref<GLTFState> r_state); + void set_extensions(TypedArray<GLTFDocumentExtension> p_extensions); + TypedArray<GLTFDocumentExtension> get_extensions() const; private: template <class T> @@ -261,8 +269,6 @@ private: Error _reparent_non_joint_skeleton_subtrees( Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, const Vector<GLTFNodeIndex> &non_joints); - Error _reparent_to_fake_joint(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, - const GLTFNodeIndex node_index); Error _determine_skeleton_roots(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i); Error _create_skeletons(Ref<GLTFState> state); @@ -280,12 +286,10 @@ private: Skeleton3D *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index); - EditorSceneImporterMeshNode3D *_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index); - Camera3D *_generate_camera(Ref<GLTFState> state, Node *scene_parent, - const GLTFNodeIndex node_index); - Node3D *_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index); - Node3D *_generate_spatial(Ref<GLTFState> state, Node *scene_parent, - const GLTFNodeIndex node_index); + ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); + Camera3D *_generate_camera(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); + Node3D *_generate_light(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); + Node3D *_generate_spatial(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); void _assign_scene_names(Ref<GLTFState> state); template <class T> T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, @@ -351,7 +355,7 @@ private: String interpolation_to_string(const GLTFAnimation::Interpolation p_interp); GLTFAnimation::Track _convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, - Ref<Animation> p_animation, Transform3D p_bone_rest, + Ref<Animation> p_animation, int32_t p_track_i, GLTFNodeIndex p_node_i); Error _encode_buffer_bins(Ref<GLTFState> state, const String &p_path); @@ -420,8 +424,8 @@ public: GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state); #endif // MODULE_GRIDMAP_ENABLED - void _convert_mult_mesh_instance_to_gltf( - MultiMeshInstance3D *p_scene_parent, + void _convert_multi_mesh_instance_to_gltf( + MultiMeshInstance3D *p_multi_mesh_instance, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state); diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/gltf_document_extension.cpp new file mode 100644 index 0000000000..a423059a9c --- /dev/null +++ b/modules/gltf/gltf_document_extension.cpp @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* gltf_document_extension.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 "gltf_document_extension.h" + +#include "gltf_document.h" + +void GLTFDocumentExtension::_bind_methods() { + // Import + ClassDB::bind_method(D_METHOD("get_import_setting_keys"), + &GLTFDocumentExtension::get_import_setting_keys); + ClassDB::bind_method(D_METHOD("import_preflight", "document"), + &GLTFDocumentExtension::import_preflight); + ClassDB::bind_method(D_METHOD("get_import_setting", "key"), + &GLTFDocumentExtension::get_import_setting); + ClassDB::bind_method(D_METHOD("set_import_setting", "key", "value"), + &GLTFDocumentExtension::set_import_setting); + ClassDB::bind_method(D_METHOD("import_post", "document", "node"), + &GLTFDocumentExtension::import_post); + // Export + ClassDB::bind_method(D_METHOD("get_export_setting_keys"), + &GLTFDocumentExtension::get_export_setting_keys); + ClassDB::bind_method(D_METHOD("get_export_setting", "key"), + &GLTFDocumentExtension::get_export_setting); + ClassDB::bind_method(D_METHOD("set_export_setting", "key", "value"), + &GLTFDocumentExtension::set_export_setting); + ClassDB::bind_method(D_METHOD("export_preflight", "document", "node"), + &GLTFDocumentExtension::export_preflight); + ClassDB::bind_method(D_METHOD("export_post", "document"), + &GLTFDocumentExtension::export_post); +} + +Array GLTFDocumentExtension::get_import_setting_keys() const { + return import_settings.keys(); +} + +Variant GLTFDocumentExtension::get_import_setting(const StringName &p_key) const { + if (!import_settings.has(p_key)) { + return Variant(); + } + return import_settings[p_key]; +} + +void GLTFDocumentExtension::set_import_setting(const StringName &p_key, Variant p_var) { + import_settings[p_key] = p_var; +} + +Array GLTFDocumentExtension::get_export_setting_keys() const { + return import_settings.keys(); +} + +Variant GLTFDocumentExtension::get_export_setting(const StringName &p_key) const { + if (!import_settings.has(p_key)) { + return Variant(); + } + return import_settings[p_key]; +} + +void GLTFDocumentExtension::set_export_setting(const StringName &p_key, Variant p_var) { + import_settings[p_key] = p_var; +} diff --git a/modules/opus/register_types.cpp b/modules/gltf/gltf_document_extension.h index 02874a9a4b..622a65708c 100644 --- a/modules/opus/register_types.cpp +++ b/modules/gltf/gltf_document_extension.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* register_types.cpp */ +/* gltf_document_extension.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,10 +28,36 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "register_types.h" +#ifndef GLTF_DOCUMENT_EXTENSION_H +#define GLTF_DOCUMENT_EXTENSION_H -// Dummy module as libvorbis is needed by other modules (theora ...) +#include "core/io/resource.h" +#include "core/variant/dictionary.h" +#include "core/variant/typed_array.h" +#include "core/variant/variant.h" +class GLTFDocument; +class GLTFDocumentExtension : public Resource { + GDCLASS(GLTFDocumentExtension, Resource); -void register_opus_types() {} + Dictionary import_settings; + Dictionary export_settings; -void unregister_opus_types() {} +protected: + static void _bind_methods(); + +public: + virtual Array get_import_setting_keys() const; + virtual Variant get_import_setting(const StringName &p_key) const; + virtual void set_import_setting(const StringName &p_key, Variant p_var); + virtual Error import_preflight(Ref<GLTFDocument> p_document) { return OK; } + virtual Error import_post(Ref<GLTFDocument> p_document, Node *p_node) { return OK; } + +public: + virtual Array get_export_setting_keys() const; + virtual Variant get_export_setting(const StringName &p_key) const; + virtual void set_export_setting(const StringName &p_key, Variant p_var); + virtual Error export_preflight(Ref<GLTFDocument> p_document, Node *p_node) { return OK; } + virtual Error export_post(Ref<GLTFDocument> p_document) { return OK; } +}; + +#endif // GLTF_DOCUMENT_EXTENSION_H diff --git a/modules/webm/register_types.h b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp index d090fe745b..56c8f5ca27 100644 --- a/modules/webm/register_types.h +++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* register_types.h */ +/* gltf_document_extension_convert_importer_mesh.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,10 +28,55 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef WEBM_REGISTER_TYPES_H -#define WEBM_REGISTER_TYPES_H +#include "gltf_document_extension_convert_importer_mesh.h" +#include "core/error/error_macros.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/resources/importer_mesh.h" -void register_webm_types(); -void unregister_webm_types(); +#include <cstddef> -#endif // WEBM_REGISTER_TYPES_H +void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() { +} + +Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFDocument> p_document, Node *p_node) { + ERR_FAIL_NULL_V(p_document, ERR_INVALID_PARAMETER); + ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); + List<Node *> queue; + queue.push_back(p_node); + List<Node *> delete_queue; + while (!queue.is_empty()) { + List<Node *>::Element *E = queue.front(); + Node *node = E->get(); + { + ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node); + if (mesh_3d) { + MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D); + Ref<ImporterMesh> mesh = mesh_3d->get_mesh(); + if (mesh.is_valid()) { + Ref<ArrayMesh> array_mesh = mesh->get_mesh(); + mesh_instance_node_3d->set_name(node->get_name()); + mesh_instance_node_3d->set_transform(mesh_3d->get_transform()); + mesh_instance_node_3d->set_mesh(array_mesh); + mesh_instance_node_3d->set_skin(mesh_3d->get_skin()); + mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path()); + node->replace_by(mesh_instance_node_3d); + delete_queue.push_back(node); + } else { + memdelete(mesh_instance_node_3d); + } + } + } + int child_count = node->get_child_count(); + for (int i = 0; i < child_count; i++) { + queue.push_back(node->get_child(i)); + } + queue.pop_front(); + } + while (!queue.is_empty()) { + List<Node *>::Element *E = delete_queue.front(); + Node *node = E->get(); + memdelete(node); + delete_queue.pop_front(); + } + return OK; +} diff --git a/modules/webm/register_types.cpp b/modules/gltf/gltf_document_extension_convert_importer_mesh.h index 8f690a6892..85ddb4d250 100644 --- a/modules/webm/register_types.cpp +++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* register_types.cpp */ +/* gltf_document_extension_convert_importer_mesh.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,20 +28,28 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "register_types.h" +#ifndef GLTF_EXTENSION_EDITOR_H +#define GLTF_EXTENSION_EDITOR_H -#include "video_stream_webm.h" +#include "core/io/resource.h" +#include "core/variant/dictionary.h" -static Ref<ResourceFormatLoaderWebm> resource_loader_webm; +#include "gltf_document.h" +#include "gltf_document_extension.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/main/node.h" +#include "scene/resources/importer_mesh.h" -void register_webm_types() { - resource_loader_webm.instantiate(); - ResourceLoader::add_resource_format_loader(resource_loader_webm, true); +class GLTFDocumentExtension; +class GLTFDocument; +class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension { + GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension); - GDREGISTER_CLASS(VideoStreamWebm); -} +protected: + static void _bind_methods(); -void unregister_webm_types() { - ResourceLoader::remove_resource_format_loader(resource_loader_webm); - resource_loader_webm.unref(); -} +public: + Error import_post(Ref<GLTFDocument> p_document, Node *p_node) override; +}; +#endif // GLTF_EXTENSION_EDITOR_H diff --git a/modules/gltf/gltf_mesh.cpp b/modules/gltf/gltf_mesh.cpp index 8c10e42c89..7134345b30 100644 --- a/modules/gltf/gltf_mesh.cpp +++ b/modules/gltf/gltf_mesh.cpp @@ -29,26 +29,37 @@ /*************************************************************************/ #include "gltf_mesh.h" -#include "editor/import/scene_importer_mesh.h" +#include "scene/resources/importer_mesh.h" void GLTFMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh"), &GLTFMesh::get_mesh); ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &GLTFMesh::set_mesh); ClassDB::bind_method(D_METHOD("get_blend_weights"), &GLTFMesh::get_blend_weights); ClassDB::bind_method(D_METHOD("set_blend_weights", "blend_weights"), &GLTFMesh::set_blend_weights); + ClassDB::bind_method(D_METHOD("get_instance_materials"), &GLTFMesh::get_instance_materials); + ClassDB::bind_method(D_METHOD("set_instance_materials", "instance_materials"), &GLTFMesh::set_instance_materials); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "blend_weights"), "set_blend_weights", "get_blend_weights"); // Vector<float> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "instance_materials"), "set_instance_materials", "get_instance_materials"); } -Ref<EditorSceneImporterMesh> GLTFMesh::get_mesh() { +Ref<ImporterMesh> GLTFMesh::get_mesh() { return mesh; } -void GLTFMesh::set_mesh(Ref<EditorSceneImporterMesh> p_mesh) { +void GLTFMesh::set_mesh(Ref<ImporterMesh> p_mesh) { mesh = p_mesh; } +Array GLTFMesh::get_instance_materials() { + return instance_materials; +} + +void GLTFMesh::set_instance_materials(Array p_instance_materials) { + instance_materials = p_instance_materials; +} + Vector<float> GLTFMesh::get_blend_weights() { return blend_weights; } diff --git a/modules/gltf/gltf_mesh.h b/modules/gltf/gltf_mesh.h index 0fc750fc9f..cc2be93c09 100644 --- a/modules/gltf/gltf_mesh.h +++ b/modules/gltf/gltf_mesh.h @@ -33,23 +33,27 @@ #include "core/io/resource.h" #include "editor/import/resource_importer_scene.h" -#include "editor/import/scene_importer_mesh.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/resources/importer_mesh.h" #include "scene/resources/mesh.h" class GLTFMesh : public Resource { GDCLASS(GLTFMesh, Resource); private: - Ref<EditorSceneImporterMesh> mesh; + Ref<ImporterMesh> mesh; Vector<float> blend_weights; + Array instance_materials; protected: static void _bind_methods(); public: - Ref<EditorSceneImporterMesh> get_mesh(); - void set_mesh(Ref<EditorSceneImporterMesh> p_mesh); + Ref<ImporterMesh> get_mesh(); + void set_mesh(Ref<ImporterMesh> p_mesh); Vector<float> get_blend_weights(); void set_blend_weights(Vector<float> p_blend_weights); + Array get_instance_materials(); + void set_instance_materials(Array p_instance_materials); }; #endif // GLTF_MESH_H diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index d6614da804..61faba0dc5 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -36,6 +36,7 @@ #include "gltf_buffer_view.h" #include "gltf_camera.h" #include "gltf_document.h" +#include "gltf_document_extension.h" #include "gltf_light.h" #include "gltf_mesh.h" #include "gltf_node.h" diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index d6020f50f0..5a60c2d328 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -38,6 +38,8 @@ #include "gltf_buffer_view.h" #include "gltf_camera.h" #include "gltf_document.h" +#include "gltf_document_extension.h" +#include "gltf_document_extension_convert_importer_mesh.h" #include "gltf_light.h" #include "gltf_mesh.h" #include "gltf_node.h" @@ -50,7 +52,7 @@ #ifndef _3D_DISABLED #ifdef TOOLS_ENABLED static void _editor_init() { - Ref<EditorSceneImporterGLTF> import_gltf; + Ref<EditorSceneFormatImporterGLTF> import_gltf; import_gltf.instantiate(); ResourceImporterScene::get_singleton()->add_importer(import_gltf); } @@ -62,7 +64,7 @@ void register_gltf_types() { #ifdef TOOLS_ENABLED ClassDB::APIType prev_api = ClassDB::get_current_api(); ClassDB::set_current_api(ClassDB::API_EDITOR); - GDREGISTER_CLASS(EditorSceneImporterGLTF); + GDREGISTER_CLASS(EditorSceneFormatImporterGLTF); GDREGISTER_CLASS(GLTFMesh); EditorPlugins::add_by_type<SceneExporterGLTFPlugin>(); ClassDB::set_current_api(prev_api); @@ -79,6 +81,8 @@ void register_gltf_types() { GDREGISTER_CLASS(GLTFCamera); GDREGISTER_CLASS(GLTFLight); GDREGISTER_CLASS(GLTFState); + GDREGISTER_CLASS(GLTFDocumentExtensionConvertImporterMesh); + GDREGISTER_CLASS(GLTFDocumentExtension); GDREGISTER_CLASS(GLTFDocument); #endif } diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index c170bb107e..08d55de976 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -603,9 +603,9 @@ void GridMapEditor::_do_paste() { _clear_clipboard_data(); } -bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) { +EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) { if (!node) { - return false; + return EditorPlugin::AFTER_GUI_INPUT_PASS; } Ref<InputEventMouseButton> mb = p_event; @@ -616,12 +616,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In floor->set_value(floor->get_value() + mb->get_factor()); } - return true; // Eaten. + return EditorPlugin::AFTER_GUI_INPUT_STOP; // Eaten. } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && (mb->is_command_pressed() || mb->is_shift_pressed())) { if (mb->is_pressed()) { floor->set_value(floor->get_value() - mb->get_factor()); } - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } if (mb->is_pressed()) { @@ -648,19 +648,22 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In _clear_clipboard_data(); input_action = INPUT_NONE; _update_paste_indicator(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else if (selection.active) { _set_selection(false); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else { input_action = INPUT_ERASE; set_items.clear(); } } else { - return false; + return EditorPlugin::AFTER_GUI_INPUT_PASS; } - return do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true); + if (do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true)) { + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + return EditorPlugin::AFTER_GUI_INPUT_PASS; } else { if ((mb->get_button_index() == MOUSE_BUTTON_RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_PAINT)) { if (set_items.size()) { @@ -677,7 +680,11 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In } set_items.clear(); input_action = INPUT_NONE; - return set_items.size() > 0; + + if (set_items.size() > 0) { + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + return EditorPlugin::AFTER_GUI_INPUT_PASS; } if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_SELECT) { @@ -690,11 +697,11 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action != INPUT_NONE) { set_items.clear(); input_action = INPUT_NONE; - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_PASTE)) { input_action = INPUT_NONE; - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } } } @@ -702,7 +709,10 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - return do_input_action(p_camera, mm->get_position(), false); + if (do_input_action(p_camera, mm->get_position(), false)) { + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + return EditorPlugin::AFTER_GUI_INPUT_PASS; } Ref<InputEventKey> k = p_event; @@ -714,16 +724,16 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In _clear_clipboard_data(); input_action = INPUT_NONE; _update_paste_indicator(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else if (selection.active) { _set_selection(false); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else { selected_palette = -1; mesh_library_palette->deselect_all(); update_palette(); _update_cursor_instance(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } } @@ -731,12 +741,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) { selection.click[edit_axis]--; _validate_selection(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) { selection.click[edit_axis]++; _validate_selection(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } } } @@ -755,12 +765,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In if (step) { floor->set_value(floor->get_value() + step); } - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } } accumulated_floor_delta = 0.0; - return false; + return EditorPlugin::AFTER_GUI_INPUT_PASS; } struct _CGMEItemSort { diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 4bc849e071..1a6b1310d8 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -232,7 +232,7 @@ protected: static void _bind_methods(); public: - bool forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event); + EditorPlugin::AfterGUIInput forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event); void edit(GridMap *p_gridmap); GridMapEditor() {} @@ -250,7 +250,7 @@ protected: void _notification(int p_what); public: - virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); } + virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); } virtual String get_name() const override { return "GridMap"; } bool has_main_screen() const override { return false; } virtual void edit(Object *p_object) override; diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index 9d6a399eff..32a31aa764 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -65,7 +65,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force Vector<uint8_t> imgdata; - imgdata.resize(height * width * sizeof(uint32_t)); + imgdata.resize(height * width * (int)sizeof(uint32_t)); { uint8_t *w = imgdata.ptrw(); @@ -75,7 +75,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force if (width < 8 || width >= 32768) { // Read flat data - f->get_buffer(ptr, width * height * 4); + f->get_buffer(ptr, (uint64_t)width * height * 4); } else { // Read RLE-encoded data diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index ba4ef3be8d..37e969db4d 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -611,6 +611,61 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int } } +LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices) { + Vector<RD::Uniform> uniforms; + { + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 0; + u.ids.push_back(dest_light_tex); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1; + u.ids.push_back(source_light_tex); + uniforms.push_back(u); + } + } + + RID compute_shader_dilate = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("dilate")); + ERR_FAIL_COND_V(compute_shader_dilate.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen + RID compute_shader_dilate_pipeline = rd->compute_pipeline_create(compute_shader_dilate); + + RID dilate_uniform_set = rd->uniform_set_create(uniforms, compute_shader_dilate, 1); + + RD::ComputeListID compute_list = rd->compute_list_begin(); + rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_dilate_pipeline); + rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0); + rd->compute_list_bind_uniform_set(compute_list, dilate_uniform_set, 1); + push_constant.region_ofs[0] = 0; + push_constant.region_ofs[1] = 0; + Vector3i group_size((atlas_size.x - 1) / 8 + 1, (atlas_size.y - 1) / 8 + 1, 1); //restore group size + + for (int i = 0; i < atlas_slices; i++) { + push_constant.atlas_slice = i; + rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); + rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z); + //no barrier, let them run all together + } + rd->compute_list_end(); + rd->free(compute_shader_dilate); + +#ifdef DEBUG_TEXTURES + for (int i = 0; i < atlas_slices; i++) { + Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); + Ref<Image> img; + img.instantiate(); + img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + img->convert(Image::FORMAT_RGBA8); + img->save_png("res://5_dilated_" + itos(i) + ".png"); + } +#endif + return BAKE_OK; +} + LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata) { if (p_step_function) { p_step_function(0.0, TTR("Begin Bake"), p_bake_userdata, true); @@ -943,11 +998,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d ERR_FAIL_COND_V(compute_shader_secondary.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen RID compute_shader_secondary_pipeline = rd->compute_pipeline_create(compute_shader_secondary); - // Dilate - RID compute_shader_dilate = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("dilate")); - ERR_FAIL_COND_V(compute_shader_dilate.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen - RID compute_shader_dilate_pipeline = rd->compute_pipeline_create(compute_shader_dilate); - // Light probes RID compute_shader_light_probes = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("light_probes")); ERR_FAIL_COND_V(compute_shader_light_probes.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen @@ -959,7 +1009,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d rd->free(compute_shader_unocclude); \ rd->free(compute_shader_primary); \ rd->free(compute_shader_secondary); \ - rd->free(compute_shader_dilate); \ rd->free(compute_shader_light_probes); PushConstant push_constant; @@ -1270,7 +1319,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d push_constant.environment_xform[3] = 0.0f; } - /* LIGHPROBES */ + /* LIGHTPROBES */ RID light_probe_buffer; @@ -1377,6 +1426,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d } #endif + { + SWAP(light_accum_tex, light_accum_tex2); + BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1)); + if (unlikely(error != BAKE_OK)) { + return error; + } + } + /* DENOISE */ if (p_use_denoiser) { @@ -1409,58 +1466,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d } } } - } -#ifdef DEBUG_TEXTURES - - for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { - Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); - img->save_exr("res://4_light_secondary_" + itos(i) + ".exr", false); - } -#endif - - /* DILATE LIGHTMAP */ - { - SWAP(light_accum_tex, light_accum_tex2); - - Vector<RD::Uniform> uniforms; { - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 0; - u.ids.push_back(light_accum_tex); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 1; - u.ids.push_back(light_accum_tex2); - uniforms.push_back(u); + SWAP(light_accum_tex, light_accum_tex2); + BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1)); + if (unlikely(error != BAKE_OK)) { + return error; } } - - RID dilate_uniform_set = rd->uniform_set_create(uniforms, compute_shader_dilate, 1); - - RD::ComputeListID compute_list = rd->compute_list_begin(); - rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_dilate_pipeline); - rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0); - rd->compute_list_bind_uniform_set(compute_list, dilate_uniform_set, 1); - push_constant.region_ofs[0] = 0; - push_constant.region_ofs[1] = 0; - group_size = Vector3i((atlas_size.x - 1) / 8 + 1, (atlas_size.y - 1) / 8 + 1, 1); //restore group size - - for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { - push_constant.atlas_slice = i; - rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); - rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z); - //no barrier, let them run all together - } - rd->compute_list_end(); } #ifdef DEBUG_TEXTURES @@ -1470,8 +1483,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d Ref<Image> img; img.instantiate(); img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); - img->convert(Image::FORMAT_RGBA8); - img->save_png("res://5_dilated_" + itos(i) + ".png"); + img->save_exr("res://4_light_secondary_" + itos(i) + ".exr", false); } #endif diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index a6a3740051..51ab60fc29 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -36,6 +36,7 @@ #include "scene/resources/mesh.h" #include "servers/rendering/rendering_device.h" +class RDShaderFile; class LightmapperRD : public Lightmapper { GDCLASS(LightmapperRD, Lightmapper) @@ -72,13 +73,13 @@ class LightmapperRD : public Lightmapper { bool operator==(const Vertex &p_vtx) const { return (position[0] == p_vtx.position[0]) && - (position[1] == p_vtx.position[1]) && - (position[2] == p_vtx.position[2]) && - (uv[0] == p_vtx.uv[0]) && - (uv[1] == p_vtx.uv[1]) && - (normal_xy[0] == p_vtx.normal_xy[0]) && - (normal_xy[1] == p_vtx.normal_xy[1]) && - (normal_z == p_vtx.normal_z); + (position[1] == p_vtx.position[1]) && + (position[2] == p_vtx.position[2]) && + (uv[0] == p_vtx.uv[0]) && + (uv[1] == p_vtx.uv[1]) && + (normal_xy[0] == p_vtx.normal_xy[0]) && + (normal_xy[1] == p_vtx.normal_xy[1]) && + (normal_z == p_vtx.normal_z); } }; @@ -231,6 +232,8 @@ class LightmapperRD : public Lightmapper { void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata); void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform); + BakeError _dilate(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices); + public: virtual void add_mesh(const MeshData &p_mesh) override; virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) override; diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl index 22172d50e4..58523dc1f8 100644 --- a/modules/lightmapper_rd/lm_common_inc.glsl +++ b/modules/lightmapper_rd/lm_common_inc.glsl @@ -81,3 +81,7 @@ layout(set = 0, binding = 8) uniform texture2DArray albedo_tex; layout(set = 0, binding = 9) uniform texture2DArray emission_tex; layout(set = 0, binding = 10) uniform sampler linear_sampler; + +// Fragment action constants +const uint FA_NONE = 0; +const uint FA_SMOOTHEN_POSITION = 1; diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index 25b334c5eb..158cd960c4 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -94,13 +94,14 @@ params; //check it, but also return distance and barycentric coords (for uv lookup) bool ray_hits_triangle(vec3 from, vec3 dir, float max_dist, vec3 p0, vec3 p1, vec3 p2, out float r_distance, out vec3 r_barycentric) { + const float EPSILON = 0.00001; const vec3 e0 = p1 - p0; const vec3 e1 = p0 - p2; vec3 triangle_normal = cross(e1, e0); float n_dot_dir = dot(triangle_normal, dir); - if (abs(n_dot_dir) < 0.01) { + if (abs(n_dot_dir) < EPSILON) { return false; } @@ -148,7 +149,7 @@ uint trace_ray(vec3 p_from, vec3 p_to ivec3 icell = ivec3(from_cell); ivec3 iendcell = ivec3(to_cell); vec3 dir_cell = normalize(rel_cell); - vec3 delta = abs(1.0 / dir_cell); //vec3(length(rel_cell)) / rel_cell); + vec3 delta = min(abs(1.0 / dir_cell), params.grid_size); // use params.grid_size as max to prevent infinity values ivec3 step = ivec3(sign(rel_cell)); vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta; @@ -420,20 +421,22 @@ void main() { light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb; active_rays += 1.0; - } else if (trace_result == RAY_MISS && params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce - // Did not hit a triangle, reach out for the sky - vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir); + } else if (trace_result == RAY_MISS) { + if (params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce + // Did not hit a triangle, reach out for the sky + vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir); - vec2 st = vec2( - atan(sky_dir.x, sky_dir.z), - acos(sky_dir.y)); + vec2 st = vec2( + atan(sky_dir.x, sky_dir.z), + acos(sky_dir.y)); - if (st.x < 0.0) - st.x += PI * 2.0; + if (st.x < 0.0) + st.x += PI * 2.0; - st /= vec2(PI * 2.0, PI); + st /= vec2(PI * 2.0, PI); - light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb; + light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb; + } active_rays += 1.0; } diff --git a/modules/lightmapper_rd/lm_raster.glsl b/modules/lightmapper_rd/lm_raster.glsl index 55ca193cc1..a86968a4f3 100644 --- a/modules/lightmapper_rd/lm_raster.glsl +++ b/modules/lightmapper_rd/lm_raster.glsl @@ -12,6 +12,7 @@ layout(location = 2) out vec2 uv_interp; layout(location = 3) out vec3 barycentric; layout(location = 4) flat out uvec3 vertex_indices; layout(location = 5) flat out vec3 face_normal; +layout(location = 6) flat out uint fragment_action; layout(push_constant, binding = 0, std430) uniform Params { vec2 atlas_size; @@ -49,6 +50,14 @@ void main() { face_normal = -normalize(cross((vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.y].position), (vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.z].position))); + { + const float FLAT_THRESHOLD = 0.99; + const vec3 norm_a = vec3(vertices.data[vertex_indices.x].normal_xy, vertices.data[vertex_indices.x].normal_z); + const vec3 norm_b = vec3(vertices.data[vertex_indices.y].normal_xy, vertices.data[vertex_indices.y].normal_z); + const vec3 norm_c = vec3(vertices.data[vertex_indices.z].normal_xy, vertices.data[vertex_indices.z].normal_z); + fragment_action = (dot(norm_a, norm_b) < FLAT_THRESHOLD || dot(norm_a, norm_c) < FLAT_THRESHOLD || dot(norm_b, norm_c) < FLAT_THRESHOLD) ? FA_SMOOTHEN_POSITION : FA_NONE; + } + gl_Position = vec4((uv_interp + params.uv_offset) * 2.0 - 1.0, 0.0001, 1.0); } @@ -78,6 +87,7 @@ layout(location = 2) in vec2 uv_interp; layout(location = 3) in vec3 barycentric; layout(location = 4) in flat uvec3 vertex_indices; layout(location = 5) in flat vec3 face_normal; +layout(location = 6) in flat uint fragment_action; layout(location = 0) out vec4 position; layout(location = 1) out vec4 normal; @@ -86,7 +96,7 @@ layout(location = 2) out vec4 unocclude; void main() { vec3 vertex_pos = vertex_interp; - { + if (fragment_action == FA_SMOOTHEN_POSITION) { // smooth out vertex position by interpolating its projection in the 3 normal planes (normal plane is created by vertex pos and normal) // because we don't want to interpolate inwards, normals found pointing inwards are pushed out. vec3 pos_a = vertices.data[vertex_indices.x].position; diff --git a/modules/mbedtls/packet_peer_mbed_dtls.h b/modules/mbedtls/packet_peer_mbed_dtls.h index 6554c74a21..92e6ab88c4 100644 --- a/modules/mbedtls/packet_peer_mbed_dtls.h +++ b/modules/mbedtls/packet_peer_mbed_dtls.h @@ -59,8 +59,6 @@ protected: Ref<SSLContextMbedTLS> ssl_ctx; mbedtls_timing_delay_context timer; - static void _bind_methods(); - Error _do_handshake(); int _set_cookie(); diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/ssl_context_mbedtls.h index 1b55a54a10..5692dec1b6 100644 --- a/modules/mbedtls/ssl_context_mbedtls.h +++ b/modules/mbedtls/ssl_context_mbedtls.h @@ -67,8 +67,6 @@ class SSLContextMbedTLS : public RefCounted { protected: bool inited = false; - static PackedByteArray _read_file(String p_path); - public: static void print_mbedtls_error(int p_ret); diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h index b89d7fb238..407479e3cc 100644 --- a/modules/mbedtls/stream_peer_mbedtls.h +++ b/modules/mbedtls/stream_peer_mbedtls.h @@ -50,8 +50,6 @@ private: protected: Ref<SSLContextMbedTLS> ssl_ctx; - static void _bind_methods(); - Error _do_handshake(); public: diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 7b52ef178a..49e9f5f97e 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -112,7 +112,7 @@ void AudioStreamPlaybackMP3::seek(float p_time) { } frames_mixed = uint32_t(mp3_stream->sample_rate * p_time); - mp3dec_ex_seek(mp3d, frames_mixed * mp3_stream->channels); + mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels); } AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() { @@ -228,9 +228,9 @@ void AudioStreamMP3::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset); ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop_offset", "get_loop_offset"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_loop", "has_loop"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_loop_offset", "get_loop_offset"); } AudioStreamMP3::AudioStreamMP3() { diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index 04ba82ef51..18a77c8b8d 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -37,5 +37,6 @@ <member name="oversample" type="float" setter="set_oversample" getter="get_oversample" default="1.5"> The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance. </member> + <member name="xr_play_area_mode" type="int" setter="set_play_area_mode" getter="get_play_area_mode" override="true" enum="XRInterface.PlayAreaMode" default="1" /> </members> </class> diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index fc1a118e4f..ba7353ace2 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -126,6 +126,8 @@ void MobileVRInterface::set_position_from_sensors() { // 9dof is a misleading marketing term coming from 3 accelerometer axis + 3 gyro axis + 3 magnetometer axis = 9 axis // but in reality this only offers 3 dof (yaw, pitch, roll) orientation + Basis orientation; + uint64_t ticks = OS::get_singleton()->get_ticks_usec(); uint64_t ticks_elapsed = ticks - last_ticks; float delta_time = (double)ticks_elapsed / 1000000.0; @@ -207,8 +209,8 @@ void MobileVRInterface::set_position_from_sensors() { }; }; - // JIC - orientation.orthonormalize(); + // and copy to our head transform + head_transform.basis = orientation.orthonormalized(); last_ticks = ticks; }; @@ -318,7 +320,7 @@ bool MobileVRInterface::initialize() { ERR_FAIL_NULL_V(xr_server, false); if (!initialized) { - // reset our sensor data and orientation + // reset our sensor data mag_count = 0; has_gyro = false; sensor_first = true; @@ -326,9 +328,15 @@ bool MobileVRInterface::initialize() { mag_next_max = Vector3(-10000, -10000, -10000); mag_current_min = Vector3(0, 0, 0); mag_current_max = Vector3(0, 0, 0); + head_transform.basis = Basis(); + head_transform.origin = Vector3(0.0, eye_height, 0.0); - // reset our orientation - orientation = Basis(); + // we must create a tracker for our head + head.instantiate(); + head->set_tracker_type(XRServer::TRACKER_HEAD); + head->set_tracker_name("head"); + head->set_tracker_desc("Players head"); + xr_server->add_tracker(head); // make this our primary interface xr_server->set_primary_interface(this); @@ -343,16 +351,38 @@ bool MobileVRInterface::initialize() { void MobileVRInterface::uninitialize() { if (initialized) { + // do any cleanup here... XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr && xr_server->get_primary_interface() == this) { - // no longer our primary interface - xr_server->set_primary_interface(nullptr); + if (xr_server != nullptr) { + if (head.is_valid()) { + xr_server->remove_tracker(head); + + head.unref(); + } + + if (xr_server->get_primary_interface() == this) { + // no longer our primary interface + xr_server->set_primary_interface(nullptr); + } } initialized = false; }; }; +bool MobileVRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) { + // This interface has no positional tracking so fix this to 3DOF + return p_mode == XR_PLAY_AREA_3DOF; +} + +XRInterface::PlayAreaMode MobileVRInterface::get_play_area_mode() const { + return XR_PLAY_AREA_3DOF; +} + +bool MobileVRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) { + return p_mode == XR_PLAY_AREA_3DOF; +} + Size2 MobileVRInterface::get_render_target_size() { _THREAD_SAFE_METHOD_ @@ -377,11 +407,10 @@ Transform3D MobileVRInterface::get_camera_transform() { float world_scale = xr_server->get_world_scale(); // just scale our origin point of our transform - Transform3D hmd_transform; - hmd_transform.basis = orientation; - hmd_transform.origin = Vector3(0.0, eye_height * world_scale, 0.0); + Transform3D _head_transform = head_transform; + _head_transform.origin *= world_scale; - transform_for_eye = (xr_server->get_reference_frame()) * hmd_transform; + transform_for_eye = (xr_server->get_reference_frame()) * _head_transform; } return transform_for_eye; @@ -409,11 +438,10 @@ Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Tra }; // just scale our origin point of our transform - Transform3D hmd_transform; - hmd_transform.basis = orientation; - hmd_transform.origin = Vector3(0.0, eye_height * world_scale, 0.0); + Transform3D _head_transform = head_transform; + _head_transform.origin *= world_scale; - transform_for_eye = p_cam_transform * (xr_server->get_reference_frame()) * hmd_transform * transform_for_eye; + transform_for_eye = p_cam_transform * (xr_server->get_reference_frame()) * _head_transform * transform_for_eye; } else { // huh? well just return what we got.... transform_for_eye = p_cam_transform; @@ -476,7 +504,16 @@ void MobileVRInterface::process() { _THREAD_SAFE_METHOD_ if (initialized) { + // update our head transform orientation set_position_from_sensors(); + + // update our head transform position (should be constant) + head_transform.origin = Vector3(0.0, eye_height, 0.0); + + if (head.is_valid()) { + // Set our head position, note in real space, reference frame and world scale is applied later + head->set_pose("default", head_transform, Vector3(), Vector3()); + } }; }; diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index a843e1188b..b5bf966247 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -53,7 +53,6 @@ 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 double eye_height = 1.85; @@ -68,6 +67,10 @@ private: double k2 = 0.215; double aspect = 1.0; + // at a minimum we need a tracker for our head + Ref<XRPositionalTracker> head; + Transform3D head_transform; + /* logic for processing our sensor data, this was originally in our positional tracker logic but I think that doesn't make sense in hindsight. It only makes marginally more sense to park it here for now, @@ -140,6 +143,10 @@ public: virtual bool initialize() override; virtual void uninitialize() override; + virtual bool supports_play_area_mode(XRInterface::PlayAreaMode p_mode) override; + virtual XRInterface::PlayAreaMode get_play_area_mode() const override; + virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 3b94949470..95c959235c 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -49,6 +49,7 @@ if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]: env_mono.add_source_files(env.modules_sources, "*.cpp") env_mono.add_source_files(env.modules_sources, "glue/*.cpp") +env_mono.add_source_files(env.modules_sources, "glue/mono_glue.gen.cpp") env_mono.add_source_files(env.modules_sources, "mono_gd/*.cpp") env_mono.add_source_files(env.modules_sources, "utils/*.cpp") @@ -57,6 +58,8 @@ env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.cpp") if env["platform"] in ["osx", "iphone"]: env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.mm") env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.m") +elif env["platform"] == "android": + env_mono.add_source_files(env.modules_sources, "mono_gd/android_mono_config.gen.cpp") if env["tools"]: env_mono.add_source_files(env.modules_sources, "editor/*.cpp") diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py index 28494bff6e..1920ef1c1a 100644 --- a/modules/mono/build_scripts/make_android_mono_config.py +++ b/modules/mono/build_scripts/make_android_mono_config.py @@ -8,7 +8,9 @@ def generate_compressed_config(config_src, output_dir): decompr_size = len(buf) import zlib - buf = zlib.compress(buf) + # Use maximum zlib compression level to further reduce file size + # (at the cost of initial build times). + buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) compr_size = len(buf) bytes_seq_str = "" diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py index 0ec7e2f433..93a66ebf6f 100644 --- a/modules/mono/build_scripts/mono_reg_utils.py +++ b/modules/mono/build_scripts/mono_reg_utils.py @@ -2,7 +2,6 @@ import os import platform if os.name == "nt": - import sys import winreg diff --git a/modules/mono/config.py b/modules/mono/config.py index 4c851a2989..df02d9a309 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -2,7 +2,7 @@ supported_platforms = ["windows", "osx", "linuxbsd", "server", "android", "haiku def can_build(env, platform): - return True + return not env["arch"].startswith("rv") def configure(env): diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index af7c54dd5b..d9b291489b 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -313,22 +313,22 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { bool CSharpLanguage::is_control_flow_keyword(String p_keyword) const { return p_keyword == "break" || - p_keyword == "case" || - p_keyword == "catch" || - p_keyword == "continue" || - p_keyword == "default" || - p_keyword == "do" || - p_keyword == "else" || - p_keyword == "finally" || - p_keyword == "for" || - p_keyword == "foreach" || - p_keyword == "goto" || - p_keyword == "if" || - p_keyword == "return" || - p_keyword == "switch" || - p_keyword == "throw" || - p_keyword == "try" || - p_keyword == "while"; + p_keyword == "case" || + p_keyword == "catch" || + p_keyword == "continue" || + p_keyword == "default" || + p_keyword == "do" || + p_keyword == "else" || + p_keyword == "finally" || + p_keyword == "for" || + p_keyword == "foreach" || + p_keyword == "goto" || + p_keyword == "if" || + p_keyword == "return" || + p_keyword == "switch" || + p_keyword == "throw" || + p_keyword == "try" || + p_keyword == "while"; } void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const { @@ -1660,7 +1660,7 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { GDMonoProperty *property = top->get_property(p_name); if (property) { - property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value, property->get_type())); + property->set_value_from_variant(mono_object, p_value); return true; } @@ -1813,8 +1813,9 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, } void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { - for (const KeyValue<StringName, PropertyInfo> &E : script->member_info) { - p_properties->push_back(E.value); + List<PropertyInfo> props; + for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = script->member_info.front(); E; E = E.next()) { + props.push_front(E.value()); } // Call _get_property_list @@ -1837,9 +1838,8 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { if (ret) { Array array = Array(GDMonoMarshal::mono_object_to_variant(ret)); for (int i = 0, size = array.size(); i < size; i++) { - p_properties->push_back(PropertyInfo::from_dict(array.get(i))); + props.push_back(PropertyInfo::from_dict(array.get(i))); } - return; } break; @@ -1847,6 +1847,10 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const { top = top->get_parent_class(); } + + for (const PropertyInfo &prop : props) { + p_properties->push_back(prop); + } } Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { @@ -1865,8 +1869,9 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool * } void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const { - if (!script->is_valid() || !script->script_class) + if (!script->is_valid() || !script->script_class) { return; + } GD_MONO_SCOPE_THREAD_ATTACH; @@ -2976,7 +2981,7 @@ bool CSharpScript::_set(const StringName &p_name, const Variant &p_value) { } void CSharpScript::_get_property_list(List<PropertyInfo> *p_properties) const { - p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } void CSharpScript::_bind_methods() { @@ -3265,8 +3270,7 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { "Script inherits from native type '" + String(native_name) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'"); } - ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + - "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'."); + ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'."); } } @@ -3499,9 +3503,15 @@ Ref<Script> CSharpScript::get_base_script() const { return Ref<Script>(); } -void CSharpScript::get_script_property_list(List<PropertyInfo> *p_list) const { - for (const KeyValue<StringName, PropertyInfo> &E : member_info) { - p_list->push_back(E.value); +void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const { + List<PropertyInfo> props; + + for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = member_info.front(); E; E = E.next()) { + props.push_front(E.value()); + } + + for (const PropertyInfo &prop : props) { + r_list->push_back(prop); } } @@ -3511,8 +3521,8 @@ int CSharpScript::get_member_line(const StringName &p_member) const { } Multiplayer::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const { - if (p_member->has_attribute(CACHED_CLASS(AnyAttribute))) { - return Multiplayer::RPC_MODE_ANY; + if (p_member->has_attribute(CACHED_CLASS(AnyPeerAttribute))) { + return Multiplayer::RPC_MODE_ANY_PEER; } if (p_member->has_attribute(CACHED_CLASS(AuthorityAttribute))) { return Multiplayer::RPC_MODE_AUTHORITY; @@ -3529,10 +3539,10 @@ Error CSharpScript::load_source_code(const String &p_path) { Error ferr = read_all_file_utf8(p_path, source); ERR_FAIL_COND_V_MSG(ferr != OK, ferr, - ferr == ERR_INVALID_DATA ? - "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded." - " Please ensure that scripts are saved in valid UTF-8 unicode." : - "Failed to read file: '" + p_path + "'."); + ferr == ERR_INVALID_DATA + ? "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded." + " Please ensure that scripts are saved in valid UTF-8 unicode." + : "Failed to read file: '" + p_path + "'."); #ifdef TOOLS_ENABLED source_changed_cache = true; diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index afc17f694a..c998d9c1e4 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -154,7 +154,7 @@ private: Set<StringName> exported_members_names; #endif - Map<StringName, PropertyInfo> member_info; + OrderedHashMap<StringName, PropertyInfo> member_info; void _clear(); @@ -215,7 +215,7 @@ public: void get_script_signal_list(List<MethodInfo> *r_signals) const override; bool get_property_default_value(const StringName &p_property, Variant &r_value) const override; - void get_script_property_list(List<PropertyInfo> *p_list) const override; + void get_script_property_list(List<PropertyInfo> *r_list) const override; void update_exports() override; void get_members(Set<StringName> *p_members) override; diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 45a6f991bf..abd860a55f 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -8,7 +8,7 @@ See also [GodotSharp]. </description> <tutorials> - <link title="C# tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link> + <link title="C# documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/index.html</link> </tutorials> <methods> <method name="new" qualifiers="vararg"> diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs index 0f50c90531..bc09e1ebf9 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs @@ -122,13 +122,19 @@ namespace GodotTools.IdeMessaging this.logger = logger; string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata"); + // FileSystemWatcher requires an existing directory + if (!Directory.Exists(projectMetadataDir)) { + // Check if the non hidden version exists + string nonHiddenProjectMetadataDir = Path.Combine(godotProjectDir, "godot", "mono", "metadata"); + if (Directory.Exists(nonHiddenProjectMetadataDir)) { + projectMetadataDir = nonHiddenProjectMetadataDir; + } else { + Directory.CreateDirectory(projectMetadataDir); + } + } MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName); - // FileSystemWatcher requires an existing directory - if (!Directory.Exists(projectMetadataDir)) - Directory.CreateDirectory(projectMetadataDir); - fsWatcher = new FileSystemWatcher(projectMetadataDir, GodotIdeMetadata.DefaultFileName); } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index e03c5fd248..148a6796d2 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -365,8 +365,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 == "enum") { - StringName search_cname = !target_itype ? target_cname : - StringName(target_itype->name + "." + (String)target_cname); + StringName search_cname = !target_itype ? target_cname : StringName(target_itype->name + "." + (String)target_cname); const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(search_cname); @@ -1524,7 +1523,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte if (getter->return_type.cname != setter_first_arg.type.cname) { // Special case for Node::set_name bool whitelisted = getter->return_type.cname == name_cache.type_StringName && - setter_first_arg.type.cname == name_cache.type_String; + setter_first_arg.type.cname == name_cache.type_String; ERR_FAIL_COND_V_MSG(!whitelisted, ERR_BUG, "Return type from getter doesn't match first argument of setter for property: '" + @@ -2481,29 +2480,29 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant & switch (p_val.get_type()) { case Variant::NIL: return p_arg_type.is_object_type || - name_cache.is_nullable_type(p_arg_type.name); + name_cache.is_nullable_type(p_arg_type.name); case Variant::BOOL: return p_arg_type.name == name_cache.type_bool; case Variant::INT: return p_arg_type.name == name_cache.type_sbyte || - p_arg_type.name == name_cache.type_short || - p_arg_type.name == name_cache.type_int || - p_arg_type.name == name_cache.type_byte || - p_arg_type.name == name_cache.type_ushort || - p_arg_type.name == name_cache.type_uint || - p_arg_type.name == name_cache.type_long || - p_arg_type.name == name_cache.type_ulong || - p_arg_type.name == name_cache.type_float || - p_arg_type.name == name_cache.type_double || - p_arg_type.is_enum; + p_arg_type.name == name_cache.type_short || + p_arg_type.name == name_cache.type_int || + p_arg_type.name == name_cache.type_byte || + p_arg_type.name == name_cache.type_ushort || + p_arg_type.name == name_cache.type_uint || + p_arg_type.name == name_cache.type_long || + p_arg_type.name == name_cache.type_ulong || + p_arg_type.name == name_cache.type_float || + p_arg_type.name == name_cache.type_double || + p_arg_type.is_enum; case Variant::FLOAT: return p_arg_type.name == name_cache.type_float || - p_arg_type.name == name_cache.type_double; + p_arg_type.name == name_cache.type_double; case Variant::STRING: case Variant::STRING_NAME: return p_arg_type.name == name_cache.type_String || - p_arg_type.name == name_cache.type_StringName || - p_arg_type.name == name_cache.type_NodePath; + p_arg_type.name == name_cache.type_StringName || + p_arg_type.name == name_cache.type_NodePath; case Variant::NODE_PATH: return p_arg_type.name == name_cache.type_NodePath; case Variant::TRANSFORM2D: @@ -2535,13 +2534,13 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant & return p_arg_type.is_object_type; case Variant::VECTOR2I: return p_arg_type.name == name_cache.type_Vector2 || - p_arg_type.name == Variant::get_type_name(p_val.get_type()); + p_arg_type.name == Variant::get_type_name(p_val.get_type()); case Variant::RECT2I: return p_arg_type.name == name_cache.type_Rect2 || - p_arg_type.name == Variant::get_type_name(p_val.get_type()); + p_arg_type.name == Variant::get_type_name(p_val.get_type()); case Variant::VECTOR3I: return p_arg_type.name == name_cache.type_Vector3 || - p_arg_type.name == Variant::get_type_name(p_val.get_type()); + p_arg_type.name == Variant::get_type_name(p_val.get_type()); default: CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type())); break; @@ -2714,7 +2713,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { if (itype.cname != name_cache.type_Object || imethod.name != "free") { WARN_PRINT("Notification: New unexpected virtual non-overridable method found." " We only expected Object.free, but found '" + - itype.name + "." + imethod.name + "'."); + itype.name + "." + imethod.name + "'."); } } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { imethod.return_type.cname = return_info.class_name; @@ -2723,7 +2722,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { imethod.return_type.cname = return_info.class_name; bool bad_reference_hint = !imethod.is_virtual && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE && - ClassDB::is_parent_class(return_info.class_name, name_cache.type_RefCounted); + ClassDB::is_parent_class(return_info.class_name, name_cache.type_RefCounted); ERR_FAIL_COND_V_MSG(bad_reference_hint, false, String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." + " Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'."); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs index 1da91ea867..b8b9bc660c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs @@ -3,7 +3,7 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Method)] - public class AnyAttribute : Attribute { } + public class AnyPeerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class AuthorityAttribute : Attribute { } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs index a28a46896b..2722b64e6d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Godot { @@ -71,5 +72,32 @@ namespace Godot _method = null; _delegate = @delegate; } + + /// <summary> + /// Calls the method represented by this <see cref="Callable"/>. + /// Arguments can be passed and should match the method's signature. + /// </summary> + /// <param name="args">Arguments that will be passed to the method call.</param> + /// <returns>The value returned by the method.</returns> + public object Call(params object[] args) + { + return godot_icall_Callable_Call(ref this, args); + } + + /// <summary> + /// Calls the method represented by this <see cref="Callable"/> in deferred mode, i.e. during the idle frame. + /// Arguments can be passed and should match the method's signature. + /// </summary> + /// <param name="args">Arguments that will be passed to the method call.</param> + public void CallDeferred(params object[] args) + { + godot_icall_Callable_CallDeferred(ref this, args); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern object godot_icall_Callable_Call(ref Callable callable, object[] args); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void godot_icall_Callable_CallDeferred(ref Callable callable, object[] args); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 8fe08e7e1d..746612477d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -117,7 +117,7 @@ namespace Godot /// { /// for (int i = 0; i < 100; i++) /// { - /// await ToSignal(GetTree(), "idle_frame"); + /// await ToSignal(GetTree(), "process_frame"); /// GD.Print($"Frame {i}"); /// } /// } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index 1694ac0320..c18f818ed2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -472,26 +472,22 @@ namespace Godot return new Quaternion(-left.x, -left.y, -left.z, -left.w); } - public static Quaternion operator *(Quaternion left, Vector3 right) + public static Vector3 operator *(Quaternion quat, Vector3 vec) { - 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) - ); +#if DEBUG + if (!quat.IsNormalized()) + { + throw new InvalidOperationException("Quaternion is not normalized."); + } +#endif + var u = new Vector3(quat.x, quat.y, quat.z); + Vector3 uv = u.Cross(vec); + return vec + (((uv * quat.w) + u.Cross(uv)) * 2); } - public static Quaternion operator *(Vector3 left, Quaternion right) + public static Vector3 operator *(Vector3 vec, Quaternion quat) { - 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) - ); + return quat.Inverse() * vec; } public static Quaternion operator *(Quaternion left, real_t right) diff --git a/modules/opus/register_types.h b/modules/mono/glue/callable_glue.cpp index af889cf809..54b65fdb94 100644 --- a/modules/opus/register_types.h +++ b/modules/mono/glue/callable_glue.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* register_types.h */ +/* callable_glue.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,10 +28,52 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef OPUS_REGISTER_TYPES_H -#define OPUS_REGISTER_TYPES_H +#ifdef MONO_GLUE_ENABLED -void register_opus_types(); -void unregister_opus_types(); +#include "../mono_gd/gd_mono_marshal.h" +#include "arguments_vector.h" -#endif // OPUS_REGISTER_TYPES_H +MonoObject *godot_icall_Callable_Call(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) { + Callable callable = GDMonoMarshal::managed_to_callable(*p_callable); + + int argc = mono_array_length(p_args); + + ArgumentsVector<Variant> arg_store(argc); + ArgumentsVector<const Variant *> args(argc); + + for (int i = 0; i < argc; i++) { + MonoObject *elem = mono_array_get(p_args, MonoObject *, i); + arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem)); + args.set(i, &arg_store.get(i)); + } + + Variant result; + Callable::CallError error; + callable.call(args.ptr(), argc, result, error); + + return GDMonoMarshal::variant_to_mono_object(result); +} + +void godot_icall_Callable_CallDeferred(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) { + Callable callable = GDMonoMarshal::managed_to_callable(*p_callable); + + int argc = mono_array_length(p_args); + + ArgumentsVector<Variant> arg_store(argc); + ArgumentsVector<const Variant *> args(argc); + + for (int i = 0; i < argc; i++) { + MonoObject *elem = mono_array_get(p_args, MonoObject *, i); + arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem)); + args.set(i, &arg_store.get(i)); + } + + callable.call_deferred(args.ptr(), argc); +} + +void godot_register_callable_icalls() { + GDMonoUtils::add_internal_call("Godot.Callable::godot_icall_Callable_Call", godot_icall_Callable_Call); + GDMonoUtils::add_internal_call("Godot.Callable::godot_icall_Callable_CallDeferred", godot_icall_Callable_CallDeferred); +} + +#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index eed3bd2167..074220bb9b 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -36,6 +36,7 @@ void godot_register_collections_icalls(); void godot_register_gd_icalls(); void godot_register_string_name_icalls(); void godot_register_nodepath_icalls(); +void godot_register_callable_icalls(); void godot_register_object_icalls(); void godot_register_rid_icalls(); void godot_register_string_icalls(); @@ -50,6 +51,7 @@ void godot_register_glue_header_icalls() { godot_register_gd_icalls(); godot_register_string_name_icalls(); godot_register_nodepath_icalls(); + godot_register_callable_icalls(); godot_register_object_icalls(); godot_register_rid_icalls(); godot_register_string_icalls(); diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 375ad413c4..24bd1ed492 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -122,7 +122,7 @@ public: private: _GodotSharpDirs() { - res_data_dir = "res://.godot/mono"; + res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().plus_file("mono"); res_metadata_dir = res_data_dir.plus_file("metadata"); res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config()); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 1b1349a3a3..f3e83b26b9 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -151,7 +151,7 @@ void gd_mono_debug_init() { if (da_args.length() == 0) { da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + - ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) + ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) .utf8(); } #else @@ -592,9 +592,9 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) { ApiAssemblyInfo::Version api_assembly_version; - const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE ? - BINDINGS_CLASS_NATIVECALLS : - BINDINGS_CLASS_NATIVECALLS_EDITOR; + const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE + ? BINDINGS_CLASS_NATIVECALLS + : BINDINGS_CLASS_NATIVECALLS_EDITOR; GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name); @@ -702,11 +702,11 @@ static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool } r_out_of_sync = GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("core", "bindings_version") || - GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") || - GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") || - GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") || - GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") || - GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); + GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") || + GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") || + GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") || + GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") || + GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); return true; } @@ -754,14 +754,10 @@ bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config } String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) { -#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \ - ( \ - (m_out_of_sync ? \ - String("The assembly is invalidated ") : \ - String("The assembly was not found ")) + \ - (m_prebuilt_exists ? \ - String("and the prebuilt assemblies are missing.") : \ - String("and we failed to copy the prebuilt assemblies."))) +#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \ + ( \ + (m_out_of_sync ? String("The assembly is invalidated ") : String("The assembly was not found ")) + \ + (m_prebuilt_exists ? String("and the prebuilt assemblies are missing.") : String("and we failed to copy the prebuilt assemblies."))) String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config); @@ -819,14 +815,14 @@ bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, c // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date // If running the project manager, load it from the prebuilt API directory - String assembly_dir = !Main::is_project_manager() ? - GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) : - GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); + String assembly_dir = !Main::is_project_manager() + ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) + : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); bool success = FileAccess::exists(assembly_path) && - load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); + load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); #else bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly); #endif @@ -834,8 +830,8 @@ bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, c if (success) { ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE); r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || - GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; } else { r_loaded_api_assembly.out_of_sync = false; } @@ -852,20 +848,20 @@ bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date // If running the project manager, load it from the prebuilt API directory - String assembly_dir = !Main::is_project_manager() ? - GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) : - GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); + String assembly_dir = !Main::is_project_manager() + ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) + : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); bool success = FileAccess::exists(assembly_path) && - load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); + load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); if (success) { ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR); r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || - GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; } else { r_loaded_api_assembly.out_of_sync = false; } @@ -985,7 +981,7 @@ bool GDMono::_load_tools_assemblies() { } bool success = load_assembly(TOOLS_ASM_NAME, &tools_assembly) && - load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly); + load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly); return success; } @@ -1363,8 +1359,8 @@ int32_t GodotSharp::get_scripts_domain_id() { bool GodotSharp::is_scripts_domain_loaded() { return GDMono::get_singleton() != nullptr && - GDMono::get_singleton()->is_runtime_initialized() && - GDMono::get_singleton()->get_scripts_domain() != nullptr; + GDMono::get_singleton()->is_runtime_initialized() && + GDMono::get_singleton()->get_scripts_domain() != nullptr; } bool GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 4170e5053f..a18fa6c6b4 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -54,8 +54,8 @@ struct Version { bool operator==(const Version &p_other) const { return godot_api_hash == p_other.godot_api_hash && - bindings_version == p_other.bindings_version && - cs_glue_version == p_other.cs_glue_version; + bindings_version == p_other.bindings_version && + cs_glue_version == p_other.cs_glue_version; } Version() {} diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 34e845a589..2bf55493e0 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -140,7 +140,7 @@ void CachedData::clear_godot_api_cache() { field_ExportAttribute_hintString = nullptr; class_SignalAttribute = nullptr; class_ToolAttribute = nullptr; - class_AnyAttribute = nullptr; + class_AnyPeerAttribute = nullptr; class_AuthorityAttribute = nullptr; class_GodotMethodAttribute = nullptr; field_GodotMethodAttribute_methodName = nullptr; @@ -265,7 +265,7 @@ void update_godot_api_cache() { CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString")); CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute)); CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute)); - CACHE_CLASS_AND_CHECK(AnyAttribute, GODOT_API_CLASS(AnyAttribute)); + CACHE_CLASS_AND_CHECK(AnyPeerAttribute, GODOT_API_CLASS(AnyPeerAttribute)); CACHE_CLASS_AND_CHECK(AuthorityAttribute, GODOT_API_CLASS(AuthorityAttribute)); 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 e60a4d5279..4b4688b4d9 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -111,7 +111,7 @@ struct CachedData { GDMonoField *field_ExportAttribute_hintString; GDMonoClass *class_SignalAttribute; GDMonoClass *class_ToolAttribute; - GDMonoClass *class_AnyAttribute; + GDMonoClass *class_AnyPeerAttribute; GDMonoClass *class_AuthorityAttribute; GDMonoClass *class_GodotMethodAttribute; GDMonoField *field_GodotMethodAttribute_methodName; diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 27b4ac7fa7..4f4480fa49 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -187,7 +187,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base #ifdef DEBUG_ENABLED String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")"; WARN_PRINT("Method '" + fullname + "' is hidden by Godot API method. Should be '" + - method->get_full_name_no_class() + "'. In class '" + namespace_name + "." + class_name + "'."); + method->get_full_name_no_class() + "'. In class '" + namespace_name + "." + class_name + "'."); #endif continue; } @@ -205,7 +205,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base // found String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")"; WARN_PRINT("Method '" + fullname + "' should be '" + m->get_full_name_no_class() + - "'. In class '" + namespace_name + "." + class_name + "'."); + "'. In class '" + namespace_name + "." + class_name + "'."); break; } diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index c9789bf270..6b395303dd 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -139,49 +139,65 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); + MonoClass *elem_class = mono_class_get_element_class(p_type.type_class->get_mono_ptr()); - if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { + if (elem_class == CACHED_CLASS_RAW(MonoObject)) { return Variant::ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) { + if (elem_class == CACHED_CLASS_RAW(uint8_t)) { return Variant::PACKED_BYTE_ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) { + if (elem_class == CACHED_CLASS_RAW(int32_t)) { return Variant::PACKED_INT32_ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) { + if (elem_class == CACHED_CLASS_RAW(int64_t)) { return Variant::PACKED_INT64_ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(float)) { + if (elem_class == CACHED_CLASS_RAW(float)) { return Variant::PACKED_FLOAT32_ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(double)) { + if (elem_class == CACHED_CLASS_RAW(double)) { return Variant::PACKED_FLOAT64_ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(String)) { + if (elem_class == CACHED_CLASS_RAW(String)) { return Variant::PACKED_STRING_ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) { + if (elem_class == CACHED_CLASS_RAW(Vector2)) { return Variant::PACKED_VECTOR2_ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) { + if (elem_class == CACHED_CLASS_RAW(Vector3)) { return Variant::PACKED_VECTOR3_ARRAY; } - if (array_type->eklass == CACHED_CLASS_RAW(Color)) { + if (elem_class == CACHED_CLASS_RAW(Color)) { return Variant::PACKED_COLOR_ARRAY; } - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + if (elem_class == CACHED_CLASS_RAW(StringName)) { + return Variant::ARRAY; + } + + if (elem_class == CACHED_CLASS_RAW(NodePath)) { + return Variant::ARRAY; + } + + if (elem_class == CACHED_CLASS_RAW(RID)) { + return Variant::ARRAY; + } + + if (mono_class_is_enum(elem_class)) { + return Variant::ARRAY; + } + + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(elem_class); if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { return Variant::ARRAY; } @@ -266,6 +282,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { return Variant::ARRAY; } + + // GodotObject + GDMonoClass *type_class = p_type.type_class; + if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { + return Variant::OBJECT; + } } break; default: { @@ -284,9 +306,8 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ switch (p_array_type.type_encoding) { case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(p_array_type.type_class->get_mono_type()); - GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); - r_elem_type = ManagedType::from_class(array_type_class); + MonoClass *elem_class = mono_class_get_element_class(p_array_type.type_class->get_mono_ptr()); + r_elem_type = ManagedType::from_class(elem_class); return true; } break; case MONO_TYPE_GENERICINST: { @@ -361,12 +382,23 @@ MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class return PackedColorArray_to_mono_array(p_var.operator PackedColorArray()); } + if (array_type->eklass == CACHED_CLASS_RAW(StringName)) { + return Array_to_mono_array(p_var.operator Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) { + return Array_to_mono_array(p_var.operator Array()); + } + + if (array_type->eklass == CACHED_CLASS_RAW(RID)) { + return Array_to_mono_array(p_var.operator Array()); + } + if (mono_class_is_assignable_from(CACHED_CLASS(GodotObject)->get_mono_ptr(), array_type->eklass)) { return Array_to_mono_array(p_var.operator ::Array(), array_type->eklass); } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to array of unsupported element type:" + - GDMonoClass::get_full_name(array_type->eklass) + "'."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to array of unsupported element type:" + GDMonoClass::get_full_name(array_type->eklass) + "'."); } MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class) { @@ -399,8 +431,7 @@ MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array)); } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" + - p_type_class->get_full_name() + "'."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" + p_type_class->get_full_name() + "'."); } MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class) { @@ -450,8 +481,12 @@ MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoCl return GDMonoUtils::create_managed_from(p_var.operator Array(), godot_array_class); } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" + - p_type_class->get_full_name() + "'."); + // GodotObject + if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) { + return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *()); + } + + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" + p_type_class->get_full_name() + "'."); } MonoObject *variant_to_mono_object(const Variant &p_var) { @@ -786,14 +821,12 @@ void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type RETURN_TYPE_VAL(uint64_t, val); } default: { - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + - GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'."); } } } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + - p_type.type_class->get_full_name() + "'."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'."); } break; #undef RETURN_TYPE_VAL case MONO_TYPE_STRING: @@ -809,8 +842,7 @@ void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type return variant_to_mono_object(p_var); } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + - itos(p_type.type_encoding) + "."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + "."); } MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) { @@ -943,14 +975,12 @@ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_ty return BOX_ENUM(enum_baseclass, val); } default: { - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + - GDMonoClass::get_full_name(enum_baseclass) + "'."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(enum_baseclass) + "'."); } } } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + - p_type.type_class->get_full_name() + "'."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'."); } break; case MONO_TYPE_STRING: return (MonoObject *)variant_to_mono_string(p_var); @@ -965,8 +995,7 @@ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_ty return variant_to_mono_object(p_var); } - ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + - itos(p_type.type_encoding) + "."); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + "."); } Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) { @@ -1118,6 +1147,18 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return mono_array_to_PackedColorArray((MonoArray *)p_obj); } + if (array_type->eklass == CACHED_CLASS_RAW(StringName)) { + return mono_array_to_Array((MonoArray *)p_obj); + } + + if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) { + return mono_array_to_Array((MonoArray *)p_obj); + } + + if (array_type->eklass == CACHED_CLASS_RAW(RID)) { + return mono_array_to_Array((MonoArray *)p_obj); + } + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { return mono_array_to_Array((MonoArray *)p_obj); @@ -1206,12 +1247,22 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); return system_generic_list_to_Array_variant(p_obj, p_type.type_class, elem_reftype); } + + // GodotObject + GDMonoClass *type_class = p_type.type_class; + if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) { + Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj)); + if (ptr != nullptr) { + RefCounted *rc = Object::cast_to<RefCounted>(ptr); + return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr); + } + return Variant(); + } } break; } if (p_fail_with_err) { - ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" + - p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + "."); + ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" + p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + "."); } else { return Variant(); } @@ -1271,7 +1322,7 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) + - ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; + ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); CRASH_COND(ctor == nullptr); @@ -1293,7 +1344,7 @@ MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoCl Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype); String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) + - ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; + ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; GDMonoMethod *godot_dict_ctor = godot_dict_class->get_method_with_desc(ctor_desc, true); CRASH_COND(godot_dict_ctor == nullptr); @@ -1315,7 +1366,6 @@ Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) { MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype); - MonoClass *elem_class = mono_class_from_mono_type(elem_type); String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + GDMonoUtils::get_type_desc(elem_type) + ">)"; GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); @@ -1324,7 +1374,10 @@ MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_cl MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); ERR_FAIL_NULL_V(mono_object, nullptr); - void *ctor_args[1] = { Array_to_mono_array(p_array, elem_class) }; + GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(p_elem_reftype); + MonoObject *godot_array = GDMonoUtils::create_managed_from(p_array, godot_array_class); + + void *ctor_args[1] = { godot_array }; MonoException *exc = nullptr; ctor->invoke_raw(mono_object, ctor_args, &exc); @@ -1683,12 +1736,12 @@ Callable managed_to_callable(const M_Callable &p_managed_callable) { CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate)); return Callable(managed_callable); } else { - Object *target = p_managed_callable.target ? - unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) : - nullptr; - StringName *method_ptr = p_managed_callable.method_string_name ? - unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)) : - nullptr; + Object *target = p_managed_callable.target + ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) + : nullptr; + StringName *method_ptr = p_managed_callable.method_string_name + ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)) + : nullptr; StringName method = method_ptr ? *method_ptr : StringName(); return Callable(target, method); } @@ -1731,12 +1784,12 @@ M_Callable callable_to_managed(const Callable &p_callable) { } Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) { - Object *owner = p_managed_signal.owner ? - unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) : - nullptr; - StringName *name_ptr = p_managed_signal.name_string_name ? - unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)) : - nullptr; + Object *owner = p_managed_signal.owner + ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) + : nullptr; + StringName *name_ptr = p_managed_signal.name_string_name + ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)) + : nullptr; StringName name = name_ptr ? *name_ptr : StringName(); return Signal(owner, name); } diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 88afc7ebc5..2f4b619b61 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -234,58 +234,58 @@ enum { #endif MATCHES_Vector2 = (MATCHES_real_t && (sizeof(Vector2) == (sizeof(real_t) * 2)) && - offsetof(Vector2, x) == (sizeof(real_t) * 0) && - offsetof(Vector2, y) == (sizeof(real_t) * 1)), + offsetof(Vector2, x) == (sizeof(real_t) * 0) && + offsetof(Vector2, y) == (sizeof(real_t) * 1)), MATCHES_Vector2i = (MATCHES_int && (sizeof(Vector2i) == (sizeof(int32_t) * 2)) && - offsetof(Vector2i, x) == (sizeof(int32_t) * 0) && - offsetof(Vector2i, y) == (sizeof(int32_t) * 1)), + offsetof(Vector2i, x) == (sizeof(int32_t) * 0) && + offsetof(Vector2i, y) == (sizeof(int32_t) * 1)), MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) && - offsetof(Rect2, position) == (sizeof(Vector2) * 0) && - offsetof(Rect2, size) == (sizeof(Vector2) * 1)), + offsetof(Rect2, position) == (sizeof(Vector2) * 0) && + offsetof(Rect2, size) == (sizeof(Vector2) * 1)), MATCHES_Rect2i = (MATCHES_Vector2i && (sizeof(Rect2i) == (sizeof(Vector2i) * 2)) && - offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) && - offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)), + offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) && + offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)), MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) && - offsetof(Vector3, x) == (sizeof(real_t) * 0) && - offsetof(Vector3, y) == (sizeof(real_t) * 1) && - offsetof(Vector3, z) == (sizeof(real_t) * 2)), + offsetof(Vector3, x) == (sizeof(real_t) * 0) && + offsetof(Vector3, y) == (sizeof(real_t) * 1) && + offsetof(Vector3, z) == (sizeof(real_t) * 2)), MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) && - offsetof(Vector3i, x) == (sizeof(int32_t) * 0) && - offsetof(Vector3i, y) == (sizeof(int32_t) * 1) && - offsetof(Vector3i, z) == (sizeof(int32_t) * 2)), + offsetof(Vector3i, x) == (sizeof(int32_t) * 0) && + offsetof(Vector3i, y) == (sizeof(int32_t) * 1) && + offsetof(Vector3i, z) == (sizeof(int32_t) * 2)), MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array MATCHES_Quaternion = (MATCHES_real_t && (sizeof(Quaternion) == (sizeof(real_t) * 4)) && - offsetof(Quaternion, x) == (sizeof(real_t) * 0) && - offsetof(Quaternion, y) == (sizeof(real_t) * 1) && - offsetof(Quaternion, z) == (sizeof(real_t) * 2) && - offsetof(Quaternion, w) == (sizeof(real_t) * 3)), + offsetof(Quaternion, x) == (sizeof(real_t) * 0) && + offsetof(Quaternion, y) == (sizeof(real_t) * 1) && + offsetof(Quaternion, z) == (sizeof(real_t) * 2) && + offsetof(Quaternion, w) == (sizeof(real_t) * 3)), MATCHES_Transform3D = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform3D) == (sizeof(Basis) + sizeof(Vector3))) && - offsetof(Transform3D, basis) == 0 && - offsetof(Transform3D, origin) == sizeof(Basis)), + offsetof(Transform3D, basis) == 0 && + offsetof(Transform3D, origin) == sizeof(Basis)), MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) && - offsetof(AABB, position) == (sizeof(Vector3) * 0) && - offsetof(AABB, size) == (sizeof(Vector3) * 1)), + offsetof(AABB, position) == (sizeof(Vector3) * 0) && + offsetof(AABB, size) == (sizeof(Vector3) * 1)), MATCHES_Color = (MATCHES_float && (sizeof(Color) == (sizeof(float) * 4)) && - offsetof(Color, r) == (sizeof(float) * 0) && - offsetof(Color, g) == (sizeof(float) * 1) && - offsetof(Color, b) == (sizeof(float) * 2) && - offsetof(Color, a) == (sizeof(float) * 3)), + offsetof(Color, r) == (sizeof(float) * 0) && + offsetof(Color, g) == (sizeof(float) * 1) && + offsetof(Color, b) == (sizeof(float) * 2) && + offsetof(Color, a) == (sizeof(float) * 3)), MATCHES_Plane = (MATCHES_Vector3 && MATCHES_real_t && (sizeof(Plane) == (sizeof(Vector3) + sizeof(real_t))) && - offsetof(Plane, normal) == 0 && - offsetof(Plane, d) == sizeof(Vector3)) + offsetof(Plane, normal) == 0 && + offsetof(Plane, d) == sizeof(Vector3)) }; // In the future we may force this if we want to ref return these structs diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index 5391b7775e..5c7cf29e88 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -65,6 +65,8 @@ GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_own type.type_class = GDMono::get_singleton()->get_class(param_type_class); } + param_buffer_size = GDMonoMarshal::variant_get_managed_unboxed_size(type); + attrs_fetched = false; attributes = nullptr; } @@ -147,24 +149,20 @@ bool GDMonoProperty::has_setter() { return mono_property_get_set_method(mono_property) != nullptr; } -void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { - MonoMethod *prop_method = mono_property_get_set_method(mono_property); - void *params[1] = { p_value }; - MonoException *exc = nullptr; - GDMonoUtils::runtime_invoke(prop_method, p_object, params, &exc); - if (exc) { - if (r_exc) { - *r_exc = exc; - } else { - GDMonoUtils::set_pending_exception(exc); - } - } -} +void GDMonoProperty::set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc) { + uint8_t *buffer = (uint8_t *)alloca(param_buffer_size); + unsigned int offset = 0; -void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { - MonoException *exc = nullptr; - GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc); + void *params[1] = { + GDMonoMarshal::variant_to_managed_unboxed(p_value, type, buffer, offset) + }; + +#ifdef DEBUG_ENABLED + CRASH_COND(offset != param_buffer_size); +#endif + MonoException *exc = nullptr; + GDMonoUtils::property_set_value(mono_property, p_object, params, &exc); if (exc) { if (r_exc) { *r_exc = exc; diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index af7a2c02e5..9bb1caa759 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -45,6 +45,8 @@ class GDMonoProperty : public IMonoClassMember { bool attrs_fetched; MonoCustomAttrInfo *attributes; + unsigned int param_buffer_size; + public: virtual GDMonoClass *get_enclosing_class() const final { return owner; } @@ -64,8 +66,7 @@ public: _FORCE_INLINE_ ManagedType get_type() const { return type; } - void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = nullptr); - void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr); + void set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc = nullptr); MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = nullptr); bool get_bool_value(MonoObject *p_object); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 13939bd014..09aa9ad948 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -450,7 +450,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { int line = si.size() ? si[0].line : __LINE__; String error_msg = "Unhandled exception"; - EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si); + EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, true, ERR_HANDLER_ERROR, si); #endif } diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h index 366662ff81..c49a62a632 100644 --- a/modules/mono/mono_gd/gd_mono_wasm_m2n.h +++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.h @@ -158,7 +158,7 @@ T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) { return (T)(size_t)p_margs->iargs[p_idx]; } else if constexpr (cookie == 'L') { static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> || - (sizeof(void *) == 8 && std::is_pointer_v<T>), + (sizeof(void *) == 8 && std::is_pointer_v<T>), "Invalid type for cookie 'L'."); union { diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 6b616dd52d..d0a27b27c1 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -35,7 +35,7 @@ #include "core/os/os.h" -// Here, after os/os.h +#define WIN32_LEAN_AND_MEAN #include <windows.h> namespace MonoRegUtils { diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index ec04d50704..64aec5d359 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -36,6 +36,7 @@ #include "core/os/os.h" #ifdef WINDOWS_ENABLED +#define WIN32_LEAN_AND_MEAN #include <windows.h> #define ENV_PATH_SEP ";" diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 2fb5e446da..74f5e6d18a 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -139,24 +139,24 @@ bool is_csharp_keyword(const String &p_name) { // Reserved keywords return p_name == "abstract" || p_name == "as" || p_name == "base" || p_name == "bool" || - p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" || - p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" || - p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" || - p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" || - p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" || - p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" || - p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" || - p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" || - p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" || - p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" || - p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" || - p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" || - p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" || - p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" || - p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" || - p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" || - p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" || - p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while"; + p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" || + p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" || + p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" || + p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" || + p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" || + p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" || + p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" || + p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" || + p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" || + p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" || + p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" || + p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" || + p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" || + p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" || + p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" || + p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" || + p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" || + p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while"; } String escape_csharp_keyword(const String &p_name) { @@ -201,11 +201,11 @@ String str_format(const char *p_format, ...) { } #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) +#define RSnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf_s(m_buffer, m_count, _TRUNCATE, m_format, m_args_copy) +#define RScprintf(m_format, m_args_copy) _vscprintf(m_format, m_args_copy) #else -#define gd_vsnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf(m_buffer, m_count, m_format, m_args_copy) -#define gd_vscprintf(m_format, m_args_copy) vsnprintf(nullptr, 0, p_format, m_args_copy) +#define RSnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf(m_buffer, m_count, m_format, m_args_copy) +#define RScprintf(m_format, m_args_copy) vsnprintf(nullptr, 0, p_format, m_args_copy) #endif String str_format(const char *p_format, va_list p_list) { @@ -231,7 +231,7 @@ char *str_format_new(const char *p_format, va_list p_list) { va_list list; va_copy(list, p_list); - int len = gd_vscprintf(p_format, list); + int len = RScprintf(p_format, list); va_end(list); len += 1; // for the trailing '/0' @@ -239,7 +239,7 @@ char *str_format_new(const char *p_format, va_list p_list) { char *buffer(memnew_arr(char, len)); va_copy(list, p_list); - gd_vsnprintf(buffer, len, p_format, list); + RSnprintf(buffer, len, p_format, list); va_end(list); return buffer; diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index bb6bc578a4..8fd3a13e1f 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -489,7 +489,7 @@ NavigationMeshGenerator::~NavigationMeshGenerator() { } void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) { - ERR_FAIL_COND(!p_nav_mesh.is_valid()); + ERR_FAIL_COND_MSG(!p_nav_mesh.is_valid(), "Invalid navigation mesh."); #ifdef TOOLS_ENABLED EditorProgress *ep(nullptr); diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub index e415d92498..f15525648f 100644 --- a/modules/ogg/SCsub +++ b/modules/ogg/SCsub @@ -3,9 +3,6 @@ Import("env") Import("env_modules") -# Only kept to build the thirdparty library used by the theora and webm -# modules. - env_ogg = env_modules.Clone() # Thirdparty source files diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp index b7a3ad2876..abb2b67ab0 100644 --- a/modules/ogg/ogg_packet_sequence.cpp +++ b/modules/ogg/ogg_packet_sequence.cpp @@ -127,9 +127,9 @@ void OGGPacketSequence::_bind_methods() { ClassDB::bind_method(D_METHOD("get_length"), &OGGPacketSequence::get_length); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "packet_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_packet_data", "get_packet_data"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "granule_positions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_packet_granule_positions", "get_packet_granule_positions"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sampling_rate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_sampling_rate", "get_sampling_rate"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "packet_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_data", "get_packet_data"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "granule_positions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_granule_positions", "get_packet_granule_positions"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sampling_rate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_sampling_rate", "get_sampling_rate"); } bool OGGPacketSequencePlayback::next_ogg_packet(ogg_packet **p_packet) const { diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index 8a10411cf6..16fea228b1 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -8,8 +8,9 @@ NoiseTexture can also generate normal map textures. The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_image] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image and the generated byte data: [codeblock] - var texture = preload("res://noise.tres") - yield(texture, "changed") + var texture = NoiseTexture.new() + texture.noise = OpenSimplexNoise.new() + await texture.changed var image = texture.get_image() var data = image.get_data() [/codeblock] diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp index 9db3f3d5fd..e36dcfcea5 100644 --- a/modules/opensimplex/noise_texture.cpp +++ b/modules/opensimplex/noise_texture.cpp @@ -80,7 +80,7 @@ void NoiseTexture::_bind_methods() { void NoiseTexture::_validate_property(PropertyInfo &property) const { if (property.name == "bump_strength") { if (!as_normal_map) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } } diff --git a/modules/opus/SCsub b/modules/opus/SCsub deleted file mode 100644 index 1437cd86df..0000000000 --- a/modules/opus/SCsub +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_modules") - -# Only kept to build the thirdparty library used by the webm module. -# AudioStreamOpus was dropped in 3.0 due to incompatibility with the new audio -# engine. If you want to port it, fetch it from the Git history. - -env_opus = env_modules.Clone() - -# Thirdparty source files - -thirdparty_obj = [] - -# Thirdparty source files -if env["builtin_opus"]: - thirdparty_dir = "#thirdparty/opus/" - - thirdparty_sources = [ - # Sync with opus_sources.mk - "opus.c", - "opus_decoder.c", - "opus_encoder.c", - "opus_multistream.c", - "opus_multistream_encoder.c", - "opus_multistream_decoder.c", - "repacketizer.c", - "analysis.c", - "mlp.c", - "mlp_data.c", - # Sync with libopusfile Makefile.am - "info.c", - "internal.c", - "opusfile.c", - "stream.c", - # Sync with celt_sources.mk - "celt/bands.c", - "celt/celt.c", - "celt/celt_encoder.c", - "celt/celt_decoder.c", - "celt/cwrs.c", - "celt/entcode.c", - "celt/entdec.c", - "celt/entenc.c", - "celt/kiss_fft.c", - "celt/laplace.c", - "celt/mathops.c", - "celt/mdct.c", - "celt/modes.c", - "celt/pitch.c", - "celt/celt_lpc.c", - "celt/quant_bands.c", - "celt/rate.c", - "celt/vq.c", - # "celt/arm/arm_celt_map.c", - # "celt/arm/armcpu.c", - # "celt/arm/celt_ne10_fft.c", - # "celt/arm/celt_ne10_mdct.c", - # "celt/arm/celt_neon_intr.c", - # Sync with silk_sources.mk - "silk/CNG.c", - "silk/code_signs.c", - "silk/init_decoder.c", - "silk/decode_core.c", - "silk/decode_frame.c", - "silk/decode_parameters.c", - "silk/decode_indices.c", - "silk/decode_pulses.c", - "silk/decoder_set_fs.c", - "silk/dec_API.c", - "silk/enc_API.c", - "silk/encode_indices.c", - "silk/encode_pulses.c", - "silk/gain_quant.c", - "silk/interpolate.c", - "silk/LP_variable_cutoff.c", - "silk/NLSF_decode.c", - "silk/NSQ.c", - "silk/NSQ_del_dec.c", - "silk/PLC.c", - "silk/shell_coder.c", - "silk/tables_gain.c", - "silk/tables_LTP.c", - "silk/tables_NLSF_CB_NB_MB.c", - "silk/tables_NLSF_CB_WB.c", - "silk/tables_other.c", - "silk/tables_pitch_lag.c", - "silk/tables_pulses_per_block.c", - "silk/VAD.c", - "silk/control_audio_bandwidth.c", - "silk/quant_LTP_gains.c", - "silk/VQ_WMat_EC.c", - "silk/HP_variable_cutoff.c", - "silk/NLSF_encode.c", - "silk/NLSF_VQ.c", - "silk/NLSF_unpack.c", - "silk/NLSF_del_dec_quant.c", - "silk/process_NLSFs.c", - "silk/stereo_LR_to_MS.c", - "silk/stereo_MS_to_LR.c", - "silk/check_control_input.c", - "silk/control_SNR.c", - "silk/init_encoder.c", - "silk/control_codec.c", - "silk/A2NLSF.c", - "silk/ana_filt_bank_1.c", - "silk/biquad_alt.c", - "silk/bwexpander_32.c", - "silk/bwexpander.c", - "silk/debug.c", - "silk/decode_pitch.c", - "silk/inner_prod_aligned.c", - "silk/lin2log.c", - "silk/log2lin.c", - "silk/LPC_analysis_filter.c", - "silk/LPC_inv_pred_gain.c", - "silk/table_LSF_cos.c", - "silk/NLSF2A.c", - "silk/NLSF_stabilize.c", - "silk/NLSF_VQ_weights_laroia.c", - "silk/pitch_est_tables.c", - "silk/resampler.c", - "silk/resampler_down2_3.c", - "silk/resampler_down2.c", - "silk/resampler_private_AR2.c", - "silk/resampler_private_down_FIR.c", - "silk/resampler_private_IIR_FIR.c", - "silk/resampler_private_up2_HQ.c", - "silk/resampler_rom.c", - "silk/sigm_Q15.c", - "silk/sort.c", - "silk/sum_sqr_shift.c", - "silk/stereo_decode_pred.c", - "silk/stereo_encode_pred.c", - "silk/stereo_find_predictor.c", - "silk/stereo_quant_pred.c", - ] - - opus_sources_silk = [] - - if env["platform"] in ["android", "iphone", "javascript"]: - env_opus.Append(CPPDEFINES=["FIXED_POINT"]) - opus_sources_silk = [ - "silk/fixed/LTP_analysis_filter_FIX.c", - "silk/fixed/LTP_scale_ctrl_FIX.c", - "silk/fixed/corrMatrix_FIX.c", - "silk/fixed/encode_frame_FIX.c", - "silk/fixed/find_LPC_FIX.c", - "silk/fixed/find_LTP_FIX.c", - "silk/fixed/find_pitch_lags_FIX.c", - "silk/fixed/find_pred_coefs_FIX.c", - "silk/fixed/noise_shape_analysis_FIX.c", - "silk/fixed/prefilter_FIX.c", - "silk/fixed/process_gains_FIX.c", - "silk/fixed/regularize_correlations_FIX.c", - "silk/fixed/residual_energy16_FIX.c", - "silk/fixed/residual_energy_FIX.c", - "silk/fixed/solve_LS_FIX.c", - "silk/fixed/warped_autocorrelation_FIX.c", - "silk/fixed/apply_sine_window_FIX.c", - "silk/fixed/autocorr_FIX.c", - "silk/fixed/burg_modified_FIX.c", - "silk/fixed/k2a_FIX.c", - "silk/fixed/k2a_Q16_FIX.c", - "silk/fixed/pitch_analysis_core_FIX.c", - "silk/fixed/vector_ops_FIX.c", - "silk/fixed/schur64_FIX.c", - "silk/fixed/schur_FIX.c", - ] - else: - opus_sources_silk = [ - "silk/float/apply_sine_window_FLP.c", - "silk/float/corrMatrix_FLP.c", - "silk/float/encode_frame_FLP.c", - "silk/float/find_LPC_FLP.c", - "silk/float/find_LTP_FLP.c", - "silk/float/find_pitch_lags_FLP.c", - "silk/float/find_pred_coefs_FLP.c", - "silk/float/LPC_analysis_filter_FLP.c", - "silk/float/LTP_analysis_filter_FLP.c", - "silk/float/LTP_scale_ctrl_FLP.c", - "silk/float/noise_shape_analysis_FLP.c", - "silk/float/prefilter_FLP.c", - "silk/float/process_gains_FLP.c", - "silk/float/regularize_correlations_FLP.c", - "silk/float/residual_energy_FLP.c", - "silk/float/solve_LS_FLP.c", - "silk/float/warped_autocorrelation_FLP.c", - "silk/float/wrappers_FLP.c", - "silk/float/autocorrelation_FLP.c", - "silk/float/burg_modified_FLP.c", - "silk/float/bwexpander_FLP.c", - "silk/float/energy_FLP.c", - "silk/float/inner_product_FLP.c", - "silk/float/k2a_FLP.c", - "silk/float/levinsondurbin_FLP.c", - "silk/float/LPC_inv_pred_gain_FLP.c", - "silk/float/pitch_analysis_core_FLP.c", - "silk/float/scale_copy_vector_FLP.c", - "silk/float/scale_vector_FLP.c", - "silk/float/schur_FLP.c", - "silk/float/sort_FLP.c", - ] - - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources + opus_sources_silk] - - # also requires libogg - if env["builtin_libogg"]: - env_opus.Prepend(CPPPATH=["#thirdparty/libogg"]) - - env_opus.Append(CPPDEFINES=["HAVE_CONFIG_H"]) - - thirdparty_include_paths = [ - "", - "celt", - "opus", - "silk", - "silk/fixed", - "silk/float", - ] - env_opus.Prepend(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths]) - - if env["platform"] == "android": - if "android_arch" in env and env["android_arch"] == "armv7": - env_opus.Append(CPPDEFINES=["OPUS_ARM_OPT"]) - elif "android_arch" in env and env["android_arch"] == "arm64v8": - env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"]) - elif env["platform"] == "iphone": - if "arch" in env and env["arch"] == "arm": - env_opus.Append(CPPDEFINES=["OPUS_ARM_OPT"]) - elif "arch" in env and env["arch"] == "arm64": - env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"]) - elif env["platform"] == "osx": - if "arch" in env and env["arch"] == "arm64": - env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"]) - - env_thirdparty = env_opus.Clone() - env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) - env.modules_sources += thirdparty_obj - - -# Godot source files - -module_obj = [] - -env_opus.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/opus/config.py b/modules/opus/config.py deleted file mode 100644 index 9ff7b2dece..0000000000 --- a/modules/opus/config.py +++ /dev/null @@ -1,6 +0,0 @@ -def can_build(env, platform): - return env.module_check_dependencies("opus", ["ogg"]) - - -def configure(env): - pass diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index cb12976090..ffa900ef99 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -390,15 +390,15 @@ static void get_modulation_value(int x, int y, const int p_2bit, const int p_mod rep_vals0[p_modulation[y + 1][x]] + rep_vals0[p_modulation[y][x - 1]] + rep_vals0[p_modulation[y][x + 1]] + 2) / - 4; + 4; } else if (p_modulation_modes[y][x] == 2) { mod_val = (rep_vals0[p_modulation[y][x - 1]] + rep_vals0[p_modulation[y][x + 1]] + 1) / - 2; + 2; } else { mod_val = (rep_vals0[p_modulation[y - 1][x]] + rep_vals0[p_modulation[y + 1][x]] + 1) / - 2; + 2; } } else { mod_val = rep_vals1[p_modulation[y][x]]; diff --git a/modules/raycast/config.py b/modules/raycast/config.py index 5de36c5322..7e8b3e9840 100644 --- a/modules/raycast/config.py +++ b/modules/raycast/config.py @@ -1,5 +1,7 @@ def can_build(env, platform): # Depends on Embree library, which only supports x86_64 and aarch64. + if env["arch"].startswith("rv") or env["arch"].startswith("ppc"): + return False if platform == "android": return env["android_arch"] in ["arm64v8", "x86_64"] diff --git a/modules/regex/config.py b/modules/regex/config.py index df9f44cb95..1248a8374d 100644 --- a/modules/regex/config.py +++ b/modules/regex/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return True + return not env["arch"].startswith("rv") def configure(env): diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 7cd4db6f67..d6a96282f3 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -7,7 +7,6 @@ env_text_server_adv = env_modules.Clone() def make_icu_data(target, source, env): - import os dst = target[0].srcnode().abspath @@ -24,7 +23,6 @@ def make_icu_data(target, source, env): f = open(source[0].srcnode().abspath, "rb") buf = f.read() - import os.path g.write('extern "C" U_EXPORT const size_t U_ICUDATA_SIZE = ' + str(len(buf)) + ";\n") g.write('extern "C" U_EXPORT const unsigned char U_ICUDATA_ENTRY_POINT[] = {\n') @@ -272,6 +270,7 @@ if env["builtin_icu"]: "common/dictionarydata.cpp", "common/dtintrv.cpp", "common/edits.cpp", + "common/emojiprops.cpp", "common/errorcode.cpp", "common/filteredbrk.cpp", "common/filterednormalizer2.cpp", @@ -293,6 +292,7 @@ if env["builtin_icu"]: "common/locresdata.cpp", "common/locutil.cpp", "common/lsr.cpp", + "common/lstmbe.cpp", "common/messagepattern.cpp", "common/normalizer2.cpp", "common/normalizer2impl.cpp", @@ -450,7 +450,7 @@ if env["builtin_icu"]: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - icu_data_name = "icudt69l.dat" + icu_data_name = "icudt70l.dat" if env_icu["tools"]: env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name) diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index eaa8825a1d..1a0b0e357d 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -46,13 +46,13 @@ #endif /*************************************************************************/ -/* hb_bmp_font_t HarfBuzz Bitmap font interface */ +/* 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); +TextServerAdvanced::bmp_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) { + bmp_font_t *bm_font = memnew(bmp_font_t); if (!bm_font) { return nullptr; @@ -64,13 +64,13 @@ TextServerAdvanced::hb_bmp_font_t *TextServerAdvanced::_hb_bmp_font_create(TextS 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); +void TextServerAdvanced::_bmp_font_destroy(void *p_data) { + bmp_font_t *bm_font = reinterpret_cast<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); +hb_bool_t TextServerAdvanced::_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 bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data); if (!bm_font->face) { return false; @@ -89,8 +89,8 @@ hb_bool_t TextServerAdvanced::hb_bmp_get_nominal_glyph(hb_font_t *p_font, void * 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); +hb_position_t TextServerAdvanced::_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) { + const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data); if (!bm_font->face) { return 0; @@ -103,8 +103,8 @@ hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_advance(hb_font_t *p_font, 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); +hb_position_t TextServerAdvanced::_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) { + const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data); if (!bm_font->face) { return 0; @@ -117,8 +117,8 @@ hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_advance(hb_font_t *p_font, 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); +hb_position_t TextServerAdvanced::_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 bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data); if (!bm_font->face) { return 0; @@ -131,8 +131,8 @@ hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].x * 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); +hb_bool_t TextServerAdvanced::_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 bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data); if (!bm_font->face) { return false; @@ -148,8 +148,8 @@ hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void 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); +hb_bool_t TextServerAdvanced::_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 bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data); if (!bm_font->face) { return false; @@ -167,8 +167,8 @@ hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_extents(hb_font_t *p_font, void * 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); +hb_bool_t TextServerAdvanced::_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 bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data); if (!bm_font->face) { return false; @@ -181,40 +181,40 @@ hb_bool_t TextServerAdvanced::hb_bmp_get_font_h_extents(hb_font_t *p_font, void return true; } -void TextServerAdvanced::hb_bmp_create_font_funcs() { +void TextServerAdvanced::_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_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr); + hb_font_funcs_set_font_h_extents_func(funcs, _bmp_get_font_h_extents, nullptr, nullptr); + hb_font_funcs_set_nominal_glyph_func(funcs, _bmp_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advance_func(funcs, _bmp_get_glyph_h_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advance_func(funcs, _bmp_get_glyph_v_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func(funcs, _bmp_get_glyph_v_origin, nullptr, nullptr); + hb_font_funcs_set_glyph_h_kerning_func(funcs, _bmp_get_glyph_h_kerning, nullptr, nullptr); + hb_font_funcs_set_glyph_extents_func(funcs, _bmp_get_glyph_extents, nullptr, nullptr); hb_font_funcs_make_immutable(funcs); } } -void TextServerAdvanced::hb_bmp_free_font_funcs() { +void TextServerAdvanced::_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); +void TextServerAdvanced::_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) { + hb_font_set_funcs(p_font, funcs, _bmp_font_create(p_face, p_unref), _bmp_font_destroy); } -hb_font_t *TextServerAdvanced::hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) { +hb_font_t *TextServerAdvanced::_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); + _bmp_font_set_funcs(font, p_face, false); return font; } @@ -442,9 +442,7 @@ bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) const { } } -static Map<StringName, int32_t> feature_sets; - -static void _insert_feature_sets() { +void TextServerAdvanced::_insert_feature_sets() { // Registered OpenType feature tags. feature_sets.insert("access_all_alternates", HB_TAG('a', 'a', 'l', 't')); feature_sets.insert("above_base_forms", HB_TAG('a', 'b', 'v', 'f')); @@ -740,6 +738,10 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_ continue; } + if (ct.offsets.size() < ct.texture_w) { + continue; + } + ret.y = 0x7FFFFFFF; ret.x = 0; @@ -956,7 +958,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( 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)); @@ -986,10 +987,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( 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; } } } @@ -1054,13 +1051,12 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma 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 + 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 + 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); @@ -1215,13 +1211,14 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_d } _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const { + ERR_FAIL_COND_V(p_size.x <= 0, false); if (p_font_data->cache.has(p_size)) { return true; } FontDataForSizeAdvanced *fd = memnew(FontDataForSizeAdvanced); fd->size = p_size; - if (p_font_data->data_ptr) { + if (p_font_data->data_ptr && (p_font_data->data_size > 0)) { // Init dynamic font. #ifdef MODULE_FREETYPE_ENABLED int error = 0; @@ -1282,6 +1279,23 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced 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 style flags and name. + if (fd->face->family_name != nullptr) { + p_font_data->font_name = String::utf8((const char *)fd->face->family_name); + } + if (fd->face->style_name != nullptr) { + p_font_data->style_name = String::utf8((const char *)fd->face->style_name); + } + p_font_data->style_flags = 0; + if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) { + p_font_data->style_flags |= FONT_BOLD; + } + if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) { + p_font_data->style_flags |= FONT_ITALIC; + } + if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) { + p_font_data->style_flags |= FONT_FIXED_WIDTH; + } // 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); @@ -1594,7 +1608,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced #endif } else { // Init bitmap font. - fd->hb_handle = hb_bmp_font_create(fd, nullptr); + fd->hb_handle = _bmp_font_create(fd, nullptr); } p_font_data->cache[p_size] = fd; return true; @@ -1651,6 +1665,66 @@ void TextServerAdvanced::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data fd->data_size = p_data_size; } +void TextServerAdvanced::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) { + FontDataAdvanced *fd = font_owner.get_or_null(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->style_flags = p_style; +} + +uint32_t /*FontStyle*/ TextServerAdvanced::font_get_style(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + return fd->style_flags; +} + +void TextServerAdvanced::font_set_style_name(RID p_font_rid, const String &p_name) { + FontDataAdvanced *fd = font_owner.get_or_null(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->style_name = p_name; +} + +String TextServerAdvanced::font_get_style_name(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, String()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String()); + return fd->style_name; +} + +void TextServerAdvanced::font_set_name(RID p_font_rid, const String &p_name) { + FontDataAdvanced *fd = font_owner.get_or_null(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->font_name = p_name; +} + +String TextServerAdvanced::font_get_name(RID p_font_rid) const { + FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, String()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String()); + return fd->font_name; +} + void TextServerAdvanced::font_set_antialiased(RID p_font_rid, bool p_antialiased) { FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -2123,6 +2197,7 @@ void TextServerAdvanced::font_set_texture_offsets(RID p_font_rid, const Vector2i 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); } @@ -2480,6 +2555,8 @@ Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const V 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.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0); + ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), 0, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + "."); + ERR_FAIL_COND_V_MSG((p_variation_selector >= 0xd800 && p_variation_selector <= 0xdfff) || (p_variation_selector > 0x10ffff), 0, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_variation_selector, 16) + "."); MutexLock lock(fd->mutex); Vector2i size = _get_size(fd, p_size); @@ -2503,6 +2580,7 @@ int32_t TextServerAdvanced::font_get_glyph_index(RID p_font_rid, int p_size, cha bool TextServerAdvanced::font_has_char(RID p_font_rid, char32_t p_char) const { FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); + ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), false, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + "."); MutexLock lock(fd->mutex); if (fd->cache.is_empty()) { @@ -2555,6 +2633,8 @@ String TextServerAdvanced::font_get_supported_chars(RID p_font_rid) const { 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.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); + ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + "."); + ERR_FAIL_COND_MSG((p_end >= 0xd800 && p_end <= 0xdfff) || (p_end > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_end, 16) + "."); MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); @@ -3182,7 +3262,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, E.value.rect.position.y -= E.value.rect.size.y / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.y); @@ -3210,7 +3290,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, E.value.rect.position.x -= E.value.rect.size.x / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.x); @@ -3373,7 +3453,7 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng E.value.rect.position.y -= E.value.rect.size.y / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.y); @@ -3401,7 +3481,7 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng E.value.rect.position.x -= E.value.rect.size.x / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.x); @@ -3673,10 +3753,10 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; } if (sd_glyphs[i].count > 0) { - bool above_min_char_treshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters; + bool above_min_char_threshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters; - if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) { - if (cut_per_word && above_min_char_treshold) { + if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) { + if (cut_per_word && above_min_char_threshold) { if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { last_valid_cut = i; found = true; @@ -3688,7 +3768,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl if (found) { trim_pos = last_valid_cut; - if (add_ellipsis && (above_min_char_treshold || enforce_ellipsis) && width - ellipsis_width <= p_width) { + if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) { ellipsis_pos = trim_pos; } break; @@ -4519,7 +4599,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { E.value.rect.position.y -= E.value.rect.size.y / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.y); @@ -4547,7 +4627,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { E.value.rect.position.x -= E.value.rect.size.x / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.x); @@ -4713,76 +4793,186 @@ float TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) cons return sd->uthk; } -struct num_system_data { - Set<String> lang; - String digits; - String percent_sign; - String exp; -}; +void TextServerAdvanced::_insert_num_systems_lang() { + // Eastern Arabic numerals. + { + NumSystemData ar; + ar.lang.insert(StringName("ar")); // Arabic + ar.lang.insert(StringName("ar_AE")); + ar.lang.insert(StringName("ar_BH")); + ar.lang.insert(StringName("ar_DJ")); + ar.lang.insert(StringName("ar_EG")); + ar.lang.insert(StringName("ar_ER")); + ar.lang.insert(StringName("ar_IL")); + ar.lang.insert(StringName("ar_IQ")); + ar.lang.insert(StringName("ar_JO")); + ar.lang.insert(StringName("ar_KM")); + ar.lang.insert(StringName("ar_KW")); + ar.lang.insert(StringName("ar_LB")); + ar.lang.insert(StringName("ar_MR")); + ar.lang.insert(StringName("ar_OM")); + ar.lang.insert(StringName("ar_PS")); + ar.lang.insert(StringName("ar_QA")); + ar.lang.insert(StringName("ar_SA")); + ar.lang.insert(StringName("ar_SD")); + ar.lang.insert(StringName("ar_SO")); + ar.lang.insert(StringName("ar_SS")); + ar.lang.insert(StringName("ar_SY")); + ar.lang.insert(StringName("ar_TD")); + ar.lang.insert(StringName("ar_YE")); + ar.lang.insert(StringName("ckb")); // Central Kurdish + ar.lang.insert(StringName("ckb_IQ")); + ar.lang.insert(StringName("ckb_IR")); + ar.lang.insert(StringName("sd")); // Sindhi + ar.lang.insert(StringName("sd_PK")); + ar.lang.insert(StringName("sd_Arab")); + ar.lang.insert(StringName("sd_Arab_PK")); + ar.digits = U"٠١٢٣٤٥٦٧٨٩٫"; + ar.percent_sign = U"٪"; + ar.exp = U"اس"; + num_systems.push_back(ar); + } + + // Persian and Urdu numerals. + { + NumSystemData pr; + pr.lang.insert(StringName("fa")); // Persian + pr.lang.insert(StringName("fa_AF")); + pr.lang.insert(StringName("fa_IR")); + pr.lang.insert(StringName("ks")); // Kashmiri + pr.lang.insert(StringName("ks_IN")); + pr.lang.insert(StringName("ks_Arab")); + pr.lang.insert(StringName("ks_Arab_IN")); + pr.lang.insert(StringName("lrc")); // Northern Luri + pr.lang.insert(StringName("lrc_IQ")); + pr.lang.insert(StringName("lrc_IR")); + pr.lang.insert(StringName("mzn")); // Mazanderani + pr.lang.insert(StringName("mzn_IR")); + pr.lang.insert(StringName("pa_PK")); // Panjabi + pr.lang.insert(StringName("pa_Arab")); + pr.lang.insert(StringName("pa_Arab_PK")); + pr.lang.insert(StringName("ps")); // Pushto + pr.lang.insert(StringName("ps_AF")); + pr.lang.insert(StringName("ps_PK")); + pr.lang.insert(StringName("ur_IN")); // Urdu + pr.lang.insert(StringName("uz_AF")); // Uzbek + pr.lang.insert(StringName("uz_Arab")); + pr.lang.insert(StringName("uz_Arab_AF")); + pr.digits = U"۰۱۲۳۴۵۶۷۸۹٫"; + pr.percent_sign = U"٪"; + pr.exp = U"اس"; + num_systems.push_back(pr); + } + + // Bengali numerals. + { + NumSystemData bn; + bn.lang.insert(StringName("as")); // Assamese + bn.lang.insert(StringName("as_IN")); + bn.lang.insert(StringName("bn")); // Bengali + bn.lang.insert(StringName("bn_BD")); + bn.lang.insert(StringName("bn_IN")); + bn.lang.insert(StringName("mni")); // Manipuri + bn.lang.insert(StringName("mni_IN")); + bn.lang.insert(StringName("mni_Beng")); + bn.lang.insert(StringName("mni_Beng_IN")); + bn.digits = U"০১২৩৪৫৬৭৮৯."; + bn.percent_sign = U"%"; + bn.exp = U"e"; + num_systems.push_back(bn); + } + + // Devanagari numerals. + { + NumSystemData mr; + mr.lang.insert(StringName("mr")); // Marathi + mr.lang.insert(StringName("mr_IN")); + mr.lang.insert(StringName("ne")); // Nepali + mr.lang.insert(StringName("ne_IN")); + mr.lang.insert(StringName("ne_NP")); + mr.lang.insert(StringName("sa")); // Sanskrit + mr.lang.insert(StringName("sa_IN")); + mr.digits = U"०१२३४५६७८९."; + mr.percent_sign = U"%"; + mr.exp = U"e"; + num_systems.push_back(mr); + } + + // Dzongkha numerals. + { + NumSystemData dz; + dz.lang.insert(StringName("dz")); // Dzongkha + dz.lang.insert(StringName("dz_BT")); + dz.digits = U"༠༡༢༣༤༥༦༧༨༩."; + dz.percent_sign = U"%"; + dz.exp = U"e"; + num_systems.push_back(dz); + } -static num_system_data num_systems[]{ - { Set<String>(), U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" }, - { Set<String>(), U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" }, - { Set<String>(), U"০১২৩৪৫৬৭৮৯.", U"%", U"e" }, - { Set<String>(), U"०१२३४५६७८९.", U"%", U"e" }, - { Set<String>(), U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" }, - { Set<String>(), U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" }, - { Set<String>(), U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" }, - { Set<String>(), String(), String(), String() }, -}; + // Santali numerals. + { + NumSystemData sat; + sat.lang.insert(StringName("sat")); // Santali + sat.lang.insert(StringName("sat_IN")); + sat.lang.insert(StringName("sat_Olck")); + sat.lang.insert(StringName("sat_Olck_IN")); + sat.digits = U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙."; + sat.percent_sign = U"%"; + sat.exp = U"e"; + num_systems.push_back(sat); + } + + // Burmese numerals. + { + NumSystemData my; + my.lang.insert(StringName("my")); // Burmese + my.lang.insert(StringName("my_MM")); + my.digits = U"၀၁၂၃၄၅၆၇၈၉."; + my.percent_sign = U"%"; + my.exp = U"e"; + num_systems.push_back(my); + } -static void _insert_num_systems_lang() { - num_systems[0].lang.insert(StringName("ar")); - num_systems[0].lang.insert(StringName("ar_AR")); - num_systems[0].lang.insert(StringName("ar_BH")); - num_systems[0].lang.insert(StringName("ar_DJ")); - num_systems[0].lang.insert(StringName("ar_EG")); - num_systems[0].lang.insert(StringName("ar_ER")); - num_systems[0].lang.insert(StringName("ar_IL")); - num_systems[0].lang.insert(StringName("ar_IQ")); - num_systems[0].lang.insert(StringName("ar_JO")); - num_systems[0].lang.insert(StringName("ar_KM")); - num_systems[0].lang.insert(StringName("ar_KW")); - num_systems[0].lang.insert(StringName("ar_LB")); - num_systems[0].lang.insert(StringName("ar_MR")); - num_systems[0].lang.insert(StringName("ar_OM")); - num_systems[0].lang.insert(StringName("ar_PS")); - num_systems[0].lang.insert(StringName("ar_QA")); - num_systems[0].lang.insert(StringName("ar_SA")); - num_systems[0].lang.insert(StringName("ar_SD")); - num_systems[0].lang.insert(StringName("ar_SO")); - num_systems[0].lang.insert(StringName("ar_SS")); - num_systems[0].lang.insert(StringName("ar_SY")); - num_systems[0].lang.insert(StringName("ar_TD")); - num_systems[0].lang.insert(StringName("ar_YE")); - - num_systems[1].lang.insert(StringName("fa")); - num_systems[1].lang.insert(StringName("ks")); - num_systems[1].lang.insert(StringName("pa_Arab")); - num_systems[1].lang.insert(StringName("ug")); - num_systems[1].lang.insert(StringName("ur_IN")); - num_systems[1].lang.insert(StringName("ur")); - num_systems[1].lang.insert(StringName("uz_Arab")); - - num_systems[2].lang.insert(StringName("as")); - num_systems[2].lang.insert(StringName("bn")); - num_systems[2].lang.insert(StringName("mni")); - - num_systems[3].lang.insert(StringName("mr")); - num_systems[3].lang.insert(StringName("ne")); - - num_systems[4].lang.insert(StringName("dz")); - - num_systems[5].lang.insert(StringName("sat")); - - num_systems[6].lang.insert(StringName("my")); + // Chakma numerals. + { + NumSystemData ccp; + ccp.lang.insert(StringName("ccp")); // Chakma + ccp.lang.insert(StringName("ccp_BD")); + ccp.lang.insert(StringName("ccp_IN")); + ccp.digits = U"𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿."; + ccp.percent_sign = U"%"; + ccp.exp = U"e"; + num_systems.push_back(ccp); + } + + // Adlam numerals. + { + NumSystemData ff; + ff.lang.insert(StringName("ff")); // Fulah + ff.lang.insert(StringName("ff_Adlm_BF")); + ff.lang.insert(StringName("ff_Adlm_CM")); + ff.lang.insert(StringName("ff_Adlm_GH")); + ff.lang.insert(StringName("ff_Adlm_GM")); + ff.lang.insert(StringName("ff_Adlm_GN")); + ff.lang.insert(StringName("ff_Adlm_GW")); + ff.lang.insert(StringName("ff_Adlm_LR")); + ff.lang.insert(StringName("ff_Adlm_MR")); + ff.lang.insert(StringName("ff_Adlm_NE")); + ff.lang.insert(StringName("ff_Adlm_NG")); + ff.lang.insert(StringName("ff_Adlm_SL")); + ff.lang.insert(StringName("ff_Adlm_SN")); + ff.digits = U"𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙."; + ff.percent_sign = U"%"; + ff.exp = U"e"; + num_systems.push_back(ff); + } } String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const { const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; - for (int i = 0; num_systems[i].lang.size() != 0; i++) { + for (int i = 0; i < num_systems.size(); i++) { if (num_systems[i].lang.has(lang)) { if (num_systems[i].digits == String()) { return p_string; @@ -4807,7 +4997,7 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_ const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; String res = p_string; - for (int i = 0; num_systems[i].lang.size() != 0; i++) { + for (int i = 0; i < num_systems.size(); i++) { if (num_systems[i].lang.has(lang)) { if (num_systems[i].digits == String()) { return p_string; @@ -4834,7 +5024,7 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_ String TextServerAdvanced::percent_sign(const String &p_language) const { const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language; - for (int i = 0; num_systems[i].lang.size() != 0; i++) { + for (int i = 0; i < num_systems.size(); i++) { if (num_systems[i].lang.has(lang)) { if (num_systems[i].percent_sign == String()) { return "%"; @@ -4845,14 +5035,47 @@ String TextServerAdvanced::percent_sign(const String &p_language) const { return "%"; } +String TextServerAdvanced::strip_diacritics(const String &p_string) const { + UErrorCode err = U_ZERO_ERROR; + + // Get NFKD normalizer singleton. + const UNormalizer2 *unorm = unorm2_getNFKDInstance(&err); + ERR_FAIL_COND_V_MSG(U_FAILURE(err), TextServer::strip_diacritics(p_string), u_errorName(err)); + + // Convert to UTF-16. + Char16String utf16 = p_string.utf16(); + + // Normalize. + Char16String normalized; + err = U_ZERO_ERROR; + int32_t len = unorm2_normalize(unorm, utf16.ptr(), -1, nullptr, 0, &err); + ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, TextServer::strip_diacritics(p_string), u_errorName(err)); + normalized.resize(len); + err = U_ZERO_ERROR; + unorm2_normalize(unorm, utf16.ptr(), -1, normalized.ptrw(), len, &err); + ERR_FAIL_COND_V_MSG(U_FAILURE(err), TextServer::strip_diacritics(p_string), u_errorName(err)); + + // Convert back to UTF-32. + String normalized_string = String::utf16(normalized.ptr(), len); + + // Strip combining characters. + String result; + for (int i = 0; i < normalized_string.length(); i++) { + if (u_getCombiningClass(normalized_string[i]) == 0) { + result += normalized_string[i]; + } + } + return result; +} + TextServerAdvanced::TextServerAdvanced() { _insert_num_systems_lang(); _insert_feature_sets(); - hb_bmp_create_font_funcs(); + _bmp_create_font_funcs(); } TextServerAdvanced::~TextServerAdvanced() { - hb_bmp_free_font_funcs(); + _bmp_free_font_funcs(); if (library != nullptr) { FT_Done_FreeType(library); } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index afb46c22e2..eb8316b200 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -50,6 +50,7 @@ #include <unicode/udata.h> #include <unicode/uiter.h> #include <unicode/uloc.h> +#include <unicode/unorm2.h> #include <unicode/uscript.h> #include <unicode/ustring.h> #include <unicode/utypes.h> @@ -79,6 +80,19 @@ class TextServerAdvanced : public TextServer { static String interface_name; static uint32_t interface_features; + struct NumSystemData { + Set<StringName> lang; + String digits; + String percent_sign; + String exp; + }; + + Vector<NumSystemData> num_systems; + Map<StringName, int32_t> feature_sets; + + void _insert_num_systems_lang(); + void _insert_feature_sets(); + // ICU support data. uint8_t *icu_data = nullptr; @@ -163,6 +177,10 @@ class TextServerAdvanced : public TextServer { Dictionary variation_coordinates; float oversampling = 0.f; + uint32_t style_flags = 0; + String font_name; + String style_name; + Map<Vector2i, FontDataForSizeAdvanced *> cache; bool face_init = false; @@ -258,24 +276,24 @@ class TextServerAdvanced : public TextServer { static hb_font_funcs_t *funcs; - struct hb_bmp_font_t { + struct 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_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); + static bmp_font_t *_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref); + static void _bmp_font_destroy(void *p_data); + static hb_bool_t _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 _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 _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 _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_bool_t _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 _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 _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 _bmp_create_font_funcs(); + static void _bmp_free_font_funcs(); + static void _bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref); + static hb_font_t *_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy); protected: static void _bind_methods(){}; @@ -307,6 +325,15 @@ public: 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_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) override; + virtual uint32_t /*FontStyle*/ font_get_style(RID p_font_rid) const override; + + virtual void font_set_style_name(RID p_font_rid, const String &p_name) override; + virtual String font_get_style_name(RID p_font_rid) const override; + + virtual void font_set_name(RID p_font_rid, const String &p_name) override; + virtual String font_get_name(RID p_font_rid) const 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; @@ -491,6 +518,8 @@ public: virtual String parse_number(const String &p_string, const String &p_language = "") const override; virtual String percent_sign(const String &p_language = "") const override; + virtual String strip_diacritics(const String &p_string) const override; + TextServerAdvanced(); ~TextServerAdvanced(); }; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index ca735bdb6c..43a636e484 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -111,22 +111,14 @@ bool TextServerFallback::is_locale_right_to_left(const String &p_locale) const { 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() }, -}; +void TextServerFallback::_insert_feature_sets() { + // Registered OpenType variation tag. + feature_sets.insert("italic", OT_TAG('i', 't', 'a', 'l')); + feature_sets.insert("optical_size", OT_TAG('o', 'p', 's', 'z')); + feature_sets.insert("slant", OT_TAG('s', 'l', 'n', 't')); + feature_sets.insert("width", OT_TAG('w', 'd', 't', 'h')); + feature_sets.insert("weight", OT_TAG('w', 'g', 'h', 't')); +} _FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) { char tag[4]; @@ -150,10 +142,8 @@ _FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) { } 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; - } + if (feature_sets.has(p_name)) { + return feature_sets[p_name]; } // No readable name, use tag string. @@ -168,9 +158,9 @@ _FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) { } 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; + for (const KeyValue<StringName, int32_t> &E : feature_sets) { + if (E.value == p_tag) { + return E.key; } } @@ -205,6 +195,10 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_ continue; } + if (ct.offsets.size() < ct.texture_w) { + continue; + } + ret.y = 0x7FFFFFFF; ret.x = 0; @@ -421,7 +415,6 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf( 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)); @@ -515,11 +508,11 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma 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 + 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 + 0] = 255; // grayscale as 1 wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j]; break; case FT_PIXEL_MODE_BGRA: { @@ -677,13 +670,14 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_d } _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const { + ERR_FAIL_COND_V(p_size.x <= 0, false); if (p_font_data->cache.has(p_size)) { return true; } FontDataForSizeFallback *fd = memnew(FontDataForSizeFallback); fd->size = p_size; - if (p_font_data->data_ptr) { + if (p_font_data->data_ptr && (p_font_data->data_size > 0)) { // Init dynamic font. #ifdef MODULE_FREETYPE_ENABLED int error = 0; @@ -742,6 +736,23 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback 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 style flags and name. + if (fd->face->family_name != nullptr) { + p_font_data->font_name = String::utf8((const char *)fd->face->family_name); + } + if (fd->face->style_name != nullptr) { + p_font_data->style_name = String::utf8((const char *)fd->face->style_name); + } + p_font_data->style_flags = 0; + if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) { + p_font_data->style_flags |= FONT_BOLD; + } + if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) { + p_font_data->style_flags |= FONT_ITALIC; + } + if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) { + p_font_data->style_flags |= FONT_FIXED_WIDTH; + } // Read OpenType variations. p_font_data->supported_varaitions.clear(); if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { @@ -832,6 +843,66 @@ void TextServerFallback::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data fd->data_size = p_data_size; } +void TextServerFallback::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) { + FontDataFallback *fd = font_owner.get_or_null(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->style_flags = p_style; +} + +uint32_t /*FontStyle*/ TextServerFallback::font_get_style(RID p_font_rid) const { + FontDataFallback *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, 0); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0); + return fd->style_flags; +} + +void TextServerFallback::font_set_style_name(RID p_font_rid, const String &p_name) { + FontDataFallback *fd = font_owner.get_or_null(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->style_name = p_name; +} + +String TextServerFallback::font_get_style_name(RID p_font_rid) const { + FontDataFallback *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, String()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String()); + return fd->style_name; +} + +void TextServerFallback::font_set_name(RID p_font_rid, const String &p_name) { + FontDataFallback *fd = font_owner.get_or_null(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->font_name = p_name; +} + +String TextServerFallback::font_get_name(RID p_font_rid) const { + FontDataFallback *fd = font_owner.get_or_null(p_font_rid); + ERR_FAIL_COND_V(!fd, String()); + + MutexLock lock(fd->mutex); + Vector2i size = _get_size(fd, 16); + ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String()); + return fd->font_name; +} + void TextServerFallback::font_set_antialiased(RID p_font_rid, bool p_antialiased) { FontDataFallback *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1304,6 +1375,7 @@ void TextServerFallback::font_set_texture_offsets(RID p_font_rid, const Vector2i 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); } @@ -1661,12 +1733,14 @@ Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const V } int32_t TextServerFallback::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_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), 0, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + "."); return (int32_t)p_char; } bool TextServerFallback::font_has_char(RID p_font_rid, char32_t p_char) const { FontDataFallback *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, false); + ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), false, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + "."); MutexLock lock(fd->mutex); if (fd->cache.is_empty()) { @@ -1719,6 +1793,8 @@ String TextServerFallback::font_get_supported_chars(RID p_font_rid) const { 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.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); + ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + "."); + ERR_FAIL_COND_MSG((p_end >= 0xd800 && p_end <= 0xdfff) || (p_end > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_end, 16) + "."); MutexLock lock(fd->mutex); Vector2i size = _get_size_outline(fd, p_size); @@ -2290,7 +2366,7 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, E.value.rect.position.y -= E.value.rect.size.y / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.y); @@ -2318,7 +2394,7 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, E.value.rect.position.x -= E.value.rect.size.x / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.x); @@ -2442,7 +2518,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng E.value.rect.position.y -= E.value.rect.size.y / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.y); @@ -2470,7 +2546,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng E.value.rect.position.x -= E.value.rect.size.x / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.x); @@ -2750,10 +2826,10 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl width -= sd_glyphs[i].advance * sd_glyphs[i].repeat; if (sd_glyphs[i].count > 0) { - bool above_min_char_treshold = (i >= ell_min_characters); + bool above_min_char_threshold = (i >= ell_min_characters); - if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) { - if (cut_per_word && above_min_char_treshold) { + if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) { + if (cut_per_word && above_min_char_threshold) { if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { last_valid_cut = i; found = true; @@ -2765,7 +2841,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl if (found) { trim_pos = last_valid_cut; - if (add_ellipsis && (above_min_char_treshold || enforce_ellipsis) && width - ellipsis_width <= p_width) { + if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) { ellipsis_pos = trim_pos; } break; @@ -2996,7 +3072,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { E.value.rect.position.y -= E.value.rect.size.y / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.y); @@ -3024,7 +3100,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { E.value.rect.position.x -= E.value.rect.size.x / 2; } break; case INLINE_ALIGN_TOP_TO: { - //NOP + // NOP } break; } full_ascent = MAX(full_ascent, -E.value.rect.position.x); @@ -3184,7 +3260,9 @@ float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) cons return sd->uthk; } -TextServerFallback::TextServerFallback(){}; +TextServerFallback::TextServerFallback() { + _insert_feature_sets(); +}; TextServerFallback::~TextServerFallback() { if (library != nullptr) { diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index f442f7e77e..2f495115e0 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -54,6 +54,8 @@ #include FT_BBOX_H #endif +#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))) + class TextServerFallback : public TextServer { GDCLASS(TextServerFallback, TextServer); _THREAD_SAFE_CLASS_ @@ -61,6 +63,10 @@ class TextServerFallback : public TextServer { static String interface_name; static uint32_t interface_features; + Map<StringName, int32_t> feature_sets; + + void _insert_feature_sets(); + // Font cache data. #ifdef MODULE_FREETYPE_ENABLED @@ -136,6 +142,10 @@ class TextServerFallback : public TextServer { Dictionary variation_coordinates; float oversampling = 0.f; + uint32_t style_flags = 0; + String font_name; + String style_name; + Map<Vector2i, FontDataForSizeFallback *> cache; bool face_init = false; @@ -228,6 +238,15 @@ public: 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_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) override; + virtual uint32_t /*FontStyle*/ font_get_style(RID p_font_rid) const override; + + virtual void font_set_style_name(RID p_font_rid, const String &p_name) override; + virtual String font_get_style_name(RID p_font_rid) const override; + + virtual void font_set_name(RID p_font_rid, const String &p_name) override; + virtual String font_get_name(RID p_font_rid) const 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; diff --git a/modules/theora/config.py b/modules/theora/config.py index b063ed51f9..7f354a8fda 100644 --- a/modules/theora/config.py +++ b/modules/theora/config.py @@ -1,4 +1,6 @@ def can_build(env, platform): + if env["arch"].startswith("rv"): + return False return env.module_check_dependencies("theora", ["ogg", "vorbis"]) diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index 2dfcd27dff..725f87b046 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -4,7 +4,7 @@ [VideoStream] resource for Ogg Theora videos. </brief_description> <description> - [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension. The Theora codec is less efficient than [VideoStreamWebm]'s VP8 and VP9, but it requires less CPU resources to decode. The Theora codec is decoded on the CPU. + [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension. The Theora codec is decoded on the CPU. [b]Note:[/b] While Ogg Theora videos can also have an [code].ogg[/code] extension, you will have to rename the extension to [code].ogv[/code] to use those videos within Godot. </description> <tutorials> diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 2f6faec8ec..4f5ae4afb0 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -108,7 +108,7 @@ void VideoStreamPlaybackTheora::video_write() { Ref<Image> img = memnew(Image(size.x, size.y, 0, Image::FORMAT_RGBA8, frame_data)); //zero copy image creation - texture->update(img); //zero copy send to visual server + texture->update(img); //zero copy send to rendering server frames_pending = 1; } @@ -603,7 +603,7 @@ float VideoStreamPlaybackTheora::get_playback_position() const { }; void VideoStreamPlaybackTheora::seek(float p_time) { - WARN_PRINT_ONCE("Seeking in Theora and WebM videos is not implemented yet (it's only supported for GDNative-provided video streams)."); + WARN_PRINT_ONCE("Seeking in Theora videos is not implemented yet (it's only supported for GDNative-provided video streams)."); } void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) { @@ -673,7 +673,7 @@ void VideoStreamTheora::_bind_methods() { ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamTheora::set_file); ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamTheora::get_file); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); } //////////// diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp index efe618012a..0e51822b01 100644 --- a/modules/upnp/upnp.cpp +++ b/modules/upnp/upnp.cpp @@ -37,10 +37,10 @@ bool UPNP::is_common_device(const String &dev) const { return dev.is_empty() || - dev.find("InternetGatewayDevice") >= 0 || - dev.find("WANIPConnection") >= 0 || - dev.find("WANPPPConnection") >= 0 || - dev.find("rootdevice") >= 0; + dev.find("InternetGatewayDevice") >= 0 || + dev.find("WANIPConnection") >= 0 || + dev.find("WANPPPConnection") >= 0 || + dev.find("rootdevice") >= 0; } int UPNP::discover(int timeout, int ttl, const String &device_filter) { diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py index b15479797c..e8990c43c8 100644 --- a/modules/visual_script/config.py +++ b/modules/visual_script/config.py @@ -17,6 +17,7 @@ def get_doc_classes(): "VisualScriptConstant", "VisualScriptConstructor", "VisualScriptCustomNode", + "VisualScriptCustomNodes", "VisualScriptDeconstruct", "VisualScriptEditor", "VisualScriptEmitSignal", diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index 372d46bc10..be6bf00e50 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -9,7 +9,7 @@ You are most likely to use this class via the Visual Script editor or when writing plugins for it. </description> <tutorials> - <link title="VisualScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> + <link title="VisualScript documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/visual_script/index.html</link> </tutorials> <methods> <method name="add_custom_signal"> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml new file mode 100644 index 0000000000..1681da7653 --- /dev/null +++ b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualScriptCustomNodes" inherits="Object" version="4.0"> + <brief_description> + Manages custom nodes for the Visual Script editor. + </brief_description> + <description> + This singleton can be used to manage (i.e., add or remove) custom nodes for the Visual Script editor. + </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> +</class> diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 54d310e636..700cc85672 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -113,7 +113,7 @@ void VisualScriptNode::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualScriptNode::_set_default_input_values); ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualScriptNode::_get_default_input_values); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_default_input_values", "_get_default_input_values"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_default_input_values", "_get_default_input_values"); ADD_SIGNAL(MethodInfo("ports_changed")); } @@ -1172,7 +1172,7 @@ void VisualScript::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data", "data"), &VisualScript::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &VisualScript::_get_data); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); ADD_SIGNAL(MethodInfo("node_ports_changed", PropertyInfo(Variant::INT, "id"))); } @@ -1688,7 +1688,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p // debugger break did not happen if (!VisualScriptLanguage::singleton->debug_break(error_str, false)) { - _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, error_str.utf8().get_data(), ERR_HANDLER_SCRIPT); + _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, error_str.utf8().get_data(), false, ERR_HANDLER_SCRIPT); } //} @@ -2370,7 +2370,6 @@ void VisualScriptLanguage::debug_get_stack_level_locals(int p_level, List<String const StringName *f = _call_stack[l].function; ERR_FAIL_COND(!_call_stack[l].instance->functions.has(*f)); - //VisualScriptInstance::Function *func = &_call_stack[l].instance->functions[*f]; VisualScriptNodeInstance *node = _call_stack[l].instance->instances[*_call_stack[l].current_id]; ERR_FAIL_COND(!node); @@ -2416,21 +2415,6 @@ void VisualScriptLanguage::debug_get_stack_level_locals(int p_level, List<String p_locals->push_back("working_mem/mem_" + itos(i)); p_values->push_back((*_call_stack[l].work_mem)[i]); } - - /* - ERR_FAIL_INDEX(p_level,_debug_call_stack_pos); - - - VisualFunction *f = _call_stack[l].function; - - List<Pair<StringName,int> > locals; - - f->debug_get_stack_member_state(*_call_stack[l].line,&locals); - for( List<Pair<StringName,int> >::Element *E = locals.front();E;E=E->next() ) { - p_locals->push_back(E->get().first); - p_values->push_back(_call_stack[l].stack[E->get().second]); - } -*/ } void VisualScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 8cb701ea20..7b4fbc67fc 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -1676,6 +1676,128 @@ String VisualScriptEditor::_validate_name(const String &p_name) const { return valid; } +void VisualScriptEditor::_on_nodes_copy() { + clipboard->nodes.clear(); + clipboard->data_connections.clear(); + clipboard->sequence_connections.clear(); + + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected()) { + int id = gn->get_name().operator String().to_int(); + Ref<VisualScriptNode> node = script->get_node(id); + if (Object::cast_to<VisualScriptFunction>(*node)) { + EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node.")); + return; + } + if (node.is_valid()) { + clipboard->nodes[id] = node->duplicate(true); + clipboard->nodes_positions[id] = script->get_node_position(id); + } + } + } + } + + if (clipboard->nodes.is_empty()) { + return; + } + + List<VisualScript::SequenceConnection> sequence_connections; + script->get_sequence_connection_list(&sequence_connections); + + for (const VisualScript::SequenceConnection &E : sequence_connections) { + if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) { + clipboard->sequence_connections.insert(E); + } + } + + List<VisualScript::DataConnection> data_connections; + script->get_data_connection_list(&data_connections); + + for (const VisualScript::DataConnection &E : data_connections) { + if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) { + clipboard->data_connections.insert(E); + } + } +} + +void VisualScriptEditor::_on_nodes_paste() { + if (clipboard->nodes.is_empty()) { + EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty!")); + return; + } + + Map<int, int> remap; + + undo_redo->create_action(TTR("Paste VisualScript Nodes")); + int idc = script->get_available_id() + 1; + + Set<int> to_select; + + Set<Vector2> existing_positions; + + { + List<int> nodes; + script->get_node_list(&nodes); + for (int &E : nodes) { + Vector2 pos = script->get_node_position(E).snapped(Vector2(2, 2)); + existing_positions.insert(pos); + } + } + + bool first_paste = true; + Vector2 position_offset = Vector2(0, 0); + + for (KeyValue<int, Ref<VisualScriptNode>> &E : clipboard->nodes) { + Ref<VisualScriptNode> node = E.value->duplicate(); + + int new_id = idc++; + to_select.insert(new_id); + + remap[E.key] = new_id; + + Vector2 paste_pos = clipboard->nodes_positions[E.key]; + + if (first_paste) { + position_offset = _get_pos_in_graph(mouse_up_position - graph->get_global_position()) - paste_pos; + first_paste = false; + } + + paste_pos += position_offset; + + while (existing_positions.has(paste_pos.snapped(Vector2(2, 2)))) { + paste_pos += Vector2(20, 20) * EDSCALE; + } + + undo_redo->add_do_method(script.ptr(), "add_node", new_id, node, paste_pos); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); + } + + for (Set<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + } + + for (Set<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) { + undo_redo->add_do_method(script.ptr(), "data_connect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->commit_action(); + + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + int id = gn->get_name().operator String().to_int(); + gn->set_selected(to_select.has(id)); + } + } +} + void VisualScriptEditor::_on_nodes_delete() { // Delete all the selected nodes. @@ -2073,20 +2195,20 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da Ref<VisualScriptNode> vnode; if (use_set) { - Ref<VisualScriptVariableSet> vnodes; - vnodes.instantiate(); - vnodes->set_variable(d["variable"]); - vnode = vnodes; + Ref<VisualScriptPropertySet> pset; + pset.instantiate(); + vnode = pset; } else { - Ref<VisualScriptVariableGet> vnodeg; - vnodeg.instantiate(); - vnodeg->set_variable(d["variable"]); - vnode = vnodeg; + Ref<VisualScriptPropertyGet> pget; + pget.instantiate(); + vnode = pget; } int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(vnode.ptr(), "set_property", d["variable"]); + undo_redo->add_do_method(vnode.ptr(), "set_base_script", script->get_path()); + undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos); undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); undo_redo->add_do_method(this, "_update_graph"); @@ -2329,12 +2451,14 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da pget.instantiate(); pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); pget->set_base_type(obj->get_class()); - vnode = pget; } undo_redo->add_do_method(script.ptr(), "add_node", base_id, vnode, pos); undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); + if (!obj->get_script().is_null()) { + undo_redo->add_do_method(vnode.ptr(), "set_base_script", Ref<Script>(obj->get_script())->get_path()); + } if (!use_get) { undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } @@ -2365,7 +2489,6 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH); pset->set_base_path(sn->get_path_to(node)); } - vnode = pset; } else { Ref<VisualScriptPropertyGet> pget; @@ -2380,9 +2503,13 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } undo_redo->add_do_method(script.ptr(), "add_node", base_id, vnode, pos); undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); + if (!obj->get_script().is_null()) { + undo_redo->add_do_method(vnode.ptr(), "set_base_script", Ref<Script>(obj->get_script())->get_path()); + } if (!use_get) { undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } + undo_redo->add_undo_method(script.ptr(), "remove_node", base_id); undo_redo->add_do_method(this, "_update_graph"); @@ -2460,7 +2587,7 @@ void VisualScriptEditor::reload_text() { String VisualScriptEditor::get_name() { String name; - if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) { + if (!script->is_built_in()) { name = script->get_path().get_file(); if (is_unsaved()) { if (script->get_path().is_empty()) { @@ -3417,7 +3544,7 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, pass_port, new_id); undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, pass_port, new_id); } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") && - !script->get_output_sequence_ports_connected(port_action_node).has(return_port)) { + !script->get_output_sequence_ports_connected(port_action_node).has(return_port)) { undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, return_port, new_id); undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, return_port, new_id); } else { @@ -3739,120 +3866,15 @@ void VisualScriptEditor::_menu_option(int p_what) { case EDIT_FIND_NODE_TYPE: { _generic_search(script->get_instance_base_type()); } break; - case EDIT_COPY_NODES: + case EDIT_COPY_NODES: { + _on_nodes_copy(); + } break; case EDIT_CUT_NODES: { - clipboard->nodes.clear(); - clipboard->data_connections.clear(); - clipboard->sequence_connections.clear(); - - for (int i = 0; i < graph->get_child_count(); i++) { - GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); - if (gn) { - if (gn->is_selected()) { - int id = gn->get_name().operator String().to_int(); - Ref<VisualScriptNode> node = script->get_node(id); - if (Object::cast_to<VisualScriptFunction>(*node)) { - EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node.")); - return; - } - if (node.is_valid()) { - clipboard->nodes[id] = node->duplicate(true); - clipboard->nodes_positions[id] = script->get_node_position(id); - } - } - } - } - - if (clipboard->nodes.is_empty()) { - break; - } - - List<VisualScript::SequenceConnection> sequence_connections; - script->get_sequence_connection_list(&sequence_connections); - - for (const VisualScript::SequenceConnection &E : sequence_connections) { - if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) { - clipboard->sequence_connections.insert(E); - } - } - - List<VisualScript::DataConnection> data_connections; - script->get_data_connection_list(&data_connections); - - for (const VisualScript::DataConnection &E : data_connections) { - if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) { - clipboard->data_connections.insert(E); - } - } - if (p_what == EDIT_CUT_NODES) { - _on_nodes_delete(); // oh yeah, also delete on cut - } - + _on_nodes_copy(); + _on_nodes_delete(); } break; case EDIT_PASTE_NODES: { - if (clipboard->nodes.is_empty()) { - EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty!")); - break; - } - - Map<int, int> remap; - - undo_redo->create_action(TTR("Paste VisualScript Nodes")); - int idc = script->get_available_id() + 1; - - Set<int> to_select; - - Set<Vector2> existing_positions; - - { - List<int> nodes; - script->get_node_list(&nodes); - for (int &E : nodes) { - Vector2 pos = script->get_node_position(E).snapped(Vector2(2, 2)); - existing_positions.insert(pos); - } - } - - for (KeyValue<int, Ref<VisualScriptNode>> &E : clipboard->nodes) { - Ref<VisualScriptNode> node = E.value->duplicate(); - - int new_id = idc++; - to_select.insert(new_id); - - remap[E.key] = new_id; - - Vector2 paste_pos = clipboard->nodes_positions[E.key]; - - while (existing_positions.has(paste_pos.snapped(Vector2(2, 2)))) { - paste_pos += Vector2(20, 20) * EDSCALE; - } - - undo_redo->add_do_method(script.ptr(), "add_node", new_id, node, paste_pos); - undo_redo->add_undo_method(script.ptr(), "remove_node", new_id); - } - - for (Set<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); - } - - for (Set<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "data_connect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); - } - - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - - undo_redo->commit_action(); - - for (int i = 0; i < graph->get_child_count(); i++) { - GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); - if (gn) { - int id = gn->get_name().operator String().to_int(); - gn->set_selected(to_select.has(id)); - } - } + _on_nodes_paste(); } break; case EDIT_CREATE_FUNCTION: { // Create Function. @@ -4262,8 +4284,6 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members); ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search); - - ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &VisualScriptEditor::add_syntax_highlighter); } VisualScriptEditor::VisualScriptEditor() { @@ -4336,6 +4356,8 @@ VisualScriptEditor::VisualScriptEditor() { graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected)); graph->connect("begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move)); graph->connect("end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move)); + graph->connect("copy_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_copy)); + graph->connect("paste_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_paste)); graph->connect("delete_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_delete)); graph->connect("duplicate_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_duplicate)); graph->connect("gui_input", callable_mp(this, &VisualScriptEditor::_graph_gui_input)); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 19f5aabac9..9467c2dea4 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -253,6 +253,8 @@ class VisualScriptEditor : public ScriptEditorBase { void _node_item_selected(); void _node_item_unselected(); + void _on_nodes_copy(); + void _on_nodes_paste(); void _on_nodes_delete(); void _on_nodes_duplicate(); diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index 99b7275008..55c707890f 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -136,7 +136,7 @@ void VisualScriptExpression::_get_property_list(List<PropertyInfo> *p_list) cons argt += "," + Variant::get_type_name(Variant::Type(i)); } - p_list->push_back(PropertyInfo(Variant::STRING, "expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, "expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::INT, "out_type", PROPERTY_HINT_ENUM, argt)); p_list->push_back(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1")); p_list->push_back(PropertyInfo(Variant::BOOL, "sequenced")); diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 205918a5f0..a2ad38bf01 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -514,7 +514,7 @@ Dictionary VisualScriptFunctionCall::_get_argument_cache() const { void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const { if (property.name == "base_type") { if (call_mode != CALL_MODE_INSTANCE) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } @@ -696,7 +696,7 @@ void VisualScriptFunctionCall::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "singleton"), "set_singleton", "get_singleton"); ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "argument_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_argument_cache", "_get_argument_cache"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "argument_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_argument_cache", "_get_argument_cache"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "function"), "set_function", "get_function"); //when set, if loaded properly, will override argument count. ADD_PROPERTY(PropertyInfo(Variant::INT, "use_default_args"), "set_use_default_args", "get_use_default_args"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "validate"), "set_validate", "get_validate"); @@ -1282,7 +1282,7 @@ VisualScriptPropertySet::AssignOp VisualScriptPropertySet::get_assign_op() const void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const { if (property.name == "base_type") { if (call_mode != CALL_MODE_INSTANCE) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } @@ -1420,7 +1420,7 @@ void VisualScriptPropertySet::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache"); ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "property"), "set_property", "get_property"); @@ -1988,7 +1988,7 @@ StringName VisualScriptPropertyGet::get_index() const { void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const { if (property.name == "base_type") { if (call_mode != CALL_MODE_INSTANCE) { - property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL; + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } } @@ -2122,7 +2122,7 @@ void VisualScriptPropertyGet::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache"); ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "property"), "set_property", "get_property"); diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index ef77c0cef3..471d8ef0ae 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -3415,8 +3415,8 @@ void VisualScriptConstructor::_bind_methods() { ClassDB::bind_method(D_METHOD("set_constructor", "constructor"), &VisualScriptConstructor::set_constructor); ClassDB::bind_method(D_METHOD("get_constructor"), &VisualScriptConstructor::get_constructor); - ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor_type", "get_constructor_type"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "constructor", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor", "get_constructor"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor_type", "get_constructor_type"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "constructor", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor", "get_constructor"); } VisualScriptConstructor::VisualScriptConstructor() { @@ -3958,7 +3958,7 @@ void VisualScriptDeconstruct::_bind_methods() { } ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_deconstruct_type", "get_deconstruct_type"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "elem_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_elem_cache", "_get_elem_cache"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "elem_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_elem_cache", "_get_elem_cache"); } VisualScriptDeconstruct::VisualScriptDeconstruct() { diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index bf2d8e9683..78881f0a53 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -90,12 +90,6 @@ public: void set_stack_size(int p_size); int get_stack_size() const; - void set_return_type_enabled(bool p_returns); - bool is_return_type_enabled() const; - - void set_return_type(Variant::Type p_type); - Variant::Type get_return_type() const; - void set_rpc_mode(Multiplayer::RPCMode p_mode); Multiplayer::RPCMode get_rpc_mode() const; diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp index cded1e587c..4b89c9ccd0 100644 --- a/modules/visual_script/visual_script_yield_nodes.cpp +++ b/modules/visual_script/visual_script_yield_nodes.cpp @@ -121,7 +121,7 @@ public: ret = STEP_EXIT_FUNCTION_BIT; break; //return the yield case VisualScriptYield::YIELD_FRAME: - state->connect_to_signal(tree, "idle_frame", Array()); + state->connect_to_signal(tree, "process_frame", Array()); break; case VisualScriptYield::YIELD_PHYSICS_FRAME: state->connect_to_signal(tree, "physics_frame", Array()); @@ -186,7 +186,7 @@ void VisualScriptYield::_bind_methods() { ClassDB::bind_method(D_METHOD("set_wait_time", "sec"), &VisualScriptYield::set_wait_time); ClassDB::bind_method(D_METHOD("get_wait_time"), &VisualScriptYield::get_wait_time); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Frame,Physics Frame,Time", PROPERTY_USAGE_NOEDITOR), "set_yield_mode", "get_yield_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Frame,Physics Frame,Time", PROPERTY_USAGE_NO_EDITOR), "set_yield_mode", "get_yield_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time"), "set_wait_time", "get_wait_time"); BIND_ENUM_CONSTANT(YIELD_FRAME); @@ -415,7 +415,7 @@ VisualScriptYieldSignal::CallMode VisualScriptYieldSignal::get_call_mode() const void VisualScriptYieldSignal::_validate_property(PropertyInfo &property) const { if (property.name == "base_type") { if (call_mode != CALL_MODE_INSTANCE) { - property.usage = PROPERTY_USAGE_NOEDITOR; + property.usage = PROPERTY_USAGE_NO_EDITOR; } } diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index e4a80c339f..d913c115d9 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -425,7 +425,7 @@ void AudioStreamOGGVorbis::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset); ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_packet_sequence", "get_packet_sequence"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_sequence", "get_packet_sequence"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset"); } diff --git a/modules/webm/SCsub b/modules/webm/SCsub deleted file mode 100644 index 44e80e2870..0000000000 --- a/modules/webm/SCsub +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_modules") - -env_webm = env_modules.Clone() - -# Thirdparty source files - -thirdparty_obj = [] - -thirdparty_dir = "#thirdparty/libsimplewebm/" -thirdparty_sources = [ - "libwebm/mkvparser/mkvparser.cc", - "OpusVorbisDecoder.cpp", - "VPXDecoder.cpp", - "WebMDemuxer.cpp", -] -thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - -env_webm.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"]) - -# also requires libogg, libvorbis and libopus -if env["builtin_libogg"]: - env_webm.Prepend(CPPPATH=["#thirdparty/libogg"]) -if env["builtin_libvorbis"]: - env_webm.Prepend(CPPPATH=["#thirdparty/libvorbis"]) -if env["builtin_opus"]: - env_webm.Prepend(CPPPATH=["#thirdparty/opus"]) - -if env["builtin_libvpx"]: - env_webm.Prepend(CPPPATH=["#thirdparty/libvpx"]) - SConscript("libvpx/SCsub") - -env_thirdparty = env_webm.Clone() -env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) -env.modules_sources += thirdparty_obj - -# Godot source files - -module_obj = [] - -env_webm.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/webm/config.py b/modules/webm/config.py deleted file mode 100644 index 99f8ace114..0000000000 --- a/modules/webm/config.py +++ /dev/null @@ -1,19 +0,0 @@ -def can_build(env, platform): - if platform in ["iphone"]: - return False - - return env.module_check_dependencies("webm", ["ogg", "opus", "vorbis"]) - - -def configure(env): - pass - - -def get_doc_classes(): - return [ - "VideoStreamWebm", - ] - - -def get_doc_path(): - return "doc_classes" diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml deleted file mode 100644 index e04d02d6ab..0000000000 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamWebm" inherits="VideoStream" version="4.0"> - <brief_description> - [VideoStream] resource for WebM videos. - </brief_description> - <description> - [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. Both the VP8 and VP9 codecs are supported. The VP8 and VP9 codecs are more efficient than [VideoStreamTheora], but they require more CPU resources to decode (especially VP9). Both the VP8 and VP9 codecs are decoded on the CPU. - [b]Note:[/b] Alpha channel (also known as transparency) is not supported. The video will always appear to have a black background, even if it originally contains an alpha channel. - [b]Note:[/b] There are known bugs and performance issues with WebM video playback in Godot. If you run into problems, try using the Ogg Theora format instead: [VideoStreamTheora] - </description> - <tutorials> - </tutorials> - <methods> - <method name="get_file"> - <return type="String" /> - <description> - Returns the WebM video file handled by this [VideoStreamWebm]. - </description> - </method> - <method name="set_file"> - <return type="void" /> - <argument index="0" name="file" type="String" /> - <description> - Sets the WebM video file that this [VideoStreamWebm] resource handles. The [code]file[/code] name should have the [code].webm[/code] extension. - </description> - </method> - </methods> -</class> diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub deleted file mode 100644 index 4334cf732b..0000000000 --- a/modules/webm/libvpx/SCsub +++ /dev/null @@ -1,382 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_modules") - -# Thirdparty sources - -libvpx_dir = "#thirdparty/libvpx/" - -libvpx_sources = [ - "vp8/vp8_dx_iface.c", - "vp8/common/generic/systemdependent.c", - "vp8/common/alloccommon.c", - "vp8/common/blockd.c", - "vp8/common/copy_c.c", - "vp8/common/debugmodes.c", - "vp8/common/dequantize.c", - "vp8/common/entropy.c", - "vp8/common/entropymode.c", - "vp8/common/entropymv.c", - "vp8/common/extend.c", - "vp8/common/filter.c", - "vp8/common/findnearmv.c", - "vp8/common/idct_blk.c", - "vp8/common/idctllm.c", - "vp8/common/loopfilter_filters.c", - "vp8/common/mbpitch.c", - "vp8/common/modecont.c", - "vp8/common/quant_common.c", - "vp8/common/reconinter.c", - "vp8/common/reconintra.c", - "vp8/common/reconintra4x4.c", - "vp8/common/rtcd.c", - "vp8/common/setupintrarecon.c", - "vp8/common/swapyv12buffer.c", - "vp8/common/treecoder.c", - "vp8/common/vp8_loopfilter.c", - "vp8/decoder/dboolhuff.c", - "vp8/decoder/decodeframe.c", - "vp8/decoder/decodemv.c", - "vp8/decoder/detokenize.c", - "vp8/decoder/onyxd_if.c", - "vp9/vp9_dx_iface.c", - "vp9/common/vp9_alloccommon.c", - "vp9/common/vp9_blockd.c", - "vp9/common/vp9_common_data.c", - "vp9/common/vp9_debugmodes.c", - "vp9/common/vp9_entropy.c", - "vp9/common/vp9_entropymode.c", - "vp9/common/vp9_entropymv.c", - "vp9/common/vp9_filter.c", - "vp9/common/vp9_frame_buffers.c", - "vp9/common/vp9_idct.c", - "vp9/common/vp9_loopfilter.c", - "vp9/common/vp9_mvref_common.c", - "vp9/common/vp9_pred_common.c", - "vp9/common/vp9_quant_common.c", - "vp9/common/vp9_reconinter.c", - "vp9/common/vp9_reconintra.c", - "vp9/common/vp9_rtcd.c", - "vp9/common/vp9_scale.c", - "vp9/common/vp9_scan.c", - "vp9/common/vp9_seg_common.c", - "vp9/common/vp9_thread_common.c", - "vp9/common/vp9_tile_common.c", - "vp9/decoder/vp9_decodeframe.c", - "vp9/decoder/vp9_decodemv.c", - "vp9/decoder/vp9_decoder.c", - "vp9/decoder/vp9_detokenize.c", - "vp9/decoder/vp9_dsubexp.c", - "vp9/decoder/vp9_dthread.c", - "vpx/src/vpx_codec.c", - "vpx/src/vpx_decoder.c", - "vpx/src/vpx_image.c", - "vpx/src/vpx_psnr.c", - "vpx_dsp/bitreader.c", - "vpx_dsp/bitreader_buffer.c", - "vpx_dsp/intrapred.c", - "vpx_dsp/inv_txfm.c", - "vpx_dsp/loopfilter.c", - "vpx_dsp/prob.c", - "vpx_dsp/vpx_convolve.c", - "vpx_dsp/vpx_dsp_rtcd.c", - "vpx_mem/vpx_mem.c", - "vpx_scale/vpx_scale_rtcd.c", - "vpx_scale/generic/yv12config.c", - "vpx_scale/generic/yv12extend.c", - "vpx_util/vpx_thread.c", -] - -libvpx_sources_mt = [ - "vp8/decoder/threading.c", -] - -libvpx_sources_intrin_x86 = [ - "vp8/common/x86/filter_x86.c", - "vp8/common/x86/loopfilter_x86.c", - "vp8/common/x86/vp8_asm_stubs.c", - "vpx_dsp/x86/vpx_asm_stubs.c", -] -libvpx_sources_intrin_x86_mmx = [ - "vp8/common/x86/idct_blk_mmx.c", -] -libvpx_sources_intrin_x86_sse2 = [ - "vp8/common/x86/idct_blk_sse2.c", - "vp9/common/x86/vp9_idct_intrin_sse2.c", - "vpx_dsp/x86/inv_txfm_sse2.c", - "vpx_dsp/x86/loopfilter_sse2.c", -] -libvpx_sources_intrin_x86_ssse3 = [ - "vpx_dsp/x86/vpx_subpixel_8t_intrin_ssse3.c", -] -libvpx_sources_intrin_x86_avx2 = [ - "vpx_dsp/x86/loopfilter_avx2.c", - "vpx_dsp/x86/vpx_subpixel_8t_intrin_avx2.c", -] -libvpx_sources_x86asm = [ - "vp8/common/x86/copy_sse2.asm", - "vp8/common/x86/copy_sse3.asm", - "vp8/common/x86/dequantize_mmx.asm", - "vp8/common/x86/idctllm_mmx.asm", - "vp8/common/x86/idctllm_sse2.asm", - "vp8/common/x86/iwalsh_mmx.asm", - "vp8/common/x86/iwalsh_sse2.asm", - "vp8/common/x86/loopfilter_sse2.asm", - "vp8/common/x86/recon_mmx.asm", - "vp8/common/x86/recon_sse2.asm", - "vp8/common/x86/subpixel_mmx.asm", - "vp8/common/x86/subpixel_sse2.asm", - "vp8/common/x86/subpixel_ssse3.asm", - "vp8/common/x86/vp8_loopfilter_mmx.asm", - "vpx_dsp/x86/intrapred_sse2.asm", - "vpx_dsp/x86/intrapred_ssse3.asm", - "vpx_dsp/x86/inv_wht_sse2.asm", - "vpx_dsp/x86/vpx_convolve_copy_sse2.asm", - "vpx_dsp/x86/vpx_subpixel_8t_sse2.asm", - "vpx_dsp/x86/vpx_subpixel_8t_ssse3.asm", - "vpx_dsp/x86/vpx_subpixel_bilinear_sse2.asm", - "vpx_dsp/x86/vpx_subpixel_bilinear_ssse3.asm", - "vpx_ports/emms.asm", -] -libvpx_sources_x86_64asm = [ - "vp8/common/x86/loopfilter_block_sse2_x86_64.asm", - "vpx_dsp/x86/inv_txfm_ssse3_x86_64.asm", -] - -libvpx_sources_arm = [ - "vpx_ports/arm_cpudetect.c", - "vp8/common/arm/loopfilter_arm.c", -] -libvpx_sources_arm_neon = [ - "vp8/common/arm/neon/bilinearpredict_neon.c", - "vp8/common/arm/neon/copymem_neon.c", - "vp8/common/arm/neon/dc_only_idct_add_neon.c", - "vp8/common/arm/neon/dequant_idct_neon.c", - "vp8/common/arm/neon/dequantizeb_neon.c", - "vp8/common/arm/neon/idct_blk_neon.c", - "vp8/common/arm/neon/idct_dequant_0_2x_neon.c", - "vp8/common/arm/neon/idct_dequant_full_2x_neon.c", - "vp8/common/arm/neon/iwalsh_neon.c", - "vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c", - "vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c", - "vp8/common/arm/neon/mbloopfilter_neon.c", - "vp8/common/arm/neon/shortidct4x4llm_neon.c", - "vp8/common/arm/neon/sixtappredict_neon.c", - "vp8/common/arm/neon/vp8_loopfilter_neon.c", - "vp9/common/arm/neon/vp9_iht4x4_add_neon.c", - "vp9/common/arm/neon/vp9_iht8x8_add_neon.c", - "vpx_dsp/arm/idct16x16_1_add_neon.c", - "vpx_dsp/arm/idct16x16_add_neon.c", - "vpx_dsp/arm/idct16x16_neon.c", - "vpx_dsp/arm/idct32x32_1_add_neon.c", - "vpx_dsp/arm/idct32x32_add_neon.c", - "vpx_dsp/arm/idct4x4_1_add_neon.c", - "vpx_dsp/arm/idct4x4_add_neon.c", - "vpx_dsp/arm/idct8x8_1_add_neon.c", - "vpx_dsp/arm/idct8x8_add_neon.c", - "vpx_dsp/arm/intrapred_neon.c", - "vpx_dsp/arm/loopfilter_16_neon.c", - "vpx_dsp/arm/loopfilter_4_neon.c", - "vpx_dsp/arm/loopfilter_8_neon.c", - "vpx_dsp/arm/loopfilter_neon.c", - "vpx_dsp/arm/vpx_convolve8_avg_neon.c", - "vpx_dsp/arm/vpx_convolve8_neon.c", - "vpx_dsp/arm/vpx_convolve_avg_neon.c", - "vpx_dsp/arm/vpx_convolve_copy_neon.c", - "vpx_dsp/arm/vpx_convolve_neon.c", -] -libvpx_sources_arm_neon_gas = [ - "vpx_dsp/arm/gas/intrapred_neon_asm.s", - "vpx_dsp/arm/gas/loopfilter_mb_neon.s", - "vpx_dsp/arm/gas/save_reg_neon.s", -] -libvpx_sources_arm_neon_armasm_ms = [ - "vpx_dsp/arm/armasm_ms/intrapred_neon_asm.asm", - "vpx_dsp/arm/armasm_ms/loopfilter_mb_neon.asm", - "vpx_dsp/arm/armasm_ms/save_reg_neon.asm", -] -libvpx_sources_arm_neon_gas_apple = [ - "vpx_dsp/arm/gas_apple/intrapred_neon_asm.s", - "vpx_dsp/arm/gas_apple/loopfilter_mb_neon.s", - "vpx_dsp/arm/gas_apple/save_reg_neon.s", -] - -libvpx_sources = [libvpx_dir + file for file in libvpx_sources] -libvpx_sources_mt = [libvpx_dir + file for file in libvpx_sources_mt] -libvpx_sources_intrin_x86 = [libvpx_dir + file for file in libvpx_sources_intrin_x86] -libvpx_sources_intrin_x86_mmx = [libvpx_dir + file for file in libvpx_sources_intrin_x86_mmx] -libvpx_sources_intrin_x86_sse2 = [libvpx_dir + file for file in libvpx_sources_intrin_x86_sse2] -libvpx_sources_intrin_x86_ssse3 = [libvpx_dir + file for file in libvpx_sources_intrin_x86_ssse3] -libvpx_sources_intrin_x86_avx2 = [libvpx_dir + file for file in libvpx_sources_intrin_x86_avx2] -libvpx_sources_x86asm = [libvpx_dir + file for file in libvpx_sources_x86asm] -libvpx_sources_x86_64asm = [libvpx_dir + file for file in libvpx_sources_x86_64asm] -libvpx_sources_arm = [libvpx_dir + file for file in libvpx_sources_arm] -libvpx_sources_arm_neon = [libvpx_dir + file for file in libvpx_sources_arm_neon] -libvpx_sources_arm_neon_gas = [libvpx_dir + file for file in libvpx_sources_arm_neon_gas] -libvpx_sources_arm_neon_armasm_ms = [libvpx_dir + file for file in libvpx_sources_arm_neon_armasm_ms] -libvpx_sources_arm_neon_gas_apple = [libvpx_dir + file for file in libvpx_sources_arm_neon_gas_apple] - - -env_libvpx = env_modules.Clone() -env_libvpx.disable_warnings() -env_libvpx.Prepend(CPPPATH=[libvpx_dir]) - -webm_multithread = env["platform"] != "javascript" - -cpu_bits = env["bits"] -webm_cpu_x86 = False -webm_cpu_arm = False -if env["platform"] == "uwp": - if "arm" in env["PROGSUFFIX"]: - webm_cpu_arm = True - else: - webm_cpu_x86 = True -else: - import platform - - is_x11_or_server_arm = env["platform"] == "linuxbsd" and ( - platform.machine().startswith("arm") or platform.machine().startswith("aarch") - ) - is_macos_x86 = env["platform"] == "osx" and ("arch" in env and (env["arch"] != "arm64")) - is_ios_x86 = env["platform"] == "iphone" and ("arch" in env and env["arch"].startswith("x86")) - is_android_x86 = env["platform"] == "android" and env["android_arch"].startswith("x86") - if is_android_x86: - cpu_bits = "32" if env["android_arch"] == "x86" else "64" - webm_cpu_x86 = ( - not is_x11_or_server_arm - and (cpu_bits == "32" or cpu_bits == "64") - and ( - env["platform"] == "windows" - or env["platform"] == "linuxbsd" - or env["platform"] == "haiku" - or is_macos_x86 - or is_android_x86 - or is_ios_x86 - ) - ) - webm_cpu_arm = ( - is_x11_or_server_arm - or (not is_macos_x86 and env["platform"] == "osx") - or (not is_ios_x86 and env["platform"] == "iphone") - or (not is_android_x86 and env["platform"] == "android") - ) - -if webm_cpu_x86: - import subprocess - import os - - yasm_paths = [ - "yasm", - "../../../yasm", - ] - - yasm_found = False - - devnull = open(os.devnull) - for yasm_path in yasm_paths: - try: - yasm_found = True - subprocess.Popen([yasm_path, "--version"], stdout=devnull, stderr=devnull).communicate() - except Exception: - yasm_found = False - if yasm_found: - break - - if not yasm_found: - webm_cpu_x86 = False - print("YASM is necessary for WebM SIMD optimizations.") - -webm_simd_optimizations = False - -if webm_cpu_x86: - if env["platform"] == "windows" or env["platform"] == "uwp": - env_libvpx["ASFORMAT"] = "win" - elif env["platform"] == "osx" or env["platform"] == "iphone": - env_libvpx["ASFORMAT"] = "macho" - else: - env_libvpx["ASFORMAT"] = "elf" - env_libvpx["ASFORMAT"] += cpu_bits - - env_libvpx["AS"] = "yasm" - env_libvpx["ASFLAGS"] = "-I" + libvpx_dir[1:] + " -f $ASFORMAT -D $ASCPU" - env_libvpx["ASCOM"] = "$AS $ASFLAGS -o $TARGET $SOURCES" - - if cpu_bits == "32": - env_libvpx["ASCPU"] = "X86_32" - elif cpu_bits == "64": - env_libvpx["ASCPU"] = "X86_64" - - env_libvpx.Append(CPPDEFINES=["WEBM_X86ASM"]) - - webm_simd_optimizations = True - -if webm_cpu_arm: - if env["platform"] == "iphone": - env_libvpx["ASFLAGS"] = "-arch armv7" - elif env["platform"] == "android" and env["android_arch"] == "armv7" or env["platform"] == "linuxbsd": - env_libvpx["ASFLAGS"] = "-mfpu=neon" - elif env["platform"] == "uwp": - env_libvpx["AS"] = "armasm" - env_libvpx["ASFLAGS"] = "" - env_libvpx["ASCOM"] = "$AS $ASFLAGS -o $TARGET $SOURCES" - - env_libvpx.Append(CPPDEFINES=["WEBM_ARMASM"]) - - webm_simd_optimizations = True - -if webm_simd_optimizations == False: - print("WebM SIMD optimizations are disabled. Check if your CPU architecture, CPU bits or platform are supported!") - -env_libvpx.add_source_files(env.modules_sources, libvpx_sources) - -if webm_multithread: - env_libvpx.add_source_files(env.modules_sources, libvpx_sources_mt) - -if webm_cpu_x86: - is_clang_or_gcc = ( - ("gcc" in os.path.basename(env["CC"])) or ("clang" in os.path.basename(env["CC"])) or ("osxcross" in env) - ) - - env_libvpx_mmx = env_libvpx.Clone() - if cpu_bits == "32" and is_clang_or_gcc: - env_libvpx_mmx.Append(CCFLAGS=["-mmmx"]) - env_libvpx_mmx.add_source_files(env.modules_sources, libvpx_sources_intrin_x86_mmx) - - env_libvpx_sse2 = env_libvpx.Clone() - if cpu_bits == "32" and is_clang_or_gcc: - env_libvpx_sse2.Append(CCFLAGS=["-msse2"]) - env_libvpx_sse2.add_source_files(env.modules_sources, libvpx_sources_intrin_x86_sse2) - - env_libvpx_ssse3 = env_libvpx.Clone() - if is_clang_or_gcc: - env_libvpx_ssse3.Append(CCFLAGS=["-mssse3"]) - env_libvpx_ssse3.add_source_files(env.modules_sources, libvpx_sources_intrin_x86_ssse3) - - env_libvpx_avx2 = env_libvpx.Clone() - if is_clang_or_gcc: - env_libvpx_avx2.Append(CCFLAGS=["-mavx2"]) - env_libvpx_avx2.add_source_files(env.modules_sources, libvpx_sources_intrin_x86_avx2) - - env_libvpx.add_source_files(env.modules_sources, libvpx_sources_intrin_x86) - - env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86asm) - if cpu_bits == "64": - env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86_64asm) -elif webm_cpu_arm: - env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm) - if env["platform"] == "android": - env_libvpx.Prepend(CPPPATH=[libvpx_dir + "third_party/android"]) - env_libvpx.add_source_files(env.modules_sources, [libvpx_dir + "third_party/android/cpu-features.c"]) - - env_libvpx_neon = env_libvpx.Clone() - env_libvpx_neon.add_source_files(env.modules_sources, libvpx_sources_arm_neon) - - if env["platform"] == "uwp": - env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_armasm_ms) - elif env["platform"] == "iphone": - env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_gas_apple) - elif (is_x11_or_server_arm and cpu_bits == "32") or ( - env["platform"] == "android" and not env["android_arch"] == "arm64v8" - ): - env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_gas) diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp deleted file mode 100644 index 12e0f5bd25..0000000000 --- a/modules/webm/video_stream_webm.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/*************************************************************************/ -/* video_stream_webm.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 "video_stream_webm.h" - -#include "core/config/project_settings.h" -#include "core/io/file_access.h" -#include "core/os/os.h" -#include "servers/audio_server.h" - -#include "thirdparty/misc/yuv2rgb.h" - -// libsimplewebm -#include <OpusVorbisDecoder.hpp> -#include <VPXDecoder.hpp> - -// libvpx -#include <vpx/vpx_image.h> - -// libwebm -#include <mkvparser/mkvparser.h> - -class MkvReader : public mkvparser::IMkvReader { -public: - MkvReader(const String &p_file) { - file = FileAccess::open(p_file, FileAccess::READ); - - ERR_FAIL_COND_MSG(!file, "Failed loading resource: '" + p_file + "'."); - } - ~MkvReader() { - if (file) { - memdelete(file); - } - } - - virtual int Read(long long pos, long len, unsigned char *buf) { - if (file) { - if (file->get_position() != (uint64_t)pos) { - file->seek(pos); - } - if (file->get_buffer(buf, len) == (uint64_t)len) { - return 0; - } - } - return -1; - } - - virtual int Length(long long *total, long long *available) { - if (file) { - const uint64_t len = file->get_length(); - if (total) { - *total = len; - } - if (available) { - *available = len; - } - return 0; - } - return -1; - } - -private: - FileAccess *file; -}; - -/**/ - -VideoStreamPlaybackWebm::VideoStreamPlaybackWebm() : - - texture(memnew(ImageTexture)) {} -VideoStreamPlaybackWebm::~VideoStreamPlaybackWebm() { - delete_pointers(); -} - -bool VideoStreamPlaybackWebm::open_file(const String &p_file) { - file_name = p_file; - webm = memnew(WebMDemuxer(new MkvReader(file_name), 0, audio_track)); - if (webm->isOpen()) { - video = memnew(VPXDecoder(*webm, OS::get_singleton()->get_processor_count())); - if (video->isOpen()) { - audio = memnew(OpusVorbisDecoder(*webm)); - if (audio->isOpen()) { - audio_frame = memnew(WebMFrame); - pcm = (float *)memalloc(sizeof(float) * audio->getBufferSamples() * webm->getChannels()); - } else { - memdelete(audio); - audio = nullptr; - } - - frame_data.resize((webm->getWidth() * webm->getHeight()) << 2); - Ref<Image> img; - img.instantiate(); - img->create(webm->getWidth(), webm->getHeight(), false, Image::FORMAT_RGBA8); - texture->create_from_image(img); - - return true; - } - memdelete(video); - video = nullptr; - } - memdelete(webm); - webm = nullptr; - return false; -} - -void VideoStreamPlaybackWebm::stop() { - if (playing) { - delete_pointers(); - - pcm = nullptr; - - audio_frame = nullptr; - video_frames = nullptr; - - video = nullptr; - audio = nullptr; - - open_file(file_name); //Should not fail here... - - video_frames_capacity = video_frames_pos = 0; - num_decoded_samples = 0; - samples_offset = -1; - video_frame_delay = video_pos = 0.0; - } - time = 0.0; - playing = false; -} - -void VideoStreamPlaybackWebm::play() { - stop(); - - delay_compensation = ProjectSettings::get_singleton()->get("audio/video/video_delay_compensation_ms"); - delay_compensation /= 1000.0; - - playing = true; -} - -bool VideoStreamPlaybackWebm::is_playing() const { - return playing; -} - -void VideoStreamPlaybackWebm::set_paused(bool p_paused) { - paused = p_paused; -} - -bool VideoStreamPlaybackWebm::is_paused() const { - return paused; -} - -void VideoStreamPlaybackWebm::set_loop(bool p_enable) { - //Empty -} - -bool VideoStreamPlaybackWebm::has_loop() const { - return false; -} - -float VideoStreamPlaybackWebm::get_length() const { - if (webm) { - return webm->getLength(); - } - return 0.0f; -} - -float VideoStreamPlaybackWebm::get_playback_position() const { - return video_pos; -} - -void VideoStreamPlaybackWebm::seek(float p_time) { - WARN_PRINT_ONCE("Seeking in Theora and WebM videos is not implemented yet (it's only supported for GDNative-provided video streams)."); -} - -void VideoStreamPlaybackWebm::set_audio_track(int p_idx) { - audio_track = p_idx; -} - -Ref<Texture2D> VideoStreamPlaybackWebm::get_texture() const { - return texture; -} - -void VideoStreamPlaybackWebm::update(float p_delta) { - if ((!playing || paused) || !video) { - return; - } - - time += p_delta; - - if (time < video_pos) { - return; - } - - bool audio_buffer_full = false; - - if (samples_offset > -1) { - //Mix remaining samples - const int to_read = num_decoded_samples - samples_offset; - const int mixed = mix_callback(mix_udata, pcm + samples_offset * webm->getChannels(), to_read); - if (mixed != to_read) { - samples_offset += mixed; - audio_buffer_full = true; - } else { - samples_offset = -1; - } - } - - const bool hasAudio = (audio && mix_callback); - while ((hasAudio && !audio_buffer_full && !has_enough_video_frames()) || - (!hasAudio && video_frames_pos == 0)) { - if (hasAudio && !audio_buffer_full && audio_frame->isValid() && - audio->getPCMF(*audio_frame, pcm, num_decoded_samples) && num_decoded_samples > 0) { - const int mixed = mix_callback(mix_udata, pcm, num_decoded_samples); - - if (mixed != num_decoded_samples) { - samples_offset = mixed; - audio_buffer_full = true; - } - } - - WebMFrame *video_frame; - if (video_frames_pos >= video_frames_capacity) { - WebMFrame **video_frames_new = (WebMFrame **)memrealloc(video_frames, ++video_frames_capacity * sizeof(void *)); - ERR_FAIL_COND(!video_frames_new); //Out of memory - (video_frames = video_frames_new)[video_frames_capacity - 1] = memnew(WebMFrame); - } - video_frame = video_frames[video_frames_pos]; - - if (!webm->readFrame(video_frame, audio_frame)) { //This will invalidate frames - break; //Can't demux, EOS? - } - - if (video_frame->isValid()) { - ++video_frames_pos; - } - }; - - bool video_frame_done = false; - while (video_frames_pos > 0 && !video_frame_done) { - WebMFrame *video_frame = video_frames[0]; - - // It seems VPXDecoder::decode has to be executed even though we might skip this frame - if (video->decode(*video_frame)) { - VPXDecoder::IMAGE_ERROR err; - VPXDecoder::Image image; - - if (should_process(*video_frame)) { - if ((err = video->getImage(image)) != VPXDecoder::NO_FRAME) { - if (err == VPXDecoder::NO_ERROR && image.w == webm->getWidth() && image.h == webm->getHeight()) { - uint8_t *w = frame_data.ptrw(); - bool converted = false; - - if (image.chromaShiftW == 0 && image.chromaShiftH == 0 && image.cs == VPX_CS_SRGB) { - uint8_t *wp = w; - unsigned char *rRow = image.planes[2]; - unsigned char *gRow = image.planes[0]; - unsigned char *bRow = image.planes[1]; - for (int i = 0; i < image.h; i++) { - for (int j = 0; j < image.w; j++) { - *wp++ = rRow[j]; - *wp++ = gRow[j]; - *wp++ = bRow[j]; - *wp++ = 255; - } - rRow += image.linesize[2]; - gRow += image.linesize[0]; - bRow += image.linesize[1]; - } - converted = true; - } else if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { - yuv420_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); - //libyuv::I420ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); - converted = true; - } else if (image.chromaShiftW == 1 && image.chromaShiftH == 0) { - yuv422_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); - //libyuv::I422ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); - converted = true; - } else if (image.chromaShiftW == 0 && image.chromaShiftH == 0) { - yuv444_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); - //libyuv::I444ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); - converted = true; - } else if (image.chromaShiftW == 2 && image.chromaShiftH == 0) { - //libyuv::I411ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2] image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); - //converted = true; - } - - if (converted) { - Ref<Image> img = memnew(Image(image.w, image.h, 0, Image::FORMAT_RGBA8, frame_data)); - texture->update(img); //Zero copy send to visual server - video_frame_done = true; - } - } - } - } - } - - video_pos = video_frame->time; - memmove(video_frames, video_frames + 1, (--video_frames_pos) * sizeof(void *)); - video_frames[video_frames_pos] = video_frame; - } - - if (video_frames_pos == 0 && webm->isEOS()) { - stop(); - } -} - -void VideoStreamPlaybackWebm::set_mix_callback(VideoStreamPlayback::AudioMixCallback p_callback, void *p_userdata) { - mix_callback = p_callback; - mix_udata = p_userdata; -} - -int VideoStreamPlaybackWebm::get_channels() const { - if (audio) { - return webm->getChannels(); - } - return 0; -} - -int VideoStreamPlaybackWebm::get_mix_rate() const { - if (audio) { - return webm->getSampleRate(); - } - return 0; -} - -inline bool VideoStreamPlaybackWebm::has_enough_video_frames() const { - if (video_frames_pos > 0) { - // FIXME: AudioServer output latency was fixed in af9bb0e, previously it used to - // systematically return 0. Now that it gives a proper latency, it broke this - // code where the delay compensation likely never really worked. - //const double audio_delay = AudioServer::get_singleton()->get_output_latency(); - const double video_time = video_frames[video_frames_pos - 1]->time; - return video_time >= time + /* audio_delay + */ delay_compensation; - } - return false; -} - -bool VideoStreamPlaybackWebm::should_process(WebMFrame &video_frame) { - // FIXME: AudioServer output latency was fixed in af9bb0e, previously it used to - // systematically return 0. Now that it gives a proper latency, it broke this - // code where the delay compensation likely never really worked. - //const double audio_delay = AudioServer::get_singleton()->get_output_latency(); - return video_frame.time >= time + /* audio_delay + */ delay_compensation; -} - -void VideoStreamPlaybackWebm::delete_pointers() { - if (pcm) { - memfree(pcm); - } - - if (audio_frame) { - memdelete(audio_frame); - } - if (video_frames) { - for (int i = 0; i < video_frames_capacity; ++i) { - memdelete(video_frames[i]); - } - memfree(video_frames); - } - - if (video) { - memdelete(video); - } - if (audio) { - memdelete(audio); - } - - if (webm) { - memdelete(webm); - } -} - -/**/ - -VideoStreamWebm::VideoStreamWebm() {} - -Ref<VideoStreamPlayback> VideoStreamWebm::instance_playback() { - Ref<VideoStreamPlaybackWebm> pb = memnew(VideoStreamPlaybackWebm); - pb->set_audio_track(audio_track); - if (pb->open_file(file)) { - return pb; - } - return nullptr; -} - -void VideoStreamWebm::set_file(const String &p_file) { - file = p_file; -} - -String VideoStreamWebm::get_file() { - return file; -} - -void VideoStreamWebm::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamWebm::set_file); - ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamWebm::get_file); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); -} - -void VideoStreamWebm::set_audio_track(int p_track) { - audio_track = p_track; -} - -//////////// - -RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { - if (r_error) { - *r_error = ERR_CANT_OPEN; - } - return RES(); - } - - VideoStreamWebm *stream = memnew(VideoStreamWebm); - stream->set_file(p_path); - - Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream); - - if (r_error) { - *r_error = OK; - } - - f->close(); - memdelete(f); - return webm_stream; -} - -void ResourceFormatLoaderWebm::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("webm"); -} - -bool ResourceFormatLoaderWebm::handles_type(const String &p_type) const { - return ClassDB::is_parent_class(p_type, "VideoStream"); -} - -String ResourceFormatLoaderWebm::get_resource_type(const String &p_path) const { - String el = p_path.get_extension().to_lower(); - if (el == "webm") { - return "VideoStreamWebm"; - } - return ""; -} diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h deleted file mode 100644 index 60e02ab38b..0000000000 --- a/modules/webm/video_stream_webm.h +++ /dev/null @@ -1,135 +0,0 @@ -/*************************************************************************/ -/* video_stream_webm.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 VIDEO_STREAM_WEBM_H -#define VIDEO_STREAM_WEBM_H - -#include "core/io/resource_loader.h" -#include "scene/resources/video_stream.h" - -class WebMFrame; -class WebMDemuxer; -class VPXDecoder; -class OpusVorbisDecoder; - -class VideoStreamPlaybackWebm : public VideoStreamPlayback { - GDCLASS(VideoStreamPlaybackWebm, VideoStreamPlayback); - - String file_name; - int audio_track = 0; - - WebMDemuxer *webm = nullptr; - VPXDecoder *video = nullptr; - OpusVorbisDecoder *audio = nullptr; - - WebMFrame **video_frames = nullptr, *audio_frame = nullptr; - int video_frames_pos = 0, video_frames_capacity = 0; - - int num_decoded_samples = 0, samples_offset = -1; - AudioMixCallback mix_callback = nullptr; - void *mix_udata = nullptr; - - bool playing = false, paused = false; - double delay_compensation = 0.0; - double time = 0.0, video_frame_delay = 0.0, video_pos = 0.0; - - Vector<uint8_t> frame_data; - Ref<ImageTexture> texture; - - float *pcm = nullptr; - -public: - VideoStreamPlaybackWebm(); - ~VideoStreamPlaybackWebm(); - - bool open_file(const String &p_file); - - virtual void stop() override; - virtual void play() override; - - virtual bool is_playing() const override; - - virtual void set_paused(bool p_paused) override; - virtual bool is_paused() const override; - - virtual void set_loop(bool p_enable) override; - virtual bool has_loop() const override; - - virtual float get_length() const override; - - virtual float get_playback_position() const override; - virtual void seek(float p_time) override; - - virtual void set_audio_track(int p_idx) override; - - virtual Ref<Texture2D> get_texture() const override; - virtual void update(float p_delta) override; - - virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override; - virtual int get_channels() const override; - virtual int get_mix_rate() const override; - -private: - inline bool has_enough_video_frames() const; - bool should_process(WebMFrame &video_frame); - - void delete_pointers(); -}; - -/**/ - -class VideoStreamWebm : public VideoStream { - GDCLASS(VideoStreamWebm, VideoStream); - - String file; - int audio_track = 0; - -protected: - static void _bind_methods(); - -public: - VideoStreamWebm(); - - virtual Ref<VideoStreamPlayback> instance_playback() override; - - virtual void set_file(const String &p_file); - String get_file(); - virtual void set_audio_track(int p_track) override; -}; - -class ResourceFormatLoaderWebm : public ResourceFormatLoader { -public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; -}; - -#endif // VIDEO_STREAM_WEBM_H diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index f6f360503f..618fe14137 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -26,7 +26,8 @@ <method name="close"> <return type="void" /> <description> - Close the peer connection and all data channels associated with it. Note, you cannot reuse this object for a new connection unless you call [method initialize]. + Close the peer connection and all data channels associated with it. + [b]Note:[/b] You cannot reuse this object for a new connection unless you call [method initialize]. </description> </method> <method name="create_data_channel"> diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp index 4e5174fc33..133bd71ddb 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.cpp +++ b/modules/webrtc/webrtc_multiplayer_peer.cpp @@ -189,7 +189,7 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr cfg["ordered"] = true; switch (mode) { - case Multiplayer::TRANSFER_MODE_ORDERED: + case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED: cfg["maxPacketLifetime"] = 1; break; case Multiplayer::TRANSFER_MODE_UNRELIABLE: @@ -334,7 +334,7 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si case Multiplayer::TRANSFER_MODE_RELIABLE: ch = CH_RELIABLE; break; - case Multiplayer::TRANSFER_MODE_ORDERED: + case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED: ch = CH_ORDERED; break; case Multiplayer::TRANSFER_MODE_UNRELIABLE: diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index ab7ef6c4d0..2f455c32fd 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -4,7 +4,7 @@ A class representing a specific WebSocket connection. </brief_description> <description> - This class represent a specific WebSocket connection, you can do lower level operations with it. + This class represents a specific WebSocket connection, allowing you to do lower level operations with it. You can choose to write to the socket in binary or text mode, and you can recognize the mode used for writing by the other peer. </description> <tutorials> diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index d7263dcf43..035d036b90 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -54,11 +54,14 @@ Error EMWSPeer::read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_strin } Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(_out_buf_size && ((uint64_t)godot_js_websocket_buffered_amount(peer_sock) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(_out_buf_size && ((uint64_t)godot_js_websocket_buffered_amount(peer_sock) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0; - godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin); + if (godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin) != 0) { + return FAILED; + } + return OK; } diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index f7a8944745..bf35c91c7f 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -129,7 +129,7 @@ void WebSocketClient::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); ClassDB::bind_method(D_METHOD("get_trusted_ssl_certificate"), &WebSocketClient::get_trusted_ssl_certificate); - ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate"), &WebSocketClient::set_trusted_ssl_certificate); + ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate", "cert"), &WebSocketClient::set_trusted_ssl_certificate); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_trusted_ssl_certificate", "get_trusted_ssl_certificate"); diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h index d04909c97d..2ca60a3b61 100644 --- a/modules/websocket/websocket_macros.h +++ b/modules/websocket/websocket_macros.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef WEBSOCKETMACTOS_H -#define WEBSOCKETMACTOS_H +#ifndef WEBSOCKET_MACROS_H +#define WEBSOCKET_MACROS_H // Defaults per peer buffers, 1024 packets with a shared 65536 bytes payload. #define DEF_PKT_SHIFT 10 @@ -65,4 +65,4 @@ public:\ protected:\ /* clang-format on */ -#endif // WEBSOCKETMACTOS_H +#endif // WEBSOCKET_MACROS_H diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index fb838109f3..e7f90fdeba 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -50,19 +50,19 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_bind_ip"), &WebSocketServer::get_bind_ip); - ClassDB::bind_method(D_METHOD("set_bind_ip"), &WebSocketServer::set_bind_ip); + ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &WebSocketServer::set_bind_ip); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bind_ip"), "set_bind_ip", "get_bind_ip"); ClassDB::bind_method(D_METHOD("get_private_key"), &WebSocketServer::get_private_key); - ClassDB::bind_method(D_METHOD("set_private_key"), &WebSocketServer::set_private_key); + ClassDB::bind_method(D_METHOD("set_private_key", "key"), &WebSocketServer::set_private_key); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", PROPERTY_USAGE_NONE), "set_private_key", "get_private_key"); ClassDB::bind_method(D_METHOD("get_ssl_certificate"), &WebSocketServer::get_ssl_certificate); - ClassDB::bind_method(D_METHOD("set_ssl_certificate"), &WebSocketServer::set_ssl_certificate); + ClassDB::bind_method(D_METHOD("set_ssl_certificate", "cert"), &WebSocketServer::set_ssl_certificate); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ssl_certificate", "get_ssl_certificate"); ClassDB::bind_method(D_METHOD("get_ca_chain"), &WebSocketServer::get_ca_chain); - ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain); + ClassDB::bind_method(D_METHOD("set_ca_chain", "ca_chain"), &WebSocketServer::set_ca_chain); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ca_chain", "get_ca_chain"); ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout); diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 7f027e1c0d..fc520ec57c 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -242,15 +242,15 @@ void WSLPeer::poll() { Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); ERR_FAIL_COND_V(_out_pkt_size && (wslay_event_get_queued_msg_count(_data->ctx) >= (1ULL << _out_pkt_size)), ERR_OUT_OF_MEMORY); - ERR_FAIL_COND_V(_out_buf_size && (wslay_event_get_queued_msg_length(_data->ctx) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(_out_buf_size && (wslay_event_get_queued_msg_length(_data->ctx) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); - struct wslay_event_msg msg; // Should I use fragmented? + struct wslay_event_msg msg; msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME; msg.msg = p_buffer; msg.msg_length = p_buffer_size; - wslay_event_queue_msg(_data->ctx, &msg); - if (wslay_event_send(_data->ctx) < 0) { + // Queue & send message. + if (wslay_event_queue_msg(_data->ctx, &msg) != 0 || wslay_event_send(_data->ctx) != 0) { close_now(); return FAILED; } diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h index 93c8bd9604..508b5a12a1 100644 --- a/modules/websocket/wsl_server.h +++ b/modules/websocket/wsl_server.h @@ -53,7 +53,7 @@ private: Ref<StreamPeer> connection; bool use_ssl = false; - int time = 0; + uint64_t time = 0; uint8_t req_buf[WSL_MAX_HEADER_SIZE] = {}; int req_pos = 0; String key; diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml index 16d671c9e9..6e224a8242 100644 --- a/modules/webxr/doc_classes/WebXRInterface.xml +++ b/modules/webxr/doc_classes/WebXRInterface.xml @@ -87,7 +87,7 @@ There are several ways to handle "controller" input: - Using [XRController3D] nodes and their [signal XRController3D.button_pressed] and [signal XRController3D.button_released] signals. This is how controllers are typically handled in AR/VR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example. The buttons codes are defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url]. - Using [method Node._unhandled_input] and [InputEventJoypadButton] or [InputEventJoypadMotion]. This works the same as normal joypads, except the [member InputEvent.device] starts at 100, so the left controller is 100 and the right controller is 101, and the button codes are also defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url]. - - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional "controllers" like a tap on the screen, a spoken voice command or a button press on the device itself. The [code]controller_id[/code] passed to these signals is the same id as used in [member XRController3D.controller_id]. + - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional "controllers" like a tap on the screen, a spoken voice command or a button press on the device itself. You can use one or all of these methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices. </description> <tutorials> diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 10c17aa672..2676b3cf80 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -163,9 +163,14 @@ String WebXRInterfaceJS::get_reference_space_type() const { Ref<XRPositionalTracker> WebXRInterfaceJS::get_controller(int p_controller_id) const { XRServer *xr_server = XRServer::get_singleton(); - ERR_FAIL_NULL_V(xr_server, nullptr); + ERR_FAIL_NULL_V(xr_server, Ref<XRPositionalTracker>()); - return xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + // TODO support more then two controllers + if (p_controller_id >= 0 && p_controller_id < 2) { + return controllers[p_controller_id]; + }; + + return Ref<XRPositionalTracker>(); } String WebXRInterfaceJS::get_visibility_state() const { @@ -224,6 +229,13 @@ bool WebXRInterfaceJS::initialize() { return false; } + // we must create a tracker for our head + head_tracker.instantiate(); + head_tracker->set_tracker_type(XRServer::TRACKER_HEAD); + head_tracker->set_tracker_name("head"); + head_tracker->set_tracker_desc("Players head"); + xr_server->add_tracker(head_tracker); + // make this our primary interface xr_server->set_primary_interface(this); @@ -254,9 +266,17 @@ bool WebXRInterfaceJS::initialize() { void WebXRInterfaceJS::uninitialize() { if (initialized) { XRServer *xr_server = XRServer::get_singleton(); - if (xr_server != nullptr && xr_server->get_primary_interface() == this) { - // no longer our primary interface - xr_server->set_primary_interface(nullptr); + if (xr_server != nullptr) { + if (head_tracker.is_valid()) { + xr_server->remove_tracker(head_tracker); + + head_tracker.unref(); + } + + if (xr_server->get_primary_interface() == this) { + // no longer our primary interface + xr_server->set_primary_interface(nullptr); + } } godot_webxr_uninitialize(); @@ -373,9 +393,9 @@ Vector<BlitToScreen> WebXRInterfaceJS::commit_views(RID p_render_target, const R } // @todo Refactor this to be based on "views" rather than "eyes". - godot_webxr_commit_for_eye(XRInterface::EYE_LEFT); + godot_webxr_commit_for_eye(1); if (godot_webxr_get_view_count() > 1) { - godot_webxr_commit_for_eye(XRInterface::EYE_RIGHT); + godot_webxr_commit_for_eye(2); } return blit_to_screen; @@ -385,6 +405,11 @@ void WebXRInterfaceJS::process() { if (initialized) { godot_webxr_sample_controller_data(); + if (head_tracker.is_valid()) { + // TODO set default pose to our head location (i.e. get_camera_transform without world scale and reference frame applied) + // head_tracker->set_pose("default", head_transform, Vector3(), Vector3()); + } + int controller_count = godot_webxr_get_controller_count(); if (controller_count == 0) { return; @@ -400,51 +425,70 @@ void WebXRInterfaceJS::_update_tracker(int p_controller_id) { 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 + 1); + // need to support more then two controllers... + if (p_controller_id < 0 || p_controller_id > 1) { + return; + } + + Ref<XRPositionalTracker> tracker = controllers[p_controller_id]; if (godot_webxr_is_controller_connected(p_controller_id)) { if (tracker.is_null()) { tracker.instantiate(); tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); // Controller id's 0 and 1 are always the left and right hands. if (p_controller_id < 2) { - tracker->set_tracker_name(p_controller_id == 0 ? "Left" : "Right"); + tracker->set_tracker_name(p_controller_id == 0 ? "left_hand" : "right_hand"); + tracker->set_tracker_desc(p_controller_id == 0 ? "Left hand controller" : "Right hand controller"); tracker->set_tracker_hand(p_controller_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT); + } else { + char name[1024]; + sprintf(name, "tracker_%i", p_controller_id); + tracker->set_tracker_name(name); + tracker->set_tracker_desc(name); } - // Use the ids we're giving to our "virtual" gamepads. - tracker->set_joy_id(p_controller_id + 100); xr_server->add_tracker(tracker); } - Input *input = Input::get_singleton(); - float *tracker_matrix = godot_webxr_get_controller_transform(p_controller_id); if (tracker_matrix) { + // Note, poses should NOT have world scale and our reference frame applied! Transform3D transform = _js_matrix_to_transform(tracker_matrix); - tracker->set_position(transform.origin); - tracker->set_orientation(transform.basis); + tracker->set_pose("default", transform, Vector3(), Vector3()); free(tracker_matrix); } + // TODO implement additional poses such as "aim" and "grip" + int *buttons = godot_webxr_get_controller_buttons(p_controller_id); if (buttons) { + // TODO buttons should be named properly, this is just a temporary fix for (int i = 0; i < buttons[0]; i++) { - input->joy_button(p_controller_id + 100, (JoyButton)i, *((float *)buttons + (i + 1))); + char name[1024]; + sprintf(name, "button_%i", i); + + float value = *((float *)buttons + (i + 1)); + bool state = value > 0.0; + tracker->set_input(name, state); } free(buttons); } int *axes = godot_webxr_get_controller_axes(p_controller_id); if (axes) { + // TODO again just a temporary fix, split these between proper float and vector2 inputs for (int i = 0; i < axes[0]; i++) { - Input::JoyAxisValue joy_axis; - joy_axis.min = -1; - joy_axis.value = *((float *)axes + (i + 1)); - input->joy_axis(p_controller_id + 100, (JoyAxis)i, joy_axis); + char name[1024]; + sprintf(name, "axis_%i", i); + + float value = *((float *)axes + (i + 1)); + ; + tracker->set_input(name, value); } free(axes); } } else if (tracker.is_valid()) { xr_server->remove_tracker(tracker); + controllers[p_controller_id].unref(); } } @@ -454,7 +498,7 @@ void WebXRInterfaceJS::_on_controller_changed() { for (int i = 0; i < 2; i++) { bool controller_connected = godot_webxr_is_controller_connected(i); if (controllers_state[i] != controller_connected) { - Input::get_singleton()->joy_connection_changed(i + 100, controller_connected, i == 0 ? "Left" : "Right", ""); + // Input::get_singleton()->joy_connection_changed(i + 100, controller_connected, i == 0 ? "Left" : "Right", ""); controllers_state[i] = controller_connected; } } diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h index eb77f35f39..6e6548c946 100644 --- a/modules/webxr/webxr_interface_js.h +++ b/modules/webxr/webxr_interface_js.h @@ -46,6 +46,7 @@ class WebXRInterfaceJS : public WebXRInterface { private: bool initialized; + Ref<XRPositionalTracker> head_tracker; String session_mode; String required_features; @@ -53,7 +54,9 @@ private: String requested_reference_space_types; String reference_space_type; + // TODO maybe turn into a vector to support more then 2 controllers... bool controllers_state[2]; + Ref<XRPositionalTracker> controllers[2]; Size2 render_targetsize; Transform3D _js_matrix_to_transform(float *p_js_matrix); diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp index 8913ef1b65..58a6216b1e 100644 --- a/modules/xatlas_unwrap/register_types.cpp +++ b/modules/xatlas_unwrap/register_types.cpp @@ -114,7 +114,7 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver input_mesh.vertexPositionStride = sizeof(float) * 3; input_mesh.vertexNormalData = p_normals; input_mesh.vertexNormalStride = sizeof(uint32_t) * 3; - input_mesh.vertexUvData = NULL; + input_mesh.vertexUvData = nullptr; input_mesh.vertexUvStride = 0; xatlas::ChartOptions chart_options; |