summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/io/http_client.cpp33
-rw-r--r--core/math/math_funcs.h18
-rw-r--r--core/os/main_loop.cpp1
-rw-r--r--core/os/main_loop.h1
-rw-r--r--core/script_debugger_remote.cpp9
-rw-r--r--core/script_debugger_remote.h5
-rw-r--r--doc/classes/EditorPlugin.xml7
-rw-r--r--doc/classes/ScriptCreateDialog.xml45
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp1
-rw-r--r--drivers/gles3/shaders/canvas.glsl9
-rw-r--r--drivers/gles3/shaders/scene.glsl12
-rw-r--r--editor/animation_track_editor.cpp2
-rw-r--r--editor/create_dialog.cpp15
-rw-r--r--editor/create_dialog.h1
-rw-r--r--editor/editor_node.cpp3
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_plugin.cpp9
-rw-r--r--editor/editor_plugin.h2
-rw-r--r--editor/export_template_manager.cpp8
-rw-r--r--editor/export_template_manager.h2
-rw-r--r--editor/filesystem_dock.cpp61
-rw-r--r--editor/filesystem_dock.h8
-rw-r--r--editor/groups_editor.cpp1
-rw-r--r--editor/icons/icon_script_create_dialog.svg10
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp17
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp20
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp26
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp178
-rw-r--r--editor/plugins/tile_map_editor_plugin.h7
-rw-r--r--editor/project_settings_editor.cpp2
-rw-r--r--editor/scene_tree_dock.h3
-rw-r--r--editor/script_create_dialog.cpp3
-rw-r--r--editor/settings_config_dialog.cpp2
-rw-r--r--editor/spatial_editor_gizmos.cpp9
-rw-r--r--main/main.cpp5
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp19
-rw-r--r--modules/gdnative/nativescript/nativescript.h3
-rw-r--r--modules/gdscript/gdscript_function.cpp20
-rw-r--r--modules/gdscript/gdscript_function.h2
-rw-r--r--modules/gdscript/gdscript_functions.cpp26
-rw-r--r--modules/gdscript/gdscript_functions.h1
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp31
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h2
-rw-r--r--modules/mono/SCsub16
-rw-r--r--modules/mono/config.py112
-rw-r--r--modules/mono/csharp_script.cpp121
-rw-r--r--modules/mono/csharp_script.h3
-rw-r--r--modules/mono/editor/csharp_project.cpp32
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp20
-rw-r--r--modules/mono/editor/monodevelop_instance.cpp16
-rw-r--r--modules/mono/glue/cs_files/Mathf.cs4
-rw-r--r--modules/mono/mono_gc_handle.cpp7
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp47
-rw-r--r--modules/mono/mono_gd/gd_mono.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp11
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.h11
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp20
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp30
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp30
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp86
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h33
-rw-r--r--modules/mono/signal_awaiter_utils.cpp20
-rw-r--r--modules/mono/tls_configure.py36
-rw-r--r--modules/mono/utils/macros.h (renamed from modules/webm/resource_importer_webm.h)45
-rw-r--r--modules/mono/utils/thread_local.cpp (renamed from modules/theora/resource_importer_theora.h)81
-rw-r--r--modules/mono/utils/thread_local.h171
-rw-r--r--modules/theora/doc_classes/ResourceImporterTheora.xml15
-rw-r--r--modules/theora/register_types.cpp16
-rw-r--r--modules/theora/resource_importer_theora.cpp90
-rw-r--r--modules/theora/video_stream_theora.cpp43
-rw-r--r--modules/theora/video_stream_theora.h10
-rw-r--r--modules/webm/doc_classes/ResourceImporterWebm.xml15
-rw-r--r--modules/webm/register_types.cpp16
-rw-r--r--modules/webm/resource_importer_webm.cpp96
-rw-r--r--modules/webm/video_stream_webm.cpp43
-rw-r--r--modules/webm/video_stream_webm.h10
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java16
-rw-r--r--platform/osx/crash_handler_osx.mm4
-rw-r--r--platform/windows/crash_handler_win.cpp3
-rw-r--r--platform/windows/os_windows.cpp70
-rw-r--r--platform/x11/crash_handler_x11.cpp4
-rw-r--r--scene/2d/animated_sprite.cpp29
-rw-r--r--scene/2d/animated_sprite.h3
-rw-r--r--scene/2d/back_buffer_copy.cpp5
-rw-r--r--scene/2d/back_buffer_copy.h1
-rw-r--r--scene/2d/canvas_item.cpp21
-rw-r--r--scene/2d/canvas_item.h39
-rw-r--r--scene/2d/light_2d.cpp12
-rw-r--r--scene/2d/light_2d.h2
-rw-r--r--scene/2d/screen_button.cpp14
-rw-r--r--scene/2d/screen_button.h3
-rw-r--r--scene/2d/sprite.cpp11
-rw-r--r--scene/2d/sprite.h1
-rw-r--r--scene/2d/tile_map.cpp10
-rw-r--r--scene/2d/tile_map.h3
-rw-r--r--scene/gui/control.cpp198
-rw-r--r--scene/gui/control.h10
-rw-r--r--scene/gui/text_edit.cpp23
-rw-r--r--scene/main/http_request.cpp20
-rw-r--r--scene/main/node.cpp2
-rw-r--r--scene/main/scene_tree.cpp12
-rw-r--r--scene/main/viewport.cpp4
-rw-r--r--scene/resources/material.cpp5
-rw-r--r--scene/resources/material.h3
-rw-r--r--servers/visual/shader_types.cpp1
107 files changed, 1578 insertions, 911 deletions
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 8d85e78226..fcbb22b5de 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -30,6 +30,7 @@
#include "http_client.h"
#include "io/stream_peer_ssl.h"
+#include "version.h"
const char *HTTPClient::_methods[METHOD_MAX] = {
"GET",
@@ -121,16 +122,30 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector
request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
}
bool add_clen = p_body.size() > 0;
+ bool add_uagent = true;
+ bool add_accept = true;
for (int i = 0; i < p_headers.size(); i++) {
request += p_headers[i] + "\r\n";
- if (add_clen && p_headers[i].find("Content-Length:") == 0) {
+ if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
add_clen = false;
}
+ if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
+ add_uagent = false;
+ }
+ if (add_accept && p_headers[i].findn("Accept:") == 0) {
+ add_accept = false;
+ }
}
if (add_clen) {
request += "Content-Length: " + itos(p_body.size()) + "\r\n";
// Should it add utf8 encoding?
}
+ if (add_uagent) {
+ request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
+ }
+ if (add_accept) {
+ request += "Accept: */*\r\n";
+ }
request += "\r\n";
CharString cs = request.utf8();
@@ -173,17 +188,31 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str
} else {
request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
}
+ bool add_uagent = true;
+ bool add_accept = true;
bool add_clen = p_body.length() > 0;
for (int i = 0; i < p_headers.size(); i++) {
request += p_headers[i] + "\r\n";
- if (add_clen && p_headers[i].find("Content-Length:") == 0) {
+ if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
add_clen = false;
}
+ if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
+ add_uagent = false;
+ }
+ if (add_accept && p_headers[i].findn("Accept:") == 0) {
+ add_accept = false;
+ }
}
if (add_clen) {
request += "Content-Length: " + itos(p_body.utf8().length()) + "\r\n";
// Should it add utf8 encoding?
}
+ if (add_uagent) {
+ request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
+ }
+ if (add_accept) {
+ request += "Accept: */*\r\n";
+ }
request += "\r\n";
request += p_body;
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 20001bb9a6..f0c0268f31 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -182,8 +182,22 @@ public:
static _ALWAYS_INLINE_ float abs(float g) { return absf(g); }
static _ALWAYS_INLINE_ int abs(int g) { return g > 0 ? g : -g; }
- static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) { return (p_x >= 0) ? Math::fmod(p_x, p_y) : p_y - Math::fmod(-p_x, p_y); }
- static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) { return (p_x >= 0) ? Math::fmod(p_x, p_y) : p_y - Math::fmod(-p_x, p_y); }
+ static _ALWAYS_INLINE_ double fposmod(double p_x, double p_y) {
+ double value = Math::fmod(p_x, p_y);
+ if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) {
+ value += p_y;
+ }
+ value += 0.0;
+ return value;
+ }
+ static _ALWAYS_INLINE_ float fposmod(float p_x, float p_y) {
+ float value = Math::fmod(p_x, p_y);
+ if ((value < 0 && p_y > 0) || (value > 0 && p_y < 0)) {
+ value += p_y;
+ }
+ value += 0.0;
+ return value;
+ }
static _ALWAYS_INLINE_ double deg2rad(double p_y) { return p_y * Math_PI / 180.0; }
static _ALWAYS_INLINE_ float deg2rad(float p_y) { return p_y * Math_PI / 180.0; }
diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp
index 916c86613e..c51801e3e2 100644
--- a/core/os/main_loop.cpp
+++ b/core/os/main_loop.cpp
@@ -58,6 +58,7 @@ void MainLoop::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING);
BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
+ BIND_CONSTANT(NOTIFICATION_CRASH);
};
void MainLoop::set_init_script(const Ref<Script> &p_init_script) {
diff --git a/core/os/main_loop.h b/core/os/main_loop.h
index 546e4e280c..f96e46141e 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -62,6 +62,7 @@ public:
// fixes this issue.
NOTIFICATION_TRANSLATION_CHANGED = 90,
NOTIFICATION_WM_ABOUT = 91,
+ NOTIFICATION_CRASH = 92,
};
virtual void input_event(const Ref<InputEvent> &p_event);
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index 0473e2cc71..3955f222f9 100644
--- a/core/script_debugger_remote.cpp
+++ b/core/script_debugger_remote.cpp
@@ -169,6 +169,10 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue)
ERR_FAIL();
}
+ if (allow_focus_steal_pid) {
+ OS::get_singleton()->enable_for_stealing_focus(allow_focus_steal_pid);
+ }
+
packet_peer_stream->put_var("debug_enter");
packet_peer_stream->put_var(2);
packet_peer_stream->put_var(p_can_continue);
@@ -1070,6 +1074,10 @@ void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p
physics_frame_time = p_physics_frame_time;
}
+void ScriptDebuggerRemote::set_allow_focus_steal_pid(OS::ProcessID p_pid) {
+ allow_focus_steal_pid = p_pid;
+}
+
ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
ScriptDebuggerRemote::ScriptDebuggerRemote() :
@@ -1091,6 +1099,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
n_errors_dropped(0),
last_msec(0),
msec_count(0),
+ allow_focus_steal_pid(0),
locking(false),
poll_every(0),
request_scene_tree(NULL),
diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h
index cc12d978d6..b68fc4f9c9 100644
--- a/core/script_debugger_remote.h
+++ b/core/script_debugger_remote.h
@@ -34,6 +34,7 @@
#include "io/packet_peer.h"
#include "io/stream_peer_tcp.h"
#include "list.h"
+#include "os/os.h"
#include "script_language.h"
class ScriptDebuggerRemote : public ScriptDebugger {
@@ -98,6 +99,8 @@ class ScriptDebuggerRemote : public ScriptDebugger {
uint64_t last_msec;
uint64_t msec_count;
+ OS::ProcessID allow_focus_steal_pid;
+
bool locking; //hack to avoid a deadloop
static void _print_handler(void *p_this, const String &p_string, bool p_error);
@@ -171,6 +174,8 @@ public:
virtual void profiling_end();
virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+ void set_allow_focus_steal_pid(OS::ProcessID p_pid);
+
ScriptDebuggerRemote();
~ScriptDebuggerRemote();
};
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index b9945f3f73..f5fbf8e313 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -235,6 +235,13 @@
<description>
</description>
</method>
+ <method name="get_script_create_dialog">
+ <return type="ScriptCreateDialog">
+ </return>
+ <description>
+ Gets the Editor's dialogue used for making scripts. Note that users can configure it before use.
+ </description>
+ </method>
<method name="get_state" qualifiers="virtual">
<return type="Dictionary">
</return>
diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml
new file mode 100644
index 0000000000..f09d282026
--- /dev/null
+++ b/doc/classes/ScriptCreateDialog.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ScriptCreateDialog" inherits="ConfirmationDialog" category="Core" version="3.1">
+ <brief_description>
+ The Editor's popup dialog for creating new [Script] files.
+ </brief_description>
+ <description>
+ The ScriptCreateDialog creates script files according to a given template for a given scripting language. The standard use is to configure its fields prior to calling a [method popup]() method.
+ [codeblock]
+ func _ready():
+ dialog.config("Node", "res://new_node.gd") # for in-engine types
+ dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # for script types
+ dialog.popup_centered()
+ [/codeblock]
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="config">
+ <return type="void">
+ </return>
+ <argument index="0" name="inherits" type="String">
+ The dialog's "Inherits" field content.
+ </argument>
+ <argument index="1" name="path" type="String">
+ The dialog's "Path" field content.
+ </argument>
+ <description>
+ Prefills required fields to configure the ScriptCreateDialog for use.
+ </description>
+ </method>
+ </methods>
+ <signals>
+ <signal name="script_created">
+ <argument index="0" name="script" type="Object">
+ </argument>
+ <description>
+ Emitted when the user clicks the OK button.
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index eb8d6c485b..9ad16ac2a2 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -898,6 +898,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n";
+ actions[VS::SHADER_SPATIAL].render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 326aab4c7c..7aafd28c35 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -518,10 +518,13 @@ FRAGMENT_SHADER_CODE
-
#ifdef USE_LIGHTING
- vec2 light_vec = light_uv_interp.zw;; //for shadow and normal mapping
+ mat3 inverse_light_matrix = mat3(inverse(light_matrix));
+ inverse_light_matrix[0] = normalize(inverse_light_matrix[0]);
+ inverse_light_matrix[1] = normalize(inverse_light_matrix[1]);
+ inverse_light_matrix[2] = normalize(inverse_light_matrix[2]);
+ vec2 light_vec = (inverse_light_matrix * vec3(light_uv_interp.zw,0.0)).xy; //for normal mapping
if (normal_used) {
normal.xy = mat2(local_rot.xy,local_rot.zw) * normal.xy;
@@ -567,7 +570,7 @@ FRAGMENT_SHADER_CODE
color*=light;
#ifdef USE_SHADOWS
-
+ light_vec = light_uv_interp.zw; //for shadows
float angle_to_light = -atan(light_vec.x,light_vec.y);
float PI = 3.14159265358979323846264;
/*int i = int(mod(floor((angle_to_light+7.0*PI/6.0)/(4.0*PI/6.0))+1.0, 3.0)); // +1 pq os indices estao em ordem 2,0,1 nos arrays
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 0e111e59a9..ed8df04377 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -323,7 +323,13 @@ void main() {
#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
vertex = world_matrix * vertex;
+
+#if defined(ENSURE_CORRECT_NORMALS)
+ mat3 normal_matrix = mat3(transpose(inverse(world_matrix)));
+ normal = normal_matrix * normal;
+#else
normal = normalize((world_matrix * vec4(normal,0.0)).xyz);
+#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
@@ -395,7 +401,13 @@ VERTEX_SHADER_CODE
#if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED)
vertex = modelview * vertex;
+
+#if defined(ENSURE_CORRECT_NORMALS)
+ mat3 normal_matrix = mat3(transpose(inverse(modelview)));
+ normal = normal_matrix * normal;
+#else
normal = normalize((modelview * vec4(normal,0.0)).xyz);
+#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 02d667031a..293684f48e 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -874,9 +874,11 @@ void AnimationTimelineEdit::set_animation(const Ref<Animation> &p_animation) {
if (animation.is_valid()) {
len_hb->show();
add_track->show();
+ play_position->show();
} else {
len_hb->hide();
add_track->hide();
+ play_position->hide();
}
update();
update_values();
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 36978e37a5..a8cbf52cd2 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -262,13 +262,17 @@ void CreateDialog::_update_search() {
if (base_type == "Node" && type.begins_with("Editor"))
continue; // do not show editor nodes
- if (base_type == "Resource" && ClassDB::is_parent_class(type, "PluginScript"))
- // PluginScript must be initialized before use, which is not possible here
- continue;
-
if (!ClassDB::can_instance(type))
continue; // can't create what can't be instanced
+ bool skip = false;
+ for (Set<StringName>::Element *E = type_blacklist.front(); E && !skip; E = E->next()) {
+ if (ClassDB::is_parent_class(type, E->get()))
+ skip = true;
+ }
+ if (skip)
+ continue;
+
if (search_box->get_text() == "") {
add_type(type, types, root, &to_select);
} else {
@@ -706,4 +710,7 @@ CreateDialog::CreateDialog() {
help_bit = memnew(EditorHelpBit);
vbc->add_margin_child(TTR("Description:"), help_bit);
help_bit->connect("request_hide", this, "_closed");
+
+ type_blacklist.insert("PluginScript"); // PluginScript must be initialized before use, which is not possible here
+ type_blacklist.insert("ScriptCreateDialog"); // This is an exposed editor Node that doesn't have an Editor prefix.
}
diff --git a/editor/create_dialog.h b/editor/create_dialog.h
index da17dcbe89..f8eec231a4 100644
--- a/editor/create_dialog.h
+++ b/editor/create_dialog.h
@@ -58,6 +58,7 @@ class CreateDialog : public ConfirmationDialog {
String preferred_search_result_type;
EditorHelpBit *help_bit;
List<StringName> type_list;
+ Set<StringName> type_blacklist;
void _item_selected();
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 6084332dfb..2ac0364778 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -3033,6 +3033,7 @@ void EditorNode::register_editor_types() {
ClassDB::register_class<EditorInspectorPlugin>();
ClassDB::register_class<EditorProperty>();
ClassDB::register_class<AnimationTrackEditPlugin>();
+ ClassDB::register_class<ScriptCreateDialog>();
// FIXME: Is this stuff obsolete, or should it be ported to new APIs?
ClassDB::register_class<EditorScenePostImport>();
@@ -4395,6 +4396,8 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
+ ClassDB::bind_method("get_script_create_dialog", &EditorNode::get_script_create_dialog);
+
ClassDB::bind_method("_sources_changed", &EditorNode::_sources_changed);
ClassDB::bind_method("_fs_changed", &EditorNode::_fs_changed);
ClassDB::bind_method("_dock_select_draw", &EditorNode::_dock_select_draw);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 4241520b30..a5f975784c 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -598,6 +598,7 @@ public:
EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; }
EditorInspector *get_inspector() { return inspector_dock->get_inspector(); }
Container *get_inspector_dock_addon_area() { return inspector_dock->get_addon_area(); }
+ ScriptCreateDialog *get_script_create_dialog() { return scene_tree_dock->get_script_create_dialog(); }
ProjectSettingsEditor *get_project_settings() { return project_settings; }
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index cc44938c25..843267d673 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -310,7 +310,7 @@ void EditorPlugin::remove_autoload_singleton(const String &p_name) {
}
ToolButton *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) {
-
+ ERR_FAIL_NULL_V(p_control, NULL);
return EditorNode::get_singleton()->add_bottom_panel_item(p_title, p_control);
}
@@ -333,6 +333,7 @@ void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
}
void EditorPlugin::add_control_to_container(CustomControlContainer p_location, Control *p_control) {
+ ERR_FAIL_NULL(p_control);
switch (p_location) {
@@ -382,6 +383,7 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C
}
void EditorPlugin::remove_control_from_container(CustomControlContainer p_location, Control *p_control) {
+ ERR_FAIL_NULL(p_control);
switch (p_location) {
@@ -717,6 +719,10 @@ EditorInterface *EditorPlugin::get_editor_interface() {
return EditorInterface::get_singleton();
}
+ScriptCreateDialog *EditorPlugin::get_script_create_dialog() {
+ return EditorNode::get_singleton()->get_script_create_dialog();
+}
+
void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_control_to_container", "container", "control"), &EditorPlugin::add_control_to_container);
@@ -753,6 +759,7 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_force_draw_over_forwarding_enabled"), &EditorPlugin::set_force_draw_over_forwarding_enabled);
ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface);
+ ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index fcc74cb1e9..72e21b2f7f 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -34,6 +34,7 @@
#include "editor/editor_inspector.h"
#include "editor/import/editor_import_plugin.h"
#include "editor/import/resource_importer_scene.h"
+#include "editor/script_create_dialog.h"
#include "io/config_file.h"
#include "scene/gui/tool_button.h"
#include "scene/main/node.h"
@@ -195,6 +196,7 @@ public:
virtual bool build(); // builds with external tools. Returns true if safe to continue running scene.
EditorInterface *get_editor_interface();
+ ScriptCreateDialog *get_script_create_dialog();
int update_overlays() const;
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index a39c8b2209..541c848ca3 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -436,6 +436,10 @@ void ExportTemplateManager::_begin_template_download(const String &p_url) {
template_list_state->set_text(TTR("Connecting to Mirror..."));
}
+void ExportTemplateManager::_window_template_downloader_closed() {
+ download_templates->cancel_request();
+}
+
void ExportTemplateManager::_notification(int p_what) {
if (p_what == NOTIFICATION_PROCESS) {
@@ -496,7 +500,6 @@ void ExportTemplateManager::_notification(int p_what) {
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
if (!is_visible_in_tree()) {
print_line("closed");
- download_templates->cancel_request();
set_process(false);
}
}
@@ -511,6 +514,7 @@ void ExportTemplateManager::_bind_methods() {
ClassDB::bind_method("_http_download_mirror_completed", &ExportTemplateManager::_http_download_mirror_completed);
ClassDB::bind_method("_http_download_templates_completed", &ExportTemplateManager::_http_download_templates_completed);
ClassDB::bind_method("_begin_template_download", &ExportTemplateManager::_begin_template_download);
+ ClassDB::bind_method("_window_template_downloader_closed", &ExportTemplateManager::_window_template_downloader_closed);
}
ExportTemplateManager::ExportTemplateManager() {
@@ -560,7 +564,9 @@ ExportTemplateManager::ExportTemplateManager() {
template_downloader = memnew(AcceptDialog);
template_downloader->set_title(TTR("Download Templates"));
template_downloader->get_ok()->set_text(TTR("Close"));
+ template_downloader->set_exclusive(true);
add_child(template_downloader);
+ template_downloader->connect("popup_hide", this, "_window_template_downloader_closed");
VBoxContainer *vbc = memnew(VBoxContainer);
template_downloader->add_child(vbc);
diff --git a/editor/export_template_manager.h b/editor/export_template_manager.h
index 62336162fd..54a645c69f 100644
--- a/editor/export_template_manager.h
+++ b/editor/export_template_manager.h
@@ -77,6 +77,8 @@ class ExportTemplateManager : public ConfirmationDialog {
void _begin_template_download(const String &p_url);
+ void _window_template_downloader_closed();
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 6de52c6176..eebf1b6ab8 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -200,6 +200,7 @@ void FileSystemDock::_notification(int p_what) {
button_hist_next->set_icon(get_icon("Forward", ei));
button_hist_prev->set_icon(get_icon("Back", ei));
+ button_show->set_icon(get_icon("GuiVisibilityVisible", "EditorIcons"));
file_options->connect("id_pressed", this, "_file_option");
folder_options->connect("id_pressed", this, "_folder_option");
@@ -317,6 +318,15 @@ void FileSystemDock::_favorites_pressed() {
_update_tree(true);
}
+void FileSystemDock::_show_current_scene_file() {
+
+ int index = EditorNode::get_editor_data().get_edited_scene();
+ String path = EditorNode::get_editor_data().get_scene_path(index);
+ if (path != String()) {
+ navigate_to_path(path);
+ }
+}
+
String FileSystemDock::get_selected_path() const {
TreeItem *sel = tree->get_selected();
@@ -1060,7 +1070,40 @@ void FileSystemDock::_duplicate_operation_confirm() {
_rescan();
}
-void FileSystemDock::_move_operation_confirm(const String &p_to_path) {
+void FileSystemDock::_move_with_overwrite() {
+ _move_operation_confirm(to_move_path, true);
+}
+
+bool FileSystemDock::_check_existing() {
+ String &p_to_path = to_move_path;
+ for (int i = 0; i < to_move.size(); i++) {
+ String ol_pth = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
+ String p_new_path = p_to_path.plus_file(ol_pth.get_file());
+ FileOrFolder p_item = to_move[i];
+
+ String old_path = (p_item.is_file || p_item.path.ends_with("/")) ? p_item.path : (p_item.path + "/");
+ String new_path = (p_item.is_file || p_new_path.ends_with("/")) ? p_new_path : (p_new_path + "/");
+
+ if (p_item.is_file && FileAccess::exists(new_path)) {
+ return false;
+ } else if (!p_item.is_file && DirAccess::exists(new_path)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overwrite) {
+ if (!overwrite) {
+ to_move_path = p_to_path;
+ bool can_move = _check_existing();
+ if (!can_move) {
+ //ask to do something
+ overwrite_dialog->popup_centered_minsize();
+ overwrite_dialog->grab_focus();
+ return;
+ }
+ }
Map<String, String> file_renames;
Map<String, String> folder_renames;
@@ -1802,6 +1845,7 @@ void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_tree"), &FileSystemDock::_update_tree);
ClassDB::bind_method(D_METHOD("_rescan"), &FileSystemDock::_rescan);
ClassDB::bind_method(D_METHOD("_favorites_pressed"), &FileSystemDock::_favorites_pressed);
+ ClassDB::bind_method(D_METHOD("_show_current_scene_file"), &FileSystemDock::_show_current_scene_file);
//ClassDB::bind_method(D_METHOD("_instance_pressed"),&ScenesDock::_instance_pressed);
ClassDB::bind_method(D_METHOD("_go_to_file_list"), &FileSystemDock::_go_to_file_list);
ClassDB::bind_method(D_METHOD("_dir_rmb_pressed"), &FileSystemDock::_dir_rmb_pressed);
@@ -1819,6 +1863,7 @@ void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_folder_option"), &FileSystemDock::_folder_option);
ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileSystemDock::_make_dir_confirm);
ClassDB::bind_method(D_METHOD("_move_operation_confirm"), &FileSystemDock::_move_operation_confirm);
+ ClassDB::bind_method(D_METHOD("_move_with_overwrite"), &FileSystemDock::_move_with_overwrite);
ClassDB::bind_method(D_METHOD("_rename_operation_confirm"), &FileSystemDock::_rename_operation_confirm);
ClassDB::bind_method(D_METHOD("_duplicate_operation_confirm"), &FileSystemDock::_duplicate_operation_confirm);
@@ -1851,6 +1896,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
ED_SHORTCUT("filesystem_dock/rename", TTR("Rename"));
HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
+ toolbar_hbc->add_constant_override("separation", 0);
add_child(toolbar_hbc);
button_hist_prev = memnew(ToolButton);
@@ -1887,6 +1933,13 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
button_favorite->set_focus_mode(FOCUS_NONE);
toolbar_hbc->add_child(button_favorite);
+ button_show = memnew(Button);
+ button_show->set_flat(true);
+ button_show->connect("pressed", this, "_show_current_scene_file");
+ toolbar_hbc->add_child(button_show);
+ button_show->set_focus_mode(FOCUS_NONE);
+ button_show->set_tooltip(TTR("Show current scene file."));
+
//Control *spacer = memnew( Control);
/*
@@ -2002,6 +2055,12 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
rename_dialog->register_text_enter(rename_dialog_text);
rename_dialog->connect("confirmed", this, "_rename_operation_confirm");
+ overwrite_dialog = memnew(ConfirmationDialog);
+ overwrite_dialog->set_text(TTR("There is already file or folder with the same name in this location."));
+ overwrite_dialog->get_ok()->set_text(TTR("Overwrite"));
+ add_child(overwrite_dialog);
+ overwrite_dialog->connect("confirmed", this, "_move_with_overwrite");
+
duplicate_dialog = memnew(ConfirmationDialog);
VBoxContainer *duplicate_dialog_vb = memnew(VBoxContainer);
duplicate_dialog->add_child(duplicate_dialog_vb);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 7b57af4e86..e8ab803cca 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -106,6 +106,7 @@ private:
Button *button_display_mode;
Button *button_hist_next;
Button *button_hist_prev;
+ Button *button_show;
LineEdit *current_path;
LineEdit *search_box;
TextureRect *search_icon;
@@ -128,6 +129,7 @@ private:
LineEdit *duplicate_dialog_text;
ConfirmationDialog *make_dir_dialog;
LineEdit *make_dir_dialog_text;
+ ConfirmationDialog *overwrite_dialog;
ScriptCreateDialog *make_script_dialog_text;
class FileOrFolder {
@@ -145,6 +147,7 @@ private:
FileOrFolder to_rename;
FileOrFolder to_duplicate;
Vector<FileOrFolder> to_move;
+ String to_move_path;
Vector<String> history;
int history_pos;
@@ -191,7 +194,9 @@ private:
void _make_dir_confirm();
void _rename_operation_confirm();
void _duplicate_operation_confirm();
- void _move_operation_confirm(const String &p_to_path);
+ void _move_with_overwrite();
+ bool _check_existing();
+ void _move_operation_confirm(const String &p_to_path, bool overwrite = false);
void _file_option(int p_option);
void _folder_option(int p_option);
@@ -205,6 +210,7 @@ private:
void _rescan();
void _favorites_pressed();
+ void _show_current_scene_file();
void _search_changed(const String &p_text);
void _dir_rmb_pressed(const Vector2 &p_pos);
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index e42f9780a6..2bfd2eb5c3 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -444,6 +444,7 @@ GroupDialog::GroupDialog() {
set_title("Group Editor");
get_cancel()->hide();
set_as_toplevel(true);
+ set_resizable(true);
error = memnew(ConfirmationDialog);
add_child(error);
diff --git a/editor/icons/icon_script_create_dialog.svg b/editor/icons/icon_script_create_dialog.svg
new file mode 100644
index 0000000000..27d6c47d49
--- /dev/null
+++ b/editor/icons/icon_script_create_dialog.svg
@@ -0,0 +1,10 @@
+<svg width="17.067" height="17.067" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<g transform="translate(0 -1036.4)">
+<path transform="translate(0 1036.4)" d="m6 1v1c-0.55228 0-1 0.44772-1 1v10h-1v-2h-2v2c2.826e-4 0.35698 0.19084 0.68674 0.5 0.86523 0.15194 0.088045 0.32439 0.13452 0.5 0.13477v1h6v-5l3-2v-3h3v-2c0-1.1046-0.89543-2-2-2z" fill="#a5efac"/>
+<path transform="translate(0 1036.4)" d="m6 1c-1.1046 0-2 0.89543-2 2v7h-3v3c0 1.1046 0.89543 2 2 2s2-0.89543 2-2v-10c0-0.55228 0.44772-1 1-1s1 0.44772 1 1v3h5v-1h-4v-2c0-1.1046-0.89543-2-2-2zm-4 10h2v2c0 0.55228-0.44772 1-1 1s-1-0.44772-1-1z" fill="#87e29f"/>
+<circle cx="3" cy="1048.4" r="0" fill="#e0e0e0"/>
+<ellipse cx="12" cy="1048.4" rx=".5" ry="3" fill="#87e29f"/>
+<ellipse transform="rotate(60)" cx="913.91" cy="513.79" rx=".5" ry="3" fill="#87e29f"/>
+<ellipse transform="rotate(120)" cx="901.91" cy="-534.57" rx=".5" ry="3" fill="#87e29f"/>
+</g>
+</svg>
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 248e386bf1..7d4415ba9e 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -328,6 +328,9 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
}
autoplay->set_pressed(current == player->get_autoplay());
+
+ AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ EditorNode::get_singleton()->update_keying();
}
void AnimationPlayerEditor::_animation_new() {
@@ -850,8 +853,11 @@ void AnimationPlayerEditor::_update_player() {
active_idx = animation->get_item_count() - 1;
}
- if (!player)
+ if (!player) {
+ AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ EditorNode::get_singleton()->update_keying();
return;
+ }
updating = false;
if (active_idx != -1) {
@@ -864,6 +870,8 @@ void AnimationPlayerEditor::_update_player() {
animation->select(0);
autoplay->set_pressed(animation->get_item_text(0) == player->get_autoplay());
_animation_selected(0);
+ } else {
+ _animation_selected(0);
}
//pause->set_pressed(player->is_paused());
@@ -1103,9 +1111,12 @@ void AnimationPlayerEditor::_animation_about_to_show_menu() {
void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
- String current = animation->get_item_text(animation->get_selected());
+ String current;
+ if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count())
+ current = animation->get_item_text(animation->get_selected());
+
Ref<Animation> anim;
- if (current != "") {
+ if (current != String()) {
anim = player->get_animation(current);
}
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 7c4cd6cb3d..48ee011fc1 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -543,7 +543,6 @@ void CanvasItemEditor::_get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResu
for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from));
- Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().to));
Vector<Vector2> bone_shape;
if (!_get_bone_shape(&bone_shape, NULL, E))
@@ -719,16 +718,17 @@ Vector2 CanvasItemEditor::_anchor_to_position(const Control *p_control, Vector2
ERR_FAIL_COND_V(!p_control, Vector2());
Transform2D parent_transform = p_control->get_transform().affine_inverse();
- Size2 parent_size = p_control->get_parent_area_size();
+ Rect2 parent_rect = p_control->get_parent_anchorable_rect();
- return parent_transform.xform(Vector2(parent_size.x * anchor.x, parent_size.y * anchor.y));
+ return parent_transform.xform(parent_rect.position + Vector2(parent_rect.size.x * anchor.x, parent_rect.size.y * anchor.y));
}
Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 position) {
ERR_FAIL_COND_V(!p_control, Vector2());
- Size2 parent_size = p_control->get_parent_area_size();
- return p_control->get_transform().xform(position) / parent_size;
+ Rect2 parent_rect = p_control->get_parent_anchorable_rect();
+
+ return (p_control->get_transform().xform(position) - parent_rect.position) / parent_rect.size;
}
void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones) {
@@ -2491,10 +2491,12 @@ void CanvasItemEditor::_draw_selection() {
Transform2D parent_transform = xform * control->get_transform().affine_inverse();
float node_pos_in_parent[4];
- node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * control->get_parent_area_size().width + control->get_margin(MARGIN_LEFT);
- node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * control->get_parent_area_size().height + control->get_margin(MARGIN_TOP);
- node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * control->get_parent_area_size().width + control->get_margin(MARGIN_RIGHT);
- node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * control->get_parent_area_size().height + control->get_margin(MARGIN_BOTTOM);
+ Rect2 parent_rect = control->get_parent_anchorable_rect();
+
+ node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * parent_rect.size.width + control->get_margin(MARGIN_LEFT) + parent_rect.position.x;
+ node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * parent_rect.size.height + control->get_margin(MARGIN_TOP) + parent_rect.position.y;
+ node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * parent_rect.size.width + control->get_margin(MARGIN_RIGHT) + parent_rect.position.x;
+ node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * parent_rect.size.height + control->get_margin(MARGIN_BOTTOM) + parent_rect.position.y;
Point2 start, end;
switch (drag_type) {
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 30fff474d7..37b8562e96 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -471,7 +471,11 @@ void SpatialEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_incl
Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) {
CameraMatrix cm;
- cm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
+ if (orthogonal) {
+ cm.set_orthogonal(camera->get_size(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
+ } else {
+ cm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
+ }
float screen_w, screen_h;
cm.get_viewport_size(screen_w, screen_h);
@@ -518,18 +522,24 @@ void SpatialEditorViewport::_select_region() {
Vector3 a = _get_screen_to_space(box[i]);
Vector3 b = _get_screen_to_space(box[(i + 1) % 4]);
- frustum.push_back(Plane(a, b, cam_pos));
+ if (orthogonal) {
+ frustum.push_back(Plane(a, (a - b).normalized()));
+ } else {
+ frustum.push_back(Plane(a, b, cam_pos));
+ }
}
- Plane near(cam_pos, -_get_camera_normal());
- near.d -= get_znear();
+ if (!orthogonal) {
+ Plane near(cam_pos, -_get_camera_normal());
+ near.d -= get_znear();
- frustum.push_back(near);
+ frustum.push_back(near);
- Plane far = -near;
- far.d += get_zfar();
+ Plane far = -near;
+ far.d += get_zfar();
- frustum.push_back(far);
+ frustum.push_back(far);
+ }
Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world()->get_scenario());
Vector<Spatial *> selected;
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index 7264af3488..19646f37b5 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -137,7 +137,7 @@ void TileMapEditor::_menu_option(int p_option) {
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false);
+ _set_cell(Point2i(j, i), invalid_cell, false, false, false);
}
}
_finish_undo();
@@ -186,24 +186,34 @@ void TileMapEditor::_canvas_mouse_exit() {
canvas_item_editor->update();
}
-int TileMapEditor::get_selected_tile() const {
+Vector<int> TileMapEditor::get_selected_tiles() const {
- int item = palette->get_current();
+ Vector<int> items = palette->get_selected_items();
- if (item == -1)
- return TileMap::INVALID_CELL;
+ if (items.size() == 0) {
+ items.push_back(TileMap::INVALID_CELL);
+ return items;
+ }
- return palette->get_item_metadata(item);
+ for (int i = items.size() - 1; i >= 0; i--) {
+ items[i] = palette->get_item_metadata(items[i]);
+ }
+ return items;
}
-void TileMapEditor::set_selected_tile(int p_tile) {
+void TileMapEditor::set_selected_tiles(Vector<int> p_tiles) {
- int idx = palette->find_metadata(p_tile);
+ palette->unselect_all();
- if (idx >= 0) {
- palette->select(idx, true);
- palette->ensure_current_is_visible();
+ for (int i = p_tiles.size() - 1; i >= 0; i--) {
+ int idx = palette->find_metadata(p_tiles[i]);
+
+ if (idx >= 0) {
+ palette->select(idx, false);
+ }
}
+
+ palette->ensure_current_is_visible();
}
void TileMapEditor::_create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new) {
@@ -246,10 +256,14 @@ void TileMapEditor::_finish_undo() {
undo_redo->commit_action();
}
-void TileMapEditor::_set_cell(const Point2i &p_pos, int p_value, bool p_flip_h, bool p_flip_v, bool p_transpose) {
+void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h, bool p_flip_v, bool p_transpose) {
ERR_FAIL_COND(!node);
+ if (p_values.size() == 0)
+ return;
+
+ int p_value = p_values[Math::rand() % p_values.size()];
int prev_val = node->get_cell(p_pos.x, p_pos.y);
bool prev_flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y);
@@ -339,7 +353,7 @@ void TileMapEditor::_update_palette() {
if (!node)
return;
- int selected = get_selected_tile();
+ Vector<int> selected = get_selected_tiles();
palette->clear();
manual_palette->clear();
manual_palette->hide();
@@ -428,14 +442,17 @@ void TileMapEditor::_update_palette() {
palette->set_item_metadata(palette->get_item_count() - 1, entries[i].id);
}
- if (selected != -1)
- set_selected_tile(selected);
- else
+ int sel_tile = selected.get(0);
+ if (selected.get(0) != TileMap::INVALID_CELL) {
+ set_selected_tiles(selected);
+ sel_tile = selected.get(Math::rand() % selected.size());
+ } else {
palette->select(0);
+ }
- if (manual_autotile && tileset->tile_get_tile_mode(get_selected_tile()) == TileSet::AUTO_TILE) {
+ if (manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) {
- const Map<Vector2, uint16_t> &tiles = tileset->autotile_get_bitmask_map(get_selected_tile());
+ const Map<Vector2, uint16_t> &tiles = tileset->autotile_get_bitmask_map(sel_tile);
Vector<Vector2> entries;
for (const Map<Vector2, uint16_t>::Element *E = tiles.front(); E; E = E->next()) {
@@ -443,7 +460,7 @@ void TileMapEditor::_update_palette() {
}
entries.sort();
- Ref<Texture> tex = tileset->tile_get_texture(get_selected_tile());
+ Ref<Texture> tex = tileset->tile_get_texture(sel_tile);
for (int i = 0; i < entries.size(); i++) {
@@ -451,9 +468,9 @@ void TileMapEditor::_update_palette() {
if (tex.is_valid()) {
- Rect2 region = tileset->tile_get_region(get_selected_tile());
- int spacing = tileset->autotile_get_spacing(get_selected_tile());
- region.size = tileset->autotile_get_size(get_selected_tile());
+ Rect2 region = tileset->tile_get_region(sel_tile);
+ int spacing = tileset->autotile_get_spacing(sel_tile);
+ region.size = tileset->autotile_get_size(sel_tile); // !!
region.position += (region.size + Vector2(spacing, spacing)) * entries[i];
if (!region.has_no_area())
@@ -488,7 +505,10 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) {
_update_palette();
}
- set_selected_tile(id);
+ Vector<int> selected;
+
+ selected.push_back(id);
+ set_selected_tiles(selected);
mirror_x->set_pressed(node->is_cell_x_flipped(p_pos.x, p_pos.y));
mirror_y->set_pressed(node->is_cell_y_flipped(p_pos.x, p_pos.y));
@@ -501,18 +521,21 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) {
PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool erase, bool preview) {
int prev_id = node->get_cell(p_start.x, p_start.y);
- int id = TileMap::INVALID_CELL;
+ Vector<int> ids;
+ ids.push_back(TileMap::INVALID_CELL);
if (!erase) {
- id = get_selected_tile();
+ ids = get_selected_tiles();
- if (id == TileMap::INVALID_CELL)
+ if (ids.size() == 0 && ids[0] == TileMap::INVALID_CELL)
return PoolVector<Vector2>();
} else if (prev_id == TileMap::INVALID_CELL) {
return PoolVector<Vector2>();
}
- if (id == prev_id) {
- return PoolVector<Vector2>();
+ for (int i = ids.size() - 1; i >= 0; i--) {
+ if (ids[i] == prev_id) {
+ return PoolVector<Vector2>();
+ }
}
Rect2i r = node->_edit_get_rect();
@@ -602,13 +625,13 @@ void TileMapEditor::_fill_points(const PoolVector<Vector2> p_points, const Dicti
int len = p_points.size();
PoolVector<Vector2>::Read pr = p_points.read();
- int id = p_op["id"];
+ Vector<int> ids = p_op["id"];
bool xf = p_op["flip_h"];
bool yf = p_op["flip_v"];
bool tr = p_op["transpose"];
for (int i = 0; i < len; i++) {
- _set_cell(pr[i], id, xf, yf, tr);
+ _set_cell(pr[i], ids, xf, yf, tr);
node->make_bitmask_area_dirty(pr[i]);
}
node->update_dirty_bitmask();
@@ -621,7 +644,7 @@ void TileMapEditor::_erase_points(const PoolVector<Vector2> p_points) {
for (int i = 0; i < len; i++) {
- _set_cell(pr[i], TileMap::INVALID_CELL);
+ _set_cell(pr[i], invalid_cell);
}
}
@@ -885,9 +908,9 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (tool == TOOL_PAINTING) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
tool = TOOL_PAINTING;
@@ -910,25 +933,25 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (tool == TOOL_PAINTING) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- _set_cell(over_tile, id, flip_h, flip_v, transpose);
+ _set_cell(over_tile, ids, flip_h, flip_v, transpose);
_finish_undo();
paint_undo.clear();
}
} else if (tool == TOOL_LINE_PAINT) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
_start_undo(TTR("Line Draw"));
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), id, flip_h, flip_v, transpose);
+ _set_cell(E->key(), ids, flip_h, flip_v, transpose);
}
_finish_undo();
@@ -938,15 +961,15 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
} else if (tool == TOOL_RECTANGLE_PAINT) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
_start_undo(TTR("Rectangle Paint"));
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), id, flip_h, flip_v, transpose);
+ _set_cell(Point2i(j, i), ids, flip_h, flip_v, transpose);
}
}
_finish_undo();
@@ -956,11 +979,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} else if (tool == TOOL_DUPLICATING) {
Point2 ofs = over_tile - rectangle.position;
+ Vector<int> ids;
_start_undo(TTR("Duplicate"));
+ ids.push_back(0);
for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
- _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose);
+ ids[0] = E->get().cell;
+ _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose);
}
_finish_undo();
@@ -970,17 +996,20 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} else if (tool == TOOL_MOVING) {
Point2 ofs = over_tile - rectangle.position;
+ Vector<int> ids;
_start_undo(TTR("Move"));
+ ids.push_back(TileMap::INVALID_CELL);
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false);
+ _set_cell(Point2i(j, i), ids, false, false, false);
}
}
for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
- _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose);
+ ids[0] = E->get().cell;
+ _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose);
}
_finish_undo();
@@ -1003,7 +1032,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Bucket Fill"));
Dictionary op;
- op["id"] = get_selected_tile();
+ op["id"] = get_selected_tiles();
op["flip_h"] = flip_h;
op["flip_v"] = flip_v;
op["transpose"] = transpose;
@@ -1079,7 +1108,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_ERASING;
- _set_cell(local, TileMap::INVALID_CELL);
+ _set_cell(local, invalid_cell);
}
return true;
@@ -1100,8 +1129,10 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} else if (tool == TOOL_BUCKET) {
+ Vector<int> ids;
+ ids.push_back(node->get_cell(over_tile.x, over_tile.y));
Dictionary pop;
- pop["id"] = node->get_cell(over_tile.x, over_tile.y);
+ pop["id"] = ids;
pop["flip_h"] = node->is_cell_x_flipped(over_tile.x, over_tile.y);
pop["flip_v"] = node->is_cell_y_flipped(over_tile.x, over_tile.y);
pop["transpose"] = node->is_cell_transposed(over_tile.x, over_tile.y);
@@ -1149,7 +1180,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
// Paint using bresenham line to prevent holes in painting if the user moves fast
Vector<Point2i> points = line(old_over_tile.x, over_tile.x, old_over_tile.y, over_tile.y);
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
for (int i = 0; i < points.size(); ++i) {
@@ -1159,7 +1190,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
paint_undo[pos] = _get_op_from_cell(pos);
}
- _set_cell(pos, id, flip_h, flip_v, transpose);
+ _set_cell(pos, ids, flip_h, flip_v, transpose);
}
return true;
@@ -1175,7 +1206,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Point2i pos = points[i];
- _set_cell(pos, TileMap::INVALID_CELL);
+ _set_cell(pos, invalid_cell);
}
return true;
@@ -1190,20 +1221,23 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (tool == TOOL_LINE_PAINT || tool == TOOL_LINE_ERASE) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
+ Vector<int> tmp_cell;
bool erasing = (tool == TOOL_LINE_ERASE);
+ tmp_cell.push_back(0);
if (erasing && paint_undo.size()) {
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), E->get().idx, E->get().xf, E->get().yf, E->get().tr);
+ tmp_cell[0] = E->get().idx;
+ _set_cell(E->key(), tmp_cell, E->get().xf, E->get().yf, E->get().tr);
}
}
paint_undo.clear();
- if (id != TileMap::INVALID_CELL) {
+ if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
Vector<Point2i> points = line(rectangle_begin.x, over_tile.x, rectangle_begin.y, over_tile.y);
@@ -1212,7 +1246,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
paint_undo[points[i]] = _get_op_from_cell(points[i]);
if (erasing)
- _set_cell(points[i], TileMap::INVALID_CELL);
+ _set_cell(points[i], invalid_cell);
}
canvas_item_editor->update();
@@ -1222,6 +1256,9 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
if (tool == TOOL_RECTANGLE_PAINT || tool == TOOL_RECTANGLE_ERASE) {
+ Vector<int> tmp_cell;
+ tmp_cell.push_back(0);
+
_select(rectangle_begin, over_tile);
if (tool == TOOL_RECTANGLE_ERASE) {
@@ -1230,7 +1267,8 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), E->get().idx, E->get().xf, E->get().yf, E->get().tr);
+ tmp_cell[0] = E->get().idx;
+ _set_cell(E->key(), tmp_cell, E->get().xf, E->get().yf, E->get().tr);
}
}
@@ -1242,7 +1280,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Point2i tile = Point2i(j, i);
paint_undo[tile] = _get_op_from_cell(tile);
- _set_cell(tile, TileMap::INVALID_CELL);
+ _set_cell(tile, invalid_cell);
}
}
}
@@ -1499,27 +1537,27 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) {
if (paint_undo.empty())
return;
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id == TileMap::INVALID_CELL)
+ if (ids.size() == 1 && ids[0] == TileMap::INVALID_CELL)
return;
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _draw_cell(id, E->key(), flip_h, flip_v, transpose, xform);
+ _draw_cell(ids[0], E->key(), flip_h, flip_v, transpose, xform);
}
} else if (tool == TOOL_RECTANGLE_PAINT) {
- int id = get_selected_tile();
+ Vector<int> ids = get_selected_tiles();
- if (id == TileMap::INVALID_CELL)
+ if (ids.size() == 1 && ids[0] == TileMap::INVALID_CELL)
return;
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform);
+ _draw_cell(ids[0], Point2i(j, i), flip_h, flip_v, transpose, xform);
}
}
} else if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) {
@@ -1557,17 +1595,17 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) {
} else if (tool == TOOL_BUCKET) {
- int tile = get_selected_tile();
- _draw_fill_preview(tile, over_tile, flip_h, flip_v, transpose, xform);
+ Vector<int> tiles = get_selected_tiles();
+ _draw_fill_preview(tiles[0], over_tile, flip_h, flip_v, transpose, xform);
} else {
- int st = get_selected_tile();
+ Vector<int> st = get_selected_tiles();
- if (st == TileMap::INVALID_CELL)
+ if (st.size() == 1 && st[0] == TileMap::INVALID_CELL)
return;
- _draw_cell(st, over_tile, flip_h, flip_v, transpose, xform);
+ _draw_cell(st[0], over_tile, flip_h, flip_v, transpose, xform);
}
}
}
@@ -1713,6 +1751,9 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
bucket_cache_tile = -1;
bucket_cache_visited = 0;
+ invalid_cell.resize(1);
+ invalid_cell[0] = TileMap::INVALID_CELL;
+
ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase Selection"), KEY_DELETE);
ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find Tile"), KEY_MASK_CMD + KEY_F);
ED_SHORTCUT("tile_map_editor/transpose", TTR("Transpose"), KEY_T);
@@ -1759,6 +1800,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
palette->set_max_columns(0);
palette->set_icon_mode(ItemList::ICON_MODE_TOP);
palette->set_max_text_lines(2);
+ palette->set_select_mode(ItemList::SELECT_MULTI);
palette->connect("item_selected", this, "_palette_selected");
palette_container->add_child(palette);
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index 77e9a33892..b8443ca962 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -157,6 +157,7 @@ class TileMapEditor : public VBoxContainer {
List<TileData> copydata;
Map<Point2i, CellOp> undo_data;
+ Vector<int> invalid_cell;
void _pick_tile(const Point2 &p_pos);
@@ -173,8 +174,8 @@ class TileMapEditor : public VBoxContainer {
void _update_copydata();
- int get_selected_tile() const;
- void set_selected_tile(int p_tile);
+ Vector<int> get_selected_tiles() const;
+ void set_selected_tiles(Vector<int> p_tile);
void _manual_toggled(bool p_enabled);
void _text_entered(const String &p_text);
@@ -187,7 +188,7 @@ class TileMapEditor : public VBoxContainer {
void _start_undo(const String &p_action);
void _finish_undo();
void _create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new);
- void _set_cell(const Point2i &p_pos, int p_value, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false);
+ void _set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false);
void _canvas_mouse_enter();
void _canvas_mouse_exit();
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index cf136a5e58..d867d1b137 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -1985,7 +1985,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
tab_container->add_child(plugin_settings);
timer = memnew(Timer);
- timer->set_wait_time(0.1);
+ timer->set_wait_time(1.5);
timer->connect("timeout", ProjectSettings::get_singleton(), "save");
timer->set_one_shot(true);
add_child(timer);
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index ed13e063bb..17deab25de 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -215,6 +215,9 @@ public:
void replace_node(Node *p_node, Node *p_by_node);
void open_script_dialog(Node *p_for_node);
+
+ ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; }
+
SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data);
};
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 57a003060e..24c4ba4cb7 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -582,6 +582,9 @@ void ScriptCreateDialog::_bind_methods() {
ClassDB::bind_method("_path_changed", &ScriptCreateDialog::_path_changed);
ClassDB::bind_method("_path_entered", &ScriptCreateDialog::_path_entered);
ClassDB::bind_method("_template_changed", &ScriptCreateDialog::_template_changed);
+
+ ClassDB::bind_method(D_METHOD("config", "inherits", "path"), &ScriptCreateDialog::config);
+
ADD_SIGNAL(MethodInfo("script_created", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
}
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index 45041bcf59..ae88b3a035 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -493,7 +493,7 @@ EditorSettingsDialog::EditorSettingsDialog() {
//get_cancel()->set_text("Close");
timer = memnew(Timer);
- timer->set_wait_time(0.1);
+ timer->set_wait_time(1.5);
timer->connect("timeout", this, "_settings_save");
timer->set_one_shot(true);
add_child(timer);
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index 873420b383..17f3b4886e 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -202,7 +202,7 @@ void EditorSpatialGizmo::add_unscaled_billboard(const Ref<Material> &p_material,
}
selectable_icon_size = p_scale;
- mesh->set_custom_aabb(AABB(Vector3(-selectable_icon_size, -selectable_icon_size, -selectable_icon_size) * 40.0f, Vector3(selectable_icon_size, selectable_icon_size, selectable_icon_size) * 80.0f));
+ mesh->set_custom_aabb(AABB(Vector3(-selectable_icon_size, -selectable_icon_size, -selectable_icon_size) * 100.0f, Vector3(selectable_icon_size, selectable_icon_size, selectable_icon_size) * 200.0f));
ins.mesh = mesh;
ins.unscaled = true;
@@ -212,7 +212,7 @@ void EditorSpatialGizmo::add_unscaled_billboard(const Ref<Material> &p_material,
VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());
}
- selectable_icon_size = p_scale * 2.0;
+ selectable_icon_size = p_scale;
instances.push_back(ins);
}
@@ -475,8 +475,9 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point,
float scale = t.origin.distance_to(p_camera->get_camera_transform().origin);
if (p_camera->get_projection() == Camera::PROJECTION_ORTHOGONAL) {
- float h = Math::abs(p_camera->get_size());
- scale = (h * 2.0);
+ float aspect = p_camera->get_viewport()->get_visible_rect().size.aspect();
+ float size = p_camera->get_size();
+ scale = size / aspect;
}
Point2 center = p_camera->unproject_position(t.origin);
diff --git a/main/main.cpp b/main/main.cpp
index e2b3bb8e6f..23acb60c89 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -715,6 +715,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
memdelete(sdr);
} else {
script_debugger = sdr;
+ sdr->set_allow_focus_steal_pid(allow_focus_steal_pid);
}
} else if (debug_mode == "local") {
@@ -1179,10 +1180,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
#endif
- if (allow_focus_steal_pid) {
- OS::get_singleton()->enable_for_stealing_focus(allow_focus_steal_pid);
- }
-
MAIN_PRINT("Main: Load Modules, Physics, Drivers, Scripts");
register_platform_apis();
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index d6abbc1bcf..7bab718b81 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -697,11 +697,21 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p
Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
if (E) {
godot_variant result;
+
+#ifdef DEBUG_ENABLED
+ current_method_call = p_method;
+#endif
+
result = E->get().method.method((godot_object *)owner,
E->get().method.method_data,
userdata,
p_argcount,
(godot_variant **)p_args);
+
+#ifdef DEBUG_ENABLED
+ current_method_call = "";
+#endif
+
Variant res = *(Variant *)&result;
godot_variant_destroy(&result);
r_error.error = Variant::CallError::CALL_OK;
@@ -716,6 +726,15 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p
}
void NativeScriptInstance::notification(int p_notification) {
+#ifdef DEBUG_ENABLED
+ if (p_notification == MainLoop::NOTIFICATION_CRASH) {
+ if (current_method_call != StringName("")) {
+ ERR_PRINTS("NativeScriptInstance detected crash on method: " + current_method_call);
+ current_method_call = "";
+ }
+ }
+#endif
+
Variant value = p_notification;
const Variant *args[1] = { &value };
call_multilevel("_notification", args, 1);
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index b47962dc37..be093dde4b 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -181,6 +181,9 @@ class NativeScriptInstance : public ScriptInstance {
Object *owner;
Ref<NativeScript> script;
+#ifdef DEBUG_ENABLED
+ StringName current_method_call;
+#endif
void _ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount);
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 61130cb58f..10599f0c38 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -1552,7 +1552,7 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
- gdfs->previous_state = Ref<GDScriptFunctionState>(this);
+ gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
}
}
@@ -1560,10 +1560,10 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar
state.result = Variant();
if (completed) {
- GDScriptFunctionState *state = this;
- while (state != NULL) {
- state->emit_signal("completed", ret);
- state = *(state->previous_state);
+ if (first_state.is_valid()) {
+ first_state->emit_signal("completed", ret);
+ } else {
+ emit_signal("completed", ret);
}
}
@@ -1614,7 +1614,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
- gdfs->previous_state = Ref<GDScriptFunctionState>(this);
+ gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
}
}
@@ -1622,10 +1622,10 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
state.result = Variant();
if (completed) {
- GDScriptFunctionState *state = this;
- while (state != NULL) {
- state->emit_signal("completed", ret);
- state = *(state->previous_state);
+ if (first_state.is_valid()) {
+ first_state->emit_signal("completed", ret);
+ } else {
+ emit_signal("completed", ret);
}
}
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 836325f0fe..770d5c8733 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -234,7 +234,7 @@ class GDScriptFunctionState : public Reference {
GDScriptFunction *function;
GDScriptFunction::CallState state;
Variant _signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
- Ref<GDScriptFunctionState> previous_state;
+ Ref<GDScriptFunctionState> first_state;
protected:
static void _bind_methods();
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index cccf09c58e..ce91e7dff3 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -105,6 +105,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"prints",
"printerr",
"printraw",
+ "print_debug",
"var2str",
"str2var",
"var2bytes",
@@ -702,6 +703,23 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
r_ret = Variant();
} break;
+ case TEXT_PRINT_DEBUG: {
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+
+ str += p_args[i]->operator String();
+ }
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ if (script->debug_get_stack_level_count() > 0) {
+ str += "\n\t";
+ str += "At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)); // + " in function '" + script->debug_get_stack_level_function(0) + "'";
+ }
+
+ //str+="\n";
+ print_line(str);
+ r_ret = Variant();
+ } break;
case VAR_TO_STR: {
VALIDATE_ARG_COUNT(1);
String vars;
@@ -1733,6 +1751,14 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
+ case TEXT_PRINT_DEBUG: {
+
+ MethodInfo mi("print_debug");
+ mi.return_val.type = Variant::NIL;
+ mi.flags |= METHOD_FLAG_VARARG;
+ return mi;
+
+ } break;
case VAR_TO_STR: {
MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var"));
mi.return_val.type = Variant::STRING;
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index 899a7ebd25..a29f06e839 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -96,6 +96,7 @@ public:
TEXT_PRINT_SPACED,
TEXT_PRINTERR,
TEXT_PRINTRAW,
+ TEXT_PRINT_DEBUG,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 4b96824dca..fc5972c810 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -231,6 +231,13 @@ void GridMapEditor::_menu_option(int p_option) {
_delete_selection();
} break;
+ case MENU_OPTION_SELECTION_FILL: {
+ if (!selection.active)
+ return;
+
+ _fill_selection();
+
+ } break;
case MENU_OPTION_GRIDMAP_SETTINGS: {
settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50) * EDSCALE);
} break;
@@ -455,6 +462,29 @@ void GridMapEditor::_delete_selection() {
_validate_selection();
}
+void GridMapEditor::_fill_selection() {
+
+ if (!selection.active)
+ return;
+
+ undo_redo->create_action(TTR("GridMap Fill Selection"));
+ for (int i = selection.begin.x; i <= selection.end.x; i++) {
+
+ for (int j = selection.begin.y; j <= selection.end.y; j++) {
+
+ for (int k = selection.begin.z; k <= selection.end.z; k++) {
+
+ undo_redo->add_do_method(node, "set_cell_item", i, j, k, selected_pallete, cursor_rot);
+ undo_redo->add_undo_method(node, "set_cell_item", i, j, k, node->get_cell_item(i, j, k), node->get_cell_item_orientation(i, j, k));
+ }
+ }
+ }
+ undo_redo->commit_action();
+
+ selection.active = false;
+ _validate_selection();
+}
+
void GridMapEditor::_update_duplicate_indicator() {
if (!selection.active || input_action != INPUT_DUPLICATE) {
@@ -1072,6 +1102,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_SHIFT + KEY_C);
options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_MASK_SHIFT + KEY_X);
+ options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_SHIFT + KEY_F);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Settings"), MENU_OPTION_GRIDMAP_SETTINGS);
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index f79d9aefa0..7c5feda125 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -168,6 +168,7 @@ class GridMapEditor : public VBoxContainer {
MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR,
MENU_OPTION_SELECTION_DUPLICATE,
MENU_OPTION_SELECTION_CLEAR,
+ MENU_OPTION_SELECTION_FILL,
MENU_OPTION_REMOVE_AREA,
MENU_OPTION_GRIDMAP_SETTINGS
@@ -200,6 +201,7 @@ class GridMapEditor : public VBoxContainer {
void _floor_changed(float p_value);
void _delete_selection();
+ void _fill_selection();
EditorNode *editor;
bool do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click);
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 03e187e5b0..89a3caf4e4 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -5,9 +5,11 @@ Import('env_modules')
env_mono = env_modules.Clone()
-from compat import byte_to_str
+# TODO move functions to their own modules
def make_cs_files_header(src, dst):
+ from compat import byte_to_str
+
with open(dst, 'w') as header:
header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n')
header.write('#ifndef _CS_FILES_DATA_H\n')
@@ -75,6 +77,13 @@ else:
if ARGUMENTS.get('yolo_copy', False):
env_mono.Append(CPPDEFINES=['YOLO_COPY'])
+# Configure TLS checks
+
+import tls_configure
+conf = Configure(env_mono)
+tls_configure.configure(conf)
+env_mono = conf.Finish()
+
# Build GodotSharpTools solution
@@ -128,12 +137,13 @@ def find_msbuild_windows():
raise RuntimeError('Cannot find mono root directory')
framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
-
mono_bin_dir = os.path.join(mono_root, 'bin')
-
msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
if os.path.isfile(msbuild_mono):
+ # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
+ # building with Mono's MSBuild. They must point to the batch files
+ # in Mono's bin directory to make sure they are executed with Mono.
mono_msbuild_env = {
'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),
diff --git a/modules/mono/config.py b/modules/mono/config.py
index ebf8512fb6..9a000a2a72 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -4,23 +4,15 @@ import os
import sys
import subprocess
+from distutils.version import LooseVersion
from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables
monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
-def find_file_in_dir(directory, files, prefix='', extension=''):
- if not extension.startswith('.'):
- extension = '.' + extension
- for curfile in files:
- if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
- return curfile
- return ''
-
-
def can_build(env, platform):
- if platform in ["javascript"]:
+ if platform in ['javascript']:
return False # Not yet supported
return True
@@ -30,6 +22,27 @@ def is_enabled():
return False
+def get_doc_classes():
+ return [
+ '@C#',
+ 'CSharpScript',
+ 'GodotSharp',
+ ]
+
+
+def get_doc_path():
+ return 'doc_classes'
+
+
+def find_file_in_dir(directory, files, prefix='', extension=''):
+ if not extension.startswith('.'):
+ extension = '.' + extension
+ for curfile in files:
+ if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
+ return curfile
+ return ''
+
+
def copy_file(src_dir, dst_dir, name):
from shutil import copyfile
@@ -55,7 +68,7 @@ def custom_path_is_dir_create(key, val, env):
def configure(env):
env.use_ptrcall = True
- env.add_module_version_string("mono")
+ env.add_module_version_string('mono')
envvars = Variables()
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
@@ -84,6 +97,9 @@ def configure(env):
if not mono_root:
raise RuntimeError('Mono installation directory not found')
+ mono_version = mono_root_try_find_mono_version(mono_root)
+ configure_for_mono_version(env, mono_version)
+
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
@@ -149,20 +165,14 @@ def configure(env):
# We can't use pkg-config to link mono statically,
# but we can still use it to find the mono root directory
if not mono_root and mono_static:
- def pkgconfig_try_find_mono_root():
- tmpenv = Environment()
- tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
- tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
- for hint_dir in tmpenv['LIBPATH']:
- name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
- if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
- return os.path.join(hint_dir, '..')
- return ''
- mono_root = pkgconfig_try_find_mono_root()
+ mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
if not mono_root:
raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
if mono_root:
+ mono_version = mono_root_try_find_mono_version(mono_root)
+ configure_for_mono_version(env, mono_version)
+
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
@@ -178,18 +188,18 @@ def configure(env):
if mono_static:
mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
- if sys.platform == "darwin":
+ if sys.platform == 'darwin':
env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
- elif sys.platform == "linux" or sys.platform == "linux2":
+ elif sys.platform == 'linux' or sys.platform == 'linux2':
env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
else:
raise RuntimeError('mono-static: Not supported on this platform')
else:
env.Append(LIBS=[mono_lib])
- if sys.platform == "darwin":
+ if sys.platform == 'darwin':
env.Append(LIBS=['iconv', 'pthread'])
- elif sys.platform == "linux" or sys.platform == "linux2":
+ elif sys.platform == 'linux' or sys.platform == 'linux2':
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
if not mono_static:
@@ -204,11 +214,14 @@ def configure(env):
else:
assert not mono_static
+ mono_version = pkgconfig_try_find_mono_version()
+ configure_for_mono_version(env, mono_version)
+
env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
mono_lib_path = ''
mono_so_name = ''
- mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
+ mono_prefix = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
tmpenv = Environment()
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
@@ -230,13 +243,44 @@ def configure(env):
env.Append(LINKFLAGS='-rdynamic')
-def get_doc_classes():
- return [
- "@C#",
- "CSharpScript",
- "GodotSharp",
- ]
+def configure_for_mono_version(env, mono_version):
+ if mono_version is None:
+ raise RuntimeError('Mono JIT compiler version not found')
+ print('Mono JIT compiler version: ' + str(mono_version))
+ if mono_version >= LooseVersion("5.12.0"):
+ env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
-def get_doc_path():
- return "doc_classes"
+def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
+ tmpenv = Environment()
+ tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
+ tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
+ for hint_dir in tmpenv['LIBPATH']:
+ name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+ if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
+ return os.path.join(hint_dir, '..')
+ return ''
+
+
+def pkgconfig_try_find_mono_version():
+ lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines()
+ greater_version = None
+ for line in lines:
+ try:
+ version = LooseVersion(line)
+ if greater_version is None or version > greater_version:
+ greater_version = version
+ except ValueError:
+ pass
+ return greater_version
+
+
+def mono_root_try_find_mono_version(mono_root):
+ from compat import decode_utf8
+
+ output = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version'])
+ first_line = decode_utf8(output.splitlines()[0])
+ try:
+ return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())])
+ except (ValueError, IndexError):
+ return None
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 2420cdb4af..62a6b96bb5 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -49,6 +49,8 @@
#include "mono_gd/gd_mono_class.h"
#include "mono_gd/gd_mono_marshal.h"
#include "signal_awaiter_utils.h"
+#include "utils/macros.h"
+#include "utils/thread_local.h"
#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
@@ -296,28 +298,28 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin
String script_template = "using " BINDINGS_NAMESPACE ";\n"
"using System;\n"
"\n"
- "public class %CLASS_NAME% : %BASE_CLASS_NAME%\n"
+ "public class %CLASS% : %BASE%\n"
"{\n"
" // Declare member variables here. Examples:\n"
" // private int a = 2;\n"
" // private string b = \"text\";\n"
"\n"
- " // Called when the node enters the scene tree for the first time."
+ " // Called when the node enters the scene tree for the first time.\n"
" public override void _Ready()\n"
" {\n"
- " "
+ " \n"
" }\n"
"\n"
- "// // Called every frame. 'delta' is the elapsed time since the previous frame."
- "// public override void _Process(float delta)\n"
- "// {\n"
- "// "
- "// }\n"
+ "// // Called every frame. 'delta' is the elapsed time since the previous frame.\n"
+ "// public override void _Process(float delta)\n"
+ "// {\n"
+ "// \n"
+ "// }\n"
"}\n";
String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
- script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name)
- .replace("%CLASS_NAME%", p_class_name);
+ script_template = script_template.replace("%BASE%", base_class_name)
+ .replace("%CLASS%", p_class_name);
Ref<CSharpScript> script;
script.instance();
@@ -474,7 +476,7 @@ String CSharpLanguage::_get_indentation() const {
Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
#ifdef DEBUG_ENABLED
- // Printing an error here will result in endless recursion, so we must be careful
+ _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
return Vector<StackInfo>();
@@ -498,15 +500,15 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
- // Printing an error here could result in endless recursion, so we must be careful
+ _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames);
- MonoArray *frames = st_get_frames(p_stack_trace, &exc);
+ MonoArray *frames = st_get_frames(p_stack_trace, (MonoObject **)&exc);
if (exc) {
- GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
return Vector<StackInfo>();
}
@@ -527,10 +529,10 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoString *file_name;
int file_line_num;
MonoString *method_decl;
- get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc);
+ get_sf_info(frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc);
if (exc) {
- GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
return Vector<StackInfo>();
}
@@ -559,12 +561,12 @@ void CSharpLanguage::frame() {
ERR_FAIL_NULL(thunk);
- MonoObject *ex;
- thunk(task_scheduler, &ex);
+ MonoException *exc = NULL;
+ thunk(task_scheduler, (MonoObject **)&exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
- ERR_FAIL();
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
+ _UNREACHABLE_();
}
}
}
@@ -743,10 +745,9 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) {
Ref<CSharpScript> scr = E->key();
- scr->signals_invalidated = true;
scr->exports_invalidated = true;
+ scr->signals_invalidated = true;
scr->reload(p_soft_reload);
- scr->update_signals();
scr->update_exports();
//restore state if saved
@@ -1076,11 +1077,11 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
GDMonoProperty *property = top->get_property(p_name);
if (property) {
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
MonoObject *value = property->get_value(mono_object, &exc);
if (exc) {
r_ret = Variant();
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
} else {
r_ret = GDMonoMarshal::mono_object_to_variant(value);
}
@@ -1488,12 +1489,12 @@ bool CSharpScript::_update_exports() {
CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
- MonoObject *ex = NULL;
- ctor->invoke(tmp_object, NULL, &ex);
+ MonoException *exc = NULL;
+ ctor->invoke(tmp_object, NULL, &exc);
- if (ex) {
+ if (exc) {
ERR_PRINT("Exception thrown from constructor of temporary MonoObject:");
- mono_print_unhandled_exception(ex);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
tmp_object = NULL;
ERR_FAIL_V(false);
}
@@ -1542,11 +1543,11 @@ bool CSharpScript::_update_exports() {
exported_members_cache.push_front(prop_info);
if (tmp_object) {
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
MonoObject *ret = property->get_value(tmp_object, &exc);
if (exc) {
exported_members_defval_cache[name] = Variant();
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
} else {
exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret);
}
@@ -1577,37 +1578,33 @@ bool CSharpScript::_update_exports() {
return false;
}
-bool CSharpScript::_update_signals() {
- if (!valid)
- return false;
-
- bool changed = false;
-
- if (signals_invalidated) {
- signals_invalidated = false;
+void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class) {
- GDMonoClass *top = script_class;
+ // no need to load the script's signals more than once
+ if (!signals_invalidated) {
+ return;
+ }
- _signals.clear();
- changed = true; // TODO Do a real check for change
+ // make sure this classes signals are empty when loading for the first time
+ _signals.clear();
- while (top && top != native) {
- const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
- for (int i = delegates.size() - 1; i >= 0; --i) {
- Vector<Argument> parameters;
+ GDMonoClass *top = p_class;
+ while (top && top != p_native_class) {
+ const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
+ for (int i = delegates.size() - 1; i >= 0; --i) {
+ Vector<Argument> parameters;
- GDMonoClass *delegate = delegates[i];
+ GDMonoClass *delegate = delegates[i];
- if (_get_signal(top, delegate, parameters)) {
- _signals[delegate->get_name()] = parameters;
- }
+ if (_get_signal(top, delegate, parameters)) {
+ _signals[delegate->get_name()] = parameters;
}
-
- top = top->get_parent_class();
}
+
+ top = top->get_parent_class();
}
- return changed;
+ signals_invalidated = false;
}
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
@@ -1846,6 +1843,8 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class) {
top = top->get_parent_class();
}
+ script->load_script_signals(script->script_class, script->native);
+
return script;
}
@@ -1916,7 +1915,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Construct
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
- ctor->invoke(mono_object, p_args, NULL);
+ ctor->invoke(mono_object, p_args);
// Tie managed to unmanaged
instance->gchandle = MonoGCHandle::create_strong(mono_object);
@@ -1971,7 +1970,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this));
placeholders.insert(si);
_update_exports();
- _update_signals();
return si;
#else
return NULL;
@@ -1990,8 +1988,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
ERR_FAIL_V(NULL);
}
- update_signals();
-
if (native) {
String native_name = native->get_name();
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
@@ -2112,6 +2108,8 @@ Error CSharpScript::reload(bool p_keep_state) {
top->fetch_methods_with_godot_api_checks(native);
top = top->get_parent_class();
}
+
+ load_script_signals(script_class, native);
}
return OK;
@@ -2171,10 +2169,6 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
}
}
-void CSharpScript::update_signals() {
- _update_signals();
-}
-
Ref<Script> CSharpScript::get_base_script() const {
// TODO search in metadata file once we have it, not important any way?
@@ -2239,9 +2233,10 @@ CSharpScript::CSharpScript() :
#ifdef TOOLS_ENABLED
source_changed_cache = false;
exports_invalidated = true;
- signals_invalidated = true;
#endif
+ signals_invalidated = true;
+
_resource_path_changed();
#ifdef DEBUG_ENABLED
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index cae2bbf40a..df597ba776 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -111,7 +111,7 @@ class CSharpScript : public Script {
void _clear();
- bool _update_signals();
+ void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params);
bool _update_exports();
@@ -149,7 +149,6 @@ public:
virtual bool has_script_signal(const StringName &p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
- virtual void update_signals();
/* TODO */ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
index e4269b0aec..bc95607743 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/mono/editor/csharp_project.cpp
@@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
Variant dir = p_dir;
Variant compile_items = p_files;
const Variant *args[2] = { &dir, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -68,11 +68,11 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll
Variant core_dll_path = p_core_dll_path;
Variant compile_items = p_files;
const Variant *args[3] = { &dir, &core_dll_path, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -89,11 +89,11 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve
Variant name = p_name;
Variant compile_items = p_files;
const Variant *args[3] = { &dir, &name, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -110,11 +110,11 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
Variant item_type = p_item_type;
Variant include = p_include;
const Variant *args[3] = { &project_path, &item_type, &include };
- MonoObject *ex = NULL;
- klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
}
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index 43d58caad2..156a01ff5a 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -512,14 +512,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
const Variant *ctor_args[2] = { &solution, &config };
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
GDMonoMethod *ctor = klass->get_method(".ctor", 2);
- ctor->invoke(mono_object, ctor_args, &ex);
+ ctor->invoke(mono_object, ctor_args, &exc);
- if (ex) {
+ if (exc) {
exited = true;
- GDMonoUtils::print_unhandled_exception(ex);
- String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
+ GDMonoUtils::debug_unhandled_exception(exc);
+ String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
ERR_FAIL();
@@ -534,14 +534,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
- ex = NULL;
+ exc = NULL;
GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3);
- build_method->invoke(mono_object, args, &ex);
+ build_method->invoke(mono_object, args, &exc);
- if (ex) {
+ if (exc) {
exited = true;
- GDMonoUtils::print_unhandled_exception(ex);
- String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
+ GDMonoUtils::debug_unhandled_exception(exc);
+ String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
ERR_FAIL();
diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp
index 48a285561d..9f05711fd6 100644
--- a/modules/mono/editor/monodevelop_instance.cpp
+++ b/modules/mono/editor/monodevelop_instance.cpp
@@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) {
ERR_FAIL_NULL(execute_method);
ERR_FAIL_COND(gc_handle.is_null());
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
Variant files = p_files;
const Variant *args[1] = { &files };
- execute_method->invoke(gc_handle->get_target(), args, &ex);
+ execute_method->invoke(gc_handle->get_target(), args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
}
@@ -68,14 +68,14 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) {
MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
GDMonoMethod *ctor = klass->get_method(".ctor", 1);
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
Variant solution = p_solution;
const Variant *args[1] = { &solution };
- ctor->invoke(obj, args, &ex);
+ ctor->invoke(obj, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs
index 0d20a12563..1ed2889833 100644
--- a/modules/mono/glue/cs_files/Mathf.cs
+++ b/modules/mono/glue/cs_files/Mathf.cs
@@ -150,7 +150,7 @@ namespace Godot
public static real_t InverseLerp(real_t from, real_t to, real_t weight)
{
- return (Clamp(weight, 0f, 1f) - from) / (to - from);
+ return (weight - from) / (to - from);
}
public static bool IsInf(real_t s)
@@ -165,7 +165,7 @@ namespace Godot
public static real_t Lerp(real_t from, real_t to, real_t weight)
{
- return from + (to - from) * Clamp(weight, 0f, 1f);
+ return from + (to - from) * weight;
}
public static real_t Log(real_t s)
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index 4e82bcd03e..12109045e0 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -34,15 +34,12 @@
uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) {
- return mono_gchandle_new(
- p_object,
- false /* do not pin the object */
- );
+ return mono_gchandle_new(p_object, /* pinned: */ false);
}
uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) {
- return mono_gchandle_new_weakref(p_object, false);
+ return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
}
Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) {
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 0646580eaa..bc84f43b4f 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -53,14 +53,6 @@
#include "main/main.h"
#endif
-void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) {
-
- (void)user_data; // UNUSED
-
- GDMonoUtils::print_unhandled_exception(exc);
- abort();
-}
-
#ifdef MONO_PRINT_HANDLER_ENABLED
void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
@@ -197,6 +189,8 @@ void GDMono::initialize() {
mono_config_parse(NULL);
+ mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
+
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
ERR_EXPLAIN("Mono: Failed to initialize runtime");
@@ -279,8 +273,6 @@ void GDMono::initialize() {
OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
#endif
- mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
-
OS::get_singleton()->print("Mono: INITIALIZED\n");
}
@@ -652,12 +644,12 @@ Error GDMono::_unload_scripts_domain() {
_GodotSharp::get_singleton()->_dispose_callback();
- MonoObject *ex = NULL;
- mono_domain_try_unload(domain, &ex);
+ MonoException *exc = NULL;
+ mono_domain_try_unload(domain, (MonoObject **)&exc);
- if (ex) {
- ERR_PRINT("Exception thrown when unloading scripts domain:");
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ ERR_PRINT("Exception thrown when unloading scripts domain");
+ GDMonoUtils::debug_unhandled_exception(exc);
return FAILED;
}
@@ -763,12 +755,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
- MonoObject *ex = NULL;
- mono_domain_try_unload(p_domain, &ex);
+ MonoException *exc = NULL;
+ mono_domain_try_unload(p_domain, (MonoObject **)&exc);
- if (ex) {
- ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:");
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`");
+ GDMonoUtils::debug_unhandled_exception(exc);
return FAILED;
}
@@ -811,6 +803,21 @@ void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
assemblies.erase(p_domain_id);
}
+void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
+
+// This method will be called by the runtime when a thrown exception is not handled.
+// It won't be called when we manually treat a thrown exception as unhandled.
+// We assume the exception was already printed before calling this hook.
+
+#ifdef DEBUG_ENABLED
+ GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
+ if (ScriptDebugger::get_singleton())
+ ScriptDebugger::get_singleton()->idle_poll();
+#endif
+ abort();
+ _UNREACHABLE_();
+}
+
GDMono::GDMono() {
singleton = this;
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 5e01152870..e0ec6ced5e 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -164,6 +164,8 @@ public:
static GDMono *get_singleton() { return singleton; }
+ static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+
// Do not use these, unless you know what you're doing
void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
GDMonoAssembly **get_loaded_assembly(const String &p_name);
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index a1a79f957f..505c030ca1 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -32,8 +32,12 @@
#include "../csharp_script.h"
#include "../mono_gc_handle.h"
+#include "../utils/macros.h"
+#include "../utils/thread_local.h"
#include "gd_mono_utils.h"
+#include <mono/metadata/exception.h>
+
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
@@ -64,4 +68,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
return;
}
+
+void unhandled_exception(MonoException *p_exc) {
+ mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
+ abort();
+ _UNREACHABLE_();
+}
+
} // namespace GDMonoInternals
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h
index abec65e7d4..50cadcedf6 100644
--- a/modules/mono/mono_gd/gd_mono_internals.h
+++ b/modules/mono/mono_gd/gd_mono_internals.h
@@ -33,11 +33,20 @@
#include <mono/jit/jit.h>
+#include "../utils/macros.h"
+
#include "core/object.h"
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
-}
+
+/**
+ * Do not call this function directly.
+ * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
+ */
+_NO_RETURN_ void unhandled_exception(MonoException *p_exc);
+
+} // namespace GDMonoInternals
#endif // GD_MONO_INTERNALS_H
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index c04fcca962..31c5bbb2fb 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -837,11 +837,13 @@ MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
- MonoObject *ex = NULL;
- MonoObject *ret = arrays_to_dict(keys, values, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(NULL);
}
@@ -858,11 +860,13 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
MonoArray *keys = NULL;
MonoArray *values = NULL;
- MonoObject *ex = NULL;
- dict_to_arrays(p_dict, &keys, &values, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(Dictionary());
}
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index ad52904945..c8df1038ce 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -95,7 +95,7 @@ void *GDMonoMethod::get_thunk() {
return mono_method_get_unmanaged_thunk(mono_method);
}
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) {
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) {
if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
@@ -104,28 +104,32 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
mono_array_set(params, MonoObject *, i, boxed_param);
}
- MonoObject *exc = NULL;
- MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc);
+ 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;
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
return ret;
} else {
- MonoObject *exc = NULL;
- mono_runtime_invoke(mono_method, p_object, NULL, &exc);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
@@ -133,21 +137,23 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
}
}
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) {
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) {
ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
return invoke_raw(p_object, NULL, r_exc);
}
-MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &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;
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index a173af83f4..444ec2a67d 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -71,9 +71,9 @@ public:
void *get_thunk();
- MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL);
- MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL);
- MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL);
+ MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL);
+ MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL);
+ MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
String get_full_name(bool p_signature = false) const;
String get_full_name_no_class() const;
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index 0fe527b199..1f837a2d78 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -138,47 +138,53 @@ bool GDMonoProperty::has_setter() {
return mono_property_get_set_method(mono_property) != NULL;
}
-void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) {
+void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **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);
- MonoObject *exc = NULL;
- mono_runtime_invoke_array(prop_method, p_object, params, &exc);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
}
-void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- mono_property_set_value(mono_property, p_object, p_params, &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;
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
}
-MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, &exc);
+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;
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
index 2a0065e850..b3f1e2114a 100644
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ b/modules/mono/mono_gd/gd_mono_property.h
@@ -62,9 +62,9 @@ public:
_FORCE_INLINE_ ManagedType get_type() const { return type; }
- void set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc = NULL);
- void set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL);
- MonoObject *get_value(MonoObject *p_object, MonoObject **r_exc = NULL);
+ void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = NULL);
+ void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
+ MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = NULL);
bool get_bool_value(MonoObject *p_object);
int get_int_value(MonoObject *p_object);
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index fbdbeaa3f7..a229552b76 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -30,12 +30,15 @@
#include "gd_mono_utils.h"
+#include <mono/metadata/exception.h>
+
#include "os/dir_access.h"
#include "os/os.h"
#include "project_settings.h"
#include "reference.h"
#include "../csharp_script.h"
+#include "../utils/macros.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
@@ -397,10 +400,10 @@ MonoDomain *create_domain(const String &p_friendly_name) {
return domain;
}
-String get_exception_name_and_message(MonoObject *p_ex) {
+String get_exception_name_and_message(MonoException *p_ex) {
String res;
- MonoClass *klass = mono_object_get_class(p_ex);
+ MonoClass *klass = mono_object_get_class((MonoObject *)p_ex);
MonoType *type = mono_class_get_type(klass);
char *full_name = mono_type_full_name(type);
@@ -410,29 +413,31 @@ String get_exception_name_and_message(MonoObject *p_ex) {
res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL);
+ MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_ex, NULL, NULL);
res += GDMonoMarshal::mono_string_to_godot(msg);
return res;
}
-void print_unhandled_exception(MonoObject *p_exc) {
- print_unhandled_exception(p_exc, false);
+void debug_print_unhandled_exception(MonoException *p_exc) {
+ print_unhandled_exception(p_exc);
+ debug_send_unhandled_exception_error(p_exc);
}
-void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
- mono_print_unhandled_exception(p_exc);
+void debug_send_unhandled_exception_error(MonoException *p_exc) {
#ifdef DEBUG_ENABLED
if (!ScriptDebugger::get_singleton())
return;
+ _TLS_RECURSION_GUARD_;
+
ScriptLanguage::StackInfo separator;
- separator.file = "";
+ separator.file = String();
separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
separator.line = 0;
Vector<ScriptLanguage::StackInfo> si;
- String exc_msg = "";
+ String exc_msg;
while (p_exc != NULL) {
GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
@@ -441,24 +446,16 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
MonoBoolean need_file_info = true;
void *ctor_args[2] = { p_exc, &need_file_info };
- MonoObject *unexpected_exc = NULL;
+ MonoException *unexpected_exc = NULL;
CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
- if (unexpected_exc != NULL) {
- mono_print_unhandled_exception(unexpected_exc);
-
- if (p_recursion_caution) {
- // Called from CSharpLanguage::get_current_stack_info,
- // so printing an error here could result in endless recursion
- OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
- return;
- } else {
- ERR_FAIL();
- }
+ if (unexpected_exc) {
+ GDMonoInternals::unhandled_exception(unexpected_exc);
+ _UNREACHABLE_();
}
Vector<ScriptLanguage::StackInfo> _si;
- if (stack_trace != NULL && !p_recursion_caution) {
+ if (stack_trace != NULL) {
_si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
for (int i = _si.size() - 1; i >= 0; i--)
si.insert(0, _si[i]);
@@ -466,10 +463,15 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
- GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException");
- p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL;
- if (p_exc != NULL)
+ GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
+ GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
+ CRASH_COND(inner_exc_prop == NULL);
+
+ MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
+ if (inner_exc != NULL)
si.insert(0, separator);
+
+ p_exc = (MonoException *)inner_exc;
}
String file = si.size() ? si[0].file : __FILE__;
@@ -481,4 +483,38 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
#endif
}
+void debug_unhandled_exception(MonoException *p_exc) {
+#ifdef DEBUG_ENABLED
+ GDMonoUtils::debug_send_unhandled_exception_error(p_exc);
+ if (ScriptDebugger::get_singleton())
+ ScriptDebugger::get_singleton()->idle_poll();
+#endif
+ GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
+ _UNREACHABLE_();
+}
+
+void print_unhandled_exception(MonoException *p_exc) {
+ mono_print_unhandled_exception((MonoObject *)p_exc);
+}
+
+void set_pending_exception(MonoException *p_exc) {
+#ifdef HAS_PENDING_EXCEPTIONS
+ if (get_runtime_invoke_count() == 0) {
+ debug_unhandled_exception(p_exc);
+ _UNREACHABLE_();
+ }
+
+ if (!mono_runtime_set_pending_exception(p_exc, false)) {
+ ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:");
+ GDMonoUtils::debug_print_unhandled_exception(p_exc);
+ }
+#else
+ debug_unhandled_exception(p_exc);
+ _UNREACHABLE_();
+#endif
+}
+
+_THREAD_LOCAL_(int)
+current_invoke_count = 0;
+
} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index fc13a00e85..4f8e5932cd 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -34,6 +34,8 @@
#include <mono/metadata/threads.h>
#include "../mono_gc_handle.h"
+#include "../utils/macros.h"
+#include "../utils/thread_local.h"
#include "gd_mono_header.h"
#include "object.h"
@@ -184,10 +186,28 @@ MonoObject *create_managed_from(const RID &p_from);
MonoDomain *create_domain(const String &p_friendly_name);
-String get_exception_name_and_message(MonoObject *p_ex);
+String get_exception_name_and_message(MonoException *p_ex);
-void print_unhandled_exception(MonoObject *p_exc);
-void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution);
+void debug_print_unhandled_exception(MonoException *p_exc);
+void debug_send_unhandled_exception_error(MonoException *p_exc);
+_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc);
+void print_unhandled_exception(MonoException *p_exc);
+
+/**
+ * Sets the exception as pending. The exception will be thrown when returning to managed code.
+ * If no managed method is being invoked by the runtime, the exception will be treated as
+ * an unhandled exception and the method will not return.
+ */
+void set_pending_exception(MonoException *p_exc);
+
+extern _THREAD_LOCAL_(int) current_invoke_count;
+
+_FORCE_INLINE_ int get_runtime_invoke_count() {
+ return current_invoke_count;
+}
+_FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
+ return current_invoke_count;
+}
} // namespace GDMonoUtils
@@ -206,4 +226,11 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution);
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
#endif
+#define GD_MONO_BEGIN_RUNTIME_INVOKE \
+ int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
+ _runtime_invoke_count_ref += 1;
+
+#define GD_MONO_END_RUNTIME_INVOKE \
+ _runtime_invoke_count_ref -= 1;
+
#endif // GD_MONOUTILS_H
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index b9d8520ac9..54720652fa 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -101,11 +101,13 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback);
- MonoObject *ex = NULL;
- thunk(get_target(), signal_args, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ thunk(get_target(), signal_args, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(Variant());
}
@@ -133,11 +135,13 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
MonoObject *awaiter = get_target();
if (awaiter) {
- MonoObject *ex = NULL;
- thunk(awaiter, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ thunk(awaiter, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V();
}
}
diff --git a/modules/mono/tls_configure.py b/modules/mono/tls_configure.py
new file mode 100644
index 0000000000..622280b00b
--- /dev/null
+++ b/modules/mono/tls_configure.py
@@ -0,0 +1,36 @@
+from __future__ import print_function
+
+def supported(result):
+ return 'supported' if result else 'not supported'
+
+
+def check_cxx11_thread_local(conf):
+ print('Checking for `thread_local` support...', end=" ")
+ result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def check_declspec_thread(conf):
+ print('Checking for `__declspec(thread)` support...', end=" ")
+ result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def check_gcc___thread(conf):
+ print('Checking for `__thread` support...', end=" ")
+ result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def configure(conf):
+ if check_cxx11_thread_local(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL'])
+ else:
+ if conf.env.msvc:
+ if check_declspec_thread(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD'])
+ elif check_gcc___thread(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD'])
diff --git a/modules/webm/resource_importer_webm.h b/modules/mono/utils/macros.h
index d61e6e2a93..337a86870e 100644
--- a/modules/webm/resource_importer_webm.h
+++ b/modules/mono/utils/macros.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_webm.h */
+/* util_macros.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,29 +28,32 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTERWEBM_H
-#define RESOURCEIMPORTERWEBM_H
+#ifndef UTIL_MACROS_H
+#define UTIL_MACROS_H
-#include "io/resource_import.h"
+// noreturn
-class ResourceImporterWebm : public ResourceImporter {
- GDCLASS(ResourceImporterWebm, 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;
+#undef _NO_RETURN_
- virtual int get_preset_count() const;
- virtual String get_preset_name(int p_idx) const;
+#ifdef __GNUC__
+#define _NO_RETURN_ __attribute__((noreturn))
+#elif _MSC_VER
+#define _NO_RETURN_ __declspec(noreturn)
+#else
+#error Platform or compiler not supported
+#endif
- 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;
+// unreachable
- 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);
+#if defined(_MSC_VER)
+#define _UNREACHABLE_() __assume(0)
+#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
+#define _UNREACHABLE_() __builtin_unreachable()
+#else
+#define _UNREACHABLE_() \
+ CRASH_NOW(); \
+ do { \
+ } while (true);
+#endif
- ResourceImporterWebm();
-};
-
-#endif // RESOURCEIMPORTERWEBM_H
+#endif // UTIL_MACROS_H
diff --git a/modules/theora/resource_importer_theora.h b/modules/mono/utils/thread_local.cpp
index e3c79287ad..6f8b0f90bc 100644
--- a/modules/theora/resource_importer_theora.h
+++ b/modules/mono/utils/thread_local.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_theora.h */
+/* thread_local.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,31 +28,72 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTEROGGTHEORA_H
-#define RESOURCEIMPORTEROGGTHEORA_H
+#include "thread_local.h"
-#include "video_stream_theora.h"
+#ifdef WINDOWS_ENABLED
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
-#include "core/io/resource_import.h"
+#include "core/os/memory.h"
+#include "core/print_string.h"
-class ResourceImporterTheora : public ResourceImporter {
- GDCLASS(ResourceImporterTheora, 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;
+struct ThreadLocalStorage::Impl {
- virtual int get_preset_count() const;
- virtual String get_preset_name(int p_idx) const;
+#ifdef WINDOWS_ENABLED
+ DWORD dwFlsIndex;
+#else
+ pthread_key_t key;
+#endif
- 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;
+ void *get_value() const {
+#ifdef WINDOWS_ENABLED
+ return FlsGetValue(dwFlsIndex);
+#else
+ return pthread_getspecific(key);
+#endif
+ }
- 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);
+ void set_value(void *p_value) const {
+#ifdef WINDOWS_ENABLED
+ FlsSetValue(dwFlsIndex, p_value);
+#else
+ pthread_setspecific(key, p_value);
+#endif
+ }
- ResourceImporterTheora();
+ Impl(void (*p_destr_callback_func)(void *)) {
+#ifdef WINDOWS_ENABLED
+ dwFlsIndex = FlsAlloc(p_destr_callback_func);
+ ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES);
+#else
+ pthread_key_create(&key, p_destr_callback_func);
+#endif
+ }
+
+ ~Impl() {
+#ifdef WINDOWS_ENABLED
+ FlsFree(dwFlsIndex);
+#else
+ pthread_key_delete(key);
+#endif
+ }
};
-#endif // RESOURCEIMPORTEROGGTHEORA_H
+void *ThreadLocalStorage::get_value() const {
+ return pimpl->get_value();
+}
+
+void ThreadLocalStorage::set_value(void *p_value) const {
+ pimpl->set_value(p_value);
+}
+
+void ThreadLocalStorage::alloc(void (*p_destr_callback)(void *)) {
+ pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback));
+}
+
+void ThreadLocalStorage::free() {
+ memdelete(pimpl);
+ pimpl = NULL;
+}
diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h
new file mode 100644
index 0000000000..7ff10b4efc
--- /dev/null
+++ b/modules/mono/utils/thread_local.h
@@ -0,0 +1,171 @@
+/*************************************************************************/
+/* thread_local.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 THREAD_LOCAL_H
+#define THREAD_LOCAL_H
+
+#ifdef HAVE_CXX11_THREAD_LOCAL
+#define _THREAD_LOCAL_(m_t) thread_local m_t
+#else
+
+#if !defined(__GNUC__) && !defined(_MSC_VER)
+#error Platform or compiler not supported
+#endif
+
+#ifdef __GNUC__
+
+#ifdef HAVE_GCC___THREAD
+#define _THREAD_LOCAL_(m_t) __thread m_t
+#else
+#define USE_CUSTOM_THREAD_LOCAL
+#endif
+
+#elif _MSC_VER
+
+#ifdef HAVE_DECLSPEC_THREAD
+#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t
+#else
+#define USE_CUSTOM_THREAD_LOCAL
+#endif
+
+#endif // __GNUC__ _MSC_VER
+
+#endif // HAVE_CXX11_THREAD_LOCAL
+
+#ifdef USE_CUSTOM_THREAD_LOCAL
+#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t>
+#endif
+
+#include "core/typedefs.h"
+
+struct ThreadLocalStorage {
+
+ void *get_value() const;
+ void set_value(void *p_value) const;
+
+ void alloc(void (*p_dest_callback)(void *));
+ void free();
+
+private:
+ struct Impl;
+ Impl *pimpl;
+};
+
+template <typename T>
+class ThreadLocal {
+
+ ThreadLocalStorage storage;
+
+ 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();
+
+ if (tls_data)
+ return static_cast<T *>(tls_data);
+
+ T *data = memnew(T(init_val));
+
+ storage.set_value(data);
+
+ return data;
+ }
+
+public:
+ ThreadLocal() :
+ ThreadLocal(T()) {}
+
+ ThreadLocal(const T &p_init_val) :
+ init_val(p_init_val) {
+ storage.alloc(&destr_callback);
+ }
+
+ ThreadLocal(const ThreadLocal &other) :
+ ThreadLocal(*other._tls_get_value()) {}
+
+ ~ThreadLocal() {
+ storage.free();
+ }
+
+ _FORCE_INLINE_ T *operator&() const {
+ return _tls_get_value();
+ }
+
+ _FORCE_INLINE_ operator T &() const {
+ return *_tls_get_value();
+ }
+
+ _FORCE_INLINE_ ThreadLocal &operator=(const T &val) {
+ T *ptr = _tls_get_value();
+ *ptr = val;
+ return *this;
+ }
+};
+
+struct FlagScopeGuard {
+
+ FlagScopeGuard(bool &p_flag) :
+ flag(p_flag) {
+ flag = !flag;
+ }
+
+ ~FlagScopeGuard() {
+ flag = !flag;
+ }
+
+private:
+ bool &flag;
+};
+
+#define _TLS_RECURSION_GUARD_V_(m_ret) \
+ static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
+ if (_recursion_flag_) \
+ return m_ret; \
+ FlagScopeGuard _recursion_guard_(_recursion_flag_);
+
+#define _TLS_RECURSION_GUARD_ \
+ static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
+ if (_recursion_flag_) \
+ return; \
+ FlagScopeGuard _recursion_guard_(_recursion_flag_);
+
+#endif // THREAD_LOCAL_H
diff --git a/modules/theora/doc_classes/ResourceImporterTheora.xml b/modules/theora/doc_classes/ResourceImporterTheora.xml
deleted file mode 100644
index 5fc40a6eba..0000000000
--- a/modules/theora/doc_classes/ResourceImporterTheora.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.1">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <demos>
- </demos>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp
index 9bc5ed903a..971fe39c44 100644
--- a/modules/theora/register_types.cpp
+++ b/modules/theora/register_types.cpp
@@ -29,18 +29,22 @@
/*************************************************************************/
#include "register_types.h"
-#include "resource_importer_theora.h"
+
#include "video_stream_theora.h"
+static ResourceFormatLoaderTheora *resource_loader_theora = NULL;
+
void register_theora_types() {
-#ifdef TOOLS_ENABLED
- Ref<ResourceImporterTheora> theora_import;
- theora_import.instance();
- ResourceFormatImporter::get_singleton()->add_importer(theora_import);
-#endif
+ resource_loader_theora = memnew(ResourceFormatLoaderTheora);
+ ResourceLoader::add_resource_format_loader(resource_loader_theora, true);
+
ClassDB::register_class<VideoStreamTheora>();
}
void unregister_theora_types() {
+
+ if (resource_loader_theora) {
+ memdelete(resource_loader_theora);
+ }
}
diff --git a/modules/theora/resource_importer_theora.cpp b/modules/theora/resource_importer_theora.cpp
deleted file mode 100644
index ee9bab74a7..0000000000
--- a/modules/theora/resource_importer_theora.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*************************************************************************/
-/* resource_importer_theora.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. */
-/*************************************************************************/
-
-#include "resource_importer_theora.h"
-
-#include "io/resource_saver.h"
-#include "os/file_access.h"
-#include "scene/resources/texture.h"
-
-String ResourceImporterTheora::get_importer_name() const {
-
- return "Theora";
-}
-
-String ResourceImporterTheora::get_visible_name() const {
-
- return "Theora";
-}
-void ResourceImporterTheora::get_recognized_extensions(List<String> *p_extensions) const {
-
- p_extensions->push_back("ogv");
- p_extensions->push_back("ogm");
-}
-
-String ResourceImporterTheora::get_save_extension() const {
- return "ogvstr";
-}
-
-String ResourceImporterTheora::get_resource_type() const {
-
- return "VideoStreamTheora";
-}
-
-bool ResourceImporterTheora::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
-
- return true;
-}
-
-int ResourceImporterTheora::get_preset_count() const {
- return 0;
-}
-String ResourceImporterTheora::get_preset_name(int p_idx) const {
-
- return String();
-}
-
-void ResourceImporterTheora::get_import_options(List<ImportOption> *r_options, int p_preset) const {
-
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
-}
-
-Error ResourceImporterTheora::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) {
-
- VideoStreamTheora *stream = memnew(VideoStreamTheora);
- stream->set_file(p_source_file);
-
- Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream);
-
- return ResourceSaver::save(p_save_path + ".ogvstr", ogv_stream);
-}
-
-ResourceImporterTheora::ResourceImporterTheora() {
-}
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 9e6307c0bf..881808873b 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -730,3 +730,46 @@ void VideoStreamTheora::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
}
+
+////////////
+
+RES ResourceFormatLoaderTheora::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();
+ }
+
+ VideoStreamTheora *stream = memnew(VideoStreamTheora);
+ stream->set_file(p_path);
+
+ Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream);
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return ogv_stream;
+}
+
+void ResourceFormatLoaderTheora::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("ogv");
+}
+
+bool ResourceFormatLoaderTheora::handles_type(const String &p_type) const {
+
+ return ClassDB::is_parent_class(p_type, "VideoStream");
+}
+
+String ResourceFormatLoaderTheora::get_resource_type(const String &p_path) const {
+
+ String el = p_path.get_extension().to_lower();
+ if (el == "ogv")
+ return "VideoStreamTheora";
+ return "";
+}
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 4bdbbdae20..7cee1b491b 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -163,7 +163,7 @@ public:
class VideoStreamTheora : public VideoStream {
GDCLASS(VideoStreamTheora, VideoStream);
- RES_BASE_EXTENSION("ogvstr");
+ RES_BASE_EXTENSION("ogv");
String file;
int audio_track;
@@ -186,4 +186,12 @@ public:
VideoStreamTheora() { audio_track = 0; }
};
+class ResourceFormatLoaderTheora : 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/modules/webm/doc_classes/ResourceImporterWebm.xml b/modules/webm/doc_classes/ResourceImporterWebm.xml
deleted file mode 100644
index 0cfab1baf0..0000000000
--- a/modules/webm/doc_classes/ResourceImporterWebm.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.1">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <demos>
- </demos>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/webm/register_types.cpp b/modules/webm/register_types.cpp
index 1183dd41f7..121b528d5b 100644
--- a/modules/webm/register_types.cpp
+++ b/modules/webm/register_types.cpp
@@ -29,18 +29,22 @@
/*************************************************************************/
#include "register_types.h"
-#include "resource_importer_webm.h"
+
#include "video_stream_webm.h"
+static ResourceFormatLoaderWebm *resource_loader_webm = NULL;
+
void register_webm_types() {
-#ifdef TOOLS_ENABLED
- Ref<ResourceImporterWebm> webm_import;
- webm_import.instance();
- ResourceFormatImporter::get_singleton()->add_importer(webm_import);
-#endif
+ resource_loader_webm = memnew(ResourceFormatLoaderWebm);
+ ResourceLoader::add_resource_format_loader(resource_loader_webm, true);
+
ClassDB::register_class<VideoStreamWebm>();
}
void unregister_webm_types() {
+
+ if (resource_loader_webm) {
+ memdelete(resource_loader_webm);
+ }
}
diff --git a/modules/webm/resource_importer_webm.cpp b/modules/webm/resource_importer_webm.cpp
deleted file mode 100644
index 7124a503e8..0000000000
--- a/modules/webm/resource_importer_webm.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*************************************************************************/
-/* resource_importer_webm.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. */
-/*************************************************************************/
-
-#include "resource_importer_webm.h"
-
-#include "io/resource_saver.h"
-#include "os/file_access.h"
-#include "scene/resources/texture.h"
-#include "video_stream_webm.h"
-
-String ResourceImporterWebm::get_importer_name() const {
-
- return "Webm";
-}
-
-String ResourceImporterWebm::get_visible_name() const {
-
- return "Webm";
-}
-void ResourceImporterWebm::get_recognized_extensions(List<String> *p_extensions) const {
-
- p_extensions->push_back("webm");
-}
-
-String ResourceImporterWebm::get_save_extension() const {
- return "webmstr";
-}
-
-String ResourceImporterWebm::get_resource_type() const {
-
- return "VideoStreamWebm";
-}
-
-bool ResourceImporterWebm::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
-
- return true;
-}
-
-int ResourceImporterWebm::get_preset_count() const {
- return 0;
-}
-String ResourceImporterWebm::get_preset_name(int p_idx) const {
-
- return String();
-}
-
-void ResourceImporterWebm::get_import_options(List<ImportOption> *r_options, int p_preset) const {
-
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
-}
-
-Error ResourceImporterWebm::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);
- }
- memdelete(f);
-
- VideoStreamWebm *stream = memnew(VideoStreamWebm);
- stream->set_file(p_source_file);
-
- Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream);
-
- return ResourceSaver::save(p_save_path + ".webmstr", webm_stream);
-}
-
-ResourceImporterWebm::ResourceImporterWebm() {
-}
diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp
index fac47225bc..1bb9a43886 100644
--- a/modules/webm/video_stream_webm.cpp
+++ b/modules/webm/video_stream_webm.cpp
@@ -443,3 +443,46 @@ void VideoStreamWebm::set_audio_track(int p_track) {
audio_track = p_track;
}
+
+////////////
+
+RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error) {
+
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ if (r_error) {
+ *r_error = ERR_CANT_OPEN;
+ }
+ memdelete(f);
+ return RES();
+ }
+
+ VideoStreamWebm *stream = memnew(VideoStreamWebm);
+ stream->set_file(p_path);
+
+ Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream);
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return webm_stream;
+}
+
+void ResourceFormatLoaderWebm::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("webm");
+}
+
+bool ResourceFormatLoaderWebm::handles_type(const String &p_type) const {
+
+ return ClassDB::is_parent_class(p_type, "VideoStream");
+}
+
+String ResourceFormatLoaderWebm::get_resource_type(const String &p_path) const {
+
+ String el = p_path.get_extension().to_lower();
+ if (el == "webm")
+ return "VideoStreamWebm";
+ return "";
+}
diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h
index dde993d154..08be50846d 100644
--- a/modules/webm/video_stream_webm.h
+++ b/modules/webm/video_stream_webm.h
@@ -109,7 +109,7 @@ private:
class VideoStreamWebm : public VideoStream {
GDCLASS(VideoStreamWebm, VideoStream);
- RES_BASE_EXTENSION("webmstr");
+ RES_BASE_EXTENSION("webm");
String file;
int audio_track;
@@ -127,4 +127,12 @@ public:
virtual void set_audio_track(int p_track);
};
+class ResourceFormatLoaderWebm : 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 // VIDEO_STREAM_WEBM_H
diff --git a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java
index 766989f953..aaf18c74bf 100644
--- a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java
+++ b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java
@@ -58,17 +58,15 @@ abstract public class HandlePurchaseTask {
public void handlePurchaseRequest(int resultCode, Intent data) {
//Log.d("XXX", "Handling purchase response");
- //int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
- PaymentsCache pc = new PaymentsCache(context);
-
- String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
- //Log.d("XXX", "Purchase data:" + purchaseData);
- String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
- //Log.d("XXX", "Purchase signature:" + dataSignature);
-
if (resultCode == Activity.RESULT_OK) {
-
try {
+ //int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
+ PaymentsCache pc = new PaymentsCache(context);
+
+ String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
+ //Log.d("XXX", "Purchase data:" + purchaseData);
+ String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
+ //Log.d("XXX", "Purchase signature:" + dataSignature);
//Log.d("SARLANGA", purchaseData);
JSONObject jo = new JSONObject(purchaseData);
diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
index 99ce25adfb..1664c5ce8e 100644
--- a/platform/osx/crash_handler_osx.mm
+++ b/platform/osx/crash_handler_osx.mm
@@ -78,6 +78,10 @@ static void handle_crash(int sig) {
// Dump the backtrace to stderr with a message to the user
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
+
+ if (OS::get_singleton()->get_main_loop())
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
char **strings = backtrace_symbols(bt_buffer, size);
if (strings) {
diff --git a/platform/windows/crash_handler_win.cpp b/platform/windows/crash_handler_win.cpp
index 804c2d44eb..76a227c608 100644
--- a/platform/windows/crash_handler_win.cpp
+++ b/platform/windows/crash_handler_win.cpp
@@ -124,6 +124,9 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
fprintf(stderr, "%s: Program crashed\n", __FUNCTION__);
+ if (OS::get_singleton()->get_main_loop())
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+
// Load the symbols:
if (!SymInitialize(process, NULL, false))
return EXCEPTION_CONTINUE_SEARCH;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index f52c8881d4..216be1064a 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -70,6 +70,30 @@ __attribute__((visibility("default"))) DWORD NvOptimusEnablement = 0x00000001;
#define WM_TOUCH 576
#endif
+typedef struct {
+ int count;
+ int screen;
+ Size2 size;
+} EnumSizeData;
+
+typedef struct {
+ int count;
+ int screen;
+ Point2 pos;
+} EnumPosData;
+
+static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+
+ EnumSizeData *data = (EnumSizeData *)dwData;
+ if (data->count == data->screen) {
+ data->size.x = lprcMonitor->right - lprcMonitor->left;
+ data->size.y = lprcMonitor->bottom - lprcMonitor->top;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
static String format_error_message(DWORD id) {
LPWSTR messageBuffer = NULL;
@@ -980,6 +1004,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
WNDCLASSEXW wc;
if (is_hidpi_allowed()) {
+ print_line("hidpi aware?");
HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
if (Shcore != NULL) {
@@ -1024,6 +1049,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
pre_fs_valid = true;
if (video_mode.fullscreen) {
+ /* this returns DPI unaware size, commenting
DEVMODE current;
memset(&current, 0, sizeof(current));
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &current);
@@ -1031,6 +1057,16 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
WindowRect.right = current.dmPelsWidth;
WindowRect.bottom = current.dmPelsHeight;
+ */
+
+ EnumSizeData data = { 0, 0, Size2() };
+ EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data);
+
+ WindowRect.right = data.size.width;
+ WindowRect.bottom = data.size.height;
+
+ print_line("wr right " + itos(WindowRect.right) + ", " + itos(WindowRect.bottom));
+
/* DEVMODE dmScreenSettings;
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
@@ -1455,12 +1491,6 @@ void OS_Windows::set_current_screen(int p_screen) {
set_window_position(ofs + get_screen_position(p_screen));
}
-typedef struct {
- int count;
- int screen;
- Point2 pos;
-} EnumPosData;
-
static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumPosData *data = (EnumPosData *)dwData;
@@ -1480,24 +1510,6 @@ Point2 OS_Windows::get_screen_position(int p_screen) const {
return data.pos;
}
-typedef struct {
- int count;
- int screen;
- Size2 size;
-} EnumSizeData;
-
-static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumSizeData *data = (EnumSizeData *)dwData;
- if (data->count == data->screen) {
- data->size.x = lprcMonitor->right - lprcMonitor->left;
- data->size.y = lprcMonitor->bottom - lprcMonitor->top;
- }
-
- data->count++;
- return TRUE;
-}
-
Size2 OS_Windows::get_screen_size(int p_screen) const {
EnumSizeData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Size2() };
@@ -1557,16 +1569,16 @@ Size2 OS_Windows::get_real_window_size() const {
}
void OS_Windows::set_window_size(const Size2 p_size) {
- video_mode.width = p_size.width;
- video_mode.height = p_size.height;
+ int w = p_size.width;
+ int h = p_size.height;
+
+ video_mode.width = w;
+ video_mode.height = h;
if (video_mode.fullscreen) {
return;
}
- int w = p_size.width;
- int h = p_size.height;
-
RECT rect;
GetWindowRect(hWnd, &rect);
diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp
index d39fc33f81..960105271b 100644
--- a/platform/x11/crash_handler_x11.cpp
+++ b/platform/x11/crash_handler_x11.cpp
@@ -55,6 +55,10 @@ static void handle_crash(int sig) {
// Dump the backtrace to stderr with a message to the user
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
+
+ if (OS::get_singleton()->get_main_loop())
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
char **strings = backtrace_symbols(bt_buffer, size);
if (strings) {
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 54194ff543..e3d1592be0 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -59,15 +59,36 @@ bool AnimatedSprite::_edit_use_pivot() const {
}
Rect2 AnimatedSprite::_edit_get_rect() const {
+ return _get_rect();
+}
+
+bool AnimatedSprite::_edit_use_rect() const {
if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
- return Node2D::_edit_get_rect();
+ return false;
+ }
+ Ref<Texture> t;
+ if (animation)
+ t = frames->get_frame(animation, frame);
+ if (t.is_null())
+ return false;
+
+ return true;
+}
+
+Rect2 AnimatedSprite::get_anchorable_rect() const {
+ return _get_rect();
+}
+
+Rect2 AnimatedSprite::_get_rect() const {
+ if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
+ return Rect2();
}
Ref<Texture> t;
if (animation)
t = frames->get_frame(animation, frame);
if (t.is_null())
- return Node2D::_edit_get_rect();
+ return Rect2();
Size2 s = t->get_size();
Point2 ofs = offset;
@@ -80,10 +101,6 @@ Rect2 AnimatedSprite::_edit_get_rect() const {
return Rect2(ofs, s);
}
-bool AnimatedSprite::_edit_use_rect() const {
- return true;
-}
-
void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index be5b1ef6d6..806052a696 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -145,6 +145,7 @@ class AnimatedSprite : public Node2D {
void _reset_timeout();
void _set_playing(bool p_playing);
bool _is_playing() const;
+ Rect2 _get_rect() const;
protected:
static void _bind_methods();
@@ -161,6 +162,8 @@ public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
+
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp
index caa1adebdb..e06c30ec6b 100644
--- a/scene/2d/back_buffer_copy.cpp
+++ b/scene/2d/back_buffer_copy.cpp
@@ -59,6 +59,11 @@ bool BackBufferCopy::_edit_use_rect() const {
return true;
}
+Rect2 BackBufferCopy::get_anchorable_rect() const {
+
+ return rect;
+}
+
void BackBufferCopy::set_rect(const Rect2 &p_rect) {
rect = p_rect;
diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h
index 752d56de2b..b1ee12544b 100644
--- a/scene/2d/back_buffer_copy.h
+++ b/scene/2d/back_buffer_copy.h
@@ -58,6 +58,7 @@ public:
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
+ Rect2 get_anchorable_rect() const;
void set_copy_mode(CopyMode p_mode);
CopyMode get_copy_mode() const;
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index f1c09594da..47326b9be2 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -321,11 +321,6 @@ void CanvasItem::hide() {
_change_notify("visible");
}
-Size2 CanvasItem::_edit_get_minimum_size() const {
-
- return Size2(-1, -1); //no limit
-}
-
void CanvasItem::_update_callback() {
if (!is_inside_tree()) {
@@ -994,7 +989,6 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect);
ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect);
ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect);
- ClassDB::bind_method(D_METHOD("_edit_get_item_and_children_rect"), &CanvasItem::_edit_get_item_and_children_rect);
ClassDB::bind_method(D_METHOD("_edit_set_rotation", "degrees"), &CanvasItem::_edit_set_rotation);
ClassDB::bind_method(D_METHOD("_edit_get_rotation"), &CanvasItem::_edit_get_rotation);
ClassDB::bind_method(D_METHOD("_edit_use_rotation"), &CanvasItem::_edit_use_rotation);
@@ -1175,21 +1169,6 @@ int CanvasItem::get_canvas_layer() const {
return 0;
}
-Rect2 CanvasItem::_edit_get_item_and_children_rect() const {
-
- Rect2 rect = _edit_get_rect();
-
- for (int i = 0; i < get_child_count(); i++) {
- CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
- if (c) {
- Rect2 sir = c->get_transform().xform(c->_edit_get_item_and_children_rect());
- rect = rect.merge(sir);
- }
- }
-
- return rect;
-}
-
CanvasItem::CanvasItem() :
xform_change(this) {
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 10d5082dfc..1e6a251c9c 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -222,6 +222,9 @@ public:
/* EDITOR */
+ // Select the node
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
// Save and restore a CanvasItem state
virtual void _edit_set_state(const Dictionary &p_state){};
virtual Dictionary _edit_get_state() const { return Dictionary(); };
@@ -234,36 +237,21 @@ public:
virtual void _edit_set_scale(const Size2 &p_scale) = 0;
virtual Size2 _edit_get_scale() const = 0;
+ // Used to rotate the node
+ virtual bool _edit_use_rotation() const { return false; };
+ virtual void _edit_set_rotation(float p_rotation){};
+ virtual float _edit_get_rotation() const { return 0.0; };
+
// Used to resize/move the node
+ virtual bool _edit_use_rect() const { return false; }; // MAYBE REPLACE BY A _edit_get_editmode()
virtual void _edit_set_rect(const Rect2 &p_rect){};
virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); };
- virtual bool _edit_use_rect() const { return false; };
-
- Rect2 _edit_get_item_and_children_rect() const;
-
- // used to select the node
- virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
-
- // Used to rotate the node
- virtual void
- _edit_set_rotation(float p_rotation){};
- virtual float _edit_get_rotation() const {
- return 0.0;
- };
- virtual bool _edit_use_rotation() const {
- return false;
- };
+ virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); }; // LOOKS WEIRD
// Used to set a pivot
+ virtual bool _edit_use_pivot() const { return false; };
virtual void _edit_set_pivot(const Point2 &p_pivot){};
- virtual Point2 _edit_get_pivot() const {
- return Point2();
- };
- virtual bool _edit_use_pivot() const {
- return false;
- };
-
- virtual Size2 _edit_get_minimum_size() const;
+ virtual Point2 _edit_get_pivot() const { return Point2(); };
/* VISIBILITY */
@@ -358,6 +346,9 @@ public:
void set_notify_transform(bool p_enable);
bool is_transform_notification_enabled() const;
+ // Used by control nodes to retreive the parent's anchorable area
+ virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); };
+
int get_canvas_layer() const;
CanvasItem();
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 9a44eb31bb..f93c7d1f79 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -59,14 +59,22 @@ bool Light2D::_edit_use_pivot() const {
Rect2 Light2D::_edit_get_rect() const {
if (texture.is_null())
- return Node2D::_edit_get_rect();
+ return Rect2();
Size2 s = texture->get_size() * _scale;
return Rect2(texture_offset - s / 2.0, s);
}
bool Light2D::_edit_use_rect() const {
- return true;
+ return !texture.is_null();
+}
+
+Rect2 Light2D::get_anchorable_rect() const {
+ if (texture.is_null())
+ return Rect2();
+
+ Size2 s = texture->get_size() * _scale;
+ return Rect2(texture_offset - s / 2.0, s);
}
void Light2D::_update_light_visibility() {
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index 543805e329..40469cfbc8 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -94,6 +94,8 @@ public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
+
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp
index 9480f18176..45f63fd5bf 100644
--- a/scene/2d/screen_button.cpp
+++ b/scene/2d/screen_button.cpp
@@ -324,19 +324,21 @@ void TouchScreenButton::_release(bool p_exiting_tree) {
}
Rect2 TouchScreenButton::_edit_get_rect() const {
-
- if (texture.is_null())
- return Rect2(0, 0, 1, 1);
- /*
if (texture.is_null())
return CanvasItem::_edit_get_rect();
- */
return Rect2(Size2(), texture->get_size());
}
bool TouchScreenButton::_edit_use_rect() const {
- return true;
+ return !texture.is_null();
+}
+
+Rect2 TouchScreenButton::get_anchorable_rect() const {
+ if (texture.is_null())
+ return CanvasItem::get_anchorable_rect();
+
+ return Rect2(Size2(), texture->get_size());
}
void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h
index b2fafcc93d..3e8adc2e5e 100644
--- a/scene/2d/screen_button.h
+++ b/scene/2d/screen_button.h
@@ -103,8 +103,9 @@ public:
bool is_pressed() const;
- Rect2 _edit_get_rect() const;
+ virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
TouchScreenButton();
};
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index 64d0771fab..ebe0e81f6e 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -63,9 +63,16 @@ Rect2 Sprite::_edit_get_rect() const {
}
bool Sprite::_edit_use_rect() const {
+ if (texture.is_null())
+ return false;
+
return true;
}
+Rect2 Sprite::get_anchorable_rect() const {
+ return get_rect();
+}
+
void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const {
Rect2 base_rect;
@@ -367,10 +374,6 @@ Rect2 Sprite::get_rect() const {
if (texture.is_null())
return Rect2(0, 0, 1, 1);
- /*
- if (texture.is_null())
- return CanvasItem::_edit_get_rect();
- */
Size2i s;
diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h
index 609ad8bb34..0a5ff002cd 100644
--- a/scene/2d/sprite.h
+++ b/scene/2d/sprite.h
@@ -115,6 +115,7 @@ public:
int get_hframes() const;
Rect2 get_rect() const;
+ virtual Rect2 get_anchorable_rect() const;
Sprite();
~Sprite();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 1d60037287..9a343ca0f0 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1152,16 +1152,6 @@ PoolVector<int> TileMap::_get_tile_data() const {
return data;
}
-Rect2 TileMap::_edit_get_rect() const {
-
- const_cast<TileMap *>(this)->_update_dirty_quadrants();
- return rect_cache;
-}
-
-bool TileMap::_edit_use_rect() const {
- return true;
-}
-
void TileMap::set_collision_layer(uint32_t p_layer) {
collision_layer = p_layer;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 3ddb143f4a..79d79ca59f 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -245,9 +245,6 @@ public:
void set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false);
int get_cellv(const Vector2 &p_pos) const;
- Rect2 _edit_get_rect() const;
- virtual bool _edit_use_rect() const;
-
void make_bitmask_area_dirty(const Vector2 &p_pos);
void update_bitmask_area(const Vector2 &p_pos);
void update_bitmask_region(const Vector2 &p_start = Vector2(), const Vector2 &p_end = Vector2());
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 6ef8016dd5..3808b788c7 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1274,35 +1274,34 @@ bool Control::has_constant(const StringName &p_name, const StringName &p_type) c
return Theme::get_default()->has_constant(p_name, type);
}
-Size2 Control::get_parent_area_size() const {
-
- ERR_FAIL_COND_V(!is_inside_tree(), Size2());
-
- Size2 parent_size;
+Rect2 Control::get_parent_anchorable_rect() const {
+ if (!is_inside_tree())
+ return Rect2();
+ Rect2 parent_rect;
if (data.parent_canvas_item) {
-
- parent_size = data.parent_canvas_item->_edit_get_rect().size;
+ parent_rect = data.parent_canvas_item->get_anchorable_rect();
} else {
-
- parent_size = get_viewport()->get_visible_rect().size;
+ parent_rect = get_viewport()->get_visible_rect();
}
- return parent_size;
+ return parent_rect;
}
-void Control::_size_changed() {
+Size2 Control::get_parent_area_size() const {
- if (!is_inside_tree())
- return;
+ return get_parent_anchorable_rect().size;
+}
- Size2 parent_size = get_parent_area_size();
+void Control::_size_changed() {
+
+ Rect2 parent_rect = get_parent_anchorable_rect();
float margin_pos[4];
for (int i = 0; i < 4; i++) {
- float area = parent_size[i & 1];
+ float area = parent_rect.size[i & 1];
margin_pos[i] = data.margin[i] + (data.anchor[i] * area);
}
@@ -1332,9 +1331,9 @@ void Control::_size_changed() {
}
// We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
- if (Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
- new_size_cache = new_size_cache.floor();
- new_pos_cache = new_pos_cache.floor();
+ if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
+ new_size_cache = new_size_cache.round();
+ new_pos_cache = new_pos_cache.round();
}
bool pos_changed = new_pos_cache != data.pos_cache;
bool size_changed = new_size_cache != data.size_cache;
@@ -1342,57 +1341,25 @@ void Control::_size_changed() {
data.pos_cache = new_pos_cache;
data.size_cache = new_size_cache;
- if (size_changed) {
- notification(NOTIFICATION_RESIZED);
- }
- if (pos_changed || size_changed) {
- item_rect_changed(size_changed);
- _change_notify_margins();
- _notify_transform();
- }
-
- if (pos_changed && !size_changed) {
- _update_canvas_item_transform(); //move because it won't be updated
- }
-}
-
-float Control::_get_parent_range(int p_idx) const {
-
- if (!is_inside_tree()) {
-
- return 0;
- }
- if (data.parent_canvas_item) {
+ if (is_inside_tree()) {
+ if (size_changed) {
+ notification(NOTIFICATION_RESIZED);
+ }
+ if (pos_changed || size_changed) {
+ item_rect_changed(size_changed);
+ _change_notify_margins();
+ _notify_transform();
+ }
- return data.parent_canvas_item->_edit_get_rect().size[p_idx & 1];
- } else {
- return get_viewport()->get_visible_rect().size[p_idx & 1];
+ if (pos_changed && !size_changed) {
+ _update_canvas_item_transform(); //move because it won't be updated
+ }
}
-
- return 0;
-}
-
-float Control::_get_range(int p_idx) const {
-
- p_idx &= 1;
-
- float parent_range = _get_parent_range(p_idx);
- float from = _a2s(data.margin[p_idx], data.anchor[p_idx], parent_range);
- float to = _a2s(data.margin[p_idx + 2], data.anchor[p_idx + 2], parent_range);
-
- return to - from;
-}
-
-float Control::_s2a(float p_val, float p_anchor, float p_range) const {
- return p_val - (p_anchor * p_range);
-}
-
-float Control::_a2s(float p_val, float p_anchor, float p_range) const {
- return Math::floor(p_val + (p_anchor * p_range));
}
void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bool p_push_opposite_anchor) {
- float parent_range = _get_parent_range((p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? 0 : 1);
+ Rect2 parent_rect = get_parent_anchorable_rect();
+ float parent_range = (p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
float previous_margin_pos = data.margin[p_margin] + data.anchor[p_margin] * parent_range;
float previous_opposite_margin_pos = data.margin[(p_margin + 2) % 4] + data.anchor[(p_margin + 2) % 4] * parent_range;
@@ -1408,9 +1375,9 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
if (!p_keep_margin) {
- data.margin[p_margin] = _s2a(previous_margin_pos, data.anchor[p_margin], parent_range);
+ data.margin[p_margin] = previous_margin_pos - data.anchor[p_margin] * parent_range;
if (p_push_opposite_anchor) {
- data.margin[(p_margin + 2) % 4] = _s2a(previous_opposite_margin_pos, data.anchor[(p_margin + 2) % 4], parent_range);
+ data.margin[(p_margin + 2) % 4] = previous_opposite_margin_pos - data.anchor[(p_margin + 2) % 4] * parent_range;
}
}
if (is_inside_tree()) {
@@ -1564,8 +1531,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
new_size.y = min_size.y;
}
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
+ Rect2 parent_rect = get_parent_anchorable_rect();
//Left
switch (p_preset) {
@@ -1577,21 +1543,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_LEFT_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- data.margin[0] = pw * (0.0 - data.anchor[0]) + p_margin;
+ data.margin[0] = parent_rect.size.x * (0.0 - data.anchor[0]) + p_margin + parent_rect.position.x;
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- data.margin[0] = pw * (0.5 - data.anchor[0]) - new_size.x / 2;
+ data.margin[0] = parent_rect.size.x * (0.5 - data.anchor[0]) - new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_RIGHT:
case PRESET_RIGHT_WIDE:
- data.margin[0] = pw * (1.0 - data.anchor[0]) - new_size.x - p_margin;
+ data.margin[0] = parent_rect.size.x * (1.0 - data.anchor[0]) - new_size.x - p_margin + parent_rect.position.x;
break;
}
@@ -1605,21 +1571,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_TOP_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- data.margin[1] = ph * (0.0 - data.anchor[1]) + p_margin;
+ data.margin[1] = parent_rect.size.y * (0.0 - data.anchor[1]) + p_margin + parent_rect.position.y;
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- data.margin[1] = ph * (0.5 - data.anchor[1]) - new_size.y / 2;
+ data.margin[1] = parent_rect.size.y * (0.5 - data.anchor[1]) - new_size.y / 2 + parent_rect.position.y;
break;
case PRESET_BOTTOM_LEFT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_BOTTOM:
case PRESET_BOTTOM_WIDE:
- data.margin[1] = ph * (1.0 - data.anchor[1]) - new_size.y - p_margin;
+ data.margin[1] = parent_rect.size.y * (1.0 - data.anchor[1]) - new_size.y - p_margin + parent_rect.position.y;
break;
}
@@ -1629,14 +1595,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_LEFT:
case PRESET_CENTER_LEFT:
case PRESET_LEFT_WIDE:
- data.margin[2] = pw * (0.0 - data.anchor[2]) + new_size.x + p_margin;
+ data.margin[2] = parent_rect.size.x * (0.0 - data.anchor[2]) + new_size.x + p_margin + parent_rect.position.x;
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- data.margin[2] = pw * (0.5 - data.anchor[2]) + new_size.x / 2;
+ data.margin[2] = parent_rect.size.x * (0.5 - data.anchor[2]) + new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
@@ -1647,7 +1613,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- data.margin[2] = pw * (1.0 - data.anchor[2]) - p_margin;
+ data.margin[2] = parent_rect.size.x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x;
break;
}
@@ -1657,14 +1623,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_TOP_RIGHT:
case PRESET_CENTER_TOP:
case PRESET_TOP_WIDE:
- data.margin[3] = ph * (0.0 - data.anchor[3]) + new_size.y + p_margin;
+ data.margin[3] = parent_rect.size.y * (0.0 - data.anchor[3]) + new_size.y + p_margin + parent_rect.position.y;
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- data.margin[3] = ph * (0.5 - data.anchor[3]) + new_size.y / 2;
+ data.margin[3] = parent_rect.size.y * (0.5 - data.anchor[3]) + new_size.y / 2 + parent_rect.position.y;
break;
case PRESET_BOTTOM_LEFT:
@@ -1675,7 +1641,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- data.margin[3] = ph * (1.0 - data.anchor[3]) - p_margin;
+ data.margin[3] = parent_rect.size.y * (1.0 - data.anchor[3]) - p_margin + parent_rect.position.y;
break;
}
@@ -1754,31 +1720,29 @@ void Control::set_global_position(const Point2 &p_point) {
set_position(inv.xform(p_point));
}
-void Control::set_position(const Size2 &p_point) {
-
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
+Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margins[4]) const {
- float x = _a2s(data.margin[0], data.anchor[0], pw);
- float y = _a2s(data.margin[1], data.anchor[1], ph);
- float x2 = _a2s(data.margin[2], data.anchor[2], pw);
- float y2 = _a2s(data.margin[3], data.anchor[3], ph);
+ Rect2 anchorable = get_parent_anchorable_rect();
+ Rect2 result = anchorable;
+ for (int i = 0; i < 4; i++) {
+ result.grow_margin((Margin)i, p_anchors[i] * anchorable.get_size()[i % 2] + p_margins[i]);
+ }
- Size2 ret = Size2(x2 - x, y2 - y);
- Size2 min = get_combined_minimum_size();
+ return result;
+}
- Size2 size = Size2(MAX(min.width, ret.width), MAX(min.height, ret.height));
- float w = size.x;
- float h = size.y;
+void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) {
- x = p_point.x;
- y = p_point.y;
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ r_margins[0] = Math::floor(p_rect.position.x - (p_anchors[0] * parent_rect_size.x));
+ r_margins[1] = Math::floor(p_rect.position.y - (p_anchors[1] * parent_rect_size.y));
+ r_margins[2] = Math::floor(p_rect.position.x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x));
+ r_margins[3] = Math::floor(p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y));
+}
- data.margin[0] = _s2a(x, data.anchor[0], pw);
- data.margin[1] = _s2a(y, data.anchor[1], ph);
- data.margin[2] = _s2a(x + w, data.anchor[2], pw);
- data.margin[3] = _s2a(y + h, data.anchor[3], ph);
+void Control::set_position(const Size2 &p_point) {
+ _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
_size_changed();
}
@@ -1791,18 +1755,7 @@ void Control::set_size(const Size2 &p_size) {
if (new_size.y < min.y)
new_size.y = min.y;
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
-
- float x = _a2s(data.margin[0], data.anchor[0], pw);
- float y = _a2s(data.margin[1], data.anchor[1], ph);
-
- float w = new_size.width;
- float h = new_size.height;
-
- data.margin[2] = _s2a(x + w, data.anchor[2], pw);
- data.margin[3] = _s2a(y + h, data.anchor[3], ph);
-
+ _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
_size_changed();
}
@@ -1833,6 +1786,11 @@ Rect2 Control::get_rect() const {
return Rect2(get_position(), get_size());
}
+Rect2 Control::get_anchorable_rect() const {
+
+ return Rect2(Point2(), get_size());
+}
+
void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) {
ERR_FAIL_COND(p_icon.is_null());
@@ -2332,12 +2290,11 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) {
Point2 points[4];
Transform2D xform = get_global_transform();
- Rect2 rect = _edit_get_rect();
- points[0] = xform.xform(rect.position);
- points[1] = xform.xform(rect.position + Point2(rect.size.x, 0));
- points[2] = xform.xform(rect.position + rect.size);
- points[3] = xform.xform(rect.position + Point2(0, rect.size.y));
+ points[0] = xform.xform(Point2());
+ points[1] = xform.xform(Point2(get_size().x, 0));
+ points[2] = xform.xform(get_size());
+ points[3] = xform.xform(Point2(0, get_size().y));
const Vector2 dir[4] = {
Vector2(-1, 0),
@@ -2391,12 +2348,11 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con
Point2 points[4];
Transform2D xform = c->get_global_transform();
- Rect2 rect = c->_edit_get_rect();
- points[0] = xform.xform(rect.position);
- points[1] = xform.xform(rect.position + Point2(rect.size.x, 0));
- points[2] = xform.xform(rect.position + rect.size);
- points[3] = xform.xform(rect.position + Point2(0, rect.size.y));
+ points[0] = xform.xform(Point2());
+ points[1] = xform.xform(Point2(get_size().x, 0));
+ points[2] = xform.xform(get_size());
+ points[3] = xform.xform(Point2(0, get_size().y));
float min = 1e7;
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 633f92f733..fa5274d854 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -220,10 +220,6 @@ private:
void _set_anchor(Margin p_margin, float p_anchor);
- float _get_parent_range(int p_idx) const;
- float _get_range(int p_idx) const;
- float _s2a(float p_val, float p_anchor, float p_range) const;
- float _a2s(float p_val, float p_anchor, float p_range) const;
void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign = true);
void _theme_changed();
@@ -233,6 +229,9 @@ private:
void _update_scroll();
void _resize(const Size2 &p_size);
+ Rect2 _compute_child_rect(const float p_anchors[4], const float p_margins[4]) const;
+ void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]);
+
void _size_changed();
String _get_tooltip() const;
@@ -283,6 +282,7 @@ public:
};
+ /* EDITOR */
virtual Dictionary _edit_get_state() const;
virtual void _edit_set_state(const Dictionary &p_state);
@@ -358,6 +358,7 @@ public:
Rect2 get_rect() const;
Rect2 get_global_rect() const;
Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server
+ Rect2 get_anchorable_rect() const;
void set_rotation(float p_radians);
void set_rotation_degrees(float p_degrees);
@@ -465,6 +466,7 @@ public:
bool is_toplevel_control() const;
Size2 get_parent_area_size() const;
+ Rect2 get_parent_anchorable_rect() const;
void grab_click_focus();
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 4fe06e9a4c..327c236f8c 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -5774,18 +5774,23 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
if (select_word(s, col, beg, end)) {
bool inside_quotes = false;
+ char selected_quote = '\0';
int qbegin = 0, qend = 0;
for (int i = 0; i < s.length(); i++) {
- if (s[i] == '"') {
- if (inside_quotes) {
- qend = i;
- inside_quotes = false;
- if (col >= qbegin && col <= qend) {
- return s.substr(qbegin, qend - qbegin);
+ if (s[i] == '"' || s[i] == '\'') {
+ if (i == 0 || s[i - 1] != '\\') {
+ if (inside_quotes && selected_quote == s[i]) {
+ qend = i;
+ inside_quotes = false;
+ selected_quote = '\0';
+ if (col >= qbegin && col <= qend) {
+ return s.substr(qbegin, qend - qbegin);
+ }
+ } else if (!inside_quotes) {
+ qbegin = i + 1;
+ inside_quotes = true;
+ selected_quote = s[i];
}
- } else {
- qbegin = i + 1;
- inside_quotes = true;
}
}
}
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index ae21775c55..4750e05633 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -30,8 +30,6 @@
#include "http_request.h"
-#include "version.h"
-
void HTTPRequest::_redirect_request(const String &p_new_url) {
}
@@ -106,28 +104,10 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h
validate_ssl = p_ssl_validate_domain;
- bool has_user_agent = false;
- bool has_accept = false;
headers = p_custom_headers;
request_data = p_request_data;
- for (int i = 0; i < headers.size(); i++) {
-
- if (headers[i].findn("user-agent:") == 0)
- has_user_agent = true;
- if (headers[i].findn("Accept:") == 0)
- has_accept = true;
- }
-
- if (!has_user_agent) {
- headers.push_back("User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")");
- }
-
- if (!has_accept) {
- headers.push_back("Accept: */*");
- }
-
requesting = true;
if (use_threads) {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 6199f52ec5..6d18cce21d 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1895,7 +1895,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
// Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later
// but remember non-instanced nodes that are hidden below instanced ones
if (descendant->data.owner != this) {
- if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this)
+ if (descendant->get_parent() && descendant->get_parent() != this && descendant->get_parent()->data.owner == this && descendant->data.owner != descendant->get_parent())
hidden_roots.push_back(descendant);
continue;
}
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 8d6e57b335..6438616cf2 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -498,7 +498,8 @@ bool SceneTree::idle(float p_time) {
_notify_group_pause("idle_process_internal", Node::NOTIFICATION_INTERNAL_PROCESS);
_notify_group_pause("idle_process", Node::NOTIFICATION_PROCESS);
- Size2 win_size = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ Size2 win_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
+
if (win_size != last_screen_size) {
last_screen_size = win_size;
@@ -656,6 +657,11 @@ void SceneTree::_notification(int p_notification) {
#endif
} break;
+ case NOTIFICATION_CRASH: {
+
+ get_root()->propagate_notification(p_notification);
+ } break;
+
default:
break;
};
@@ -1112,7 +1118,7 @@ void SceneTree::_update_root_rect() {
}
//actual screen video mode
- Size2 video_mode = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ Size2 video_mode = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
Size2 desired_res = stretch_min;
Size2 viewport_size;
@@ -2004,7 +2010,7 @@ SceneTree::SceneTree() {
stretch_aspect = STRETCH_ASPECT_IGNORE;
stretch_shrink = 1;
- last_screen_size = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
+ last_screen_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height);
_update_root_rect();
if (ScriptDebugger::get_singleton()) {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index a894b82a94..01302d4214 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -626,7 +626,7 @@ Rect2 Viewport::get_visible_rect() const {
if (size == Size2()) {
- r = Rect2(Point2(), Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height));
+ r = Rect2(Point2(), Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height));
} else {
r = Rect2(Point2(), size);
@@ -1404,6 +1404,8 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
mb->get_button_index() == BUTTON_WHEEL_UP ||
mb->get_button_index() == BUTTON_WHEEL_LEFT ||
mb->get_button_index() == BUTTON_WHEEL_RIGHT));
+ Ref<InputEventPanGesture> pn = p_input;
+ cant_stop_me_now = pn.is_valid() || cant_stop_me_now;
bool ismouse = ev.is_valid() || Object::cast_to<InputEventMouseMotion>(*p_input) != NULL;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 5e7569586a..654d7b884e 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -393,6 +393,9 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_DONT_RECEIVE_SHADOWS]) {
code += ",shadows_disabled";
}
+ if (flags[FLAG_ENSURE_CORRECT_NORMALS]) {
+ code += ",ensure_correct_normals";
+ }
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -1852,6 +1855,7 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_do_not_receive_shadows"), "set_flag", "get_flag", FLAG_DONT_RECEIVE_SHADOWS);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_ensure_correct_normals"), "set_flag", "get_flag", FLAG_ENSURE_CORRECT_NORMALS);
ADD_GROUP("Vertex Color", "vertex_color");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_use_as_albedo"), "set_flag", "get_flag", FLAG_ALBEDO_FROM_VERTEX_COLOR);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_is_srgb"), "set_flag", "get_flag", FLAG_SRGB_VERTEX_COLOR);
@@ -2042,6 +2046,7 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD);
BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS);
+ BIND_ENUM_CONSTANT(FLAG_ENSURE_CORRECT_NORMALS);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index ce733bfb8d..87594213bc 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -189,6 +189,7 @@ public:
FLAG_USE_ALPHA_SCISSOR,
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
FLAG_DONT_RECEIVE_SHADOWS,
+ FLAG_ENSURE_CORRECT_NORMALS,
FLAG_MAX
};
@@ -237,7 +238,7 @@ private:
uint64_t blend_mode : 2;
uint64_t depth_draw_mode : 2;
uint64_t cull_mode : 2;
- uint64_t flags : 15;
+ uint64_t flags : 16;
uint64_t detail_blend_mode : 2;
uint64_t diffuse_mode : 3;
uint64_t specular_mode : 2;
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index 95193f7a8f..a92e1b06d2 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -172,6 +172,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_SPATIAL].modes.insert("skip_vertex_transform");
shader_modes[VS::SHADER_SPATIAL].modes.insert("world_vertex_coords");
+ shader_modes[VS::SHADER_SPATIAL].modes.insert("ensure_correct_normals");
shader_modes[VS::SHADER_SPATIAL].modes.insert("shadows_disabled");