diff options
70 files changed, 1677 insertions, 141 deletions
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index fcbb22b5de..f1620f1493 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -279,6 +279,7 @@ void HTTPClient::close() { chunk_left = 0; read_until_eof = false; response_num = 0; + handshaking = false; } Error HTTPClient::poll() { @@ -327,16 +328,40 @@ Error HTTPClient::poll() { } break; case StreamPeerTCP::STATUS_CONNECTED: { if (ssl) { - Ref<StreamPeerSSL> ssl = StreamPeerSSL::create(); - Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); - if (err != OK) { + Ref<StreamPeerSSL> ssl; + if (!handshaking) { + // Connect the StreamPeerSSL and start handshaking + ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); + ssl->set_blocking_handshake_enabled(false); + Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); + if (err != OK) { + close(); + status = STATUS_SSL_HANDSHAKE_ERROR; + return ERR_CANT_CONNECT; + } + connection = ssl; + handshaking = true; + } else { + // We are already handshaking, which means we can use your already active SSL connection + ssl = static_cast<Ref<StreamPeerSSL> >(connection); + ssl->poll(); // Try to finish the handshake + } + + if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { + // Handshake has been successfull + handshaking = false; + status = STATUS_CONNECTED; + return OK; + } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { + // Handshake has failed close(); status = STATUS_SSL_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - connection = ssl; + // ... we will need to poll more for handshake to finish + } else { + status = STATUS_CONNECTED; } - status = STATUS_CONNECTED; return OK; } break; case StreamPeerTCP::STATUS_ERROR: @@ -669,6 +694,7 @@ HTTPClient::HTTPClient() { response_num = 0; ssl = false; blocking = false; + handshaking = false; read_chunk_size = 4096; } diff --git a/core/io/http_client.h b/core/io/http_client.h index 38ec82ce8c..82b56b01db 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -165,6 +165,7 @@ private: bool ssl; bool ssl_verify_host; bool blocking; + bool handshaking; Vector<uint8_t> response_str; diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 012ba78c6d..c71af6b641 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -52,6 +52,14 @@ bool StreamPeerSSL::is_available() { return available; } +void StreamPeerSSL::set_blocking_handshake_enabled(bool p_enabled) { + blocking_handshake = p_enabled; +} + +bool StreamPeerSSL::is_blocking_handshake_enabled() const { + return blocking_handshake; +} + PoolByteArray StreamPeerSSL::get_project_cert_array() { PoolByteArray out; @@ -84,16 +92,21 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); - ClassDB::bind_method(D_METHOD("accept_stream", "stream"), &StreamPeerSSL::accept_stream); + ClassDB::bind_method(D_METHOD("accept_stream"), &StreamPeerSSL::accept_stream); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); + ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled); + ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerSSL::is_blocking_handshake_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled"); BIND_ENUM_CONSTANT(STATUS_DISCONNECTED); BIND_ENUM_CONSTANT(STATUS_CONNECTED); - BIND_ENUM_CONSTANT(STATUS_ERROR_NO_CERTIFICATE); + BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); } StreamPeerSSL::StreamPeerSSL() { + blocking_handshake = true; } diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 77301a7c87..870704e875 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -49,14 +49,20 @@ protected: friend class Main; static bool initialize_certs; + bool blocking_handshake; + public: enum Status { STATUS_DISCONNECTED, + STATUS_HANDSHAKING, STATUS_CONNECTED, - STATUS_ERROR_NO_CERTIFICATE, + STATUS_ERROR, STATUS_ERROR_HOSTNAME_MISMATCH }; + void set_blocking_handshake_enabled(bool p_enabled); + bool is_blocking_handshake_enabled() const; + virtual void poll() = 0; virtual Error accept_stream(Ref<StreamPeer> p_base) = 0; virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 7f9f4b638a..b1fd66e566 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -515,7 +515,11 @@ Error ProjectSettings::_load_settings_text(const String p_path) { } } else { // config_version is checked and dropped - set(section + "/" + assign, value); + if (section == String()) { + set(assign, value); + } else { + set(section + "/" + assign, value); + } } } else if (next_tag.name != String()) { section = next_tag.name; diff --git a/core/script_language.cpp b/core/script_language.cpp index acbe3b34db..37ba3cfc62 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "script_language.h" +#include "project_settings.h" ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; int ScriptServer::_language_count = 0; @@ -103,6 +104,20 @@ void ScriptServer::unregister_language(ScriptLanguage *p_language) { void ScriptServer::init_languages() { + { //load global classes + global_classes_clear(); + if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { + Array script_classes = ProjectSettings::get_singleton()->get("_global_script_classes"); + + for (int i = 0; i < script_classes.size(); i++) { + Dictionary c = script_classes[i]; + if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) + continue; + add_global_class(c["class"], c["base"], c["language"], c["path"]); + } + } + } + for (int i = 0; i < _language_count; i++) { _languages[i]->init(); } @@ -113,6 +128,7 @@ void ScriptServer::finish_languages() { for (int i = 0; i < _language_count; i++) { _languages[i]->finish(); } + global_classes_clear(); } void ScriptServer::set_reload_scripts_on_save(bool p_enable) { @@ -139,6 +155,67 @@ void ScriptServer::thread_exit() { } } +HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes; + +void ScriptServer::global_classes_clear() { + global_classes.clear(); +} + +void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) { + GlobalScriptClass g; + g.language = p_language; + g.path = p_path; + g.base = p_base; + global_classes[p_class] = g; +} +void ScriptServer::remove_global_class(const StringName &p_class) { + global_classes.erase(p_class); +} +bool ScriptServer::is_global_class(const StringName &p_class) { + return global_classes.has(p_class); +} +StringName ScriptServer::get_global_class_language(const StringName &p_class) { + ERR_FAIL_COND_V(!global_classes.has(p_class), StringName()); + return global_classes[p_class].language; +} +String ScriptServer::get_global_class_path(const String &p_class) { + ERR_FAIL_COND_V(!global_classes.has(p_class), String()); + return global_classes[p_class].path; +} + +StringName ScriptServer::get_global_class_base(const String &p_class) { + ERR_FAIL_COND_V(!global_classes.has(p_class), String()); + return global_classes[p_class].base; +} +void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { + const StringName *K = NULL; + List<StringName> classes; + while ((K = global_classes.next(K))) { + classes.push_back(*K); + } + classes.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = classes.front(); E; E = E->next()) { + r_global_classes->push_back(E->get()); + } +} +void ScriptServer::save_global_classes() { + List<StringName> gc; + get_global_class_list(&gc); + Array gcarr; + for (List<StringName>::Element *E = gc.front(); E; E = E->next()) { + Dictionary d; + d["class"] = E->get(); + d["language"] = global_classes[E->get()].language; + d["path"] = global_classes[E->get()].path; + d["base"] = global_classes[E->get()].base; + gcarr.push_back(d); + } + + ProjectSettings::get_singleton()->set("_global_script_classes", gcarr); + ProjectSettings::get_singleton()->save(); +} + +//////////////////// void ScriptInstance::get_property_state(List<Pair<StringName, Variant> > &state) { List<PropertyInfo> pinfo; diff --git a/core/script_language.h b/core/script_language.h index e7748f93e2..2950b35109 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -54,6 +54,14 @@ class ScriptServer { static bool scripting_enabled; static bool reload_scripts_on_save; + struct GlobalScriptClass { + StringName language; + String path; + String base; + }; + + static HashMap<StringName, GlobalScriptClass> global_classes; + public: static ScriptEditRequestFunction edit_request_func; @@ -70,6 +78,16 @@ public: static void thread_enter(); static void thread_exit(); + static void global_classes_clear(); + static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path); + static void remove_global_class(const StringName &p_class); + static bool is_global_class(const StringName &p_class); + static StringName get_global_class_language(const StringName &p_class); + static String get_global_class_path(const String &p_class); + static StringName get_global_class_base(const String &p_class); + static void get_global_class_list(List<StringName> *r_global_classes); + static void save_global_classes(); + static void init_languages(); static void finish_languages(); }; @@ -285,7 +303,10 @@ public: virtual void frame(); - virtual ~ScriptLanguage(){}; + virtual bool handles_global_class_type(const String &p_type) const { return false; } + virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const { return String(); } + + virtual ~ScriptLanguage() {} }; extern uint8_t script_encryption_key[32]; diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 35c120cd6a..7fcb827252 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -4,7 +4,16 @@ Generic array datatype. </brief_description> <description> - Generic array, contains several elements of any type, accessible by numerical index starting at 0. Negative indices can be used to count from the right, like in Python. Arrays are always passed by reference. + Generic array, contains several elements of any type, accessible by a numerical index starting at 0. Negative indices can be used to count from the back, like in Python (-1 is the last element, -2 the second to last, etc.). Example: + [codeblock] + var array = ["One", 2, 3, "Four"] + print(array[0]) # One + print(array[2]) # 3 + print(array[-1]) # Four + array[2] = "Three" + print(array[-2]) # Three + [/codeblock] + Arrays are always passed by reference. </description> <tutorials> </tutorials> diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml index 19be34978d..c1682e71e5 100644 --- a/doc/classes/Line2D.xml +++ b/doc/classes/Line2D.xml @@ -117,5 +117,8 @@ <constant name="LINE_TEXTURE_TILE" value="1" enum="LineTextureMode"> Tiles the texture over the line. The texture need to be imported with Repeat Enabled for it to work properly. </constant> + <constant name="LINE_TEXTURE_STRETCH" value="2" enum="LineTextureMode"> + Stretches the texture across the line. Import the texture with Repeat Disabled for best results. + </constant> </constants> </class> diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index 068a14cb8a..bab89f649a 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -235,6 +235,7 @@ public: void textures_keep_original(bool p_enable) {} void texture_set_proxy(RID p_proxy, RID p_base) {} + void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) {} /* SKY API */ diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index d5232a6511..daa421d45c 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -140,6 +140,10 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con texture = texture->get_ptr(); + if (texture->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } + if (texture->render_target) { texture->render_target->used_in_frame = true; } @@ -909,6 +913,10 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons t = t->get_ptr(); + if (t->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } + glBindTexture(t->target, t->tex_id); } } else { diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index ca39531b0d..b268d4c723 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -674,6 +674,15 @@ void RasterizerStorageGLES2::texture_set_proxy(RID p_texture, RID p_proxy) { } } +void RasterizerStorageGLES2::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { + + Texture *texture = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!texture); + + texture->redraw_if_visible = p_enable; + +} + void RasterizerStorageGLES2::texture_set_detect_3d_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { // TODO } diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index df8c2fcf47..b2c8b620a6 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -176,12 +176,15 @@ public: bool active; GLenum tex_id; + uint16_t stored_cube_sides; RenderTarget *render_target; Ref<Image> images[6]; + bool redraw_if_visible; + Texture() { flags = 0; width = 0; @@ -205,6 +208,8 @@ public: proxy = NULL; render_target = NULL; + + redraw_if_visible = false; } _ALWAYS_INLINE_ Texture *get_ptr() { @@ -264,6 +269,8 @@ public: virtual void texture_set_detect_srgb_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata); virtual void texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata); + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable); + /* SKY API */ virtual RID sky_create(); diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index f214943bcf..f859e49b5b 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -211,6 +211,10 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con } else { + if (texture->redraw_if_visible) { //check before proxy, because this is usually used with proxies + VisualServerRaster::redraw_request(); + } + texture = texture->get_ptr(); if (texture->render_target) @@ -248,6 +252,10 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con } else { + if (normal_map->redraw_if_visible) { //check before proxy, because this is usually used with proxies + VisualServerRaster::redraw_request(); + } + normal_map = normal_map->get_ptr(); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, normal_map->tex_id); @@ -1266,6 +1274,10 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons continue; } + if (t->redraw_if_visible) { //check before proxy, because this is usually used with proxies + VisualServerRaster::redraw_request(); + } + t = t->get_ptr(); if (storage->config.srgb_decode_supported && t->using_srgb) { diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 7c2af755cd..9d0fb462f4 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1225,7 +1225,12 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m } else { + if (t->redraw_if_visible) { //must check before proxy because this is often used with proxies + VisualServerRaster::redraw_request(); + } + t = t->get_ptr(); //resolve for proxies + #ifdef TOOLS_ENABLED if (t->detect_3d) { t->detect_3d(t->detect_3d_ud); @@ -1569,6 +1574,11 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) { RasterizerStorageGLES3::Texture *t = storage->texture_owner.get(c.texture); t = t->get_ptr(); //resolve for proxies + + if (t->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } + #ifdef TOOLS_ENABLED if (t->detect_3d) { t->detect_3d(t->detect_3d_ud); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 4a3ebf7a7c..eb25d6c7a1 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -1328,6 +1328,13 @@ void RasterizerStorageGLES3::texture_set_proxy(RID p_texture, RID p_proxy) { } } +void RasterizerStorageGLES3::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { + + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + texture->redraw_if_visible = p_enable; +} + RID RasterizerStorageGLES3::sky_create() { Sky *sky = memnew(Sky); diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 7a2d56f69b..80df21941b 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -268,6 +268,7 @@ public: GLuint tex_id; bool using_srgb; + bool redraw_if_visible; uint16_t stored_cube_sides; @@ -306,6 +307,7 @@ public: detect_normal = NULL; detect_normal_ud = NULL; proxy = NULL; + redraw_if_visible = false; } _ALWAYS_INLINE_ Texture *get_ptr() { @@ -366,6 +368,7 @@ public: virtual void texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata); virtual void texture_set_proxy(RID p_texture, RID p_proxy); + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable); /* SKY API */ diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index a8cbf52cd2..6b2a072e20 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -243,6 +243,18 @@ void CreateDialog::_update_search() { _parse_fs(EditorFileSystem::get_singleton()->get_filesystem()); */ + List<StringName> global_classes; + ScriptServer::get_global_class_list(&global_classes); + + Map<String, List<String> > global_class_map; + for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { + String base = ScriptServer::get_global_class_base(E->get()); + if (!global_class_map.has(base)) { + global_class_map[base] = List<String>(); + } + global_class_map[base].push_back(E->get()); + } + HashMap<String, TreeItem *> types; TreeItem *root = search_options->create_item(); @@ -293,6 +305,32 @@ void CreateDialog::_update_search() { add_type(I->get(), types, root, &to_select); } + if (global_class_map.has(type) && ClassDB::is_parent_class(type, base_type)) { + for (List<String>::Element *J = global_class_map[type].front(); J; J = J->next()) { + bool show = search_box->get_text().is_subsequence_ofi(J->get()); + + if (!show) + continue; + + if (!types.has(type)) + add_type(type, types, root, &to_select); + + TreeItem *ti; + if (types.has(type)) + ti = types[type]; + else + ti = search_options->get_root(); + + TreeItem *item = search_options->create_item(ti); + item->set_metadata(0, J->get()); + item->set_text(0, J->get() + " (" + ScriptServer::get_global_class_path(J->get()).get_file() + ")"); + item->set_icon(0, _get_editor_icon(type)); + if (!to_select || J->get() == search_box->get_text()) { + to_select = item; + } + } + } + if (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) { //there are custom types based on this... cool. @@ -444,6 +482,17 @@ Object *CreateDialog::instance_selected() { custom = md; if (custom != String()) { + + if (ScriptServer::is_global_class(custom)) { + RES script = ResourceLoader::load(ScriptServer::get_global_class_path(custom)); + ERR_FAIL_COND_V(!script.is_valid(), NULL); + + Object *obj = ClassDB::instance(ScriptServer::get_global_class_base(custom)); + ERR_FAIL_COND_V(!obj, NULL); + + obj->set_script(script.get_ref_ptr()); + return obj; + } return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom); } else { return ClassDB::instance(selected->get_text(0)); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index d8ae1da72e..d8ab41fa05 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -125,6 +125,14 @@ bool EditorFileSystemDirectory::get_file_import_is_valid(int p_idx) const { return files[p_idx]->import_valid; } +String EditorFileSystemDirectory::get_file_script_class_name(int p_idx) const { + return files[p_idx]->script_class_name; +} + +String EditorFileSystemDirectory::get_file_script_class_extends(int p_idx) const { + return files[p_idx]->script_class_extends; +} + StringName EditorFileSystemDirectory::get_file_type(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, files.size(), ""); @@ -149,6 +157,8 @@ void EditorFileSystemDirectory::_bind_methods() { ClassDB::bind_method(D_METHOD("get_file", "idx"), &EditorFileSystemDirectory::get_file); ClassDB::bind_method(D_METHOD("get_file_path", "idx"), &EditorFileSystemDirectory::get_file_path); ClassDB::bind_method(D_METHOD("get_file_type", "idx"), &EditorFileSystemDirectory::get_file_type); + ClassDB::bind_method(D_METHOD("get_file_script_class_name", "idx"), &EditorFileSystemDirectory::get_file_script_class_name); + ClassDB::bind_method(D_METHOD("get_file_script_class_extends", "idx"), &EditorFileSystemDirectory::get_file_script_class_extends); ClassDB::bind_method(D_METHOD("get_file_import_is_valid", "idx"), &EditorFileSystemDirectory::get_file_import_is_valid); ClassDB::bind_method(D_METHOD("get_name"), &EditorFileSystemDirectory::get_name); ClassDB::bind_method(D_METHOD("get_path"), &EditorFileSystemDirectory::get_path); @@ -189,7 +199,7 @@ void EditorFileSystem::_scan_filesystem() { String project = ProjectSettings::get_singleton()->get_resource_path(); - String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3"); + String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache4"); FileAccess *f = FileAccess::open(fscache, FileAccess::READ); if (f) { @@ -209,7 +219,7 @@ void EditorFileSystem::_scan_filesystem() { } else { Vector<String> split = l.split("::"); - ERR_CONTINUE(split.size() != 6); + ERR_CONTINUE(split.size() != 7); String name = split[0]; String file; @@ -221,8 +231,10 @@ void EditorFileSystem::_scan_filesystem() { fc.modification_time = split[2].to_int64(); fc.import_modification_time = split[3].to_int64(); fc.import_valid = split[4].to_int64() != 0; + fc.script_class_name = split[5].get_slice("<>", 0); + fc.script_class_extends = split[5].get_slice("<>", 1); - String deps = split[5].strip_edges(); + String deps = split[6].strip_edges(); if (deps.length()) { Vector<String> dp = deps.split("<>"); for (int i = 0; i < dp.size(); i++) { @@ -239,7 +251,7 @@ void EditorFileSystem::_scan_filesystem() { memdelete(f); } - String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update3"); + String update_cache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4"); if (FileAccess::exists(update_cache)) { { @@ -287,7 +299,7 @@ void EditorFileSystem::_scan_filesystem() { } void EditorFileSystem::_save_filesystem_cache() { - String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3"); + String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache4"); FileAccess *f = FileAccess::open(fscache, FileAccess::WRITE); if (f == NULL) { @@ -563,6 +575,7 @@ void EditorFileSystem::scan() { scanning = false; emit_signal("filesystem_changed"); emit_signal("sources_changed", sources_changed.size() > 0); + _queue_update_script_classes(); } else { @@ -706,6 +719,9 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess fi->modified_time = fc->modification_time; fi->import_modified_time = fc->import_modification_time; fi->import_valid = fc->import_valid; + fi->script_class_name = fc->script_class_name; + fi->script_class_extends = fc->script_class_extends; + if (fc->type == String()) { fi->type = ResourceLoader::get_resource_type(path); //there is also the chance that file type changed due to reimport, must probably check this somehow here (or kind of note it for next time in another file?) @@ -715,6 +731,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess } else { fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path); + fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends); fi->modified_time = 0; fi->import_modified_time = 0; fi->import_valid = ResourceLoader::is_import_valid(path); @@ -734,9 +751,12 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess fi->deps = fc->deps; fi->import_modified_time = 0; fi->import_valid = true; + fi->script_class_name = fc->script_class_name; + fi->script_class_extends = fc->script_class_extends; } else { //new or modified time fi->type = ResourceLoader::get_resource_type(path); + fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends); fi->deps = _get_dependencies(path); fi->modified_time = mt; fi->import_modified_time = 0; @@ -835,6 +855,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const fi->modified_time = FileAccess::get_modified_time(path); fi->import_modified_time = 0; fi->type = ResourceLoader::get_resource_type(path); + fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends); fi->import_valid = ResourceLoader::is_import_valid(path); { @@ -1044,6 +1065,7 @@ void EditorFileSystem::_notification(int p_what) { if (_update_scan_actions()) emit_signal("filesystem_changed"); emit_signal("sources_changed", sources_changed.size() > 0); + _queue_update_script_classes(); } } else if (!scanning) { @@ -1059,6 +1081,7 @@ void EditorFileSystem::_notification(int p_what) { _update_scan_actions(); emit_signal("filesystem_changed"); emit_signal("sources_changed", sources_changed.size() > 0); + _queue_update_script_classes(); } } } break; @@ -1087,7 +1110,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, for (int i = 0; i < p_dir->files.size(); i++) { - String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid); + String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends; s += "::"; for (int j = 0; j < p_dir->files[i]->deps.size(); j++) { @@ -1268,7 +1291,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_filesystem_path(const String &p void EditorFileSystem::_save_late_updated_files() { //files that already existed, and were modified, need re-scanning for dependencies upon project restart. This is done via saving this special file - String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update3"); + String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_update4"); FileAccessRef f = FileAccess::open(fscache, FileAccess::WRITE); for (Set<String>::Element *E = late_update_files.front(); E; E = E->next()) { f->store_line(E->get()); @@ -1293,6 +1316,67 @@ Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) { return ret; } +String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const { + + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) { + String global_name; + String extends; + + global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends); + *r_extends = extends; + return global_name; + } + } + *r_extends = String(); + return String(); +} + +void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) { + int filecount = p_dir->files.size(); + const EditorFileSystemDirectory::FileInfo *const *files = p_dir->files.ptr(); + for (int i = 0; i < filecount; i++) { + if (files[i]->script_class_name == String()) { + continue; + } + + String lang; + for (int j = 0; j < ScriptServer::get_language_count(); j++) { + if (ScriptServer::get_language(j)->handles_global_class_type(files[i]->type)) { + lang = ScriptServer::get_language(j)->get_name(); + } + } + + ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i)); + } + for (int i = 0; i < p_dir->get_subdir_count(); i++) { + _scan_script_classes(p_dir->get_subdir(i)); + } +} + +void EditorFileSystem::update_script_classes() { + + if (!update_script_classes_queued) + return; + + update_script_classes_queued = false; + ScriptServer::global_classes_clear(); + if (get_filesystem()) { + _scan_script_classes(get_filesystem()); + } + + ScriptServer::save_global_classes(); +} + +void EditorFileSystem::_queue_update_script_classes() { + if (update_script_classes_queued) { + return; + } + + update_script_classes_queued = true; + call_deferred("update_script_classes"); +} + void EditorFileSystem::update_file(const String &p_file) { EditorFileSystemDirectory *fs = NULL; @@ -1311,7 +1395,9 @@ void EditorFileSystem::update_file(const String &p_file) { memdelete(fs->files[cpos]); fs->files.remove(cpos); } + call_deferred("emit_signal", "filesystem_changed"); //update later + _queue_update_script_classes(); return; } @@ -1351,6 +1437,7 @@ void EditorFileSystem::update_file(const String &p_file) { } fs->files[cpos]->type = type; + fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends); fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); fs->files[cpos]->deps = _get_dependencies(p_file); fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file); @@ -1359,6 +1446,7 @@ void EditorFileSystem::update_file(const String &p_file) { EditorResourcePreview::get_singleton()->check_for_invalidation(p_file); call_deferred("emit_signal", "filesystem_changed"); //update later + _queue_update_script_classes(); } void EditorFileSystem::_reimport_file(const String &p_file) { @@ -1611,6 +1699,7 @@ void EditorFileSystem::_bind_methods() { ClassDB::bind_method(D_METHOD("update_file", "path"), &EditorFileSystem::update_file); ClassDB::bind_method(D_METHOD("get_filesystem_path", "path"), &EditorFileSystem::get_filesystem_path); ClassDB::bind_method(D_METHOD("get_file_type", "path"), &EditorFileSystem::get_file_type); + ClassDB::bind_method(D_METHOD("update_script_classes"), &EditorFileSystem::update_script_classes); ADD_SIGNAL(MethodInfo("filesystem_changed")); ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist"))); @@ -1664,6 +1753,7 @@ EditorFileSystem::EditorFileSystem() { memdelete(da); scan_total = 0; + update_script_classes_queued = false; } EditorFileSystem::~EditorFileSystem() { diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index a587d2879a..1aa35f4782 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -58,6 +58,8 @@ class EditorFileSystemDirectory : public Object { bool import_valid; Vector<String> deps; bool verified; //used for checking changes + String script_class_name; + String script_class_extends; }; struct FileInfoSort { @@ -86,6 +88,8 @@ public: StringName get_file_type(int p_idx) const; Vector<String> get_file_deps(int p_idx) const; bool get_file_import_is_valid(int p_idx) const; + String get_file_script_class_name(int p_idx) const; //used for scripts + String get_file_script_class_extends(int p_idx) const; //used for scripts EditorFileSystemDirectory *get_parent(); @@ -157,6 +161,8 @@ class EditorFileSystem : public Node { uint64_t import_modification_time; Vector<String> deps; bool import_valid; + String script_class_name; + String script_class_extends; }; HashMap<String, FileCache> file_cache; @@ -215,6 +221,12 @@ class EditorFileSystem : public Node { } }; + void _scan_script_classes(EditorFileSystemDirectory *p_dir); + volatile bool update_script_classes_queued; + void _queue_update_script_classes(); + + String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const; + protected: void _notification(int p_what); static void _bind_methods(); @@ -237,6 +249,8 @@ public: void reimport_files(const Vector<String> &p_files); + void update_script_classes(); + EditorFileSystem(); ~EditorFileSystem(); }; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 72adb9aa47..70bc090bc4 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3861,7 +3861,7 @@ ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) { tb->set_focus_mode(Control::FOCUS_NONE); bottom_panel_vb->add_child(p_item); bottom_panel_hb->raise(); - bottom_panel_hb->add_child(tb); + bottom_panel_hb_editors->add_child(tb); p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL); p_item->hide(); BottomPanelItem bpi; @@ -3925,7 +3925,7 @@ void EditorNode::remove_bottom_panel_item(Control *p_item) { _bottom_panel_switch(false, 0); } bottom_panel_vb->remove_child(bottom_panel_items[i].control); - bottom_panel_hb->remove_child(bottom_panel_items[i].button); + bottom_panel_hb_editors->remove_child(bottom_panel_items[i].button); memdelete(bottom_panel_items[i].button); bottom_panel_items.remove(i); break; @@ -3955,6 +3955,11 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) { } center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE); center_split->set_collapsed(false); + if (bottom_panel_raise->is_pressed()) { + top_split->hide(); + } + bottom_panel_raise->show(); + } else { bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer")); for (int i = 0; i < bottom_panel_items.size(); i++) { @@ -3964,6 +3969,10 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) { } center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN); center_split->set_collapsed(true); + bottom_panel_raise->hide(); + if (bottom_panel_raise->is_pressed()) { + top_split->show(); + } } } @@ -4373,6 +4382,17 @@ Vector<Ref<EditorResourceConversionPlugin> > EditorNode::find_resource_conversio return ret; } +void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) { + + if (p_pressed) { + top_split->hide(); + bottom_panel_raise->set_icon(gui_base->get_icon("ShrinkBottomDock", "EditorIcons")); + } else { + top_split->show(); + bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons")); + } +} + void EditorNode::_bind_methods() { ClassDB::bind_method("_menu_option", &EditorNode::_menu_option); @@ -4441,6 +4461,7 @@ void EditorNode::_bind_methods() { ClassDB::bind_method(D_METHOD("_dim_timeout"), &EditorNode::_dim_timeout); ClassDB::bind_method(D_METHOD("_resources_reimported"), &EditorNode::_resources_reimported); + ClassDB::bind_method(D_METHOD("_bottom_panel_raise_toggled"), &EditorNode::_bottom_panel_raise_toggled); ADD_SIGNAL(MethodInfo("play_pressed")); ADD_SIGNAL(MethodInfo("pause_pressed")); @@ -5269,6 +5290,19 @@ EditorNode::EditorNode() { bottom_panel_hb = memnew(HBoxContainer); bottom_panel_vb->add_child(bottom_panel_hb); + bottom_panel_hb_editors = memnew(HBoxContainer); + bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL); + bottom_panel_hb->add_child(bottom_panel_hb_editors); + bottom_panel_raise = memnew(ToolButton); + bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons")); + + bottom_panel_raise->set_shortcut(ED_SHORTCUT("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KEY_MASK_SHIFT | KEY_F12)); + + bottom_panel_hb->add_child(bottom_panel_raise); + bottom_panel_raise->hide(); + bottom_panel_raise->set_toggle_mode(true); + bottom_panel_raise->connect("toggled", this, "_bottom_panel_raise_toggled"); + log = memnew(EditorLog); ToolButton *output_button = add_bottom_panel_item(TTR("Output"), log); log->set_tool_button(output_button); diff --git a/editor/editor_node.h b/editor/editor_node.h index a5f975784c..7aa060fe14 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -377,7 +377,11 @@ private: PanelContainer *bottom_panel; HBoxContainer *bottom_panel_hb; + HBoxContainer *bottom_panel_hb_editors; VBoxContainer *bottom_panel_vb; + ToolButton *bottom_panel_raise; + + void _bottom_panel_raise_toggled(bool); EditorInterface *editor_interface; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index fefadde64e..1f45902008 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2004,7 +2004,7 @@ void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) { void EditorPropertyResource::_open_editor_pressed() { RES res = get_edited_object()->get(get_edited_property()); if (res.is_valid()) { - EditorNode::get_singleton()->edit_item(res.ptr()); + EditorNode::get_singleton()->edit_resource(res.ptr()); } } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index eebf1b6ab8..f65fb5365b 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1862,7 +1862,7 @@ void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_file_option"), &FileSystemDock::_file_option); ClassDB::bind_method(D_METHOD("_folder_option"), &FileSystemDock::_folder_option); ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileSystemDock::_make_dir_confirm); - ClassDB::bind_method(D_METHOD("_move_operation_confirm"), &FileSystemDock::_move_operation_confirm); + ClassDB::bind_method(D_METHOD("_move_operation_confirm", "to_path", "overwrite"), &FileSystemDock::_move_operation_confirm, DEFVAL(false)); ClassDB::bind_method(D_METHOD("_move_with_overwrite"), &FileSystemDock::_move_with_overwrite); ClassDB::bind_method(D_METHOD("_rename_operation_confirm"), &FileSystemDock::_rename_operation_confirm); ClassDB::bind_method(D_METHOD("_duplicate_operation_confirm"), &FileSystemDock::_duplicate_operation_confirm); diff --git a/editor/icons/icon_animated_texture.svg b/editor/icons/icon_animated_texture.svg new file mode 100644 index 0000000000..dd039df6a7 --- /dev/null +++ b/editor/icons/icon_animated_texture.svg @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_animated_texture.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10"> + <filter + inkscape:collect="always" + style="color-interpolation-filters:sRGB" + id="filter822" + x="-0.012" + width="1.024" + y="-0.012" + height="1.024"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.07" + id="feGaussianBlur824" /> + </filter> + </defs> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="836" + inkscape:window-height="480" + id="namedview8" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="8" + inkscape:cy="8" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="0" + inkscape:current-layer="g4" /> + <g + transform="translate(0 -1036.4)" + id="g4"> + <path + d="m1 1037.4v14h1.1667v-2h1.8333v2h8v-2h2v2h1v-14h-1v2h-2v-2h-8v2h-1.8333v-2zm1.1667 4h1.8333v2h-1.8333zm9.8333 0h2v2h-2zm-9.8333 4h1.8333v2h-1.8333zm9.8333 0h2v2h-2z" + fill="#cea4f1" + id="path2" + style="fill:#e0e0e0;fill-opacity:1;filter:url(#filter822)" /> + </g> +</svg> diff --git a/editor/icons/icon_expand_bottom_dock.svg b/editor/icons/icon_expand_bottom_dock.svg new file mode 100644 index 0000000000..5a1760f377 --- /dev/null +++ b/editor/icons/icon_expand_bottom_dock.svg @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_expand_bottom_dock.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1853" + inkscape:window-height="1016" + id="namedview8" + showgrid="false" + inkscape:zoom="20.85965" + inkscape:cx="9.4509357" + inkscape:cy="6.016355" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg6" /> + <path + style="fill:#e0e0e0" + d="M 4.2130251,4.516057 0.6774912,8.0515909 H 3.2131761 V 13.025308 H 5.2130155 V 8.0515909 H 7.7487004 L 4.2131665,4.516057 Z" + id="path829" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccccc" /> + <path + inkscape:connector-curvature="0" + id="path831" + d="M 11.907306,4.6119359 8.3717718,8.1474698 h 2.5356852 v 4.9737172 h 1.999839 V 8.1474698 h 2.535685 L 11.907447,4.6119359 Z" + style="fill:#e0e0e0" + sodipodi:nodetypes="ccccccccc" /> + <rect + style="fill:#e0e0e0;fill-opacity:1" + id="rect855" + width="14" + height="1.8305085" + x="1.2881356" + y="1.3700738" /> +</svg> diff --git a/editor/icons/icon_new_root.svg b/editor/icons/icon_new_root.svg new file mode 100644 index 0000000000..51c79f038d --- /dev/null +++ b/editor/icons/icon_new_root.svg @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg8" + sodipodi:docname="icon_new_root.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata14"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs12" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1474" + inkscape:window-height="755" + id="namedview10" + showgrid="false" + inkscape:zoom="29.5" + inkscape:cx="9.9306919" + inkscape:cy="7.2213369" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="0" + inkscape:current-layer="svg8" /> + <path + style="fill:#e0e0e0" + d="m 2,4.7813475 v 2.0494746 c -0.6177049,0.3566305 -0.998733,1.0152377 -1,1.7285 0,1.1045694 0.8954305,1.9999999 2,1.9999999 0.7139771,-5.54e-4 1.3735116,-0.381678 1.7305,-0.9999995 h 1.3545593 c 0.3566306,0.6177035 1.0152377,0.9987325 1.7285,0.9999995 1.1045696,0 1.9999996,-0.8954305 1.9999996,-1.9999999 0,-1.1045695 -0.89543,-2 -1.9999996,-2 -0.7139771,5.537e-4 -1.3735116,0.3816774 -1.7305,1 H 4.7285 C 4.5537191,7.2563119 4.3025219,7.0044423 3.99998,6.8288521 V 4.7793775 C 3.4615087,4.8084067 2.7017179,4.8161838 2,4.7813475 Z" + id="path2" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccscccccc" /> + <path + style="fill:#e0e0e0" + d="m 6.8474576,9.6288045 v 1.2020165 c -0.617705,0.35663 -0.998733,1.015237 -1,1.7285 0,1.104569 0.89543,2 2,2 0.713977,-5.54e-4 1.373512,-0.381678 1.7305,-1 h 1.2867634 c 0.35663,0.617704 1.015237,0.998733 1.7285,1 1.104569,0 1.999999,-0.895431 1.999999,-2 0,-1.10457 -0.89543,-2 -1.999999,-2 -0.713977,5.53e-4 -1.373512,0.381677 -1.7305,1 H 9.5759576 c -0.174781,-0.303011 -0.425978,-0.55488 -0.72852,-0.73047 V 9.6268345 c 0,0 -1.264363,0.03681 -1.99998,0.002 z" + id="path827" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccsccccccc" /> + <path + sodipodi:nodetypes="ccccccc" + inkscape:connector-curvature="0" + id="path829" + d="m 2.7966098,1.3559322 c -1.104569,0 -2.00000003,0.8954305 -2.00000003,2 5.54e-4,0.7139771 0.38167803,1.3735116 1.00000003,1.7305 0.757716,0.266212 0.949133,0.2840609 1.99998,-0.00197 0.617705,-0.3566306 0.998733,-1.0152377 1,-1.7285 0,-1.1045695 -0.89543,-2 -2,-2 z" + style="fill:#84ffb1;fill-opacity:1" /> +</svg> diff --git a/editor/icons/icon_shrink_bottom_dock.svg b/editor/icons/icon_shrink_bottom_dock.svg new file mode 100644 index 0000000000..c1e8c1bfdb --- /dev/null +++ b/editor/icons/icon_shrink_bottom_dock.svg @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_shrink_bottom_dock.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1853" + inkscape:window-height="1016" + id="namedview8" + showgrid="false" + inkscape:zoom="20.85965" + inkscape:cx="9.4509357" + inkscape:cy="6.016355" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg6" /> + <path + style="fill:#e0e0e0" + d="M 11.907447,9.9752038 15.442981,6.4396699 H 12.907296 V 1.4659528 h -1.999839 l 0,4.9737171 -2.5356852,0 3.5355342,3.5355339 z" + id="path829" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccccc" /> + <path + inkscape:connector-curvature="0" + id="path831" + d="M 4.2131662,9.8793249 7.7487004,6.343791 H 5.2130152 V 1.3700738 H 3.2131762 V 6.343791 h -2.535685 l 3.535534,3.5355339 z" + style="fill:#e0e0e0" + sodipodi:nodetypes="ccccccccc" /> + <rect + style="fill:#e0e0e0;fill-opacity:1" + id="rect855" + width="14" + height="1.8305085" + x="-14.832336" + y="-13.121187" + transform="scale(-1)" /> +</svg> diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index aa4673f41e..876da7f61a 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1872,6 +1872,7 @@ void ScriptEditor::save_all_scripts() { } _update_script_names(); + EditorFileSystem::get_singleton()->update_script_classes(); } void ScriptEditor::apply_scripts() const { diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 915da1e802..682ca744ff 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -157,7 +157,7 @@ void VisualShaderEditor::_update_graph() { vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED); }*/ - node->set_offset(position * EDSCALE); + node->set_offset(position); node->set_title(vsnode->get_caption()); node->set_name(itos(nodes[n_i])); @@ -544,6 +544,17 @@ void VisualShaderEditor::_node_selected(Object *p_node) { //EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true); } +void VisualShaderEditor::_input(const Ref<InputEvent> p_event) { + if (graph->has_focus()) { + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { + add_node->get_popup()->set_position(get_viewport()->get_mouse_position()); + add_node->get_popup()->show_modal(); + } + } +} + void VisualShaderEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { @@ -700,6 +711,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_mode_selected", &VisualShaderEditor::_mode_selected); ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item); ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port); + ClassDB::bind_method("_input", &VisualShaderEditor::_input); } VisualShaderEditor *VisualShaderEditor::singleton = NULL; @@ -813,12 +825,14 @@ void VisualShaderEditorPlugin::make_visible(bool p_visible) { //editor->animation_panel_make_visible(true); button->show(); editor->make_bottom_panel_item_visible(visual_shader_editor); + visual_shader_editor->set_process_input(true); //visual_shader_editor->set_process(true); } else { if (visual_shader_editor->is_visible_in_tree()) editor->hide_bottom_panel(); button->hide(); + visual_shader_editor->set_process_input(false); //visual_shader_editor->set_process(false); } } diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index f89d073dde..f86374ff6b 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -94,6 +94,7 @@ class VisualShaderEditor : public VBoxContainer { void _input_select_item(Ref<VisualShaderNodeInput> input, String name); void _preview_select_port(int p_node, int p_port); + void _input(const Ref<InputEvent> p_event); protected: void _notification(int p_what); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 77ee65879b..2ffaa0ca12 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -551,6 +551,32 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { reparent_dialog->set_current(nodeset); } break; + case TOOL_MAKE_ROOT: { + + List<Node *> nodes = editor_selection->get_selected_node_list(); + ERR_FAIL_COND(nodes.size() != 1); + + Node *node = nodes.front()->get(); + Node *root = get_tree()->get_edited_scene_root(); + + if (node == root) + return; + + editor_data->get_undo_redo().create_action("Make node as Root"); + _node_replace_owner(root, node, node, MODE_DO); + editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node); + editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", node); + editor_data->get_undo_redo().add_do_method(node, "set_filename", root->get_filename()); + + editor_data->get_undo_redo().add_undo_method(node, "set_filename", String()); + editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", root); + editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node); + _node_replace_owner(root, node, root, MODE_UNDO); + editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); + editor_data->get_undo_redo().add_undo_method(scene_tree, "update_tree"); + editor_data->get_undo_redo().add_undo_reference(root); + editor_data->get_undo_redo().commit_action(); + } break; case TOOL_MULTI_EDIT: { Node *root = EditorNode::get_singleton()->get_edited_scene(); @@ -757,6 +783,26 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } } break; + case TOOL_CREATE_2D_SCENE: + case TOOL_CREATE_3D_SCENE: + case TOOL_CREATE_USER_INTERFACE: { + + Node *new_node; + switch (p_tool) { + case TOOL_CREATE_2D_SCENE: new_node = memnew(Node2D); break; + case TOOL_CREATE_3D_SCENE: new_node = memnew(Spatial); break; + case TOOL_CREATE_USER_INTERFACE: new_node = memnew(Control); break; + } + + editor_data->get_undo_redo().create_action("New Scene Root"); + editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", new_node); + editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); + editor_data->get_undo_redo().add_do_reference(new_node); + editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)NULL); + editor_data->get_undo_redo().commit_action(); + + } break; + default: { if (p_tool >= EDIT_SUBRESOURCE_BASE) { @@ -803,6 +849,35 @@ void SceneTreeDock::_notification(int p_what) { EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", this, "_selection_changed"); + create_root_dialog->add_child(memnew(Label(TTR("Create Root Node:")))); + + Button *button_2d = memnew(Button); + create_root_dialog->add_child(button_2d); + + button_2d->set_text(TTR("2D Scene")); + button_2d->set_icon(get_icon("Node2D", "EditorIcons")); + button_2d->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_2D_SCENE, false)); + + Button *button_3d = memnew(Button); + create_root_dialog->add_child(button_3d); + button_3d->set_text(TTR("3D Scene")); + button_3d->set_icon(get_icon("Spatial", "EditorIcons")); + button_3d->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_3D_SCENE, false)); + + Button *button_ui = memnew(Button); + create_root_dialog->add_child(button_ui); + button_ui->set_text(TTR("User Interface")); + button_ui->set_icon(get_icon("Control", "EditorIcons")); + button_ui->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_USER_INTERFACE, false)); + + Button *button_custom = memnew(Button); + create_root_dialog->add_child(button_custom); + button_custom->set_text(TTR("Custom Node")); + button_custom->set_icon(get_icon("Add", "EditorIcons")); + button_custom->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); + + create_root_dialog->add_spacer(); + } break; case NOTIFICATION_ENTER_TREE: { @@ -820,21 +895,49 @@ void SceneTreeDock::_notification(int p_what) { filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); } break; + case NOTIFICATION_PROCESS: { + + bool show_create_root = bool(EDITOR_GET("interface/editors/show_scene_tree_root_selection")) && get_tree()->get_edited_scene_root() == NULL; + + if (show_create_root != create_root_dialog->is_visible_in_tree()) { + if (show_create_root) { + create_root_dialog->show(); + scene_tree->hide(); + } else { + create_root_dialog->hide(); + scene_tree->show(); + } + } + + } break; } } -void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root) { +void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode) { if (p_base != p_node) { if (p_node->get_owner() == p_base) { UndoRedo *undo_redo = &editor_data->get_undo_redo(); - undo_redo->add_do_method(p_node, "set_owner", p_root); - undo_redo->add_undo_method(p_node, "set_owner", p_base); + switch (p_mode) { + case MODE_BIDI: { + undo_redo->add_do_method(p_node, "set_owner", p_root); + undo_redo->add_undo_method(p_node, "set_owner", p_base); + + } break; + case MODE_DO: { + undo_redo->add_do_method(p_node, "set_owner", p_root); + + } break; + case MODE_UNDO: { + undo_redo->add_undo_method(p_node, "set_owner", p_root); + + } break; + } } } for (int i = 0; i < p_node->get_child_count(); i++) { - _node_replace_owner(p_base, p_node->get_child(i), p_root); + _node_replace_owner(p_base, p_node->get_child(i), p_root, p_mode); } } @@ -1904,6 +2007,8 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); if (selection.size() == 1) { + + menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); menu->add_separator(); menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM); @@ -2090,6 +2195,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN); ED_SHORTCUT("scene_tree/duplicate", TTR("Duplicate"), KEY_MASK_CMD | KEY_D); ED_SHORTCUT("scene_tree/reparent", TTR("Reparent")); + ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root")); ED_SHORTCUT("scene_tree/merge_from_scene", TTR("Merge From Scene")); ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene")); ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KEY_MASK_CMD | KEY_C); @@ -2153,6 +2259,10 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel remote_tree = NULL; button_hb->hide(); + create_root_dialog = memnew(VBoxContainer); + vbc->add_child(create_root_dialog); + create_root_dialog->hide(); + scene_tree = memnew(SceneTreeEditor(false, true, true)); vbc->add_child(scene_tree); @@ -2227,4 +2337,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel add_child(clear_inherit_confirm); set_process_input(true); + set_process(true); + + EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true); } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 17deab25de..fd74611bde 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -68,6 +68,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_MOVE_DOWN, TOOL_DUPLICATE, TOOL_REPARENT, + TOOL_MAKE_ROOT, TOOL_NEW_SCENE_FROM, TOOL_MERGE_FROM_SCENE, TOOL_MULTI_EDIT, @@ -80,7 +81,12 @@ class SceneTreeDock : public VBoxContainer { TOOL_SCENE_OPEN, TOOL_SCENE_CLEAR_INHERITANCE, TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM, - TOOL_SCENE_OPEN_INHERITED + TOOL_SCENE_OPEN_INHERITED, + + TOOL_CREATE_2D_SCENE, + TOOL_CREATE_3D_SCENE, + TOOL_CREATE_USER_INTERFACE, + }; enum { @@ -134,13 +140,22 @@ class SceneTreeDock : public VBoxContainer { Node *edited_scene; EditorNode *editor; + VBoxContainer *create_root_dialog; + void _add_children_to_popup(Object *p_obj, int p_depth); void _node_reparent(NodePath p_path, bool p_keep_global_xform); void _do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform); void _set_owners(Node *p_owner, const Array &p_nodes); - void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root); + + enum ReplaceOwnerMode { + MODE_BIDI, + MODE_DO, + MODE_UNDO + }; + + void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode = MODE_BIDI); void _load_request(const String &p_path); void _script_open_request(const Ref<Script> &p_script); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index f23e7854a5..b3ebd4fe4b 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1739,6 +1739,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "assert", "breakpoint", "class", + "class_name", "extends", "is", "func", @@ -1788,6 +1789,50 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { } } +bool GDScriptLanguage::handles_global_class_type(const String &p_type) const { + + return p_type == "GDScript"; +} + +String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type) const { + + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + if (err) { + return String(); + } + + int len = f->get_len(); + sourcef.resize(len + 1); + PoolVector<uint8_t>::Write w = sourcef.write(); + int r = f->get_buffer(w.ptr(), len); + f->close(); + memdelete(f); + ERR_FAIL_COND_V(r != len, String()); + w[len] = 0; + + String s; + if (s.parse_utf8((const char *)w.ptr())) { + return String(); + } + + GDScriptParser parser; + + parser.parse(s, p_path.get_base_dir(), true, p_path); + + if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) { + + const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree()); + if (r_base_type && c->extends_used && c->extends_class.size() == 1) { + *r_base_type = c->extends_class[0]; //todo, should work much better + } + return c->name; + } + + return String(); +} + GDScriptLanguage::GDScriptLanguage() { calls = 0; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index a35b0a10d5..d1c57a0330 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -439,6 +439,11 @@ public: virtual void get_recognized_extensions(List<String> *p_extensions) const; + /* GLOBAL CLASSES */ + + virtual bool handles_global_class_type(const String &p_type) const; + virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const; + GDScriptLanguage(); ~GDScriptLanguage(); }; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 85c36647a1..7ce19859ca 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -278,6 +278,41 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: return idx | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root) } + /* TRY GLOBAL CLASSES */ + + if (ScriptServer::is_global_class(identifier)) { + + const GDScriptParser::ClassNode *class_node = codegen.class_node; + while (class_node->owner) { + class_node = class_node->owner; + } + + if (class_node->name == identifier) { + _set_error("Using own name in class file is not allowed (creates a cyclic reference)", p_expression); + return -1; + } + + RES res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier)); + if (res.is_null()) { + _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression); + return -1; + } + + Variant key = res; + int idx; + + if (!codegen.constant_map.has(key)) { + + idx = codegen.constant_map.size(); + codegen.constant_map[key] = idx; + + } else { + idx = codegen.constant_map[key]; + } + + return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //make it a local constant (faster access) + } + #ifdef TOOLS_ENABLED if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 9650563ee6..d62112d3f1 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3112,6 +3112,28 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } break; + case GDScriptTokenizer::TK_PR_CLASS_NAME: { + + if (p_class->owner) { + _set_error("'class_name' is only valid for the main class namespace."); + return; + } + if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) { + + _set_error("'class_name' syntax: 'class_name <UniqueName>'"); + return; + } + + p_class->name = tokenizer->get_token_identifier(1); + + if (self_path != String() && ScriptServer::is_global_class(p_class->name) && ScriptServer::get_global_class_path(p_class->name) != self_path) { + _set_error("Unique global class '" + p_class->name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name)); + return; + } + + tokenizer->advance(2); + + } break; case GDScriptTokenizer::TK_PR_TOOL: { if (p_class->tool) { @@ -3138,6 +3160,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { name = tokenizer->get_token_identifier(1); tokenizer->advance(2); + if (ScriptServer::is_global_class(name)) { + _set_error("Can't override name of unique global class '" + name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name)); + return; + } + ClassNode *newclass = alloc_node<ClassNode>(); newclass->initializer = alloc_node<BlockNode>(); newclass->initializer->parent_class = newclass; diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 3c8e1ddbe4..9517b95f3f 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -91,6 +91,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "match", "func", "class", + "class_name", "extends", "is", "onready", @@ -187,6 +188,7 @@ static const _kws _keyword_list[] = { //func { GDScriptTokenizer::TK_PR_FUNCTION, "func" }, { GDScriptTokenizer::TK_PR_CLASS, "class" }, + { GDScriptTokenizer::TK_PR_CLASS_NAME, "class_name" }, { GDScriptTokenizer::TK_PR_EXTENDS, "extends" }, { GDScriptTokenizer::TK_PR_IS, "is" }, { GDScriptTokenizer::TK_PR_ONREADY, "onready" }, @@ -1137,7 +1139,7 @@ void GDScriptTokenizerText::advance(int p_amount) { ////////////////////////////////////////////////////////////////////////////////////////////////////// -#define BYTECODE_VERSION 12 +#define BYTECODE_VERSION 13 Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) { diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index c4f1f9fd94..c1f611fe73 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -96,6 +96,7 @@ public: TK_CF_MATCH, TK_PR_FUNCTION, TK_PR_CLASS, + TK_PR_CLASS_NAME, TK_PR_EXTENDS, TK_PR_IS, TK_PR_ONREADY, diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp index a63e53ec1f..884c26ddfe 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.cpp +++ b/modules/mbedtls/stream_peer_mbed_tls.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "stream_peer_mbed_tls.h" +#include "mbedtls/platform_util.h" +#include "os/file_access.h" static void my_debug(void *ctx, int level, const char *file, int line, @@ -81,6 +83,36 @@ int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) { return got; } +void StreamPeerMbedTLS::_cleanup() { + + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + + base = Ref<StreamPeer>(); + status = STATUS_DISCONNECTED; +} + +Error StreamPeerMbedTLS::_do_handshake() { + int ret = 0; + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ERR_PRINTS("TLS handshake error: " + itos(ret)); + _print_error(ret); + disconnect_from_stream(); + status = STATUS_ERROR; + return FAILED; + } else if (!blocking_handshake) { + // Will retry via poll later + return OK; + } + } + + status = STATUS_CONNECTED; + return OK; +} + Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname) { base = p_base; @@ -95,6 +127,7 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); if (ret != 0) { ERR_PRINTS(" failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret)); + _cleanup(); return FAILED; } @@ -112,29 +145,24 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida mbedtls_ssl_set_bio(&ssl, this, bio_send, bio_recv, NULL); - while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { - if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - ERR_PRINTS("TLS handshake error: " + itos(ret)); - _print_error(ret); - status = STATUS_ERROR_HOSTNAME_MISMATCH; - return FAILED; - } - } + status = STATUS_HANDSHAKING; - connected = true; - status = STATUS_CONNECTED; + if ((ret = _do_handshake()) != OK) { + status = STATUS_ERROR_HOSTNAME_MISMATCH; + return FAILED; + } return OK; } Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base) { - return ERR_UNAVAILABLE; + return OK; } Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) { - ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED); Error err; int sent = 0; @@ -155,7 +183,7 @@ Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) { Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED); r_sent = 0; @@ -177,7 +205,7 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) { - ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED); Error err; @@ -199,7 +227,7 @@ Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) { Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED); r_received = 0; @@ -218,27 +246,30 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r void StreamPeerMbedTLS::poll() { - ERR_FAIL_COND(!connected); + ERR_FAIL_COND(status != STATUS_CONNECTED && status != STATUS_HANDSHAKING); ERR_FAIL_COND(!base.is_valid()); + if (status == STATUS_HANDSHAKING) { + _do_handshake(); + return; + } + int ret = mbedtls_ssl_read(&ssl, NULL, 0); if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { _print_error(ret); disconnect_from_stream(); - return; } } int StreamPeerMbedTLS::get_available_bytes() const { - ERR_FAIL_COND_V(!connected, 0); + ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0); return mbedtls_ssl_get_bytes_avail(&ssl); } StreamPeerMbedTLS::StreamPeerMbedTLS() { - connected = false; status = STATUS_DISCONNECTED; } @@ -248,17 +279,10 @@ StreamPeerMbedTLS::~StreamPeerMbedTLS() { void StreamPeerMbedTLS::disconnect_from_stream() { - if (!connected) + if (status != STATUS_CONNECTED && status != STATUS_HANDSHAKING) return; - mbedtls_ssl_free(&ssl); - mbedtls_ssl_config_free(&conf); - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - - base = Ref<StreamPeer>(); - connected = false; - status = STATUS_DISCONNECTED; + _cleanup(); } StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const { diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h index 2b96a194a1..7f4e5a4513 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.h +++ b/modules/mbedtls/stream_peer_mbed_tls.h @@ -48,8 +48,6 @@ private: Status status; String hostname; - bool connected; - Ref<StreamPeer> base; static StreamPeerSSL *_create_func(); @@ -57,9 +55,11 @@ private: static int bio_recv(void *ctx, unsigned char *buf, size_t len); static int bio_send(void *ctx, const unsigned char *buf, size_t len); + void _cleanup(); protected: static mbedtls_x509_crt cacert; + mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_ssl_context ssl; @@ -67,6 +67,8 @@ protected: static void _bind_methods(); + Error _do_handshake(); + public: virtual void poll(); virtual Error accept_stream(Ref<StreamPeer> p_base); diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 8a2d789dc5..ef798fc790 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -297,7 +297,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC runOnUiThread(new Runnable() { @Override public void run() { - view.setKeepScreenOn("True".equals(GodotLib.getGlobal("display/driver/keep_screen_on"))); + view.setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); } }); } diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index dd5ce4ab10..cc4985eb0c 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -643,7 +643,7 @@ static int frame_count = 0; view_controller.view = glView; window.rootViewController = view_controller; - _set_keep_screen_on(bool(GLOBAL_DEF("display/window/keep_screen_on", true)) ? YES : NO); + _set_keep_screen_on(bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)) ? YES : NO); glView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO; printf("cadisaplylink: %d", glView.useCADisplayLink); diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 8cdd13df90..6e6e0fb1aa 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -290,7 +290,7 @@ Error OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_au if (is_keep_screen_on()) display_request->RequestActive(); - set_keep_screen_on(GLOBAL_DEF("display/window/keep_screen_on", true)); + set_keep_screen_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)); return OK; } diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 3e61dd05f4..e9e895b5bb 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -349,7 +349,7 @@ void Line2D::_bind_methods() { ADD_GROUP("Fill", ""); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile"), "set_texture_mode", "get_texture_mode"); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile,Stretch"), "set_texture_mode", "get_texture_mode"); ADD_GROUP("Capping", ""); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "joint_mode", PROPERTY_HINT_ENUM, "Sharp,Bevel,Round"), "set_joint_mode", "get_joint_mode"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "begin_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_begin_cap_mode", "get_begin_cap_mode"); @@ -368,6 +368,7 @@ void Line2D::_bind_methods() { BIND_ENUM_CONSTANT(LINE_TEXTURE_NONE); BIND_ENUM_CONSTANT(LINE_TEXTURE_TILE); + BIND_ENUM_CONSTANT(LINE_TEXTURE_STRETCH); ClassDB::bind_method(D_METHOD("_gradient_changed"), &Line2D::_gradient_changed); } diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 24c48982cd..6918018c12 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -52,8 +52,8 @@ public: enum LineTextureMode { LINE_TEXTURE_NONE = 0, - LINE_TEXTURE_TILE - // TODO STRETCH mode + LINE_TEXTURE_TILE, + LINE_TEXTURE_STRETCH }; Line2D(); diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 845788bada..74ad3e79d0 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -146,7 +146,9 @@ void LineBuilder::build() { float current_distance1 = 0.f; float total_distance = 0.f; _interpolate_color = gradient != NULL; - bool distance_required = _interpolate_color || texture_mode == Line2D::LINE_TEXTURE_TILE; + bool distance_required = _interpolate_color || + texture_mode == Line2D::LINE_TEXTURE_TILE || + texture_mode == Line2D::LINE_TEXTURE_STRETCH; if (distance_required) total_distance = calculate_total_distance(points); if (_interpolate_color) @@ -170,7 +172,7 @@ void LineBuilder::build() { if (texture_mode == Line2D::LINE_TEXTURE_TILE) { uvx0 = 0.5f / tile_aspect; } - new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, 1.f, 1.f)); + new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, fmin(uvx0 * 2, 1.f), 1.f)); total_distance += width; current_distance0 += hw; current_distance1 = current_distance0; @@ -292,6 +294,9 @@ void LineBuilder::build() { if (texture_mode == Line2D::LINE_TEXTURE_TILE) { uvx0 = current_distance0 / (width * tile_aspect); uvx1 = current_distance1 / (width * tile_aspect); + } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) { + uvx0 = current_distance0 / total_distance; + uvx1 = current_distance1 / total_distance; } strip_add_quad(pos_up1, pos_down1, color1, uvx1); @@ -378,6 +383,8 @@ void LineBuilder::build() { } if (texture_mode == Line2D::LINE_TEXTURE_TILE) { uvx1 = current_distance1 / (width * tile_aspect); + } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) { + uvx1 = current_distance1 / total_distance; } strip_add_quad(pos_up1, pos_down1, color1, uvx1); @@ -386,7 +393,7 @@ void LineBuilder::build() { if (end_cap_mode == Line2D::LINE_CAP_ROUND) { // Note: color is not used in case we don't interpolate... Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0); - new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f, 0.f, 1.f, 1.f)); + new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f / tile_aspect, 0.f, 1.0f / tile_aspect, 1.f)); } } diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index feb11089d0..713d1daeef 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -32,8 +32,8 @@ #include "core/method_bind_ext.gen.inc" #include "engine.h" +#include "math_funcs.h" #include "scene/scene_string_names.h" - void PhysicsBody2D::_notification(int p_what) { /* @@ -971,11 +971,11 @@ RigidBody2D::~RigidBody2D() { ////////////////////////// -Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia) { +Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes) { Collision col; - if (move_and_collide(p_motion, p_infinite_inertia, col)) { + if (move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes)) { if (motion_cache.is_null()) { motion_cache.instance(); motion_cache->owner = this; @@ -989,11 +989,48 @@ Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p return Ref<KinematicCollision2D>(); } -bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision) { +bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) { + + Physics2DServer::SeparationResult sep_res[8]; //max 8 rays + + Transform2D gt = get_global_transform(); + + Vector2 recover; + int hits = Physics2DServer::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin); + int deepest = -1; + float deepest_depth; + for (int i = 0; i < hits; i++) { + if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) { + deepest = i; + deepest_depth = sep_res[i].collision_depth; + } + } + + gt.elements[2] += recover; + set_global_transform(gt); + + if (deepest != -1) { + r_collision.collider = sep_res[deepest].collider_id; + r_collision.collider_metadata = sep_res[deepest].collider_metadata; + r_collision.collider_shape = sep_res[deepest].collider_shape; + r_collision.collider_vel = sep_res[deepest].collider_velocity; + r_collision.collision = sep_res[deepest].collision_point; + r_collision.normal = sep_res[deepest].collision_normal; + r_collision.local_shape = sep_res[deepest].collision_local_shape; + r_collision.travel = recover; + r_collision.remainder = Vector2(); + + return true; + } else { + return false; + } +} + +bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes) { Transform2D gt = get_global_transform(); Physics2DServer::MotionResult result; - bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result); + bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes); if (colliding) { r_collision.collider_metadata = result.collider_metadata; @@ -1027,48 +1064,67 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const while (p_max_slides) { Collision collision; + bool found_collision = false; + + for (int i = 0; i < 2; i++) { + bool collided; + if (i == 0) { //collide + collided = move_and_collide(motion, p_infinite_inertia, collision); + if (!collided) { + motion = Vector2(); //clear because no collision happened and motion completed + } + } else { //separate raycasts (if any) + collided = separate_raycast_shapes(p_infinite_inertia, collision); + if (collided) { + collision.remainder = motion; //keep + collision.travel = Vector2(); + } + } - bool collided = move_and_collide(motion, p_infinite_inertia, collision); - - if (collided) { - - motion = collision.remainder; - - if (p_floor_direction == Vector2()) { - //all is a wall - on_wall = true; - } else { - if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor + if (collided) { + found_collision = true; + } - on_floor = true; - floor_velocity = collision.collider_vel; + if (collided) { - Vector2 rel_v = lv - floor_velocity; - Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + motion = collision.remainder; - if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) { - Transform2D gt = get_global_transform(); - gt.elements[2] -= collision.travel; - set_global_transform(gt); - return Vector2(); - } - } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling - on_ceiling = true; - } else { + if (p_floor_direction == Vector2()) { + //all is a wall on_wall = true; + } else { + if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor + + on_floor = true; + floor_velocity = collision.collider_vel; + + Vector2 rel_v = lv - floor_velocity; + Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + + if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) { + Transform2D gt = get_global_transform(); + gt.elements[2] -= collision.travel; + set_global_transform(gt); + return Vector2(); + } + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling + on_ceiling = true; + } else { + on_wall = true; + } } - } - Vector2 n = collision.normal; - motion = motion.slide(n); - lv = lv.slide(n); + Vector2 n = collision.normal; + motion = motion.slide(n); + lv = lv.slide(n); - colliders.push_back(collision); + colliders.push_back(collision); + } + } - } else { + if (!found_collision) { break; } - p_max_slides--; if (motion == Vector2()) break; @@ -1077,6 +1133,22 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const return lv; } +bool KinematicBody2D::snap_to_floor(const Vector2 &p_direction, float p_floor_max_angle) { + + Transform2D gt = get_global_transform(); + Physics2DServer::MotionResult result; + bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_direction, false, margin, &result, false); + if (colliding) { + gt.elements[2] += result.motion; + if (Math::acos(p_direction.normalized().dot(-result.collision_normal)) < p_floor_max_angle) { + on_floor = true; + } + set_global_transform(gt); + } + + return colliding; +} + bool KinematicBody2D::is_on_floor() const { return on_floor; @@ -1140,8 +1212,9 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) { void KinematicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody2D::_move, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes"), &KinematicBody2D::_move, DEFVAL(true), DEFVAL(true)); ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("snap_to_floor", "motion", "max_floor_angle"), &KinematicBody2D::snap_to_floor, DEFVAL(Math::deg2rad(45.0))); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 0fda3c5c05..1873c977a9 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -290,22 +290,26 @@ private: bool on_floor; bool on_ceiling; bool on_wall; + Vector<Collision> colliders; Vector<Ref<KinematicCollision2D> > slide_colliders; Ref<KinematicCollision2D> motion_cache; _FORCE_INLINE_ bool _ignores_mode(Physics2DServer::BodyMode) const; - Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true); + Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true); Ref<KinematicCollision2D> _get_slide_collision(int p_bounce); protected: static void _bind_methods(); public: - bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision); + bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true); + bool snap_to_floor(const Vector2 &p_direction, float p_floor_max_angle = Math::deg2rad((float)45)); bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia); + bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision); + void set_safe_margin(float p_margin); float get_safe_margin() const; diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 0190dcbfc3..7236eba685 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -294,7 +294,7 @@ protected: static void _bind_methods(); public: - bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision); + bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz); bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia); void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 6dcd5ca8ea..65904410d3 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -288,8 +288,8 @@ void AnimationNodeOneShot::_bind_methods() { ADD_GROUP("", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); - BIND_CONSTANT(MIX_MODE_BLEND) - BIND_CONSTANT(MIX_MODE_ADD) + BIND_ENUM_CONSTANT(MIX_MODE_BLEND) + BIND_ENUM_CONSTANT(MIX_MODE_ADD) } AnimationNodeOneShot::AnimationNodeOneShot() { diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index c5ad980806..36587a1e91 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -71,9 +71,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); - BIND_CONSTANT(SWITCH_MODE_IMMEDIATE); - BIND_CONSTANT(SWITCH_MODE_SYNC); - BIND_CONSTANT(SWITCH_MODE_AT_END); + BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE); + BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC); + BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END); } AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 83ec9f819b..4fa66e8ede 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -877,7 +877,7 @@ void AnimationTree::_process_graph(float p_delta) { continue; t->loc = t->loc.linear_interpolate(loc, blend); - if (t->rot_blend_accum==0) { + if (t->rot_blend_accum == 0) { t->rot = rot; t->rot_blend_accum = blend; } else { @@ -1052,7 +1052,7 @@ void AnimationTree::_process_graph(float p_delta) { float len = t->start > time ? (a->get_length() - t->start) + time : time - t->start; if (len > t->len) { - stop=true; + stop = true; } } @@ -1065,7 +1065,7 @@ void AnimationTree::_process_graph(float p_delta) { } } - float db = Math::linear2db(MAX(blend,0.00001)); + float db = Math::linear2db(MAX(blend, 0.00001)); if (t->object->has_method("set_unit_db")) { t->object->call("set_unit_db", db); } else { @@ -1310,16 +1310,17 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform); - - ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationTree::_node_removed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_tree_root", "get_tree_root"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player",PROPERTY_HINT_NODE_PATH_VALID_TYPES,"AnimationPlayer"), "set_animation_player", "get_animation_player"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode"); ADD_GROUP("Root Motion", "root_motion_"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track"); + + BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); + BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE); } AnimationTree::AnimationTree() { diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 6ec67aca6b..e5bd1c453d 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -257,9 +257,7 @@ void ScrollBar::_notification(int p_what) { Point2 ofs; - VisualServer *vs = VisualServer::get_singleton(); - - vs->canvas_item_add_texture_rect(ci, Rect2(Point2(), decr->get_size()), decr->get_rid()); + decr->draw(ci, Point2()); if (orientation == HORIZONTAL) ofs.x += decr->get_width(); @@ -280,7 +278,7 @@ void ScrollBar::_notification(int p_what) { else ofs.height += area.height; - vs->canvas_item_add_texture_rect(ci, Rect2(ofs, decr->get_size()), incr->get_rid()); + incr->draw(ci, ofs); Rect2 grabber_rect; if (orientation == HORIZONTAL) { diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 3e23d79540..8f78e137b4 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -603,6 +603,7 @@ void register_scene_types() { ClassDB::register_class<CurveTexture>(); ClassDB::register_class<GradientTexture>(); ClassDB::register_class<ProxyTexture>(); + ClassDB::register_class<AnimatedTexture>(); ClassDB::register_class<CubeMap>(); ClassDB::register_class<Animation>(); ClassDB::register_virtual_class<Font>(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 54f5aea160..2baad555c0 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1669,3 +1669,208 @@ ProxyTexture::~ProxyTexture() { VS::get_singleton()->free(proxy); } +////////////////////////////////////////////// + +void AnimatedTexture::_update_proxy() { + + _THREAD_SAFE_METHOD_ + + float delta; + if (prev_ticks == 0) { + delta = 0; + prev_ticks = OS::get_singleton()->get_ticks_usec(); + } else { + uint64_t ticks = OS::get_singleton()->get_ticks_usec(); + delta = float(double(ticks - prev_ticks) / 1000000.0); + prev_ticks = ticks; + } + + time += delta; + + float limit; + + if (fps == 0) { + limit = 0; + } else { + limit = 1.0 / fps; + } + + int iter_max = frame_count; + while (iter_max) { + float frame_limit = limit + frames[current_frame].delay_sec; + + if (time > frame_limit) { + current_frame++; + if (current_frame >= frame_count) { + current_frame = 0; + } + time -= frame_limit; + } else { + break; + } + iter_max--; + } + + if (frames[current_frame].texture.is_valid()) { + VisualServer::get_singleton()->texture_set_proxy(proxy, frames[current_frame].texture->get_rid()); + } +} + +void AnimatedTexture::set_frames(int p_frames) { + ERR_FAIL_COND(p_frames < 1 || p_frames > MAX_FRAMES); + + _THREAD_SAFE_METHOD_ + + frame_count = p_frames; +} +int AnimatedTexture::get_frames() const { + return frame_count; +} + +void AnimatedTexture::set_frame_texture(int p_frame, const Ref<Texture> &p_texture) { + ERR_FAIL_INDEX(p_frame, MAX_FRAMES); + + _THREAD_SAFE_METHOD_ + + frames[p_frame].texture = p_texture; +} +Ref<Texture> AnimatedTexture::get_frame_texture(int p_frame) const { + ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, Ref<Texture>()); + + _THREAD_SAFE_METHOD_ + + return frames[p_frame].texture; +} + +void AnimatedTexture::set_frame_delay(int p_frame, float p_delay_sec) { + ERR_FAIL_INDEX(p_frame, MAX_FRAMES); + + _THREAD_SAFE_METHOD_ + + frames[p_frame].delay_sec = p_delay_sec; +} +float AnimatedTexture::get_frame_delay(int p_frame) const { + ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0); + + _THREAD_SAFE_METHOD_ + + return frames[p_frame].delay_sec; +} + +void AnimatedTexture::set_fps(float p_fps) { + ERR_FAIL_COND(p_fps < 0 || p_fps >= 1000); + + fps = p_fps; +} +float AnimatedTexture::get_fps() const { + return fps; +} + +int AnimatedTexture::get_width() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return 1; + } + + return frames[current_frame].texture->get_width(); +} +int AnimatedTexture::get_height() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return 1; + } + + return frames[current_frame].texture->get_height(); +} +RID AnimatedTexture::get_rid() const { + return proxy; +} + +bool AnimatedTexture::has_alpha() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return false; + } + + return frames[current_frame].texture->has_alpha(); +} + +Ref<Image> AnimatedTexture::get_data() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return Ref<Image>(); + } + + return frames[current_frame].texture->get_data(); +} + +void AnimatedTexture::set_flags(uint32_t p_flags) { +} +uint32_t AnimatedTexture::get_flags() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return 0; + } + + return frames[current_frame].texture->get_flags(); +} + +void AnimatedTexture::_validate_property(PropertyInfo &property) const { + + String prop = property.name; + if (prop.begins_with("frame_")) { + int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int(); + if (frame >= frame_count) { + property.usage = 0; + } + } +} + +void AnimatedTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_frames", "frames"), &AnimatedTexture::set_frames); + ClassDB::bind_method(D_METHOD("get_frames"), &AnimatedTexture::get_frames); + + ClassDB::bind_method(D_METHOD("set_fps", "fps"), &AnimatedTexture::set_fps); + ClassDB::bind_method(D_METHOD("get_fps"), &AnimatedTexture::get_fps); + + ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture); + ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture); + + ClassDB::bind_method(D_METHOD("set_frame_delay", "frame", "delay"), &AnimatedTexture::set_frame_delay); + ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay); + + ClassDB::bind_method(D_METHOD("_update_proxy"), &AnimatedTexture::_update_proxy); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps"); + + for (int i = 0; i < MAX_FRAMES; i++) { + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_frame_texture", "get_frame_texture", i); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_frame_delay", "get_frame_delay", i); + } +} + +AnimatedTexture::AnimatedTexture() { + proxy = VS::get_singleton()->texture_create(); + VisualServer::get_singleton()->texture_set_force_redraw_if_visible(proxy, true); + time = 0; + frame_count = 1; + fps = 4; + prev_ticks = 0; + current_frame = 0; + VisualServer::get_singleton()->connect("frame_pre_draw", this, "_update_proxy"); +} + +AnimatedTexture::~AnimatedTexture() { + VS::get_singleton()->free(proxy); +} diff --git a/scene/resources/texture.h b/scene/resources/texture.h index d81fd3b19b..c994bdad5f 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -34,10 +34,11 @@ #include "curve.h" #include "io/resource_loader.h" #include "math_2d.h" +#include "os/mutex.h" +#include "os/thread_safe.h" #include "resource.h" #include "scene/resources/color_ramp.h" #include "servers/visual_server.h" - /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -521,4 +522,70 @@ public: ~ProxyTexture(); }; +class AnimatedTexture : public Texture { + GDCLASS(AnimatedTexture, Texture) + + _THREAD_SAFE_CLASS_ + +private: + enum { + MAX_FRAMES = 256 + }; + + RID proxy; + + struct Frame { + + Ref<Texture> texture; + float delay_sec; + + Frame() { + delay_sec = 0; + } + }; + + Frame frames[MAX_FRAMES]; + int frame_count; + int current_frame; + + float fps; + + float time; + + uint64_t prev_ticks; + + void _update_proxy(); + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + void set_frames(int p_frames); + int get_frames() const; + + void set_frame_texture(int p_frame, const Ref<Texture> &p_texture); + Ref<Texture> get_frame_texture(int p_frame) const; + + void set_frame_delay(int p_frame, float p_delay_sec); + float get_frame_delay(int p_frame) const; + + void set_fps(float p_fps); + float get_fps() const; + + virtual int get_width() const; + virtual int get_height() const; + virtual RID get_rid() const; + + virtual bool has_alpha() const; + + virtual void set_flags(uint32_t p_flags); + virtual uint32_t get_flags() const; + + virtual Ref<Image> get_data() const; + + AnimatedTexture(); + ~AnimatedTexture(); +}; + #endif diff --git a/servers/physics_2d/collision_solver_2d_sw.cpp b/servers/physics_2d/collision_solver_2d_sw.cpp index efee98a35a..6ce019f36e 100644 --- a/servers/physics_2d/collision_solver_2d_sw.cpp +++ b/servers/physics_2d/collision_solver_2d_sw.cpp @@ -72,7 +72,7 @@ bool CollisionSolver2DSW::solve_static_line(const Shape2DSW *p_shape_A, const Tr return found; } -bool CollisionSolver2DSW::solve_raycast(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis) { +bool CollisionSolver2DSW::solve_raycast(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis) { const RayShape2DSW *ray = static_cast<const RayShape2DSW *>(p_shape_A); if (p_shape_B->get_type() == Physics2DServer::SHAPE_RAY) @@ -80,6 +80,11 @@ bool CollisionSolver2DSW::solve_raycast(const Shape2DSW *p_shape_A, const Transf Vector2 from = p_transform_A.get_origin(); Vector2 to = from + p_transform_A[1] * ray->get_length(); + if (p_motion_A != Vector2()) { + //not the best but should be enough + Vector2 normal = (to - from).normalized(); + to += normal * MAX(0.0, normal.dot(p_motion_A)); + } Vector2 support_A = to; Transform2D invb = p_transform_B.affine_inverse(); @@ -270,9 +275,9 @@ bool CollisionSolver2DSW::solve(const Shape2DSW *p_shape_A, const Transform2D &p } if (swap) { - return solve_raycast(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, sep_axis); + return solve_raycast(p_shape_B, p_motion_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, sep_axis); } else { - return solve_raycast(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, sep_axis); + return solve_raycast(p_shape_A, p_motion_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, sep_axis); } } else if (concave_B) { diff --git a/servers/physics_2d/collision_solver_2d_sw.h b/servers/physics_2d/collision_solver_2d_sw.h index e39c41fb75..6faa166115 100644 --- a/servers/physics_2d/collision_solver_2d_sw.h +++ b/servers/physics_2d/collision_solver_2d_sw.h @@ -41,7 +41,7 @@ private: static bool solve_static_line(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result); static void concave_callback(void *p_userdata, Shape2DSW *p_convex); static bool solve_concave(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = NULL, real_t p_margin_A = 0, real_t p_margin_B = 0); - static bool solve_raycast(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = NULL); + static bool solve_raycast(const Shape2DSW *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *sep_axis = NULL); public: static bool solve(const Shape2DSW *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const Shape2DSW *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *sep_axis = NULL, real_t p_margin_A = 0, real_t p_margin_B = 0); diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp index a14fed8184..faabed84e9 100644 --- a/servers/physics_2d/physics_2d_server_sw.cpp +++ b/servers/physics_2d/physics_2d_server_sw.cpp @@ -962,14 +962,24 @@ void Physics2DServerSW::body_set_pickable(RID p_body, bool p_pickable) { body->set_pickable(p_pickable); } -bool Physics2DServerSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, MotionResult *r_result) { +bool Physics2DServerSW::body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, MotionResult *r_result, bool p_exclude_raycast_shapes) { Body2DSW *body = body_owner.get(p_body); ERR_FAIL_COND_V(!body, false); ERR_FAIL_COND_V(!body->get_space(), false); ERR_FAIL_COND_V(body->get_space()->is_locked(), false); - return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, p_margin, r_result); + return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes); +} + +int Physics2DServerSW::body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin) { + + Body2DSW *body = body_owner.get(p_body); + ERR_FAIL_COND_V(!body, false); + ERR_FAIL_COND_V(!body->get_space(), false); + ERR_FAIL_COND_V(body->get_space()->is_locked(), false); + + return body->get_space()->test_body_ray_separation(body, p_transform, p_infinite_inertia, r_recover_motion, r_results, p_result_max, p_margin); } Physics2DDirectBodyState *Physics2DServerSW::body_get_direct_state(RID p_body) { diff --git a/servers/physics_2d/physics_2d_server_sw.h b/servers/physics_2d/physics_2d_server_sw.h index 036eb934e1..e5961b9011 100644 --- a/servers/physics_2d/physics_2d_server_sw.h +++ b/servers/physics_2d/physics_2d_server_sw.h @@ -232,7 +232,8 @@ public: virtual void body_set_pickable(RID p_body, bool p_pickable); - virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = NULL); + virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true); + virtual int body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001); // this function only works on physics process, errors and returns null otherwise virtual Physics2DDirectBodyState *body_get_direct_state(RID p_body); diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h index a15e8bde8b..3119b930d7 100644 --- a/servers/physics_2d/physics_2d_server_wrap_mt.h +++ b/servers/physics_2d/physics_2d_server_wrap_mt.h @@ -245,10 +245,16 @@ public: FUNC2(body_set_pickable, RID, bool); - bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = NULL) { + bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true) { ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); - return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r_result); + return physics_2d_server->body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes); + } + + int body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001) { + + ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); + return physics_2d_server->body_test_ray_separation(p_body, p_transform, p_infinite_inertia, r_recover_motion, r_results, p_result_max, p_margin); } // this function only works on physics process, errors and returns null otherwise diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index 0e1f74d8d0..6e45951f42 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -487,7 +487,156 @@ int Space2DSW::_cull_aabb_for_body(Body2DSW *p_body, const Rect2 &p_aabb) { return amount; } -bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, Physics2DServer::MotionResult *r_result) { +int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, Physics2DServer::SeparationResult *r_results, int p_result_max, real_t p_margin) { + + Rect2 body_aabb; + + for (int i = 0; i < p_body->get_shape_count(); i++) { + + if (i == 0) + body_aabb = p_body->get_shape_aabb(i); + else + body_aabb = body_aabb.merge(p_body->get_shape_aabb(i)); + } + + // Undo the currently transform the physics server is aware of and apply the provided one + body_aabb = p_transform.xform(p_body->get_inv_transform().xform(body_aabb)); + body_aabb = body_aabb.grow(p_margin); + + Transform2D body_transform = p_transform; + + for (int i = 0; i < p_result_max; i++) { + //reset results + r_results[i].collision_depth = 0; + } + + int rays_found = 0; + + { + // raycast AND separate + + const int max_results = 32; + int recover_attempts = 4; + Vector2 sr[max_results * 2]; + Physics2DServerSW::CollCbkData cbk; + cbk.max = max_results; + Physics2DServerSW::CollCbkData *cbkptr = &cbk; + CollisionSolver2DSW::CallbackResult cbkres = Physics2DServerSW::_shape_col_cbk; + + do { + + Vector2 recover_motion; + + bool collided = false; + + int amount = _cull_aabb_for_body(p_body, body_aabb); + int ray_index = 0; + + for (int j = 0; j < p_body->get_shape_count(); j++) { + if (p_body->is_shape_set_as_disabled(j)) + continue; + + Shape2DSW *body_shape = p_body->get_shape(j); + + if (body_shape->get_type() != Physics2DServer::SHAPE_RAY) + continue; + + Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j); + + for (int i = 0; i < amount; i++) { + + const CollisionObject2DSW *col_obj = intersection_query_results[i]; + int shape_idx = intersection_query_subindex_results[i]; + + cbk.amount = 0; + cbk.ptr = sr; + cbk.invalid_by_dir = 0; + + if (CollisionObject2DSW::TYPE_BODY == col_obj->get_type()) { + const Body2DSW *b = static_cast<const Body2DSW *>(col_obj); + if (p_infinite_inertia && Physics2DServer::BODY_MODE_STATIC != b->get_mode() && Physics2DServer::BODY_MODE_KINEMATIC != b->get_mode()) { + continue; + } + } + + if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) { + + cbk.valid_dir = body_shape_xform.get_axis(1).normalized(); + cbk.valid_depth = p_margin; //only valid depth is the collision margin + cbk.invalid_by_dir = 0; + + } else { + cbk.valid_dir = Vector2(); + cbk.valid_depth = 0; + cbk.invalid_by_dir = 0; + } + + Shape2DSW *against_shape = col_obj->get_shape(shape_idx); + if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), cbkres, cbkptr, NULL, p_margin)) { + if (cbk.amount > 0) { + collided = true; + } + + if (ray_index < p_result_max) { + Physics2DServer::SeparationResult &result = r_results[ray_index]; + + for (int k = 0; k < cbk.amount; k++) { + Vector2 a = sr[k * 2 + 0]; + Vector2 b = sr[k * 2 + 1]; + + recover_motion += (b - a) * 0.4; + + float depth = a.distance_to(b); + if (depth > result.collision_depth) { + + result.collision_depth = depth; + result.collision_point = b; + result.collision_normal = (b - a).normalized(); + result.collision_local_shape = shape_idx; + result.collider = col_obj->get_self(); + result.collider_id = col_obj->get_instance_id(); + result.collider_metadata = col_obj->get_shape_metadata(shape_idx); + if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) { + Body2DSW *body = (Body2DSW *)col_obj; + + Vector2 rel_vec = b - body->get_transform().get_origin(); + result.collider_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity(); + } + } + } + } + } + } + + ray_index++; + } + + rays_found = MAX(ray_index, rays_found); + + if (!collided || recover_motion == Vector2()) { + break; + } + + body_transform.elements[2] += recover_motion; + body_aabb.position += recover_motion; + + recover_attempts--; + } while (recover_attempts); + } + + //optimize results (remove non colliding) + for (int i = 0; i < rays_found; i++) { + if (r_results[i].collision_depth == 0) { + rays_found--; + SWAP(r_results[i], r_results[rays_found]); + } + } + + r_recover_motion = body_transform.elements[2] - p_transform.elements[2]; + return rays_found; +} + +bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, Physics2DServer::MotionResult *r_result, bool p_exclude_raycast_shapes) { //give me back regular physics engine logic //this is madness @@ -547,8 +696,12 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co if (p_body->is_shape_set_as_disabled(j)) continue; - Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j); Shape2DSW *body_shape = p_body->get_shape(j); + if (p_exclude_raycast_shapes && body_shape->get_type() == Physics2DServer::SHAPE_RAY) { + continue; + } + + Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j); for (int i = 0; i < amount; i++) { const CollisionObject2DSW *col_obj = intersection_query_results[i]; @@ -635,8 +788,12 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co if (p_body->is_shape_set_as_disabled(body_shape_idx)) continue; - Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(body_shape_idx); Shape2DSW *body_shape = p_body->get_shape(body_shape_idx); + if (p_exclude_raycast_shapes && body_shape->get_type() == Physics2DServer::SHAPE_RAY) { + continue; + } + + Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(body_shape_idx); bool stuck = false; diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h index 79349c46f3..959e15e12d 100644 --- a/servers/physics_2d/space_2d_sw.h +++ b/servers/physics_2d/space_2d_sw.h @@ -182,7 +182,8 @@ public: int get_collision_pairs() const { return collision_pairs; } - bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, Physics2DServer::MotionResult *r_result); + bool test_body_motion(Body2DSW *p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, Physics2DServer::MotionResult *r_result, bool p_exclude_raycast_shapes = true); + int test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, Physics2DServer::SeparationResult *r_results, int p_result_max, real_t p_margin); void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); } _FORCE_INLINE_ bool is_debugging_contacts() const { return !contact_debug.empty(); } diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h index ba5232f7fe..1d04fbc5c6 100644 --- a/servers/physics_2d_server.h +++ b/servers/physics_2d_server.h @@ -479,7 +479,22 @@ public: Variant collider_metadata; }; - virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, float p_margin = 0.001, MotionResult *r_result = NULL) = 0; + virtual bool body_test_motion(RID p_body, const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, float p_margin = 0.001, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true) = 0; + + struct SeparationResult { + + float collision_depth; + Vector2 collision_point; + Vector2 collision_normal; + Vector2 collider_velocity; + int collision_local_shape; + ObjectID collider_id; + RID collider; + int collider_shape; + Variant collider_metadata; + }; + + virtual int body_test_ray_separation(RID p_body, const Transform2D &p_transform, bool p_infinite_inertia, Vector2 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001) = 0; /* JOINT API */ diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 3245e7be70..0b37d266e7 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -201,6 +201,7 @@ public: virtual void textures_keep_original(bool p_enable) = 0; virtual void texture_set_proxy(RID p_proxy, RID p_base) = 0; + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) = 0; /* SKY API */ diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index 163aa9bb07..5f207b1d3f 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -95,10 +95,11 @@ void VisualServerRaster::request_frame_drawn_callback(Object *p_where, const Str void VisualServerRaster::draw(bool p_swap_buffers) { - changes = 0; - + //needs to be done before changes is reset to 0, to not force the editor to redraw VS::get_singleton()->emit_signal("frame_pre_draw"); + changes = 0; + VSG::rasterizer->begin_frame(); VSG::scene->update_dirty_instances(); //update scene stuff diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index c03005592f..ec0d02ed2a 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -173,6 +173,8 @@ public: BIND2(texture_set_proxy, RID, RID) + BIND2(texture_set_force_redraw_if_visible, RID, bool) + /* SKY API */ BIND0R(RID, sky_create) diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 54fcd166c9..48f0ec46f3 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -107,6 +107,8 @@ public: FUNC2(texture_set_proxy, RID, RID) + FUNC2(texture_set_force_redraw_if_visible, RID, bool) + /* SKY API */ FUNCRID(sky) diff --git a/servers/visual_server.h b/servers/visual_server.h index 5027009634..968cb852ed 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -144,6 +144,7 @@ public: virtual void textures_keep_original(bool p_enable) = 0; virtual void texture_set_proxy(RID p_proxy, RID p_base) = 0; + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) = 0; /* SKY API */ |