diff options
45 files changed, 573 insertions, 266 deletions
diff --git a/core/resource.cpp b/core/resource.cpp index 87c92ca5b1..e0a40b6f3c 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -365,17 +365,38 @@ bool Resource::is_translation_remapped() const {  //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored  void Resource::set_id_for_path(const String &p_path, int p_id) {  	if (p_id == -1) { -		id_for_path.erase(p_path); +		if (ResourceCache::path_cache_lock) { +			ResourceCache::path_cache_lock->write_lock(); +		} +		ResourceCache::resource_path_cache[p_path].erase(get_path()); +		if (ResourceCache::path_cache_lock) { +			ResourceCache::path_cache_lock->write_unlock(); +		}  	} else { -		id_for_path[p_path] = p_id; +		if (ResourceCache::path_cache_lock) { +			ResourceCache::path_cache_lock->write_lock(); +		} +		ResourceCache::resource_path_cache[p_path][get_path()] = p_id; +		if (ResourceCache::path_cache_lock) { +			ResourceCache::path_cache_lock->write_unlock(); +		}  	}  }  int Resource::get_id_for_path(const String &p_path) const { - -	if (id_for_path.has(p_path)) { -		return id_for_path[p_path]; +	if (ResourceCache::path_cache_lock) { +		ResourceCache::path_cache_lock->read_lock(); +	} +	if (ResourceCache::resource_path_cache[p_path].has(get_path())) { +		int result = ResourceCache::resource_path_cache[p_path][get_path()]; +		if (ResourceCache::path_cache_lock) { +			ResourceCache::path_cache_lock->read_unlock(); +		} +		return result;  	} else { +		if (ResourceCache::path_cache_lock) { +			ResourceCache::path_cache_lock->read_unlock(); +		}  		return -1;  	}  } @@ -430,12 +451,21 @@ Resource::~Resource() {  }  HashMap<String, Resource *> ResourceCache::resources; +#ifdef TOOLS_ENABLED +HashMap<String, HashMap<String, int> > ResourceCache::resource_path_cache; +#endif  RWLock *ResourceCache::lock = NULL; +#ifdef TOOLS_ENABLED +RWLock *ResourceCache::path_cache_lock = NULL; +#endif  void ResourceCache::setup() {  	lock = RWLock::create(); +#ifdef TOOLS_ENABLED +	path_cache_lock = RWLock::create(); +#endif  }  void ResourceCache::clear() { diff --git a/core/resource.h b/core/resource.h index 038b4f6278..3e1fe07137 100644 --- a/core/resource.h +++ b/core/resource.h @@ -84,9 +84,7 @@ protected:  	void _set_path(const String &p_path);  	void _take_over_path(const String &p_path); -#ifdef TOOLS_ENABLED -	Map<String, int> id_for_path; -#endif +  public:  	static Node *(*_get_local_scene_func)(); //used by editor @@ -152,6 +150,10 @@ class ResourceCache {  	friend class ResourceLoader; //need the lock  	static RWLock *lock;  	static HashMap<String, Resource *> resources; +#ifdef TOOLS_ENABLED +	static HashMap<String, HashMap<String, int> > resource_path_cache; // each tscn has a set of resource paths and IDs +	static RWLock *path_cache_lock; +#endif // TOOLS_ENABLED  	friend void unregister_core_types();  	static void clear();  	friend void register_core_types(); diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index f6c7a7d1b5..6528704bb5 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -107,6 +107,10 @@  		</member>  		<member name="geometry/parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" default="0">  		</member> +		<member name="geometry/source_geometry_mode" type="int" setter="set_source_geometry_mode" getter="get_source_geometry_mode" default="0"> +		</member> +		<member name="geometry/source_group_name" type="String" setter="set_source_group_name" getter="get_source_group_name"> +		</member>  		<member name="polygon/verts_per_poly" type="float" setter="set_verts_per_poly" getter="get_verts_per_poly" default="6.0">  		</member>  		<member name="region/merge_size" type="float" setter="set_region_merge_size" getter="get_region_merge_size" default="20.0"> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index b206d4a4d2..1f685aab81 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -40,8 +40,9 @@  			<return type="String">  			</return>  			<description> -				The string returned from this method is displayed as a warning in the "Scene Dock" if the script that overrides it is a [code]tool[/code] script. +				The string returned from this method is displayed as a warning in the Scene Dock if the script that overrides it is a [code]tool[/code] script.  				Returning an empty string produces no warning. +				Call [method update_configuration_warning] when the warning needs to be updated for this node.  			</description>  		</method>  		<method name="_input" qualifiers="virtual"> @@ -818,6 +819,14 @@  				Sets whether this is an instance load placeholder. See [InstancePlaceholder].  			</description>  		</method> +		<method name="update_configuration_warning"> +			<return type="void"> +			</return> +			<description> +				Updates the warning displayed for this node in the Scene Dock. +				Use [method _get_configuration_warning] to setup the warning message to display. +			</description> +		</method>  	</methods>  	<members>  		<member name="custom_multiplayer" type="MultiplayerAPI" setter="set_custom_multiplayer" getter="get_custom_multiplayer"> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index faf2ac1ff9..01c8ee4779 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -16,8 +16,13 @@  			</return>  			<argument index="0" name="image" type="Texture">  			</argument> +			<argument index="1" name="width" type="int" default="0"> +			</argument> +			<argument index="2" name="height" type="int" default="0"> +			</argument>  			<description> -				Adds an image's opening and closing tags to the tag stack. +				Adds an image's opening and closing tags to the tag stack, optionally providing a [code]width[/code] and [code]height[/code] to resize the image. +				If [code]width[/code] or [code]height[/code] is set to 0, the image size will be adjusted in order to keep the original aspect ratio.  			</description>  		</method>  		<method name="add_text"> diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 74e8df60f9..7183d34d4f 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -5727,16 +5727,24 @@ void AnimationTrackEditor::_show_imported_anim_warning() const {  }  void AnimationTrackEditor::_select_all_tracks_for_copy() { +  	TreeItem *track = track_copy_select->get_root()->get_children(); +	if (!track) +		return; + +	bool all_selected = true; +	while (track) { +		if (!track->is_checked(0)) +			all_selected = false; + +		track = track->get_next(); +	} + +	track = track_copy_select->get_root()->get_children();  	while (track) { -		track->set_checked(0, selected_all_tracks); +		track->set_checked(0, !all_selected);  		track = track->get_next();  	} -	selected_all_tracks = !selected_all_tracks; -	if (selected_all_tracks) -		select_all_button->set_text(TTR("Select All")); -	else -		select_all_button->set_text(TTR("Select None"));  }  void AnimationTrackEditor::_bind_methods() { @@ -6067,25 +6075,22 @@ AnimationTrackEditor::AnimationTrackEditor() {  	track_copy_dialog = memnew(ConfirmationDialog);  	add_child(track_copy_dialog); -	track_copy_dialog->set_title(TTR("Select tracks to copy:")); +	track_copy_dialog->set_title(TTR("Select Tracks to Copy"));  	track_copy_dialog->get_ok()->set_text(TTR("Copy"));  	VBoxContainer *track_vbox = memnew(VBoxContainer);  	track_copy_dialog->add_child(track_vbox); -	selected_all_tracks = true; +	Button *select_all_button = memnew(Button); +	select_all_button->set_text(TTR("Select All/None")); +	select_all_button->connect("pressed", this, "_select_all_tracks_for_copy"); +	track_vbox->add_child(select_all_button);  	track_copy_select = memnew(Tree);  	track_copy_select->set_h_size_flags(SIZE_EXPAND_FILL);  	track_copy_select->set_v_size_flags(SIZE_EXPAND_FILL);  	track_copy_select->set_hide_root(true);  	track_vbox->add_child(track_copy_select); -	track_copy_options = memnew(HBoxContainer); -	track_vbox->add_child(track_copy_options); -	select_all_button = memnew(Button); -	select_all_button->set_text(TTR("Select All")); -	select_all_button->connect("pressed", this, "_select_all_tracks_for_copy"); -	track_copy_options->add_child(select_all_button);  	track_copy_dialog->connect("confirmed", this, "_edit_menu_pressed", varray(EDIT_COPY_TRACKS_CONFIRM));  	animation_changing_awaiting_update = false;  } diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 830d5b52d3..fd28d8f4d1 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -465,11 +465,8 @@ class AnimationTrackEditor : public VBoxContainer {  	void _selection_changed(); -	bool selected_all_tracks;  	ConfirmationDialog *track_copy_dialog;  	Tree *track_copy_select; -	HBoxContainer *track_copy_options; -	Button *select_all_button;  	struct TrackClipboard {  		NodePath full_path; diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 1e5eabc24e..f5a01dfb04 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -417,6 +417,7 @@ ConnectDialog::ConnectDialog() {  	dst_method = memnew(LineEdit);  	dst_method->set_h_size_flags(SIZE_EXPAND_FILL); +	dst_method->connect("text_entered", this, "_builtin_text_entered");  	dstm_hb->add_child(dst_method);  	advanced = memnew(CheckButton); diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index b6d27d84e0..97c796c707 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -64,7 +64,7 @@  	Ref<DynamicFont> m_name;                                    \  	m_name.instance();                                          \  	m_name->set_size(m_size);                                   \ -	if (CustomFont.is_valid()) {                                \ +	if (CustomFontBold.is_valid()) {                            \  		m_name->set_font_data(CustomFontBold);                  \  		m_name->add_fallback(DefaultFontBold);                  \  	} else {                                                    \ diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 4a1e93eaad..e4f64597b1 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1467,6 +1467,10 @@ void EditorHelp::_notification(int p_what) {  			_update_doc();  		} break; +		case NOTIFICATION_THEME_CHANGED: { + +			_class_desc_resized(); +		} break;  		default: break;  	}  } diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index e978713c34..fbb66744a7 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2399,19 +2399,11 @@ void EditorPropertyResource::_update_menu_items() {  				inheritors_array.push_back(t); -				int id = TYPE_BASE_ID + idx; - -				if (!icon.is_valid() && has_icon(t, "EditorIcons")) { -					icon = get_icon(t, "EditorIcons"); -				} - -				if (icon.is_valid()) { +				if (!icon.is_valid()) +					icon = get_icon(has_icon(t, "EditorIcons") ? t : "Object", "EditorIcons"); -					menu->add_icon_item(icon, vformat(TTR("New %s"), t), id); -				} else { - -					menu->add_item(vformat(TTR("New %s"), t), id); -				} +				int id = TYPE_BASE_ID + idx; +				menu->add_icon_item(icon, vformat(TTR("New %s"), t), id);  				idx++;  			} @@ -2615,14 +2607,6 @@ void EditorPropertyResource::update_property() {  						get_tree()->call_deferred("call_group", "_editor_resource_properties", "_fold_other_editors", this);  					}  					opened_editor = true; -					/* -					Button *open_in_editor = memnew(Button); -					open_in_editor->set_text(TTR("Open Editor")); -					open_in_editor->set_icon(get_icon("Edit", "EditorIcons")); -					sub_inspector_vbox->add_child(open_in_editor); -					open_in_editor->connect("pressed", this, "_open_editor_pressed"); -					open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER); -					*/  				}  			} @@ -2652,7 +2636,7 @@ void EditorPropertyResource::update_property() {  		assign->set_tooltip("");  	} else { -		assign->set_icon(EditorNode::get_singleton()->get_object_icon(res.operator->(), "Node")); +		assign->set_icon(EditorNode::get_singleton()->get_object_icon(res.operator->(), "Object"));  		if (res->get_name() != String()) {  			assign->set_text(res->get_name()); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 3ea59115b0..672844117d 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -559,8 +559,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {  	hints["editors/3d/freelook/freelook_base_speed"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_base_speed", PROPERTY_HINT_RANGE, "0.0, 10, 0.01");  	_initial_set("editors/3d/freelook/freelook_activation_modifier", 0);  	hints["editors/3d/freelook/freelook_activation_modifier"] = PropertyInfo(Variant::INT, "editors/3d/freelook/freelook_activation_modifier", PROPERTY_HINT_ENUM, "None,Shift,Alt,Meta,Ctrl"); -	_initial_set("editors/3d/freelook/freelook_modifier_speed_factor", 3.0); -	hints["editors/3d/freelook/freelook_modifier_speed_factor"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_modifier_speed_factor", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.1");  	_initial_set("editors/3d/freelook/freelook_speed_zoom_link", false);  	// 2D diff --git a/editor/icons/icon_crypto_key.svg b/editor/icons/icon_crypto_key.svg new file mode 100644 index 0000000000..45b53c815d --- /dev/null +++ b/editor/icons/icon_crypto_key.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 4.233 4.233" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2.397.34-.374.373-.375.374v.375l.188.187-1.497 1.496v.375l.374.374h.374l.187-.188.282-.092.092-.282.282-.093.093-.28.094-.28.28-.095.187-.187.187.187h.374l.375-.375.373-.373.001-.374-1.122-1.122zm.374.858a.264.264 0 1 1 .002.528.264.264 0 0 1 -.002-.528z" fill="#e0e0e0"/></svg>
\ No newline at end of file diff --git a/editor/icons/icon_x509_certificate.svg b/editor/icons/icon_x509_certificate.svg new file mode 100644 index 0000000000..e175fa3234 --- /dev/null +++ b/editor/icons/icon_x509_certificate.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 4.233 4.233" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.967.263-3.704.001v2.646h1.427a.993.993 0 0 1 -.022-.096.993.993 0 0 1 -.012-.099.993.993 0 0 1 -.002-.07.993.993 0 0 1 .005-.1.993.993 0 0 1 .014-.097.993.993 0 0 1 .025-.096.993.993 0 0 1 .034-.093.993.993 0 0 1 .043-.09.993.993 0 0 1 .052-.085.993.993 0 0 1 .06-.079.993.993 0 0 1 .068-.072.993.993 0 0 1 .074-.066.993.993 0 0 1 .08-.057.993.993 0 0 1 .087-.05.993.993 0 0 1 .09-.04.993.993 0 0 1 .095-.031.993.993 0 0 1 .096-.022.993.993 0 0 1 .099-.012.993.993 0 0 1 .07-.003.993.993 0 0 1 .099.006.993.993 0 0 1 .098.014.993.993 0 0 1 .096.025.993.993 0 0 1 .094.034.993.993 0 0 1 .089.043.993.993 0 0 1 .084.052.993.993 0 0 1 .08.06.993.993 0 0 1 .072.068.993.993 0 0 1 .065.074.993.993 0 0 1 .058.08.993.993 0 0 1 .05.087.993.993 0 0 1 .04.09.993.993 0 0 1 .031.095.993.993 0 0 1 .022.096.993.993 0 0 1 .012.099.993.993 0 0 1 .002.07.993.993 0 0 1 -.004.1.993.993 0 0 1 -.015.097.993.993 0 0 1 -.017.068h.365z" fill="#e0e0e0"/><g fill="#ff8484"><path d="m2.116 3.175v.793l.53-.253.529.253v-.793z"/><circle cx="2.646" cy="2.645" r=".794"/></g></svg>
\ No newline at end of file diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp index d6df3bd369..7ba1796600 100644 --- a/editor/node_dock.cpp +++ b/editor/node_dock.cpp @@ -129,6 +129,7 @@ NodeDock::NodeDock() {  	select_a_node = memnew(Label);  	select_a_node->set_text(TTR("Select a single node to edit its signals and groups.")); +	select_a_node->set_custom_minimum_size(Size2(100 * EDSCALE, 0));  	select_a_node->set_v_size_flags(SIZE_EXPAND_FILL);  	select_a_node->set_valign(Label::VALIGN_CENTER);  	select_a_node->set_align(Label::ALIGN_CENTER); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 095350d0e1..1d8fd38858 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2049,12 +2049,11 @@ void SpatialEditorViewport::_update_freelook(real_t delta) {  		return;  	} -	Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1)); -	Vector3 right = camera->get_transform().basis.xform(Vector3(1, 0, 0)); -	Vector3 up = camera->get_transform().basis.xform(Vector3(0, 1, 0)); +	const Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1)); +	const Vector3 right = camera->get_transform().basis.xform(Vector3(1, 0, 0)); +	const Vector3 up = camera->get_transform().basis.xform(Vector3(0, 1, 0));  	Vector3 direction; -	bool speed_modifier = false;  	if (is_shortcut_pressed("spatial_editor/freelook_left")) {  		direction -= right; @@ -2074,17 +2073,17 @@ void SpatialEditorViewport::_update_freelook(real_t delta) {  	if (is_shortcut_pressed("spatial_editor/freelook_down")) {  		direction -= up;  	} -	if (is_shortcut_pressed("spatial_editor/freelook_speed_modifier")) { -		speed_modifier = true; -	}  	real_t speed = freelook_speed; -	if (speed_modifier) { -		real_t modifier_speed_factor = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_modifier_speed_factor"); -		speed *= modifier_speed_factor; + +	if (is_shortcut_pressed("spatial_editor/freelook_speed_modifier")) { +		speed *= 3.0; +	} +	if (is_shortcut_pressed("spatial_editor/freelook_slow_modifier")) { +		speed *= 0.333333;  	} -	Vector3 motion = direction * speed * delta; +	const Vector3 motion = direction * speed * delta;  	cursor.pos += motion;  	cursor.eye_pos += motion;  } @@ -3588,6 +3587,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed  	ED_SHORTCUT("spatial_editor/freelook_up", TTR("Freelook Up"), KEY_E);  	ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_Q);  	ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); +	ED_SHORTCUT("spatial_editor/freelook_slow_modifier", TTR("Freelook Slow Modifier"), KEY_ALT);  	preview_camera = memnew(CheckBox);  	preview_camera->set_text(TTR("Preview")); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 9b3bf8ad5b..1d82735328 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1944,11 +1944,10 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context  								ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);  								r_result.insert(option.display, option);  							} -						} else { -							for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) { -								ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER); -								r_result.insert(option.display, option); -							} +						} +						for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) { +							ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER); +							r_result.insert(option.display, option);  						}  					}  					if (!p_only_functions) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index cf326bef36..da6a52ff0d 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -858,11 +858,23 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s  				if (current_function) {  					int arg_idx = current_function->arguments.find(identifier);  					if (arg_idx != -1) { -						if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { -							// Assignment is not really usage -							current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] - 1; -						} else { -							current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1; +						switch (tokenizer->get_token()) { +							case GDScriptTokenizer::TK_OP_ASSIGN_ADD: +							case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND: +							case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR: +							case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR: +							case GDScriptTokenizer::TK_OP_ASSIGN_DIV: +							case GDScriptTokenizer::TK_OP_ASSIGN_MOD: +							case GDScriptTokenizer::TK_OP_ASSIGN_MUL: +							case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT: +							case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT: +							case GDScriptTokenizer::TK_OP_ASSIGN_SUB: +							case GDScriptTokenizer::TK_OP_ASSIGN: { +								// Assignment is not really usage +							} break; +							default: { +								current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1; +							}  						}  					}  				} @@ -6109,12 +6121,18 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data  			break;  	} +	// Some classes are prefixed with `_` internally +	if (!ClassDB::class_exists(expr_native)) { +		expr_native = "_" + expr_native; +	} +  	switch (p_container.kind) {  		case DataType::NATIVE: {  			if (p_container.is_meta_type) {  				return ClassDB::is_parent_class(expr_native, GDScriptNativeClass::get_class_static());  			} else { -				return ClassDB::is_parent_class(expr_native, p_container.native_type); +				StringName container_native = ClassDB::class_exists(p_container.native_type) ? p_container.native_type : StringName("_" + p_container.native_type); +				return ClassDB::is_parent_class(expr_native, container_native);  			}  		} break;  		case DataType::SCRIPT: diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 8b20b0ff48..4730e9b6bc 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -849,12 +849,8 @@ void GDScriptTokenizerText::_advance() {  										_make_error("Unterminated String");  										return;  									} -									if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { -										_make_error("Malformed hex constant in string"); -										return; -									} -									CharType v; +									CharType v = 0;  									if (c >= '0' && c <= '9') {  										v = c - '0';  									} else if (c >= 'a' && c <= 'f') { @@ -864,8 +860,8 @@ void GDScriptTokenizerText::_advance() {  										v = c - 'A';  										v += 10;  									} else { -										ERR_PRINT("BUG"); -										v = 0; +										_make_error("Malformed hex constant in string"); +										return;  									}  									res <<= 4; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index c97524a54d..86a8df652c 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -354,6 +354,11 @@ void GridMapEditor::_set_selection(bool p_active, const Vector3 &p_begin, const  	selection.current = p_end;  	_update_selection_transform(); + +	options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_CLEAR), !selection.active); +	options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_CUT), !selection.active); +	options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_DUPLICATE), !selection.active); +	options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_FILL), !selection.active);  }  bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click) { @@ -1465,7 +1470,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {  		}  	} -	selection.active = false; +	_set_selection(false);  	updating = false;  	accumulated_floor_delta = 0.0;  } diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp index c5b60f2dca..320591cf7c 100644 --- a/modules/recast/navigation_mesh_generator.cpp +++ b/modules/recast/navigation_mesh_generator.cpp @@ -131,7 +131,7 @@ void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces,  	}  } -void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask) { +void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {  	if (Object::cast_to<MeshInstance>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { @@ -263,8 +263,10 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran  		p_accumulated_transform = p_accumulated_transform * spatial->get_transform();  	} -	for (int i = 0; i < p_node->get_child_count(); i++) { -		_parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask); +	if (p_recurse_children) { +		for (int i = 0; i < p_node->get_child_count(); i++) { +			_parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask, p_recurse_children); +		}  	}  } @@ -439,7 +441,21 @@ void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p  	Vector<float> vertices;  	Vector<int> indices; -	_parse_geometry(Object::cast_to<Spatial>(p_node)->get_transform().affine_inverse(), p_node, vertices, indices, p_nav_mesh->get_parsed_geometry_type(), p_nav_mesh->get_collision_mask()); +	List<Node *> parse_nodes; + +	if (p_nav_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_NAVMESH_CHILDREN) { +		parse_nodes.push_back(p_node); +	} else { +		p_node->get_tree()->get_nodes_in_group(p_nav_mesh->get_source_group_name(), &parse_nodes); +	} + +	Transform navmesh_xform = Object::cast_to<Spatial>(p_node)->get_transform().affine_inverse(); +	for (const List<Node *>::Element *E = parse_nodes.front(); E; E = E->next()) { +		int geometry_type = p_nav_mesh->get_parsed_geometry_type(); +		uint32_t collision_mask = p_nav_mesh->get_collision_mask(); +		bool recurse_children = p_nav_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT; +		_parse_geometry(navmesh_xform, E->get(), vertices, indices, geometry_type, collision_mask, recurse_children); +	}  	if (vertices.size() > 0 && indices.size() > 0) { diff --git a/modules/recast/navigation_mesh_generator.h b/modules/recast/navigation_mesh_generator.h index 30a6e3c835..f19622a4a9 100644 --- a/modules/recast/navigation_mesh_generator.h +++ b/modules/recast/navigation_mesh_generator.h @@ -47,7 +47,7 @@ protected:  	static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies);  	static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);  	static void _add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices); -	static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask); +	static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);  	static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);  	static void _build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep, diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index fad766ea5d..983db60d5e 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -64,9 +64,15 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int  }  } -Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocols, const Vector<String> p_custom_headers) { +Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) { + +	String proto_string; +	for (int i = 0; i < p_protocols.size(); i++) { +		if (i != 0) +			proto_string += ","; +		proto_string += p_protocols[i]; +	} -	String proto_string = p_protocols.join(",");  	String str = "ws://";  	if (p_custom_headers.size()) { diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 2d35f7f0f6..67705891b2 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -50,7 +50,7 @@ public:  	bool _is_connecting;  	Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); -	Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocol = PoolVector<String>(), const Dictionary p_custom_headers = Dictionary()); +	Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>());  	Ref<WebSocketPeer> get_peer(int p_peer_id) const;  	void disconnect_from_host(int p_code = 1000, String p_reason = "");  	IP_Address get_connected_host() const; diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index c4bb459ad0..9a6a30d613 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -33,7 +33,7 @@  #include "emws_server.h"  #include "core/os/os.h" -Error EMWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { +Error EMWSServer::listen(int p_port, Vector<String> p_protocols, bool gd_mp_api) {  	return FAILED;  } diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index a5e5b4090e..e8da8c26b4 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -43,7 +43,7 @@ class EMWSServer : public WebSocketServer {  public:  	Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); -	Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); +	Error listen(int p_port, Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false);  	void stop();  	bool is_listening() const;  	bool has_peer(int p_id) const; diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index a422f65cfc..ad70c9c0e1 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -181,8 +181,12 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,  	_connection = _tcp;  	_use_ssl = p_ssl;  	_host = p_host; -	_protocols.clear(); -	_protocols.append_array(p_protocols); +	// Strip edges from protocols. +	_protocols.resize(p_protocols.size()); +	String *pw = _protocols.ptrw(); +	for (int i = 0; i < p_protocols.size(); i++) { +		pw[i] = p_protocols[i].strip_edges(); +	}  	_key = WSLPeer::generate_key();  	// TODO custom extra headers (allow overriding this too?) diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index 993dceafb9..2181775b99 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -80,11 +80,12 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) {  	if (headers.has("sec-websocket-protocol")) {  		Vector<String> protos = headers["sec-websocket-protocol"].split(",");  		for (int i = 0; i < protos.size(); i++) { +			String proto = protos[i].strip_edges();  			// Check if we have the given protocol  			for (int j = 0; j < p_protocols.size(); j++) { -				if (protos[i] != p_protocols[j]) +				if (proto != p_protocols[j])  					continue; -				protocol = protos[i]; +				protocol = proto;  				break;  			}  			// Found a protocol @@ -158,7 +159,12 @@ Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp  	ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE);  	_is_multiplayer = gd_mp_api; -	_protocols.append_array(p_protocols); +	// Strip edges from protocols. +	_protocols.resize(p_protocols.size()); +	String *pw = _protocols.ptrw(); +	for (int i = 0; i < p_protocols.size(); i++) { +		pw[i] = p_protocols[i].strip_edges(); +	}  	_server->listen(p_port);  	return OK; diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 739aa285bf..247f006a69 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -56,11 +56,14 @@ import android.hardware.SensorManager;  import android.os.Build;  import android.os.Bundle;  import android.os.Environment; +import android.os.Handler; +import android.os.Looper;  import android.os.Messenger;  import android.os.VibrationEffect;  import android.os.Vibrator;  import android.provider.Settings.Secure;  import android.support.annotation.Keep; +import android.support.annotation.Nullable;  import android.support.v4.content.ContextCompat;  import android.view.Display;  import android.view.KeyEvent; @@ -126,6 +129,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo  	private boolean activityResumed;  	private int mState; +	// Used to dispatch events to the main thread. +	private final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); +  	static private Intent mCurrentIntent;  	@Override @@ -187,6 +193,20 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo  			Godot.singletons[Godot.singleton_count++] = this;  		} +		/** +		 * Invoked once during the Godot Android initialization process after creation of the +		 * {@link GodotView} view. +		 * <p> +		 * This method should be overridden by descendants of this class that would like to add +		 * their view/layout to the Godot view hierarchy. +		 * +		 * @return the view to be included; null if no views should be included. +		 */ +		@Nullable +		protected View onMainCreateView(Activity activity) { +			return null; +		} +  		protected void onMainActivityResult(int requestCode, int resultCode, Intent data) {  		} @@ -306,6 +326,20 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo  			public void run() {  				GodotLib.setup(current_command_line);  				setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); + +				// The Godot Android plugins are setup on completion of GodotLib.setup +				mainThreadHandler.post(new Runnable() { +					@Override +					public void run() { +						// Include the non-null views returned in the Godot view hierarchy. +						for (int i = 0; i < singleton_count; i++) { +							View view = singletons[i].onMainCreateView(Godot.this); +							if (view != null) { +								layout.addView(view); +							} +						} +					} +				});  			}  		});  	} diff --git a/platform/osx/dir_access_osx.mm b/platform/osx/dir_access_osx.mm index ada142005b..75f50aaa28 100644 --- a/platform/osx/dir_access_osx.mm +++ b/platform/osx/dir_access_osx.mm @@ -48,18 +48,25 @@ String DirAccessOSX::fix_unicode_name(const char *p_name) const {  }  int DirAccessOSX::get_drive_count() { -	NSArray *vols = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths]; +	NSArray *res_keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsSystemImmutableKey, nil]; +	NSArray *vols = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:res_keys options:NSVolumeEnumerationSkipHiddenVolumes]; +  	return [vols count];  }  String DirAccessOSX::get_drive(int p_drive) { -	NSArray *vols = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths]; +	NSArray *res_keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsSystemImmutableKey, nil]; +	NSArray *vols = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:res_keys options:NSVolumeEnumerationSkipHiddenVolumes];  	int count = [vols count];  	ERR_FAIL_INDEX_V(p_drive, count, ""); -	NSString *path = vols[p_drive]; -	return String([path UTF8String]); +	String volname; +	NSString *path = [vols[p_drive] path]; + +	volname.parse_utf8([path UTF8String]); + +	return volname;  }  #endif //posix_enabled diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 85c423964b..9da41914d7 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -37,6 +37,9 @@  void CPUParticles2D::set_emitting(bool p_emitting) { +	if (emitting == p_emitting) +		return; +  	emitting = p_emitting;  	if (emitting)  		set_process_internal(true); @@ -535,6 +538,74 @@ static float rand_from_seed(uint32_t &seed) {  	return float(seed % uint32_t(65536)) / 65535.0;  } +void CPUParticles2D::_update_internal() { + +	if (particles.size() == 0 || !is_visible_in_tree()) { +		_set_redraw(false); +		return; +	} + +	float delta = get_process_delta_time(); +	if (emitting) { +		inactive_time = 0; +	} else { +		inactive_time += delta; +		if (inactive_time > lifetime * 1.2) { +			set_process_internal(false); +			_set_redraw(false); + +			//reset variables +			time = 0; +			inactive_time = 0; +			frame_remainder = 0; +			cycle = 0; +			return; +		} +	} +	_set_redraw(true); + +	if (time == 0 && pre_process_time > 0.0) { + +		float frame_time; +		if (fixed_fps > 0) +			frame_time = 1.0 / fixed_fps; +		else +			frame_time = 1.0 / 30.0; + +		float todo = pre_process_time; + +		while (todo >= 0) { +			_particles_process(frame_time); +			todo -= frame_time; +		} +	} + +	if (fixed_fps > 0) { +		float frame_time = 1.0 / fixed_fps; +		float decr = frame_time; + +		float ldelta = delta; +		if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 +			ldelta = 0.1; +		} else if (ldelta <= 0.0) { //unlikely but.. +			ldelta = 0.001; +		} +		float todo = frame_remainder + ldelta; + +		while (todo >= frame_time) { +			_particles_process(frame_time); +			todo -= decr; +		} + +		frame_remainder = todo; + +	} else { +		_particles_process(delta); +	} + +	_update_particle_data_buffer(); +} +  void CPUParticles2D::_particles_process(float p_delta) {  	p_delta *= speed_scale; @@ -1000,6 +1071,10 @@ void CPUParticles2D::_notification(int p_what) {  	}  	if (p_what == NOTIFICATION_DRAW) { +		// first update before rendering to avoid one frame delay after emitting starts +		if (emitting && (time == 0)) +			_update_internal(); +  		if (!redraw)  			return; // don't add to render list @@ -1017,71 +1092,7 @@ void CPUParticles2D::_notification(int p_what) {  	}  	if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - -		if (particles.size() == 0 || !is_visible_in_tree()) { -			_set_redraw(false); -			return; -		} - -		float delta = get_process_delta_time(); -		if (emitting) { -			inactive_time = 0; -		} else { -			inactive_time += delta; -			if (inactive_time > lifetime * 1.2) { -				set_process_internal(false); -				_set_redraw(false); - -				//reset variables -				time = 0; -				inactive_time = 0; -				frame_remainder = 0; -				cycle = 0; -				return; -			} -		} -		_set_redraw(true); - -		if (time == 0 && pre_process_time > 0.0) { - -			float frame_time; -			if (fixed_fps > 0) -				frame_time = 1.0 / fixed_fps; -			else -				frame_time = 1.0 / 30.0; - -			float todo = pre_process_time; - -			while (todo >= 0) { -				_particles_process(frame_time); -				todo -= frame_time; -			} -		} - -		if (fixed_fps > 0) { -			float frame_time = 1.0 / fixed_fps; -			float decr = frame_time; - -			float ldelta = delta; -			if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 -				ldelta = 0.1; -			} else if (ldelta <= 0.0) { //unlikely but.. -				ldelta = 0.001; -			} -			float todo = frame_remainder + ldelta; - -			while (todo >= frame_time) { -				_particles_process(frame_time); -				todo -= decr; -			} - -			frame_remainder = todo; - -		} else { -			_particles_process(delta); -		} - -		_update_particle_data_buffer(); +		_update_internal();  	}  	if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index da668664b9..47b4568dd4 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -174,6 +174,7 @@ private:  	Vector2 gravity; +	void _update_internal();  	void _particles_process(float p_delta);  	void _update_particle_data_buffer(); diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index d8156a0afe..847d08b025 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -37,8 +37,8 @@  void Joint2D::_update_joint(bool p_only_free) {  	if (joint.is_valid()) { -		if (ba.is_valid() && bb.is_valid()) -			Physics2DServer::get_singleton()->body_remove_collision_exception(ba, bb); +		if (ba.is_valid() && bb.is_valid() && exclude_from_collision) +			Physics2DServer::get_singleton()->joint_disable_collisions_between_bodies(joint, false);  		Physics2DServer::get_singleton()->free(joint);  		joint = RID(); @@ -61,8 +61,6 @@ void Joint2D::_update_joint(bool p_only_free) {  	if (!body_a || !body_b)  		return; -	SWAP(body_a, body_b); -  	joint = _configure_joint(body_a, body_b);  	if (!joint.is_valid()) @@ -133,6 +131,8 @@ void Joint2D::set_exclude_nodes_from_collision(bool p_enable) {  	if (exclude_from_collision == p_enable)  		return; + +	_update_joint(true);  	exclude_from_collision = p_enable;  	_update_joint();  } diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index 93ff60bc4e..5c895ecf22 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -46,9 +46,17 @@ PoolVector<Face3> CPUParticles::get_faces(uint32_t p_usage_flags) const {  void CPUParticles::set_emitting(bool p_emitting) { +	if (emitting == p_emitting) +		return; +  	emitting = p_emitting; -	if (emitting) +	if (emitting) {  		set_process_internal(true); + +		// first update before rendering to avoid one frame delay after emitting starts +		if (time == 0) +			_update_internal(); +	}  }  void CPUParticles::set_amount(int p_amount) { @@ -508,6 +516,81 @@ static float rand_from_seed(uint32_t &seed) {  	return float(seed % uint32_t(65536)) / 65535.0;  } +void CPUParticles::_update_internal() { + +	if (particles.size() == 0 || !is_visible_in_tree()) { +		_set_redraw(false); +		return; +	} + +	float delta = get_process_delta_time(); +	if (emitting) { +		inactive_time = 0; +	} else { +		inactive_time += delta; +		if (inactive_time > lifetime * 1.2) { +			set_process_internal(false); +			_set_redraw(false); + +			//reset variables +			time = 0; +			inactive_time = 0; +			frame_remainder = 0; +			cycle = 0; +			return; +		} +	} +	_set_redraw(true); + +	bool processed = false; + +	if (time == 0 && pre_process_time > 0.0) { + +		float frame_time; +		if (fixed_fps > 0) +			frame_time = 1.0 / fixed_fps; +		else +			frame_time = 1.0 / 30.0; + +		float todo = pre_process_time; + +		while (todo >= 0) { +			_particles_process(frame_time); +			processed = true; +			todo -= frame_time; +		} +	} + +	if (fixed_fps > 0) { +		float frame_time = 1.0 / fixed_fps; +		float decr = frame_time; + +		float ldelta = delta; +		if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 +			ldelta = 0.1; +		} else if (ldelta <= 0.0) { //unlikely but.. +			ldelta = 0.001; +		} +		float todo = frame_remainder + ldelta; + +		while (todo >= frame_time) { +			_particles_process(frame_time); +			processed = true; +			todo -= decr; +		} + +		frame_remainder = todo; + +	} else { +		_particles_process(delta); +		processed = true; +	} + +	if (processed) { +		_update_particle_data_buffer(); +	} +} +  void CPUParticles::_particles_process(float p_delta) {  	p_delta *= speed_scale; @@ -1068,85 +1151,24 @@ void CPUParticles::_notification(int p_what) {  	if (p_what == NOTIFICATION_ENTER_TREE) {  		set_process_internal(emitting); + +		// first update before rendering to avoid one frame delay after emitting starts +		if (emitting && (time == 0)) +			_update_internal();  	}  	if (p_what == NOTIFICATION_EXIT_TREE) {  		_set_redraw(false);  	} -	if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - -		if (particles.size() == 0 || !is_visible_in_tree()) { -			_set_redraw(false); -			return; -		} - -		float delta = get_process_delta_time(); -		if (emitting) { -			inactive_time = 0; -		} else { -			inactive_time += delta; -			if (inactive_time > lifetime * 1.2) { -				set_process_internal(false); -				_set_redraw(false); - -				//reset variables -				time = 0; -				inactive_time = 0; -				frame_remainder = 0; -				cycle = 0; -				return; -			} -		} -		_set_redraw(true); - -		bool processed = false; - -		if (time == 0 && pre_process_time > 0.0) { - -			float frame_time; -			if (fixed_fps > 0) -				frame_time = 1.0 / fixed_fps; -			else -				frame_time = 1.0 / 30.0; - -			float todo = pre_process_time; - -			while (todo >= 0) { -				_particles_process(frame_time); -				processed = true; -				todo -= frame_time; -			} -		} - -		if (fixed_fps > 0) { -			float frame_time = 1.0 / fixed_fps; -			float decr = frame_time; - -			float ldelta = delta; -			if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 -				ldelta = 0.1; -			} else if (ldelta <= 0.0) { //unlikely but.. -				ldelta = 0.001; -			} -			float todo = frame_remainder + ldelta; - -			while (todo >= frame_time) { -				_particles_process(frame_time); -				processed = true; -				todo -= decr; -			} - -			frame_remainder = todo; - -		} else { -			_particles_process(delta); -			processed = true; -		} +	if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { +		// first update before rendering to avoid one frame delay after emitting starts +		if (emitting && (time == 0)) +			_update_internal(); +	} -		if (processed) { -			_update_particle_data_buffer(); -		} +	if (p_what == NOTIFICATION_INTERNAL_PROCESS) { +		_update_internal();  	}  	if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h index 66b37f359a..635265be7f 100644 --- a/scene/3d/cpu_particles.h +++ b/scene/3d/cpu_particles.h @@ -173,6 +173,7 @@ private:  	Vector3 gravity; +	void _update_internal();  	void _particles_process(float p_delta);  	void _update_particle_data_buffer(); diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index f82543b789..496dc4b411 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -108,6 +108,24 @@ bool NavigationMesh::get_collision_mask_bit(int p_bit) const {  	return get_collision_mask() & (1 << p_bit);  } +void NavigationMesh::set_source_geometry_mode(int p_geometry_mode) { +	ERR_FAIL_INDEX(p_geometry_mode, SOURCE_GEOMETRY_MAX); +	source_geometry_mode = static_cast<SourceGeometryMode>(p_geometry_mode); +	_change_notify(); +} + +int NavigationMesh::get_source_geometry_mode() const { +	return source_geometry_mode; +} + +void NavigationMesh::set_source_group_name(StringName p_group_name) { +	source_group_name = p_group_name; +} + +StringName NavigationMesh::get_source_group_name() const { +	return source_group_name; +} +  void NavigationMesh::set_cell_size(float p_value) {  	cell_size = p_value;  } @@ -387,6 +405,12 @@ void NavigationMesh::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit);  	ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit); +	ClassDB::bind_method(D_METHOD("set_source_geometry_mode", "mask"), &NavigationMesh::set_source_geometry_mode); +	ClassDB::bind_method(D_METHOD("get_source_geometry_mode"), &NavigationMesh::get_source_geometry_mode); + +	ClassDB::bind_method(D_METHOD("set_source_group_name", "mask"), &NavigationMesh::set_source_group_name); +	ClassDB::bind_method(D_METHOD("get_source_group_name"), &NavigationMesh::get_source_group_name); +  	ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationMesh::set_cell_size);  	ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationMesh::get_cell_size); @@ -462,6 +486,8 @@ void NavigationMesh::_bind_methods() {  	ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type");  	ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type");  	ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); +	ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/source_geometry_mode", PROPERTY_HINT_ENUM, "Navmesh Children, Group With Children, Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); +	ADD_PROPERTY(PropertyInfo(Variant::STRING, "geometry/source_group_name"), "set_source_group_name", "get_source_group_name");  	ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/size", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_size", "get_cell_size");  	ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/height", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_height", "get_cell_height"); @@ -489,6 +515,13 @@ void NavigationMesh::_validate_property(PropertyInfo &property) const {  			return;  		}  	} + +	if (property.name == "geometry/source_group_name") { +		if (source_geometry_mode == SOURCE_GEOMETRY_NAVMESH_CHILDREN) { +			property.usage = 0; +			return; +		} +	}  }  NavigationMesh::NavigationMesh() { @@ -509,6 +542,8 @@ NavigationMesh::NavigationMesh() {  	partition_type = SAMPLE_PARTITION_WATERSHED;  	parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES;  	collision_mask = 0xFFFFFFFF; +	source_geometry_mode = SOURCE_GEOMETRY_NAVMESH_CHILDREN; +	source_group_name = "navmesh";  	filter_low_hanging_obstacles = false;  	filter_ledge_spans = false;  	filter_walkable_low_height_spans = false; diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h index 5fbf3998ff..8467f80f0e 100644 --- a/scene/3d/navigation_mesh.h +++ b/scene/3d/navigation_mesh.h @@ -77,6 +77,13 @@ public:  		PARSED_GEOMETRY_MAX  	}; +	enum SourceGeometryMode { +		SOURCE_GEOMETRY_NAVMESH_CHILDREN = 0, +		SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN, +		SOURCE_GEOMETRY_GROUPS_EXPLICIT, +		SOURCE_GEOMETRY_MAX +	}; +  protected:  	float cell_size;  	float cell_height; @@ -96,6 +103,9 @@ protected:  	ParsedGeometryType parsed_geometry_type;  	uint32_t collision_mask; +	SourceGeometryMode source_geometry_mode; +	StringName source_group_name; +  	bool filter_low_hanging_obstacles;  	bool filter_ledge_spans;  	bool filter_walkable_low_height_spans; @@ -114,6 +124,12 @@ public:  	void set_collision_mask_bit(int p_bit, bool p_value);  	bool get_collision_mask_bit(int p_bit) const; +	void set_source_geometry_mode(int p_source_mode); +	int get_source_geometry_mode() const; + +	void set_source_group_name(StringName p_group_name); +	StringName get_source_group_name() const; +  	void set_cell_size(float p_value);  	float get_cell_size() const; diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index df831f92ef..9a659ef4af 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -690,11 +690,10 @@ void Spatial::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_targe  	Transform lookat;  	lookat.origin = p_pos; -	Vector3 original_scale(get_global_transform().basis.get_scale()); +	Vector3 original_scale(get_scale());  	lookat = lookat.looking_at(p_target, p_up); -	// as basis was normalized, we just need to apply original scale back -	lookat.basis.scale(original_scale);  	set_global_transform(lookat); +	set_scale(original_scale);  }  Vector3 Spatial::to_local(Vector3 p_global) const { diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 510f1b18ad..4edd4b8530 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -452,6 +452,11 @@ void Label::regenerate_word_cache() {  			current_word_size += char_width;  			line_width += char_width;  			total_char_cache++; + +			// allow autowrap to cut words when they exceed line width +			if (autowrap && (current_word_size > width)) { +				separatable = true; +			}  		}  		if ((autowrap && (line_width >= width) && ((last && last->char_pos >= 0) || separatable)) || insert_newline) { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 42cb89b2d6..2ae60a17ea 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -624,19 +624,19 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &  				if (p_mode == PROCESS_POINTER && r_click_char)  					*r_click_char = 0; -				ENSURE_WIDTH(img->image->get_width()); +				ENSURE_WIDTH(img->size.width); -				bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->image->get_height(), img->image->get_height())); +				bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->size.height, img->size.height));  				if (visible)  					line_is_blank = false;  				if (p_mode == PROCESS_DRAW && visible) { -					img->image->draw(ci, p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->image->get_height())); +					img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size));  				}  				p_char_count++; -				ADVANCE(img->image->get_width()); -				CHECK_HEIGHT((img->image->get_height() + font->get_descent())); +				ADVANCE(img->size.width); +				CHECK_HEIGHT((img->size.height + font->get_descent()));  			} break;  			case ITEM_NEWLINE: { @@ -1634,7 +1634,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub  	}  } -void RichTextLabel::add_image(const Ref<Texture> &p_image) { +void RichTextLabel::add_image(const Ref<Texture> &p_image, const int p_width, const int p_height) {  	if (current->type == ITEM_TABLE)  		return; @@ -1643,6 +1643,30 @@ void RichTextLabel::add_image(const Ref<Texture> &p_image) {  	ItemImage *item = memnew(ItemImage);  	item->image = p_image; + +	if (p_width > 0) { +		// custom width +		item->size.width = p_width; +		if (p_height > 0) { +			// custom height +			item->size.height = p_height; +		} else { +			// calculate height to keep aspect ratio +			item->size.height = p_image->get_height() * p_width / p_image->get_width(); +		} +	} else { +		if (p_height > 0) { +			// custom height +			item->size.height = p_height; +			// calculate width to keep aspect ratio +			item->size.width = p_image->get_width() * p_height / p_image->get_height(); +		} else { +			// keep original width and height +			item->size.height = p_image->get_height(); +			item->size.width = p_image->get_width(); +		} +	} +  	_add_item(item, false);  } @@ -2125,6 +2149,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {  			int end = p_bbcode.find("[", brk_end);  			if (end == -1)  				end = p_bbcode.length(); +  			String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1);  			Ref<Texture> texture = ResourceLoader::load(image, "Texture"); @@ -2133,6 +2158,32 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {  			pos = end;  			tag_stack.push_front(tag); +		} else if (tag.begins_with("img=")) { + +			int width = 0; +			int height = 0; + +			String params = tag.substr(4, tag.length()); +			int sep = params.find("x"); +			if (sep == -1) { +				width = params.to_int(); +			} else { +				width = params.substr(0, sep).to_int(); +				height = params.substr(sep + 1, params.length()).to_int(); +			} + +			int end = p_bbcode.find("[", brk_end); +			if (end == -1) +				end = p_bbcode.length(); + +			String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1); + +			Ref<Texture> texture = ResourceLoader::load(image, "Texture"); +			if (texture.is_valid()) +				add_image(texture, width, height); + +			pos = end; +			tag_stack.push_front("img");  		} else if (tag.begins_with("color=")) {  			String col = tag.substr(6, tag.length()); @@ -2581,7 +2632,7 @@ void RichTextLabel::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);  	ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);  	ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); -	ClassDB::bind_method(D_METHOD("add_image", "image"), &RichTextLabel::add_image); +	ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0));  	ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);  	ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);  	ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 1c90d974e4..6cd69b9187 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -148,6 +148,7 @@ private:  	struct ItemImage : public Item {  		Ref<Texture> image; +		Size2 size;  		ItemImage() { type = ITEM_IMAGE; }  	}; @@ -406,7 +407,7 @@ protected:  public:  	String get_text();  	void add_text(const String &p_text); -	void add_image(const Ref<Texture> &p_image); +	void add_image(const Ref<Texture> &p_image, const int p_width = 0, const int p_height = 0);  	void add_newline();  	bool remove_line(const int p_line);  	void push_font(const Ref<Font> &p_font); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 5f9b913e8c..e35d858635 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -935,7 +935,7 @@ void TextEdit::_notification(int p_what) {  				int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line;  				if (minimap_line >= 0) {  					minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi); -					minimap_line -= (smooth_scroll_enabled ? 1 : 0); +					minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0);  				}  				int minimap_draw_amount = minimap_visible_lines + times_line_wraps(minimap_line + 1); @@ -2146,7 +2146,7 @@ void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const  	int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line;  	if (first_visible_line > 0 && minimap_line >= 0) {  		minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi); -		minimap_line -= (smooth_scroll_enabled ? 1 : 0); +		minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0);  	} else {  		minimap_line = 0;  	} @@ -2843,19 +2843,30 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {  				// No need to indent if we are going upwards.  				if (auto_indent && !(k->get_command() && k->get_shift())) { -					// Indent once again if previous line will end with ':' or '{' and the line is not a comment +					// Indent once again if previous line will end with ':','{','[','(' and the line is not a comment  					// (i.e. colon/brace precedes current cursor position). -					if (cursor.column > 0 && (text[cursor.line][cursor.column - 1] == ':' || text[cursor.line][cursor.column - 1] == '{') && !is_line_comment(cursor.line)) { -						if (indent_using_spaces) { -							ins += space_indent; -						} else { -							ins += "\t"; -						} +					if (cursor.column > 0) { +						char prev_char = text[cursor.line][cursor.column - 1]; +						switch (prev_char) { +							case ':': +							case '{': +							case '[': +							case '(': { +								if (!is_line_comment(cursor.line)) { +									if (indent_using_spaces) { +										ins += space_indent; +									} else { +										ins += "\t"; +									} -						// No need to move the brace below if we are not taking the text with us. -						if (text[cursor.line][cursor.column] == '}' && !k->get_command()) { -							brace_indent = true; -							ins += "\n" + ins.substr(1, ins.length() - 2); +									// No need to move the brace below if we are not taking the text with us. +									char closing_char = _get_right_pair_symbol(prev_char); +									if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !k->get_command()) { +										brace_indent = true; +										ins += "\n" + ins.substr(1, ins.length() - 2); +									} +								} +							} break;  						}  					}  				} diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 7b6c90766f..217dacfbfe 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2832,6 +2832,8 @@ void Node::_bind_methods() {  	ClassDB::bind_method(D_METHOD("rset_unreliable", "property", "value"), &Node::rset_unreliable);  	ClassDB::bind_method(D_METHOD("rset_unreliable_id", "peer_id", "property", "value"), &Node::rset_unreliable_id); +	ClassDB::bind_method(D_METHOD("update_configuration_warning"), &Node::update_configuration_warning); +  	BIND_CONSTANT(NOTIFICATION_ENTER_TREE);  	BIND_CONSTANT(NOTIFICATION_EXIT_TREE);  	BIND_CONSTANT(NOTIFICATION_MOVED_IN_PARENT); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 830d314245..38ad6886b1 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -78,6 +78,17 @@ bool SceneTreeTimer::is_pause_mode_process() {  	return process_pause;  } +void SceneTreeTimer::release_connections() { + +	List<Connection> connections; +	get_all_signal_connections(&connections); + +	for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { +		Connection const &connection = E->get(); +		disconnect(connection.signal, connection.target, connection.method); +	} +} +  SceneTreeTimer::SceneTreeTimer() {  	time_left = 0;  	process_pause = true; @@ -611,6 +622,12 @@ void SceneTree::finish() {  		memdelete(root); //delete root  		root = NULL;  	} + +	// cleanup timers +	for (List<Ref<SceneTreeTimer> >::Element *E = timers.front(); E; E = E->next()) { +		E->get()->release_connections(); +	} +	timers.clear();  }  void SceneTree::quit() { diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index d387886d61..ef847ebb5b 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -61,6 +61,8 @@ public:  	void set_pause_mode_process(bool p_pause_mode_process);  	bool is_pause_mode_process(); +	void release_connections(); +  	SceneTreeTimer();  };  |