diff options
65 files changed, 1869 insertions, 249 deletions
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 8ebd9d6cd9..b8fd13d67c 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -107,3 +107,83 @@ void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { ERR_FAIL_COND(loader_count >= MAX_LOADERS); loader[loader_count++] = p_loader; } + +///////////////// + +RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + memdelete(f); + return RES(); + } + + uint8_t header[4] = { 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + bool unrecognized = header[0] != 'G' || header[1] != 'D' || header[2] != 'I' || header[3] != 'M'; + if (unrecognized) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + String extension = f->get_pascal_string(); + + int idx = -1; + + for (int i = 0; i < ImageLoader::loader_count; i++) { + if (ImageLoader::loader[i]->recognize(extension)) { + idx = i; + break; + } + } + + if (idx == -1) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + Ref<Image> image; + image.instance(); + + Error err = ImageLoader::loader[idx]->load_image(image, f, false, 1.0); + + memdelete(f); + + if (err != OK) { + if (r_error) { + *r_error = err; + } + return RES(); + } + + if (r_error) { + *r_error = OK; + } + + return image; +} + +void ResourceFormatLoaderImage::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("image"); +} + +bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { + + return p_type == "Image"; +} + +String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { + + return p_path.get_extension().to_lower() == "image" ? "Image" : String(); +} diff --git a/core/io/image_loader.h b/core/io/image_loader.h index 052a8b8a40..fbb654c326 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -32,9 +32,11 @@ #define IMAGE_LOADER_H #include "image.h" +#include "io/resource_loader.h" #include "list.h" #include "os/file_access.h" #include "ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -55,6 +57,7 @@ class ImageLoader; class ImageFormatLoader { friend class ImageLoader; + friend class ResourceFormatLoaderImage; protected: virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) = 0; @@ -70,7 +73,7 @@ class ImageLoader { enum { MAX_LOADERS = 8 }; - + friend class ResourceFormatLoaderImage; static ImageFormatLoader *loader[MAX_LOADERS]; static int loader_count; @@ -83,4 +86,12 @@ public: static void add_image_format_loader(ImageFormatLoader *p_loader); }; +class ResourceFormatLoaderImage : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + #endif diff --git a/core/object.cpp b/core/object.cpp index 8c9d3557f8..a0c64feb09 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1209,7 +1209,15 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int } } - if (c.flags & CONNECT_ONESHOT) { + bool disconnect = c.flags & CONNECT_ONESHOT; +#ifdef TOOLS_ENABLED + if (disconnect && (c.flags & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) { + //this signal was connected from the editor, and is being edited. just dont disconnect for now + disconnect = false; + } +#endif + if (disconnect) { + _ObjectSignalDisconnectData dd; dd.signal = p_name; dd.target = target; diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 9bcc2d4530..0e7e63dbd8 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -41,6 +41,7 @@ #include "input_map.h" #include "io/config_file.h" #include "io/http_client.h" +#include "io/image_loader.h" #include "io/marshalls.h" #include "io/multiplayer_api.h" #include "io/networked_multiplayer_peer.h" @@ -60,11 +61,14 @@ #include "path_remap.h" #include "project_settings.h" #include "translation.h" + #include "undo_redo.h" static ResourceFormatSaverBinary *resource_saver_binary = NULL; static ResourceFormatLoaderBinary *resource_loader_binary = NULL; static ResourceFormatImporter *resource_format_importer = NULL; +static ResourceFormatLoaderImage *resource_format_image = NULL; + static _ResourceLoader *_resource_loader = NULL; static _ResourceSaver *_resource_saver = NULL; static _OS *_os = NULL; @@ -111,6 +115,9 @@ void register_core_types() { resource_format_importer = memnew(ResourceFormatImporter); ResourceLoader::add_resource_format_loader(resource_format_importer); + resource_format_image = memnew(ResourceFormatLoaderImage); + ResourceLoader::add_resource_format_loader(resource_format_image); + ClassDB::register_class<Object>(); ClassDB::register_virtual_class<Script>(); @@ -237,6 +244,8 @@ void unregister_core_types() { memdelete(_geometry); + if (resource_format_image) + memdelete(resource_format_image); if (resource_saver_binary) memdelete(resource_saver_binary); if (resource_loader_binary) diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index 15bbb1625c..753a506058 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -16,6 +16,7 @@ <return type="float"> </return> <description> + Returns the length of the audio stream in seconds. </description> </method> </methods> diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml index d6e75f8377..26d0b1a83d 100644 --- a/doc/classes/AudioStreamPlayer.xml +++ b/doc/classes/AudioStreamPlayer.xml @@ -17,7 +17,7 @@ <return type="float"> </return> <description> - Returns the position in the [AudioStream]. + Returns the position in the [AudioStream] in seconds. </description> </method> <method name="play"> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 2990906f7c..73d60e49b7 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -68,7 +68,7 @@ <return type="Array"> </return> <description> - Returns an array of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]). + Returns a [Vector2] array with the positions of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]). </description> </method> <method name="get_used_cells_by_id" qualifiers="const"> diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h index 0c53c4ba72..49e45eea9f 100644 --- a/drivers/gles2/shader_gles2.h +++ b/drivers/gles2/shader_gles2.h @@ -300,7 +300,7 @@ public: case ShaderLanguage::TYPE_MAT3: { GLfloat mat[9]; - for (int i = 0; i < 0; i++) { + for (int i = 0; i < 9; i++) { mat[i] = values[i].real; } @@ -311,7 +311,7 @@ public: case ShaderLanguage::TYPE_MAT4: { GLfloat mat[16]; - for (int i = 0; i < 0; i++) { + for (int i = 0; i < 16; i++) { mat[i] = values[i].real; } diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 97c4a98aab..0053b6311f 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -225,7 +225,7 @@ void RasterizerGLES3::set_current_render_target(RID p_render_target) { if (!p_render_target.is_valid() && storage->frame.current_rt && storage->frame.clear_request) { //handle pending clear request, if the framebuffer was not cleared glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); - print_line("unbind clear of: " + storage->frame.clear_request_color); + glClearColor( storage->frame.clear_request_color.r, storage->frame.clear_request_color.g, diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 20e8a7915f..15e40bfba1 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -62,6 +62,7 @@ #include "editor/import/editor_scene_importer_gltf.h" #include "editor/import/resource_importer_bitmask.h" #include "editor/import/resource_importer_csv_translation.h" +#include "editor/import/resource_importer_image.h" #include "editor/import/resource_importer_obj.h" #include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_texture.h" @@ -108,6 +109,7 @@ #include "editor/plugins/shader_graph_editor_plugin.h" #include "editor/plugins/skeleton_2d_editor_plugin.h" #include "editor/plugins/skeleton_editor_plugin.h" +#include "editor/plugins/skeleton_ik_editor_plugin.h" #include "editor/plugins/spatial_editor_plugin.h" #include "editor/plugins/sprite_editor_plugin.h" #include "editor/plugins/sprite_frames_editor_plugin.h" @@ -1752,6 +1754,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } else { tab_closing = editor_data.get_edited_scene(); } + if (!editor_data.get_edited_scene_root(tab_closing)) { + // empty tab + _scene_tab_closed(tab_closing); + break; + } } // fallthrough case SCENE_TAB_CLOSE: @@ -3844,6 +3851,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { Ref<InputEventMouseButton> mb = p_input; if (mb.is_valid()) { + if (scene_tabs->get_hovered_tab() >= 0) { if (mb->get_button_index() == BUTTON_MIDDLE && mb->is_pressed()) { _scene_tab_closed(scene_tabs->get_hovered_tab()); @@ -3853,6 +3861,26 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { _menu_option_confirm(FILE_NEW_SCENE, true); } } + if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { + + // context menu + scene_tabs_context_menu->clear(); + scene_tabs_context_menu->set_size(Size2(1, 1)); + + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), FILE_NEW_SCENE); + if (scene_tabs->get_hovered_tab() >= 0) { + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), FILE_SAVE_SCENE); + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), FILE_SAVE_AS_SCENE); + } + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES); + if (scene_tabs->get_hovered_tab() >= 0) { + scene_tabs_context_menu->add_separator(); + scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE); + scene_tabs_context_menu->add_item(TTR("Close Tab"), FILE_CLOSE); + } + scene_tabs_context_menu->set_position(mb->get_global_position()); + scene_tabs_context_menu->popup(); + } } } @@ -4638,6 +4666,10 @@ EditorNode::EditorNode() { import_texture.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_texture); + Ref<ResourceImporterImage> import_image; + import_image.instance(); + ResourceFormatImporter::get_singleton()->add_importer(import_image); + Ref<ResourceImporterCSVTranslation> import_csv_translation; import_csv_translation.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation); @@ -4908,6 +4940,7 @@ EditorNode::EditorNode() { scene_tabs = memnew(Tabs); scene_tabs->add_style_override("tab_fg", gui_base->get_stylebox("SceneTabFG", "EditorStyles")); scene_tabs->add_style_override("tab_bg", gui_base->get_stylebox("SceneTabBG", "EditorStyles")); + scene_tabs->set_select_with_rmb(true); scene_tabs->add_tab("unsaved"); scene_tabs->set_tab_align(Tabs::ALIGN_LEFT); scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); @@ -4925,6 +4958,11 @@ EditorNode::EditorNode() { tabbar_container = memnew(HBoxContainer); scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL); + scene_tabs_context_menu = memnew(PopupMenu); + tabbar_container->add_child(scene_tabs_context_menu); + scene_tabs_context_menu->connect("id_pressed", this, "_menu_option"); + scene_tabs_context_menu->set_hide_on_window_lose_focus(true); + srt->add_child(tabbar_container); tabbar_container->add_child(scene_tabs); distraction_free = memnew(ToolButton); @@ -5031,6 +5069,7 @@ EditorNode::EditorNode() { file_menu->set_tooltip(TTR("Operations with scene files.")); p = file_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene")), FILE_NEW_SCENE); p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene...")), FILE_NEW_INHERITED_SCENE); p->add_shortcut(ED_SHORTCUT("editor/open_scene", TTR("Open Scene..."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE); @@ -5076,6 +5115,7 @@ EditorNode::EditorNode() { left_menu_hb->add_child(project_menu); p = project_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->add_item(TTR("Project Settings"), RUN_SETTINGS); p->add_separator(); p->connect("id_pressed", this, "_menu_option"); @@ -5111,6 +5151,7 @@ EditorNode::EditorNode() { left_menu_hb->add_child(debug_menu); p = debug_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->set_hide_on_item_selection(false); p->add_check_item(TTR("Deploy with Remote Debug"), RUN_DEPLOY_REMOTE_DEBUG); p->set_item_tooltip(p->get_item_count() - 1, TTR("When exporting or deploying, the resulting executable will attempt to connect to the IP of this computer in order to be debugged.")); @@ -5135,8 +5176,9 @@ EditorNode::EditorNode() { settings_menu->set_text(TTR("Editor")); settings_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); left_menu_hb->add_child(settings_menu); - p = settings_menu->get_popup(); + p = settings_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->add_item(TTR("Editor Settings"), SETTINGS_PREFERENCES); p->add_separator(); @@ -5172,6 +5214,7 @@ EditorNode::EditorNode() { left_menu_hb->add_child(help_menu); p = help_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->connect("id_pressed", this, "_menu_option"); p->add_icon_item(gui_base->get_icon("ClassList", "EditorIcons"), TTR("Classes"), HELP_CLASSES); p->add_icon_item(gui_base->get_icon("HelpSearch", "EditorIcons"), TTR("Search"), HELP_SEARCH); @@ -5554,6 +5597,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(SkeletonEditorPlugin(this))); + add_editor_plugin(memnew(SkeletonIKEditorPlugin(this))); add_editor_plugin(memnew(PhysicalBonePlugin(this))); // FIXME: Disabled as (according to reduz) users were complaining that it gets in the way diff --git a/editor/editor_node.h b/editor/editor_node.h index b7224c9393..85aa37ec7e 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -223,6 +223,7 @@ private: //main tabs Tabs *scene_tabs; + PopupMenu *scene_tabs_context_menu; Panel *tab_preview_panel; TextureRect *tab_preview; int tab_closing; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 37f86cc912..1718badbfa 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1983,9 +1983,11 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { */ file_options = memnew(PopupMenu); + file_options->set_hide_on_window_lose_focus(true); add_child(file_options); folder_options = memnew(PopupMenu); + folder_options->set_hide_on_window_lose_focus(true); add_child(folder_options); split_box = memnew(VSplitContainer); diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp new file mode 100644 index 0000000000..b6a67c0cd3 --- /dev/null +++ b/editor/import/resource_importer_image.cpp @@ -0,0 +1,79 @@ +#include "resource_importer_image.h" + +#include "io/image_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "scene/resources/texture.h" + +String ResourceImporterImage::get_importer_name() const { + + return "image"; +} + +String ResourceImporterImage::get_visible_name() const { + + return "Image"; +} +void ResourceImporterImage::get_recognized_extensions(List<String> *p_extensions) const { + + ImageLoader::get_recognized_extensions(p_extensions); +} + +String ResourceImporterImage::get_save_extension() const { + return "image"; +} + +String ResourceImporterImage::get_resource_type() const { + + return "Image"; +} + +bool ResourceImporterImage::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + + return true; +} + +int ResourceImporterImage::get_preset_count() const { + return 0; +} +String ResourceImporterImage::get_preset_name(int p_idx) const { + + return String(); +} + +void ResourceImporterImage::get_import_options(List<ImportOption> *r_options, int p_preset) const { +} + +Error ResourceImporterImage::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { + + FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); + if (!f) { + ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + } + + size_t len = f->get_len(); + + Vector<uint8_t> data; + data.resize(len); + + f->get_buffer(data.ptrw(), len); + + memdelete(f); + + f = FileAccess::open(p_save_path + ".image", FileAccess::WRITE); + + //save the header GDIM + const uint8_t header[4] = { 'G', 'D', 'I', 'M' }; + f->store_buffer(header, 4); + //SAVE the extension (so it can be recognized by the loader later + f->store_pascal_string(p_source_file.get_extension().to_lower()); + //SAVE the actual image + f->store_buffer(data.ptr(), len); + + memdelete(f); + + return OK; +} + +ResourceImporterImage::ResourceImporterImage() { +} diff --git a/editor/import/resource_importer_image.h b/editor/import/resource_importer_image.h new file mode 100644 index 0000000000..5aadd00a35 --- /dev/null +++ b/editor/import/resource_importer_image.h @@ -0,0 +1,27 @@ +#ifndef RESOURCE_IMPORTER_IMAGE_H +#define RESOURCE_IMPORTER_IMAGE_H + +#include "image.h" +#include "io/resource_import.h" + +class ResourceImporterImage : public ResourceImporter { + GDCLASS(ResourceImporterImage, ResourceImporter) +public: + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); + + ResourceImporterImage(); +}; + +#endif // RESOURCE_IMPORTER_IMAGE_H diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index b8dd4a87b7..5babf6419c 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -224,6 +224,13 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p while (true) { String l = f->get_line().strip_edges(); + while (l.length() && l[l.length() - 1] == '\\') { + String add = f->get_line().strip_edges(); + l += add; + if (add == String()) { + break; + } + } if (l.begins_with("v ")) { //vertex @@ -264,10 +271,12 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p face[0] = v[1].split("/"); face[1] = v[2].split("/"); ERR_FAIL_COND_V(face[0].size() == 0, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(face[0].size() != face[1].size(), ERR_FILE_CORRUPT); for (int i = 2; i < v.size() - 1; i++) { face[2] = v[i + 1].split("/"); + ERR_FAIL_COND_V(face[0].size() != face[2].size(), ERR_FILE_CORRUPT); for (int j = 0; j < 3; j++) { diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 0d25b3685a..9acbceec92 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -78,7 +78,7 @@ bool EditorTexturePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture"); } -Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) const { Ref<Image> img; Ref<AtlasTexture> atex = p_from; @@ -138,12 +138,66 @@ EditorTexturePreviewPlugin::EditorTexturePreviewPlugin() { //////////////////////////////////////////////////////////////////////////// +bool EditorImagePreviewPlugin::handles(const String &p_type) const { + + return p_type == "Image"; +} + +Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { + + Ref<Image> img = p_from; + + if (img.is_null() || img->empty()) + return Ref<Image>(); + + img = img->duplicate(); + img->clear_mipmaps(); + + int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); + thumbnail_size *= EDSCALE; + if (img->is_compressed()) { + if (img->decompress() != OK) + return Ref<Image>(); + } else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) { + img->convert(Image::FORMAT_RGBA8); + } + + int width, height; + if (img->get_width() > thumbnail_size && img->get_width() >= img->get_height()) { + + width = thumbnail_size; + height = img->get_height() * thumbnail_size / img->get_width(); + } else if (img->get_height() > thumbnail_size && img->get_height() >= img->get_width()) { + + height = thumbnail_size; + width = img->get_width() * thumbnail_size / img->get_height(); + } else { + + width = img->get_width(); + height = img->get_height(); + } + + img->resize(width, height); + post_process_preview(img); + + Ref<ImageTexture> ptex; + ptex.instance(); + + ptex->create_from_image(img, 0); + return ptex; +} + +EditorImagePreviewPlugin::EditorImagePreviewPlugin() { +} + +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////// bool EditorBitmapPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "BitMap"); } -Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { Ref<BitMap> bm = p_from; @@ -215,12 +269,12 @@ bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "PackedScene"); } -Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES &p_from) const { return generate_from_path(p_from->get_path()); } -Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path) { +Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path) const { String temp_path = EditorSettings::get_singleton()->get_cache_dir(); String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text(); @@ -269,7 +323,7 @@ bool EditorMaterialPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Material"); //any material } -Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) const { Ref<Material> material = p_from; ERR_FAIL_COND_V(material.is_null(), Ref<Texture>()); @@ -281,7 +335,7 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) { VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture preview_done = false; - VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant()); + VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMaterialPreviewPlugin *>(this), "_preview_done", Variant()); while (!preview_done) { OS::get_singleton()->delay_usec(10); @@ -436,7 +490,7 @@ bool EditorScriptPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Script"); } -Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) const { Ref<Script> scr = p_from; if (scr.is_null()) @@ -559,7 +613,7 @@ bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "AudioStream"); } -Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from) const { Ref<AudioStream> stream = p_from; ERR_FAIL_COND_V(stream.is_null(), Ref<Texture>()); @@ -657,7 +711,7 @@ bool EditorMeshPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Mesh"); //any Mesh } -Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) const { Ref<Mesh> mesh = p_from; ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture>()); @@ -684,7 +738,7 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) { VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture preview_done = false; - VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant()); + VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMeshPreviewPlugin *>(this), "_preview_done", Variant()); while (!preview_done) { OS::get_singleton()->delay_usec(10); @@ -771,16 +825,7 @@ bool EditorFontPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "DynamicFontData"); } -Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) { - if (canvas.is_valid()) { - VS::get_singleton()->viewport_remove_canvas(viewport, canvas); - } - - canvas = VS::get_singleton()->canvas_create(); - canvas_item = VS::get_singleton()->canvas_item_create(); - - VS::get_singleton()->viewport_attach_canvas(viewport, canvas); - VS::get_singleton()->canvas_item_set_parent(canvas_item, canvas); +Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) const { Ref<DynamicFontData> SampledFont; SampledFont.instance(); @@ -809,7 +854,7 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) { VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture preview_done = false; - VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant()); + VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant()); while (!preview_done) { OS::get_singleton()->delay_usec(10); @@ -829,7 +874,7 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) { return ptex; } -Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from) const { return generate_from_path(p_from->get_path()); } @@ -842,6 +887,12 @@ EditorFontPreviewPlugin::EditorFontPreviewPlugin() { VS::get_singleton()->viewport_set_size(viewport, 128, 128); VS::get_singleton()->viewport_set_active(viewport, true); viewport_texture = VS::get_singleton()->viewport_get_texture(viewport); + + canvas = VS::get_singleton()->canvas_create(); + canvas_item = VS::get_singleton()->canvas_item_create(); + + VS::get_singleton()->viewport_attach_canvas(viewport, canvas); + VS::get_singleton()->canvas_item_set_parent(canvas_item, canvas); } EditorFontPreviewPlugin::~EditorFontPreviewPlugin() { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 140d9f849f..8bd7943383 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -39,16 +39,25 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorTexturePreviewPlugin(); }; +class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorImagePreviewPlugin, EditorResourcePreviewGenerator) +public: + virtual bool handles(const String &p_type) const; + virtual Ref<Texture> generate(const RES &p_from) const; + + EditorImagePreviewPlugin(); +}; + class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorBitmapPreviewPlugin(); }; @@ -57,8 +66,8 @@ class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); - virtual Ref<Texture> generate_from_path(const String &p_path); + virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate_from_path(const String &p_path) const; EditorPackedScenePreviewPlugin(); }; @@ -77,7 +86,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; - volatile bool preview_done; + mutable volatile bool preview_done; void _preview_done(const Variant &p_udata); @@ -86,7 +95,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorMaterialPreviewPlugin(); ~EditorMaterialPreviewPlugin(); @@ -95,7 +104,7 @@ public: class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorScriptPreviewPlugin(); }; @@ -103,7 +112,7 @@ public: class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorAudioStreamPreviewPlugin(); }; @@ -121,7 +130,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; - volatile bool preview_done; + mutable volatile bool preview_done; void _preview_done(const Variant &p_udata); @@ -130,7 +139,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorMeshPreviewPlugin(); ~EditorMeshPreviewPlugin(); @@ -144,7 +153,7 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator { RID viewport_texture; RID canvas; RID canvas_item; - volatile bool preview_done; + mutable volatile bool preview_done; void _preview_done(const Variant &p_udata); @@ -153,8 +162,8 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); - virtual Ref<Texture> generate_from_path(const String &p_path); + virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate_from_path(const String &p_path) const; EditorFontPreviewPlugin(); ~EditorFontPreviewPlugin(); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 3b82e6578b..6cc8f91e38 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -2402,26 +2402,25 @@ void ScriptEditor::_make_script_list_context_menu() { if (se) { context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save"), FILE_SAVE); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save_as"), FILE_SAVE_AS); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_all"), CLOSE_ALL); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_other_tabs"), CLOSE_OTHER_TABS); - context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/copy_path"), FILE_COPY_PATH); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/show_in_file_system"), SHOW_IN_FILE_SYSTEM); - + } + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_all"), CLOSE_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_other_tabs"), CLOSE_OTHER_TABS); + context_menu->add_separator(); + if (se) { Ref<Script> scr = se->get_edited_resource(); if (scr != NULL) { context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT); if (!scr.is_null() && scr->is_tool()) { - context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/run_file"), FILE_RUN); + context_menu->add_separator(); } } - } else { - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/copy_path"), FILE_COPY_PATH); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/show_in_file_system"), SHOW_IN_FILE_SYSTEM); + context_menu->add_separator(); } - context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_up"), WINDOW_MOVE_UP); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_down"), WINDOW_MOVE_DOWN); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_sort"), WINDOW_SORT); @@ -2902,6 +2901,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { context_menu = memnew(PopupMenu); add_child(context_menu); context_menu->connect("id_pressed", this, "_menu_option"); + context_menu->set_hide_on_window_lose_focus(true); overview_vbox = memnew(VBoxContainer); overview_vbox->set_custom_minimum_size(Size2(0, 90)); @@ -2956,6 +2956,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu = memnew(MenuButton); menu_hb->add_child(file_menu); file_menu->set_text(TTR("File")); + file_menu->get_popup()->set_hide_on_window_lose_focus(true); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New")), FILE_NEW); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open")), FILE_OPEN); file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT); @@ -3005,6 +3006,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_search_menu = memnew(MenuButton); menu_hb->add_child(script_search_menu); script_search_menu->set_text(TTR("Search")); + script_search_menu->get_popup()->set_hide_on_window_lose_focus(true); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT); script_search_menu->get_popup()->connect("id_pressed", this, "_menu_option"); @@ -3013,6 +3015,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debug_menu = memnew(MenuButton); menu_hb->add_child(debug_menu); debug_menu->set_text(TTR("Debug")); + debug_menu->get_popup()->set_hide_on_window_lose_focus(true); debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT); debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP); debug_menu->get_popup()->add_separator(); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 165d7e32b9..0f48d42cf2 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -967,7 +967,6 @@ void ScriptTextEditor::_edit_option(int p_op) { } } break; - case HELP_CONTEXTUAL: { String text = tx->get_selection_text(); @@ -977,6 +976,15 @@ void ScriptTextEditor::_edit_option(int p_op) { emit_signal("request_help_search", text); } } break; + case LOOKUP_SYMBOL: { + + String text = tx->get_word_under_cursor(); + if (text == "") + text = tx->get_selection_text(); + if (text != "") { + _lookup_symbol(text, tx->cursor_get_line(), tx->cursor_get_column()); + } + } break; } } @@ -1182,19 +1190,13 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { if (mb.is_valid()) { - if (mb->get_button_index() == BUTTON_RIGHT) { - + if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { int col, row; TextEdit *tx = code_editor->get_text_edit(); tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col); Vector2 mpos = mb->get_global_position() - tx->get_global_position(); tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); - bool has_color = (tx->get_word_at_pos(mpos) == "Color"); - int fold_state = 0; - bool can_fold = tx->can_fold(row); - bool is_folded = tx->is_folded(row); - if (tx->is_right_click_moving_caret()) { if (tx->is_selection_active()) { @@ -1214,38 +1216,62 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { } } - if (!mb->is_pressed()) { - if (has_color) { - String line = tx->get_line(row); - color_line = row; - int begin = 0; - int end = 0; - bool valid = false; - for (int i = col; i < line.length(); i++) { - if (line[i] == '(') { - begin = i; - continue; - } else if (line[i] == ')') { - end = i + 1; - valid = true; - break; - } + String word_at_mouse = tx->get_word_at_pos(mpos); + if (word_at_mouse == "") + word_at_mouse = tx->get_word_under_cursor(); + if (word_at_mouse == "") + word_at_mouse = tx->get_selection_text(); + + bool has_color = (word_at_mouse == "Color"); + int fold_state = 0; + bool foldable = tx->can_fold(row) || tx->is_folded(row); + bool open_docs = false; + bool goto_definition = false; + + if (word_at_mouse.is_resource_file()) { + open_docs = true; + } else { + + Node *base = get_tree()->get_edited_scene_root(); + if (base) { + base = _find_node_for_script(base, base, script); + } + ScriptLanguage::LookupResult result; + if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), word_at_mouse, script->get_path().get_base_dir(), base, result) == OK) { + open_docs = true; + } + } + + if (has_color) { + String line = tx->get_line(row); + color_line = row; + int begin = 0; + int end = 0; + bool valid = false; + for (int i = col; i < line.length(); i++) { + if (line[i] == '(') { + begin = i; + continue; + } else if (line[i] == ')') { + end = i + 1; + valid = true; + break; } - if (valid) { - color_args = line.substr(begin, end - begin); - String stripped = color_args.replace(" ", "").replace("(", "").replace(")", ""); - Vector<float> color = stripped.split_floats(","); - if (color.size() > 2) { - float alpha = color.size() > 3 ? color[3] : 1.0f; - color_picker->set_pick_color(Color(color[0], color[1], color[2], alpha)); - } - color_panel->set_position(get_global_transform().xform(get_local_mouse_position())); - } else { - has_color = false; + } + if (valid) { + color_args = line.substr(begin, end - begin); + String stripped = color_args.replace(" ", "").replace("(", "").replace(")", ""); + Vector<float> color = stripped.split_floats(","); + if (color.size() > 2) { + float alpha = color.size() > 3 ? color[3] : 1.0f; + color_picker->set_pick_color(Color(color[0], color[1], color[2], alpha)); } + color_panel->set_position(get_global_transform().xform(get_local_mouse_position())); + } else { + has_color = false; } - _make_context_menu(tx->is_selection_active(), has_color, can_fold, is_folded); } + _make_context_menu(tx->is_selection_active(), has_color, foldable, open_docs, goto_definition); } } } @@ -1264,7 +1290,7 @@ void ScriptTextEditor::_color_changed(const Color &p_color) { code_editor->get_text_edit()->set_line(color_line, new_line); } -void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded) { +void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition) { context_menu->clear(); if (p_selection) { @@ -1287,13 +1313,17 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE); } - if (p_can_fold || p_is_folded) + if (p_foldable) context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); - if (p_color) { + if (p_color || p_open_docs || p_goto_definition) { context_menu->add_separator(); - context_menu->add_item(TTR("Pick Color"), EDIT_PICK_COLOR); + if (p_open_docs) + context_menu->add_item(TTR("Lookup Symbol"), LOOKUP_SYMBOL); + if (p_color) + context_menu->add_item(TTR("Pick Color"), EDIT_PICK_COLOR); } + context_menu->set_position(get_global_transform().xform(get_local_mouse_position())); context_menu->set_size(Vector2(1, 1)); context_menu->popup(); @@ -1327,6 +1357,7 @@ ScriptTextEditor::ScriptTextEditor() { context_menu = memnew(PopupMenu); add_child(context_menu); context_menu->connect("id_pressed", this, "_edit_option"); + context_menu->set_hide_on_window_lose_focus(true); color_panel = memnew(PopupPanel); add_child(color_panel); @@ -1338,6 +1369,7 @@ ScriptTextEditor::ScriptTextEditor() { edit_menu = memnew(MenuButton); edit_menu->set_text(TTR("Edit")); + edit_menu->get_popup()->set_hide_on_window_lose_focus(true); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); @@ -1391,6 +1423,7 @@ ScriptTextEditor::ScriptTextEditor() { search_menu = memnew(MenuButton); edit_hb->add_child(search_menu); search_menu->set_text(TTR("Search")); + search_menu->get_popup()->set_hide_on_window_lose_focus(true); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index f0b00a9117..334d410dbe 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -112,6 +112,7 @@ class ScriptTextEditor : public ScriptEditorBase { DEBUG_GOTO_NEXT_BREAKPOINT, DEBUG_GOTO_PREV_BREAKPOINT, HELP_CONTEXTUAL, + LOOKUP_SYMBOL, }; protected: @@ -131,7 +132,7 @@ protected: void _change_syntax_highlighter(int p_idx); void _edit_option(int p_op); - void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded); + void _make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition); void _text_edit_gui_input(const Ref<InputEvent> &ev); void _color_changed(const Color &p_color); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 7650cd6ae7..ea1876c27a 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -465,7 +465,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { if (mb.is_valid()) { - if (mb->get_button_index() == BUTTON_RIGHT) { + if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { int col, row; TextEdit *tx = shader_editor->get_text_edit(); @@ -491,10 +491,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { tx->cursor_set_column(col); } } - - if (!mb->is_pressed()) { - _make_context_menu(tx->is_selection_active()); - } + _make_context_menu(tx->is_selection_active()); } } } @@ -546,6 +543,7 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { context_menu = memnew(PopupMenu); add_child(context_menu); context_menu->connect("id_pressed", this, "_menu_option"); + context_menu->set_hide_on_window_lose_focus(true); VBoxContainer *main_container = memnew(VBoxContainer); HBoxContainer *hbc = memnew(HBoxContainer); @@ -554,6 +552,7 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { //edit_menu->set_position(Point2(5, -1)); edit_menu->set_text(TTR("Edit")); + edit_menu->get_popup()->set_hide_on_window_lose_focus(true); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); @@ -578,7 +577,7 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { search_menu = memnew(MenuButton); //search_menu->set_position(Point2(38, -1)); search_menu->set_text(TTR("Search")); - + search_menu->get_popup()->set_hide_on_window_lose_focus(true); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); diff --git a/editor/plugins/skeleton_ik_editor_plugin.cpp b/editor/plugins/skeleton_ik_editor_plugin.cpp new file mode 100644 index 0000000000..2d343d3edd --- /dev/null +++ b/editor/plugins/skeleton_ik_editor_plugin.cpp @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* skeleton_ik_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_ik_editor_plugin.h" + +#include "scene/animation/skeleton_ik.h" + +void SkeletonIKEditorPlugin::_play() { + + if (!skeleton_ik) + return; + + if (!skeleton_ik->get_parent_skeleton()) + return; + + if (play_btn->is_pressed()) { + + initial_bone_poses.resize(skeleton_ik->get_parent_skeleton()->get_bone_count()); + for (int i = 0; i < skeleton_ik->get_parent_skeleton()->get_bone_count(); ++i) { + initial_bone_poses.write[i] = skeleton_ik->get_parent_skeleton()->get_bone_pose(i); + } + + skeleton_ik->start(); + } else { + skeleton_ik->stop(); + + if (initial_bone_poses.size() != skeleton_ik->get_parent_skeleton()->get_bone_count()) + return; + + for (int i = 0; i < skeleton_ik->get_parent_skeleton()->get_bone_count(); ++i) { + skeleton_ik->get_parent_skeleton()->set_bone_pose(i, initial_bone_poses[i]); + } + } +} + +void SkeletonIKEditorPlugin::edit(Object *p_object) { + + if (p_object != skeleton_ik) { + if (skeleton_ik) { + play_btn->set_pressed(false); + _play(); + } + } + + SkeletonIK *s = Object::cast_to<SkeletonIK>(p_object); + if (!s) + return; + + skeleton_ik = s; +} + +bool SkeletonIKEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("SkeletonIK"); +} + +void SkeletonIKEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) + play_btn->show(); + else + play_btn->hide(); +} + +void SkeletonIKEditorPlugin::_bind_methods() { + + ClassDB::bind_method("_play", &SkeletonIKEditorPlugin::_play); +} + +SkeletonIKEditorPlugin::SkeletonIKEditorPlugin(EditorNode *p_node) { + + editor = p_node; + play_btn = memnew(Button); + play_btn->set_icon(editor->get_gui_base()->get_icon("Play", "EditorIcons")); + play_btn->set_text(TTR("Play IK")); + play_btn->set_toggle_mode(true); + play_btn->hide(); + play_btn->connect("pressed", this, "_play"); + add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, play_btn); + skeleton_ik = NULL; +} + +SkeletonIKEditorPlugin::~SkeletonIKEditorPlugin() {} diff --git a/editor/plugins/skeleton_ik_editor_plugin.h b/editor/plugins/skeleton_ik_editor_plugin.h new file mode 100644 index 0000000000..e645bea39a --- /dev/null +++ b/editor/plugins/skeleton_ik_editor_plugin.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* skeleton_ik_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_EDITOR_PLUGIN_H +#define SKELETON_IK_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" + +class SkeletonIK; + +class SkeletonIKEditorPlugin : public EditorPlugin { + + GDCLASS(SkeletonIKEditorPlugin, EditorPlugin); + + SkeletonIK *skeleton_ik; + + Button *play_btn; + EditorNode *editor; + Vector<Transform> initial_bone_poses; + + void _play(); + +protected: + static void _bind_methods(); + +public: + virtual String get_name() const { return "SkeletonIK"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + SkeletonIKEditorPlugin(EditorNode *p_node); + ~SkeletonIKEditorPlugin(); +}; + +#endif // SKELETON_IK_EDITOR_PLUGIN_H diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index d8982c751c..73a9c8ac1a 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -803,7 +803,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { 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; + case TOOL_CREATE_USER_INTERFACE: { + Control *node = memnew(Control); + node->set_anchors_and_margins_preset(PRESET_WIDE); //more useful for resizable UIs. + new_node = node; + + } break; } editor_data->get_undo_redo().create_action("New Scene Root"); @@ -1985,6 +1990,8 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (selection.size() == 1) { + Node *selected = selection[0]; + subresources.clear(); menu_subresources->clear(); menu_subresources->set_size(Size2(1, 1)); @@ -1995,18 +2002,23 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); menu->add_separator(); + menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); - menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + Ref<Script> existing = selected->get_script(); + if (existing.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + } menu->add_separator(); menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); } menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE); - menu->add_separator(); - menu->add_icon_shortcut(get_icon("MoveUp", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP); - menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); - menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); - menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); - + if (scene_tree->get_selected() != edited_scene) { + menu->add_separator(); + menu->add_icon_shortcut(get_icon("MoveUp", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP); + menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); + menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); + 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); @@ -2325,6 +2337,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel menu = memnew(PopupMenu); add_child(menu); menu->connect("id_pressed", this, "_tool_selected"); + menu->set_hide_on_window_lose_focus(true); menu_subresources = memnew(PopupMenu); menu_subresources->set_name("Sub-Resources"); menu_subresources->connect("id_pressed", this, "_tool_selected"); diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index 1686a6e87e..9fc7230f91 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -123,7 +123,7 @@ void SoftBodyBullet::update_visual_server(SoftBodyVisualServerHandler *p_visual_ void SoftBodyBullet::set_soft_mesh(const Ref<Mesh> &p_mesh) { - if (p_mesh.is_null() || !p_mesh->surface_is_softbody_friendly(0)) + if (p_mesh.is_null()) soft_mesh.unref(); else soft_mesh = p_mesh; diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs index e2457ff98b..43de9156f2 100644 --- a/modules/mono/glue/cs_files/GD.cs +++ b/modules/mono/glue/cs_files/GD.cs @@ -64,6 +64,11 @@ namespace Godot return ResourceLoader.Load(path); } + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) ResourceLoader.Load(path); + } + public static void Print(params object[] what) { NativeCalls.godot_icall_Godot_print(what); diff --git a/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs b/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs new file mode 100644 index 0000000000..ceecc589e6 --- /dev/null +++ b/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + public static partial class ResourceLoader + { + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) Load(path); + } + } +} diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index ad74f73d74..4e515cde28 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -40,9 +40,7 @@ String GDMonoClass::get_full_name(MonoClass *p_mono_class) { MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class)); MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoString *str = mono_object_to_string((MonoObject *)type_obj, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return GDMonoMarshal::mono_string_to_godot(str); diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index c8df1038ce..630bda8b4e 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -105,9 +105,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, } MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); if (exc) { ret = NULL; @@ -121,9 +119,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, return ret; } else { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc); if (exc) { if (r_exc) { @@ -144,9 +140,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) { MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc); if (exc) { ret = NULL; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index a1c710c26c..ce66e0c8db 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -139,15 +139,23 @@ bool GDMonoProperty::has_setter() { } void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { - void *params[1] = { p_value }; - set_value(p_object, params, r_exc); + MonoMethod *prop_method = mono_property_get_set_method(mono_property); + MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); + mono_array_set(params, MonoObject *, 0, p_value); + MonoException *exc = NULL; + GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc); + if (exc) { + if (r_exc) { + *r_exc = exc; + } else { + GDMonoUtils::set_pending_exception(exc); + } + } } void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_property_set_value(mono_property, p_object, p_params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc); if (exc) { if (r_exc) { @@ -160,9 +168,7 @@ void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoExcept MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, NULL, &exc); if (exc) { ret = NULL; diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 21efd4064a..911d629956 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -414,7 +414,7 @@ MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { void *args[1] = { &new_array }; MonoException *exc = NULL; - mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc); + GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return mono_object; @@ -444,7 +444,7 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) void *args[1] = { &new_dict }; MonoException *exc = NULL; - mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc); + GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return mono_object; @@ -476,9 +476,7 @@ String get_exception_name_and_message(MonoException *p_exc) { res += ": "; MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_exc, NULL, NULL); - GD_MONO_END_RUNTIME_INVOKE; + MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, NULL, NULL); res += GDMonoMarshal::mono_string_to_godot(msg); return res; @@ -489,9 +487,7 @@ void set_exception_message(MonoException *p_exc, String message) { MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); MonoString *msg = GDMonoMarshal::mono_string_from_godot(message); void *params[1] = { msg }; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_property_set_value(prop, (MonoObject *)p_exc, params, NULL); - GD_MONO_END_RUNTIME_INVOKE; + property_set_value(prop, (MonoObject *)p_exc, params, NULL); } void debug_print_unhandled_exception(MonoException *p_exc) { @@ -592,4 +588,38 @@ void set_pending_exception(MonoException *p_exc) { _THREAD_LOCAL_(int) current_invoke_count = 0; +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)&p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)&p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; +} + +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index d6774ed41d..bf8860c85a 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -230,6 +230,14 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() { return current_invoke_count; } +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc); +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc); + +MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc); + +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); + } // namespace GDMonoUtils #define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL))) diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp index 6f8b0f90bc..ae9f130518 100644 --- a/modules/mono/utils/thread_local.cpp +++ b/modules/mono/utils/thread_local.cpp @@ -63,7 +63,13 @@ struct ThreadLocalStorage::Impl { #endif } - Impl(void (*p_destr_callback_func)(void *)) { +#ifdef WINDOWS_ENABLED +#define _CALLBACK_FUNC_ __stdcall +#else +#define _CALLBACK_FUNC_ +#endif + + Impl(void (_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) { #ifdef WINDOWS_ENABLED dwFlsIndex = FlsAlloc(p_destr_callback_func); ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES); @@ -89,10 +95,12 @@ void ThreadLocalStorage::set_value(void *p_value) const { pimpl->set_value(p_value); } -void ThreadLocalStorage::alloc(void (*p_destr_callback)(void *)) { +void ThreadLocalStorage::alloc(void (_CALLBACK_FUNC_ *p_destr_callback)(void *)) { pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback)); } +#undef _CALLBACK_FUNC_ + void ThreadLocalStorage::free() { memdelete(pimpl); pimpl = NULL; diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h index 7ff10b4efc..783e40dc01 100644 --- a/modules/mono/utils/thread_local.h +++ b/modules/mono/utils/thread_local.h @@ -65,12 +65,18 @@ #include "core/typedefs.h" +#ifdef WINDOWS_ENABLED +#define _CALLBACK_FUNC_ __stdcall +#else +#define _CALLBACK_FUNC_ +#endif + struct ThreadLocalStorage { void *get_value() const; void set_value(void *p_value) const; - void alloc(void (*p_dest_callback)(void *)); + void alloc(void (_CALLBACK_FUNC_ *p_dest_callback)(void *)); void free(); private: @@ -85,17 +91,10 @@ class ThreadLocal { T init_val; -#ifdef WINDOWS_ENABLED -#define _CALLBACK_FUNC_ __stdcall -#else -#define _CALLBACK_FUNC_ -#endif - static void _CALLBACK_FUNC_ destr_callback(void *tls_data) { memdelete(static_cast<T *>(tls_data)); } -#undef _CALLBACK_FUNC_ T *_tls_get_value() const { void *tls_data = storage.get_value(); @@ -156,6 +155,8 @@ private: bool &flag; }; +#undef _CALLBACK_FUNC_ + #define _TLS_RECURSION_GUARD_V_(m_ret) \ static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \ if (_recursion_flag_) \ diff --git a/platform/windows/lang_table.h b/platform/windows/lang_table.h index 1a966b502a..78bfadfeae 100644 --- a/platform/windows/lang_table.h +++ b/platform/windows/lang_table.h @@ -186,6 +186,7 @@ static const _WinLocale _win_locales[] = { { "zh_CN", LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED }, { "zh_HK", LANG_CHINESE, SUBLANG_CHINESE_HONGKONG }, { "zh_SG", LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE }, + { "zh_TW", LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL }, { 0, 0, 0 }, }; diff --git a/platform_methods.py b/platform_methods.py index 0e0d93d0a6..4300216427 100644 --- a/platform_methods.py +++ b/platform_methods.py @@ -7,6 +7,11 @@ import subprocess # NOTE: The multiprocessing module is not compatible with SCons due to conflict on cPickle +if sys.version_info[0] < 3: + JSON_SERIALIZABLE_TYPES = (bool, int, long, float, basestring) +else: + JSON_SERIALIZABLE_TYPES = (bool, int, float, str) + def run_in_subprocess(builder_function): @@ -17,9 +22,9 @@ def run_in_subprocess(builder_function): target = [node.srcnode().abspath for node in target] source = [node.srcnode().abspath for node in source] - # Short circuit on non-Windows platforms - if os.name != 'nt': - return builder_function(target, source, None) + # Short circuit on non-Windows platforms, no need to run in subprocess + if sys.platform not in ('win32', 'cygwin'): + return builder_function(target, source, env) # Identify module module_name = builder_function.__module__ @@ -32,14 +37,25 @@ def run_in_subprocess(builder_function): subprocess_env = os.environ.copy() subprocess_env['PYTHONPATH'] = os.pathsep.join([os.getcwd()] + sys.path) + # Keep only JSON serializable environment items + filtered_env = dict( + (key, value) + for key, value in env.items() + if isinstance(value, JSON_SERIALIZABLE_TYPES) + ) + # Save parameters - args = (target, source, None) + args = (target, source, filtered_env) data = dict(fn=function_name, args=args) json_path = os.path.join(os.environ['TMP'], uuid.uuid4().hex + '.json') with open(json_path, 'wt') as json_file: json.dump(data, json_file, indent=2) + json_file_size = os.stat(json_path).st_size + + print('Executing builder function in subprocess: ' + 'module_path=%r, parameter_file=%r, parameter_file_size=%r, target=%r, source=%r' % ( + module_path, json_path, json_file_size, target, source)) try: - print('Executing builder function in subprocess: module_path=%r; data=%r' % (module_path, data)) exit_code = subprocess.call([sys.executable, module_path, json_path], env=subprocess_env) finally: try: diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 9a2046991b..0fe427d5fc 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -468,6 +468,10 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("get_keep_aspect_mode"), &Camera::get_keep_aspect_mode); ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera::get_doppler_tracking); + + ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera::set_cull_mask_bit); + ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera::get_cull_mask_bit); + //ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current ); ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode"); @@ -550,6 +554,20 @@ uint32_t Camera::get_cull_mask() const { return layers; } +void Camera::set_cull_mask_bit(int p_layer, bool p_enable) { + ERR_FAIL_INDEX(p_layer, 32); + if (p_enable) { + set_cull_mask(layers | (1 << p_layer)); + } else { + set_cull_mask(layers & (~(1 << p_layer))); + } +} + +bool Camera::get_cull_mask_bit(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, 32, false); + return (layers & (1 << p_layer)); +} + Vector<Plane> Camera::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 1b506e0c4f..97705d8ae0 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -142,6 +142,9 @@ public: void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; + void set_cull_mask_bit(int p_layer, bool p_enable); + bool get_cull_mask_bit(int p_layer) const; + virtual Vector<Plane> get_frustum() const; void set_environment(const Ref<Environment> &p_environment); diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 4143989671..80bf422c98 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -557,6 +557,7 @@ protected: private: static Skeleton *find_skeleton_parent(Node *p_parent); + void _fix_joint_offset(); void _reload_joint(); diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 4b6b59b2d3..f45cb02211 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -600,9 +600,12 @@ PhysicalBone *Skeleton::_get_physical_bone_parent(int p_bone) { void Skeleton::_rebuild_physical_bones_cache() { const int b_size = bones.size(); for (int i = 0; i < b_size; ++i) { - bones.write[i].cache_parent_physical_bone = _get_physical_bone_parent(i); - if (bones[i].physical_bone) - bones[i].physical_bone->_on_bone_parent_changed(); + PhysicalBone *parent_pb = _get_physical_bone_parent(i); + if (parent_pb != bones[i].physical_bone) { + bones.write[i].cache_parent_physical_bone = parent_pb; + if (bones[i].physical_bone) + bones[i].physical_bone->_on_bone_parent_changed(); + } } } @@ -740,6 +743,8 @@ void Skeleton::_bind_methods() { #endif // _3D_DISABLED + ClassDB::bind_method(D_METHOD("set_bone_ignore_animation", "bone", "ignore"), &Skeleton::set_bone_ignore_animation); + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index 9672acb57a..a8413ebaf2 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -39,6 +39,8 @@ */ #ifndef _3D_DISABLED +typedef int BoneId; + class PhysicalBone; #endif // _3D_DISABLED @@ -96,7 +98,7 @@ class Skeleton : public Spatial { void _make_dirty(); bool dirty; - //bind helpers + // bind helpers Array _get_bound_child_nodes_to_bone(int p_bone) const { Array bound; diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp index 8498dc34c0..980c348c9b 100644 --- a/scene/3d/soft_body.cpp +++ b/scene/3d/soft_body.cpp @@ -98,7 +98,7 @@ SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { point_index = obj_tocopy.point_index; spatial_attachment_path = obj_tocopy.spatial_attachment_path; spatial_attachment = obj_tocopy.spatial_attachment; - vertex_offset_transform = obj_tocopy.vertex_offset_transform; + offset = obj_tocopy.offset; } void SoftBody::_update_pickable() { @@ -133,8 +133,8 @@ bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const { if ("pinned_points" == which) { Array arr_ret; - const int pinned_points_indices_size = pinned_points_indices.size(); - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + const int pinned_points_indices_size = pinned_points.size(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); arr_ret.resize(pinned_points_indices_size); for (int i = 0; i < pinned_points_indices_size; ++i) { @@ -157,13 +157,14 @@ bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const { void SoftBody::_get_property_list(List<PropertyInfo> *p_list) const { - const int pinned_points_indices_size = pinned_points_indices.size(); + const int pinned_points_indices_size = pinned_points.size(); p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "pinned_points")); for (int i = 0; i < pinned_points_indices_size; ++i) { p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index")); p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path")); + p_list->push_back(PropertyInfo(Variant::VECTOR3, "attachments/" + itos(i) + "/offset")); } } @@ -172,17 +173,17 @@ bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) { const int p_indices_size = p_indices.size(); { // Remove the pined points on physics server that will be removed by resize - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); - if (p_indices_size < pinned_points_indices.size()) { - for (int i = pinned_points_indices.size() - 1; i >= p_indices_size; --i) { + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + if (p_indices_size < pinned_points.size()) { + for (int i = pinned_points.size() - 1; i >= p_indices_size; --i) { pin_point(r[i].point_index, false); } } } - pinned_points_indices.resize(p_indices_size); + pinned_points.resize(p_indices_size); - PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); int point_index; for (int i = 0; i < p_indices_size; ++i) { point_index = p_indices.get(i); @@ -197,13 +198,17 @@ bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) { } bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { - if (pinned_points_indices.size() <= p_item) { + if (pinned_points.size() <= p_item) { return false; } if ("spatial_attachment_path" == p_what) { - PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); pin_point(w[p_item].point_index, true, p_value); + _make_cache_dirty(); + } else if ("offset" == p_what) { + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + w[p_item].offset = p_value; } else { return false; } @@ -212,15 +217,17 @@ bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String & } bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { - if (pinned_points_indices.size() <= p_item) { + if (pinned_points.size() <= p_item) { return false; } - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); if ("point_index" == p_what) { r_ret = r[p_item].point_index; } else if ("spatial_attachment_path" == p_what) { r_ret = r[p_item].spatial_attachment_path; + } else if ("offset" == p_what) { + r_ret = r[p_item].offset; } else { return false; } @@ -229,6 +236,8 @@ bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Var } void SoftBody::_changed_callback(Object *p_changed, const char *p_prop) { + update_physics_server(); + _reset_points_offsets(); #ifdef TOOLS_ENABLED if (p_changed == this) { update_configuration_warning(); @@ -240,12 +249,13 @@ void SoftBody::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { - if (Engine::get_singleton()->is_editor_hint()) + if (Engine::get_singleton()->is_editor_hint()) { + add_change_receptor(this); + } RID space = get_world()->get_space(); PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, space); - PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); update_physics_server(); } break; case NOTIFICATION_READY: { @@ -255,20 +265,32 @@ void SoftBody::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { - if (!simulation_started) { - PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); - - _update_cache_pin_points_datas(); - // Submit bone attachment - const int pinned_points_indices_size = pinned_points_indices.size(); - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); - for (int i = 0; i < pinned_points_indices_size; ++i) { - if (!r[i].spatial_attachment) { - // Use soft body position to update the point position - PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (get_global_transform() * r[i].vertex_offset_transform).origin); - } else { - PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (r[i].spatial_attachment->get_global_transform() * r[i].vertex_offset_transform).origin); - } + if (Engine::get_singleton()->is_editor_hint()) { + _reset_points_offsets(); + return; + } + + PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); + + set_notify_transform(false); + // Required to be top level with Transform at center of world in order to modify VisualServer only to support custom Transform + set_as_toplevel(true); + set_transform(Transform()); + set_notify_transform(true); + + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + + if (!simulation_started) + return; + + _update_cache_pin_points_datas(); + // Submit bone attachment + const int pinned_points_indices_size = pinned_points.size(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + for (int i = 0; i < pinned_points_indices_size; ++i) { + if (r[i].spatial_attachment) { + PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, r[i].spatial_attachment->get_global_transform().xform(r[i].offset)); } } } break; @@ -408,8 +430,15 @@ void SoftBody::_draw_soft_mesh() { void SoftBody::update_physics_server() { - if (Engine::get_singleton()->is_editor_hint()) + if (Engine::get_singleton()->is_editor_hint()) { + + if (get_mesh().is_valid()) + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + else + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL); + return; + } if (get_mesh().is_valid()) { @@ -430,6 +459,9 @@ void SoftBody::become_mesh_owner() { if (!mesh_owner) { mesh_owner = true; + Vector<Ref<Material> > copy_materials; + copy_materials.append_array(materials); + ERR_FAIL_COND(!mesh->get_surface_count()); // Get current mesh array and create new mesh array with necessary flag for softbody @@ -443,11 +475,10 @@ void SoftBody::become_mesh_owner() { Ref<ArrayMesh> soft_mesh; soft_mesh.instance(); soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_format); + soft_mesh->surface_set_material(0, mesh->surface_get_material(0)); set_mesh(soft_mesh); - Vector<Ref<Material> > copy_materials; - copy_materials.append_array(materials); for (int i = copy_materials.size() - 1; 0 <= i; --i) { set_surface_material(i, copy_materials[i]); } @@ -506,15 +537,15 @@ const NodePath &SoftBody::get_parent_collision_ignore() const { } void SoftBody::set_pinned_points_indices(PoolVector<SoftBody::PinnedPoint> p_pinned_points_indices) { - pinned_points_indices = p_pinned_points_indices; - PoolVector<PinnedPoint>::Read w = pinned_points_indices.read(); - for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { + pinned_points = p_pinned_points_indices; + PoolVector<PinnedPoint>::Read w = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { pin_point(p_pinned_points_indices[i].point_index, true); } } PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() { - return pinned_points_indices; + return pinned_points; } void SoftBody::add_collision_exception_with(Node *p_node) { @@ -651,6 +682,8 @@ SoftBody::SoftBody() : pinned_points_cache_dirty(true) { PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id()); + //set_notify_transform(true); + set_physics_process_internal(true); } SoftBody::~SoftBody() { @@ -658,36 +691,30 @@ SoftBody::~SoftBody() { void SoftBody::reset_softbody_pin() { PhysicsServer::get_singleton()->soft_body_remove_all_pinned_points(physics_rid); - PoolVector<PinnedPoint>::Read pps = pinned_points_indices.read(); - for (int i = pinned_points_indices.size() - 1; 0 < i; --i) { + PoolVector<PinnedPoint>::Read pps = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 < i; --i) { PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true); } } -void SoftBody::_update_cache_pin_points_datas() { - if (pinned_points_cache_dirty) { - pinned_points_cache_dirty = false; +void SoftBody::_make_cache_dirty() { + pinned_points_cache_dirty = true; +} - PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); - for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { +void SoftBody::_update_cache_pin_points_datas() { + if (!pinned_points_cache_dirty) + return; - if (!w[i].spatial_attachment_path.is_empty()) { - w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path)); - if (w[i].spatial_attachment) { + pinned_points_cache_dirty = false; - Transform point_global_transform(get_global_transform()); - point_global_transform.translate(PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index)); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { - // Local transform relative to spatial attachment node - w[i].vertex_offset_transform = w[i].spatial_attachment->get_global_transform().affine_inverse() * point_global_transform; - continue; - } else { - ERR_PRINTS("The node with path: " + String(w[i].spatial_attachment_path) + " was not found or is not a spatial node."); - } - } - // Local transform relative to Soft body - w[i].vertex_offset_transform.origin = PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index); - w[i].vertex_offset_transform.basis = Basis(); + if (!w[i].spatial_attachment_path.is_empty()) { + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path)); + } + if (!w[i].spatial_attachment) { + ERR_PRINT("Spatial node not defined in the pinned point, Softbody undefined behaviour!"); } } } @@ -699,22 +726,54 @@ void SoftBody::_pin_point_on_physics_server(int p_point_index, bool pin) { void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { SoftBody::PinnedPoint *pinned_point; if (-1 == _get_pinned_point(p_point_index, pinned_point)) { + // Create new PinnedPoint pp; pp.point_index = p_point_index; pp.spatial_attachment_path = p_spatial_attachment_path; - pinned_points_indices.push_back(pp); + + if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) { + pp.spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path)); + pp.offset = (pp.spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pp.point_index)); + } + + pinned_points.push_back(pp); + } else { - // Update + pinned_point->point_index = p_point_index; pinned_point->spatial_attachment_path = p_spatial_attachment_path; + + if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) { + pinned_point->spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path)); + pinned_point->offset = (pinned_point->spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pinned_point->point_index)); + } + } +} + +void SoftBody::_reset_points_offsets() { + + if (!Engine::get_singleton()->is_editor_hint()) + return; + + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + + if (!r[i].spatial_attachment) + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(r[i].spatial_attachment_path)); + + if (!r[i].spatial_attachment) + continue; + + w[i].offset = (r[i].spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, r[i].point_index)); } } void SoftBody::_remove_pinned_point(int p_point_index) { const int id(_has_pinned_point(p_point_index)); if (-1 != id) { - pinned_points_indices.remove(id); + pinned_points.remove(id); } } @@ -724,14 +783,14 @@ int SoftBody::_get_pinned_point(int p_point_index, SoftBody::PinnedPoint *&r_poi r_point = NULL; return -1; } else { - r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points_indices.read()[id]); + r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points.read()[id]); return id; } } int SoftBody::_has_pinned_point(int p_point_index) const { - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); - for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { if (p_point_index == r[i].point_index) { return i; } diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h index f44f337698..cee32b9651 100644 --- a/scene/3d/soft_body.h +++ b/scene/3d/soft_body.h @@ -72,9 +72,7 @@ public: int point_index; NodePath spatial_attachment_path; Spatial *spatial_attachment; // Cache - /// This is the offset from the soft body to point or attachment to point - /// Depend if the spatial_attachment_node is NULL or not - Transform vertex_offset_transform; // Cache + Vector3 offset; PinnedPoint(); PinnedPoint(const PinnedPoint &obj_tocopy); @@ -89,7 +87,7 @@ private: uint32_t collision_mask; uint32_t collision_layer; NodePath parent_collision_ignore; - PoolVector<PinnedPoint> pinned_points_indices; + PoolVector<PinnedPoint> pinned_points; bool simulation_started; bool pinned_points_cache_dirty; @@ -186,9 +184,14 @@ public: private: void reset_softbody_pin(); + + void _make_cache_dirty(); void _update_cache_pin_points_datas(); + void _pin_point_on_physics_server(int p_point_index, bool pin); void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path); + void _reset_points_offsets(); + void _remove_pinned_point(int p_point_index); int _get_pinned_point(int p_point_index, PinnedPoint *&r_point) const; int _has_pinned_point(int p_point_index) const; diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 00541a7d8a..767518dc83 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -105,12 +105,28 @@ uint32_t VisualInstance::get_layer_mask() const { return layers; } +void VisualInstance::set_layer_mask_bit(int p_layer, bool p_enable) { + ERR_FAIL_INDEX(p_layer, 32); + if (p_enable) { + set_layer_mask(layers | (1 << p_layer)); + } else { + set_layer_mask(layers & (~(1 << p_layer))); + } +} + +bool VisualInstance::get_layer_mask_bit(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, 32, false); + return (layers & (1 << p_layer)); +} + void VisualInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_visual_instance_rid"), &VisualInstance::_get_visual_instance_rid); ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance::set_base); ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance::set_layer_mask); ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance::get_layer_mask); + ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance::set_layer_mask_bit); + ClassDB::bind_method(D_METHOD("get_layer_mask_bit", "layer"), &VisualInstance::get_layer_mask_bit); ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance::get_transformed_aabb); diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 8458a343b2..9249bc04ce 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -73,6 +73,9 @@ public: void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; + void set_layer_mask_bit(int p_layer, bool p_enable); + bool get_layer_mask_bit(int p_layer) const; + VisualInstance(); ~VisualInstance(); }; diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp new file mode 100644 index 0000000000..4991cedfab --- /dev/null +++ b/scene/animation/skeleton_ik.cpp @@ -0,0 +1,551 @@ +/*************************************************************************/ +/* skeleton_ik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/** + * @author AndreaCatania + */ + +#include "skeleton_ik.h" + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { + for (int i = childs.size() - 1; 0 <= i; --i) { + if (p_bone_id == childs[i].bone) { + return &childs.write[i]; + } + } + return NULL; +} + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { + const int infant_child_id = childs.size(); + childs.resize(infant_child_id + 1); + childs.write[infant_child_id].bone = p_bone_id; + childs.write[infant_child_id].parent_item = this; + return &childs.write[infant_child_id]; +} + +/// Build a chain that starts from the root to tip +void FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { + + ERR_FAIL_COND(-1 == p_task->root_bone); + + Chain &chain(p_task->chain); + + chain.tips.resize(p_task->end_effectors.size()); + chain.chain_root.bone = p_task->root_bone; + chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); + chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; + chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); + chain.middle_chain_item = NULL; + + // Holds all IDs that are composing a single chain in reverse order + Vector<BoneId> chain_ids; + // This is used to know the chain size + int sub_chain_size; + // Resize only one time in order to fit all joints for performance reason + chain_ids.resize(p_task->skeleton->get_bone_count()); + + for (int x = p_task->end_effectors.size() - 1; 0 <= x; --x) { + + const EndEffector *ee(&p_task->end_effectors[x]); + ERR_FAIL_COND(p_task->root_bone >= ee->tip_bone); + ERR_FAIL_INDEX(ee->tip_bone, p_task->skeleton->get_bone_count()); + + sub_chain_size = 0; + // Picks all IDs that composing a single chain in reverse order (except the root) + BoneId chain_sub_tip(ee->tip_bone); + while (chain_sub_tip > p_task->root_bone) { + + chain_ids.write[sub_chain_size++] = chain_sub_tip; + chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); + } + + BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); + + // Build chain by reading chain ids in reverse order + // For each chain item id will be created a ChainItem if doesn't exists + ChainItem *sub_chain(&chain.chain_root); + for (int i = sub_chain_size - 1; 0 <= i; --i) { + + ChainItem *child_ci(sub_chain->find_child(chain_ids[i])); + if (!child_ci) { + + child_ci = sub_chain->add_child(chain_ids[i]); + + child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); + + child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); + child_ci->current_pos = child_ci->initial_transform.origin; + + if (child_ci->parent_item) { + child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + } + } + + sub_chain = child_ci; + + if (middle_chain_item_id == i) { + chain.middle_chain_item = child_ci; + } + } + + if (!middle_chain_item_id) + chain.middle_chain_item = NULL; + + // Initialize current tip + chain.tips.write[x].chain_item = sub_chain; + chain.tips.write[x].end_effector = ee; + + if (p_force_simple_chain) { + // NOTE: + // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) + // is not yet created. + // Remove this code when this is done + break; + } + } +} + +void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_chain_item) { + + if (!p_chain_item) + return; + + p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); + p_chain_item->current_pos = p_chain_item->initial_transform.origin; + + for (int i = p_chain_item->childs.size() - 1; 0 <= i; --i) { + update_chain(p_sk, &p_chain_item->childs.write[i]); + } +} + +void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { + + real_t distance_to_goal(1e4); + real_t previous_distance_to_goal(0); + int can_solve(p_task->max_iterations); + while (distance_to_goal > p_task->min_distance && Math::abs(previous_distance_to_goal - distance_to_goal) > 0.005 && can_solve) { + previous_distance_to_goal = distance_to_goal; + --can_solve; + + solve_simple_backwards(p_task->chain, p_solve_magnet); + solve_simple_forwards(p_task->chain, p_solve_magnet); + + distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + } +} + +void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + Vector3 goal; + ChainItem *sub_chain_tip; + if (p_solve_magnet) { + goal = r_chain.magnet_position; + sub_chain_tip = r_chain.middle_chain_item; + } else { + goal = r_chain.tips[0].end_effector->goal_transform.origin; + sub_chain_tip = r_chain.tips[0].chain_item; + } + + while (sub_chain_tip) { + sub_chain_tip->current_pos = goal; + + if (sub_chain_tip->parent_item) { + // Not yet in the chain root + // So calculate next goal location + + const Vector3 look_parent((sub_chain_tip->parent_item->current_pos - sub_chain_tip->current_pos).normalized()); + goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length); + + // [TODO] Constraints goes here + } + + sub_chain_tip = sub_chain_tip->parent_item; + } +} + +void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + ChainItem *sub_chain_root(&r_chain.chain_root); + Vector3 origin(r_chain.chain_root.initial_transform.origin); + + while (sub_chain_root) { // Reach the tip + sub_chain_root->current_pos = origin; + + if (!sub_chain_root->childs.empty()) { + + ChainItem &child(sub_chain_root->childs.write[0]); + + // Is not tip + // So calculate next origin location + + // Look child + sub_chain_root->current_ori = (child.current_pos - sub_chain_root->current_pos).normalized(); + origin = sub_chain_root->current_pos + (sub_chain_root->current_ori * child.length); + + // [TODO] Constraints goes here + + if (p_solve_magnet && sub_chain_root == r_chain.middle_chain_item) { + // In case of magnet solving this is the tip + sub_chain_root = NULL; + } else { + sub_chain_root = &child; + } + } else { + + // Is tip + sub_chain_root = NULL; + } + } +} + +FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform) { + + FabrikInverseKinematic::EndEffector ee; + ee.tip_bone = tip_bone; + + Task *task(memnew(Task)); + task->skeleton = p_sk; + task->root_bone = root_bone; + task->end_effectors.push_back(ee); + task->goal_global_transform = goal_transform; + + build_chain(task); + + return task; +} + +void FabrikInverseKinematic::free_task(Task *p_task) { + if (p_task) + memdelete(p_task); +} + +void FabrikInverseKinematic::set_goal(Task *p_task, const Transform &p_goal) { + p_task->goal_global_transform = p_goal; +} + +void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta) { + + if (blending_delta >= 0.99f) { + // Update the end_effector (local transform) without blending + p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; + } else { + + // End effector in local transform + const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); + + // Update the end_effector (local transform) by blending with current pose + p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); + } +} + +void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position) { + + if (blending_delta <= 0.01f) { + return; // Skip solving + } + + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); + + update_chain(p_task->skeleton, &p_task->chain.chain_root); + + if (p_use_magnet && p_task->chain.middle_chain_item) { + p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta); + solve_simple(p_task, true); + } + solve_simple(p_task, false); + + // Assign new bone position. + ChainItem *ci(&p_task->chain.chain_root); + while (ci) { + Transform new_bone_pose(ci->initial_transform); + new_bone_pose.origin = ci->current_pos; + + if (!ci->childs.empty()) { + + /// Rotate basis + const Vector3 initial_ori((ci->childs[0].initial_transform.origin - ci->initial_transform.origin).normalized()); + const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); + + if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { + const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); + new_bone_pose.basis.rotate(rot_axis, rot_angle); + } + } else { + // Set target orientation to tip + new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis; + } + + p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose); + + if (!ci->childs.empty()) + ci = &ci->childs.write[0]; + else + ci = NULL; + } +} + +void SkeletonIK::_validate_property(PropertyInfo &property) const { + + if (property.name == "root_bone" || property.name == "tip_bone") { + + if (skeleton) { + + String names; + for (int i = 0; i < skeleton->get_bone_count(); i++) { + if (i > 0) + names += ","; + names += skeleton->get_bone_name(i); + } + + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = names; + } else { + + property.hint = PROPERTY_HINT_NONE; + property.hint_string = ""; + } + } +} + +void SkeletonIK::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_root_bone", "root_bone"), &SkeletonIK::set_root_bone); + ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonIK::get_root_bone); + + ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK::set_tip_bone); + ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK::get_tip_bone); + + ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK::set_interpolation); + ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK::get_interpolation); + + ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK::set_target_transform); + ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK::get_target_transform); + + ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK::get_target_node); + + ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK::set_use_magnet); + ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK::is_using_magnet); + + ClassDB::bind_method(D_METHOD("set_magnet_position", "local_position"), &SkeletonIK::set_magnet_position); + ClassDB::bind_method(D_METHOD("get_magnet_position"), &SkeletonIK::get_magnet_position); + + ClassDB::bind_method(D_METHOD("get_parent_skeleton"), &SkeletonIK::get_parent_skeleton); + ClassDB::bind_method(D_METHOD("is_running"), &SkeletonIK::is_running); + + ClassDB::bind_method(D_METHOD("set_min_distance", "min_distance"), &SkeletonIK::set_min_distance); + ClassDB::bind_method(D_METHOD("get_min_distance"), &SkeletonIK::get_min_distance); + + ClassDB::bind_method(D_METHOD("set_max_iterations", "iterations"), &SkeletonIK::set_max_iterations); + ClassDB::bind_method(D_METHOD("get_max_iterations"), &SkeletonIK::get_max_iterations); + + ClassDB::bind_method(D_METHOD("start", "one_time"), &SkeletonIK::start, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("stop"), &SkeletonIK::stop); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_bone"), "set_root_bone", "get_root_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tip_bone"), "set_tip_bone", "get_tip_bone"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_distance"), "set_min_distance", "get_min_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); +} + +void SkeletonIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + skeleton = Object::cast_to<Skeleton>(get_parent()); + reload_chain(); + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + + if (target_node_override) + reload_goal(); + + _solve_chain(); + + } break; + case NOTIFICATION_EXIT_TREE: { + reload_chain(); + } break; + } +} + +SkeletonIK::SkeletonIK() : + Node(), + interpolation(1), + skeleton(NULL), + target_node_override(NULL), + use_magnet(false), + min_distance(0.01), + max_iterations(10), + task(NULL) { + + set_process_priority(1); +} + +SkeletonIK::~SkeletonIK() { + FabrikInverseKinematic::free_task(task); + task = NULL; +} + +void SkeletonIK::set_root_bone(const StringName &p_root_bone) { + root_bone = p_root_bone; + reload_chain(); +} + +StringName SkeletonIK::get_root_bone() const { + return root_bone; +} + +void SkeletonIK::set_tip_bone(const StringName &p_tip_bone) { + tip_bone = p_tip_bone; + reload_chain(); +} + +StringName SkeletonIK::get_tip_bone() const { + return tip_bone; +} + +void SkeletonIK::set_interpolation(real_t p_interpolation) { + interpolation = p_interpolation; +} + +real_t SkeletonIK::get_interpolation() const { + return interpolation; +} + +void SkeletonIK::set_target_transform(const Transform &p_target) { + target = p_target; + reload_goal(); +} + +const Transform &SkeletonIK::get_target_transform() const { + return target; +} + +void SkeletonIK::set_target_node(const NodePath &p_node) { + target_node_path_override = p_node; + target_node_override = NULL; + reload_goal(); +} + +NodePath SkeletonIK::get_target_node() { + return target_node_path_override; +} + +void SkeletonIK::set_use_magnet(bool p_use) { + use_magnet = p_use; +} + +bool SkeletonIK::is_using_magnet() const { + return use_magnet; +} + +void SkeletonIK::set_magnet_position(const Vector3 &p_local_position) { + magnet_position = p_local_position; +} + +const Vector3 &SkeletonIK::get_magnet_position() const { + return magnet_position; +} + +void SkeletonIK::set_min_distance(real_t p_min_distance) { + min_distance = p_min_distance; +} + +void SkeletonIK::set_max_iterations(int p_iterations) { + max_iterations = p_iterations; +} + +bool SkeletonIK::is_running() { + return is_processing_internal(); +} + +void SkeletonIK::start(bool p_one_time) { + if (p_one_time) { + set_process_internal(false); + _solve_chain(); + } else { + set_process_internal(true); + } +} + +void SkeletonIK::stop() { + set_process_internal(false); +} + +Transform SkeletonIK::_get_target_transform() { + + if (!target_node_override && !target_node_path_override.is_empty()) + target_node_override = Object::cast_to<Spatial>(get_node(target_node_path_override)); + + if (target_node_override) + return target_node_override->get_global_transform(); + else + return target; +} + +void SkeletonIK::reload_chain() { + + FabrikInverseKinematic::free_task(task); + task = NULL; + + if (!skeleton) + return; + + task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform()); + task->max_iterations = max_iterations; + task->min_distance = min_distance; +} + +void SkeletonIK::reload_goal() { + if (!task) + return; + + FabrikInverseKinematic::set_goal(task, _get_target_transform()); +} + +void SkeletonIK::_solve_chain() { + if (!task) + return; + FabrikInverseKinematic::solve(task, interpolation, use_magnet, magnet_position); +} diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h new file mode 100644 index 0000000000..366c599c01 --- /dev/null +++ b/scene/animation/skeleton_ik.h @@ -0,0 +1,212 @@ +/*************************************************************************/ +/* skeleton_ik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_H +#define SKELETON_IK_H + +/** + * @author AndreaCatania + */ + +#include "core/math/transform.h" +#include "scene/3d/skeleton.h" + +class FabrikInverseKinematic { + + struct EndEffector { + BoneId tip_bone; + Transform goal_transform; + }; + + struct ChainItem { + + Vector<ChainItem> childs; + ChainItem *parent_item; + + // Bone info + BoneId bone; + PhysicalBone *pb; + + real_t length; + /// Positions relative to root bone + Transform initial_transform; + Vector3 current_pos; + // Direction from this bone to child + Vector3 current_ori; + + ChainItem() : + parent_item(NULL), + bone(-1), + pb(NULL), + length(0) {} + + ChainItem *find_child(const BoneId p_bone_id); + ChainItem *add_child(const BoneId p_bone_id); + }; + + struct ChainTip { + ChainItem *chain_item; + const EndEffector *end_effector; + + ChainTip() : + chain_item(NULL), + end_effector(NULL) {} + + ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : + chain_item(p_chain_item), + end_effector(p_end_effector) {} + + ChainTip(const ChainTip &p_other_ct) : + chain_item(p_other_ct.chain_item), + end_effector(p_other_ct.end_effector) {} + }; + + struct Chain { + ChainItem chain_root; + ChainItem *middle_chain_item; + Vector<ChainTip> tips; + Vector3 magnet_position; + }; + +public: + struct Task : public RID_Data { + RID self; + Skeleton *skeleton; + + Chain chain; + + // Settings + real_t min_distance; + int max_iterations; + + // Bone data + BoneId root_bone; + Vector<EndEffector> end_effectors; + + Transform goal_global_transform; + + Task() : + skeleton(NULL), + min_distance(0.01), + max_iterations(10), + root_bone(-1) {} + }; + +private: + /// Init a chain that starts from the root to tip + static void build_chain(Task *p_task, bool p_force_simple_chain = true); + + static void update_chain(const Skeleton *p_sk, ChainItem *p_chain_item); + + static void solve_simple(Task *p_task, bool p_solve_magnet); + /// Special solvers that solve only chains with one end effector + static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet); + +public: + static Task *create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform); + static void free_task(Task *p_task); + // The goal of chain should be always in local space + static void set_goal(Task *p_task, const Transform &p_goal); + static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); + static void solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position); +}; + +class SkeletonIK : public Node { + GDCLASS(SkeletonIK, Node); + + StringName root_bone; + StringName tip_bone; + real_t interpolation; + Transform target; + NodePath target_node_path_override; + bool use_magnet; + Vector3 magnet_position; + + real_t min_distance; + int max_iterations; + + Skeleton *skeleton; + Spatial *target_node_override; + FabrikInverseKinematic::Task *task; + +protected: + virtual void + _validate_property(PropertyInfo &property) const; + + static void _bind_methods(); + virtual void _notification(int p_notification); + +public: + SkeletonIK(); + virtual ~SkeletonIK(); + + void set_root_bone(const StringName &p_root_bone); + StringName get_root_bone() const; + + void set_tip_bone(const StringName &p_tip_bone); + StringName get_tip_bone() const; + + void set_interpolation(real_t p_interpolation); + real_t get_interpolation() const; + + void set_target_transform(const Transform &p_target); + const Transform &get_target_transform() const; + + void set_target_node(const NodePath &p_node); + NodePath get_target_node(); + + void set_use_magnet(bool p_use); + bool is_using_magnet() const; + + void set_magnet_position(const Vector3 &p_constraint); + const Vector3 &get_magnet_position() const; + + void set_min_distance(real_t p_min_distance); + real_t get_min_distance() const { return min_distance; } + + void set_max_iterations(int p_iterations); + int get_max_iterations() const { return max_iterations; } + + Skeleton *get_parent_skeleton() const { return skeleton; } + + bool is_running(); + + void start(bool p_one_time = false); + void stop(); + +private: + Transform _get_target_transform(); + void reload_chain(); + void reload_goal(); + void _solve_chain(); +}; + +#endif // SKELETON_IK_H diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 12aeed1520..18f06eca31 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -663,6 +663,9 @@ void Control::_notification(int p_notification) { bool Control::clips_input() const { + if (get_script_instance()) { + return get_script_instance()->call(SceneStringNames::get_singleton()->_clips_input); + } return false; } bool Control::has_point(const Point2 &p_point) const { @@ -2828,6 +2831,7 @@ void Control::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data"))); BIND_VMETHOD(MethodInfo("drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data"))); BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input")); ADD_GROUP("Anchor", "anchor_"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index cd4ece0950..ab762e19ee 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -530,6 +530,11 @@ void PopupMenu::_notification(int p_what) { } } break; + case MainLoop::NOTIFICATION_WM_FOCUS_OUT: { + + if (hide_on_window_lose_focus) + hide(); + } break; case NOTIFICATION_MOUSE_ENTER: { grab_focus(); @@ -1249,6 +1254,16 @@ float PopupMenu::get_submenu_popup_delay() const { return submenu_timer->get_wait_time(); } +void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) { + + hide_on_window_lose_focus = p_enabled; +} + +bool PopupMenu::is_hide_on_window_lose_focus() const { + + return hide_on_window_lose_focus; +} + String PopupMenu::get_tooltip(const Point2 &p_pos) const { int over = _get_mouse_over(p_pos); @@ -1353,6 +1368,10 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_submenu_popup_delay", "seconds"), &PopupMenu::set_submenu_popup_delay); ClassDB::bind_method(D_METHOD("get_submenu_popup_delay"), &PopupMenu::get_submenu_popup_delay); + + ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus); + ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus); + ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 8ec51c7d3a..a06a17c9fe 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -101,6 +101,7 @@ class PopupMenu : public Popup { bool hide_on_item_selection; bool hide_on_checkable_item_selection; bool hide_on_multistate_item_selection; + bool hide_on_window_lose_focus; Vector2 moved; Array _get_items() const; @@ -207,6 +208,9 @@ public: virtual void popup(const Rect2 &p_bounds = Rect2()); + void set_hide_on_window_lose_focus(bool p_enabled); + bool is_hide_on_window_lose_focus() const; + PopupMenu(); ~PopupMenu(); }; diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 50bd1d867c..2075f7ce70 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -189,7 +189,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { update(); } - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb->is_pressed() && (mb->get_button_index() == BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == BUTTON_RIGHT))) { // clicks Point2 pos(mb->get_position().x, mb->get_position().y); @@ -920,6 +920,14 @@ int Tabs::get_tabs_rearrange_group() const { return tabs_rearrange_group; } +void Tabs::set_select_with_rmb(bool p_enabled) { + select_with_rmb = p_enabled; +} + +bool Tabs::get_select_with_rmb() const { + return select_with_rmb; +} + void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input); @@ -950,6 +958,9 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group); ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group); + ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &Tabs::set_select_with_rmb); + ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &Tabs::get_select_with_rmb); + ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_close", PropertyInfo(Variant::INT, "tab"))); @@ -988,6 +999,8 @@ Tabs::Tabs() { offset = 0; max_drawn_tab = 0; + select_with_rmb = false; + min_width = 0; scrolling_enabled = true; buttons_visible = false; diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index 3b38e7f2cb..e204f4364b 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -83,6 +83,8 @@ private: int rb_hover; bool rb_pressing; + bool select_with_rmb; + int cb_hover; bool cb_pressing; CloseButtonDisplayPolicy cb_displaypolicy; @@ -150,6 +152,9 @@ public: void set_tabs_rearrange_group(int p_group_id); int get_tabs_rearrange_group() const; + void set_select_with_rmb(bool p_enabled); + bool get_select_with_rmb() const; + void ensure_tab_visible(int p_idx); void set_min_width(int p_width); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index b7b26d1c55..6144240328 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -240,7 +240,7 @@ void Node::_propagate_enter_tree() { void Node::_propagate_exit_tree() { - //block while removing children +//block while removing children #ifdef DEBUG_ENABLED @@ -725,6 +725,17 @@ const Map<StringName, MultiplayerAPI::RPCMode>::Element *Node::get_node_rset_mod return data.rpc_properties.find(p_property); } +bool Node::can_process_notification(int p_what) const { + switch (p_what) { + case NOTIFICATION_PHYSICS_PROCESS: return data.physics_process; + case NOTIFICATION_PROCESS: return data.idle_process; + case NOTIFICATION_INTERNAL_PROCESS: return data.idle_process_internal; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: return data.physics_process_internal; + } + + return true; +} + bool Node::can_process() const { ERR_FAIL_COND_V(!is_inside_tree(), false); @@ -1404,11 +1415,6 @@ bool Node::is_greater_than(const Node *p_node) const { return res; } -bool Node::has_priority_higher_than(const Node *p_node) const { - ERR_FAIL_NULL_V(p_node, false); - return data.process_priority > p_node->data.process_priority; -} - void Node::get_owned_by(Node *p_by, List<Node *> *p_owned) { if (data.owner == p_by) diff --git a/scene/main/node.h b/scene/main/node.h index 4b8f584ba7..f3422618ce 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -72,7 +72,7 @@ public: struct ComparatorWithPriority { - bool operator()(const Node *p_a, const Node *p_b) const { return p_b->has_priority_higher_than(p_a) || p_b->is_greater_than(p_a); } + bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; } }; private: @@ -265,7 +265,6 @@ public: bool is_a_parent_of(const Node *p_node) const; bool is_greater_than(const Node *p_node) const; - bool has_priority_higher_than(const Node *p_node) const; NodePath get_path() const; NodePath get_path_to(const Node *p_node) const; @@ -364,6 +363,7 @@ public: void set_pause_mode(PauseMode p_mode); PauseMode get_pause_mode() const; bool can_process() const; + bool can_process_notification(int p_what) const; void request_ready(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 1b2e87dd99..e99f785848 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -951,6 +951,8 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio if (!n->can_process()) continue; + if (!n->can_process_notification(p_notification)) + continue; n->notification(p_notification); //ERR_FAIL_COND(node_count != g.nodes.size()); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index a4fd35304a..ffac166453 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -204,6 +204,7 @@ #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body.h" #include "scene/3d/visibility_notifier.h" +#include "scene/animation/skeleton_ik.h" #include "scene/resources/environment.h" #include "scene/resources/physics_material.h" #endif @@ -361,6 +362,7 @@ void register_scene_types() { ClassDB::register_class<Spatial>(); ClassDB::register_virtual_class<SpatialGizmo>(); ClassDB::register_class<Skeleton>(); + ClassDB::register_class<SkeletonIK>(); ClassDB::register_class<AnimationPlayer>(); ClassDB::register_class<Tween>(); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index b95e0495d9..07783d5f4a 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -31,6 +31,7 @@ #include "packed_scene.h" #include "core/core_string_names.h" +#include "engine.h" #include "io/resource_loader.h" #include "project_settings.h" #include "scene/2d/node_2d.h" @@ -279,7 +280,12 @@ Node *SceneState::instance(GenEditState p_edit_state) const { stray_instances.push_back(node); //can't be added, go to stray list } } else { - node->_set_name_nocheck(snames[n.name]); + if (Engine::get_singleton()->is_editor_hint()) { + //validate name if using editor, to avoid broken + node->set_name(snames[n.name]); + } else { + node->_set_name_nocheck(snames[n.name]); + } } } @@ -389,7 +395,15 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.name = _nm_get_string(p_node->get_name(), name_map); nd.instance = -1; //not instanced by default - nd.index = p_node->get_index(); + + //really convoluted condition, but it basically checks that index is only saved when part of an inherited scene OR the node parent is from the edited scene + if (p_owner->get_scene_inherited_state().is_null() && (p_node == p_owner || (p_node->get_owner() == p_owner && (p_node->get_parent() == p_owner || p_node->get_parent()->get_owner() == p_owner)))) { + //do not save index, because it belongs to saved scene and scene is not inherited + nd.index = -1; + } else { + //part of an inherited scene, or parent is from an instanced scene + nd.index = p_node->get_index(); + } // if this node is part of an instanced scene or sub-instanced scene // we need to get the corresponding instance states. diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index bf765385d0..661606c7ef 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -102,6 +102,8 @@ SceneStringNames::SceneStringNames() { _update_scroll = StaticCString::create("_update_scroll"); _update_xform = StaticCString::create("_update_xform"); + _clips_input = StaticCString::create("_clips_input"); + _proxgroup_add = StaticCString::create("_proxgroup_add"); _proxgroup_remove = StaticCString::create("_proxgroup_remove"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index b88cf7d8d7..817158f9f3 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -127,6 +127,8 @@ public: StringName _update_scroll; StringName _update_xform; + StringName _clips_input; + StringName _proxgroup_add; StringName _proxgroup_remove; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 1783ef4525..d4fb8d98b0 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -2306,24 +2306,54 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const { return false; } -bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types) { +bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message) { if (p_node->type == Node::TYPE_OPERATOR) { OperatorNode *op = static_cast<OperatorNode *>(p_node); + if (op->op == OP_INDEX) { - return _validate_assign(op->arguments[0], p_builtin_types); + return _validate_assign(op->arguments[0], p_builtin_types, r_message); + + } else if (_is_operator_assign(op->op)) { + //chained assignment + return _validate_assign(op->arguments[1], p_builtin_types, r_message); + + } else if (op->op == OP_CALL) { + if (r_message) + *r_message = RTR("Assignment to function."); + return false; } - } - if (p_node->type == Node::TYPE_VARIABLE) { + } else if (p_node->type == Node::TYPE_MEMBER) { + + MemberNode *member = static_cast<MemberNode *>(p_node); + return _validate_assign(member->owner, p_builtin_types, r_message); + + } else if (p_node->type == Node::TYPE_VARIABLE) { VariableNode *var = static_cast<VariableNode *>(p_node); - if (p_builtin_types.has(var->name) && p_builtin_types[var->name].constant) { - return false; //ops not valid + + if (shader->uniforms.has(var->name)) { + if (r_message) + *r_message = RTR("Assignment to uniform."); + return false; + } + + if (shader->varyings.has(var->name) && current_function != String("vertex")) { + if (r_message) + *r_message = RTR("Varyings can only be assigned in vertex function."); + return false; + } + + if (!(p_builtin_types.has(var->name) && p_builtin_types[var->name].constant)) { + return true; } } - return true; + + if (r_message) + *r_message = "Assignment to constant expression."; + return false; } ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) { @@ -3090,10 +3120,14 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ERR_FAIL_V(NULL); } - if (_is_operator_assign(op->op) && !_validate_assign(expression[next_op - 1].node, p_builtin_types)) { + if (_is_operator_assign(op->op)) { - _set_error("Assignment to constant expression."); - return NULL; + String assign_message; + if (!_validate_assign(expression[next_op - 1].node, p_builtin_types, &assign_message)) { + + _set_error(assign_message); + return NULL; + } } if (expression[next_op + 1].is_op) { diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h index 9b84c5f195..b5fd567c07 100644 --- a/servers/visual/shader_language.h +++ b/servers/visual/shader_language.h @@ -617,7 +617,7 @@ private: bool _find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = NULL, IdentifierType *r_type = NULL); bool _is_operator_assign(Operator p_op) const; - bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types); + bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message = NULL); bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = NULL); diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index a1c6e83296..0b4bbffddf 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -669,7 +669,7 @@ void VisualServerCanvas::canvas_item_add_polygon(RID p_item, const Vector<Point2 int color_size = p_colors.size(); int uv_size = p_uvs.size(); ERR_FAIL_COND(color_size != 0 && color_size != 1 && color_size != pointcount); - ERR_FAIL_COND(uv_size != 0 && (uv_size != pointcount || !p_texture.is_valid())); + ERR_FAIL_COND(uv_size != 0 && (uv_size != pointcount)); #endif Vector<int> indices = Geometry::triangulate_polygon(p_points); diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index 6bf3670e5a..c7d33ec43c 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -161,6 +161,7 @@ void VisualServerRaster::set_boot_image(const Ref<Image> &p_image, const Color & VSG::rasterizer->set_boot_image(p_image, p_color, p_scale); } void VisualServerRaster::set_default_clear_color(const Color &p_color) { + VSG::viewport->set_default_clear_color(p_color); } bool VisualServerRaster::has_feature(Features p_feature) const { diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp index dd6bc3cf26..a700fcf11b 100644 --- a/servers/visual/visual_server_viewport.cpp +++ b/servers/visual/visual_server_viewport.cpp @@ -252,7 +252,9 @@ void VisualServerViewport::draw_viewports() { // process all our active interfaces ARVRServer::get_singleton()->_process(); - clear_color = GLOBAL_GET("rendering/environment/default_clear_color"); + if (Engine::get_singleton()->is_editor_hint()) { + clear_color = GLOBAL_GET("rendering/environment/default_clear_color"); + } //sort viewports active_viewports.sort_custom<ViewportSort>(); @@ -660,5 +662,9 @@ bool VisualServerViewport::free(RID p_rid) { return false; } +void VisualServerViewport::set_default_clear_color(const Color &p_color) { + clear_color = p_color; +} + VisualServerViewport::VisualServerViewport() { } diff --git a/servers/visual/visual_server_viewport.h b/servers/visual/visual_server_viewport.h index c0c83c0450..f915e26b81 100644 --- a/servers/visual/visual_server_viewport.h +++ b/servers/visual/visual_server_viewport.h @@ -188,6 +188,7 @@ public: virtual int viewport_get_render_info(RID p_viewport, VS::ViewportRenderInfo p_info); virtual void viewport_set_debug_draw(RID p_viewport, VS::ViewportDebugDraw p_draw); + void set_default_clear_color(const Color &p_color); void draw_viewports(); bool free(RID p_rid); |