mirror of
https://github.com/godotengine/godot.git
synced 2024-11-10 06:03:09 +00:00
Merge pull request #97082 from akien-mga/4.3-cherrypicks
Some checks are pending
🔗 GHA / 📊 Static checks (push) Waiting to run
🔗 GHA / 🤖 Android (push) Blocked by required conditions
🔗 GHA / 🍏 iOS (push) Blocked by required conditions
🔗 GHA / 🐧 Linux (push) Blocked by required conditions
🔗 GHA / 🍎 macOS (push) Blocked by required conditions
🔗 GHA / 🏁 Windows (push) Blocked by required conditions
🔗 GHA / 🌐 Web (push) Blocked by required conditions
🔗 GHA / 🪲 Godot CPP (push) Blocked by required conditions
Some checks are pending
🔗 GHA / 📊 Static checks (push) Waiting to run
🔗 GHA / 🤖 Android (push) Blocked by required conditions
🔗 GHA / 🍏 iOS (push) Blocked by required conditions
🔗 GHA / 🐧 Linux (push) Blocked by required conditions
🔗 GHA / 🍎 macOS (push) Blocked by required conditions
🔗 GHA / 🏁 Windows (push) Blocked by required conditions
🔗 GHA / 🌐 Web (push) Blocked by required conditions
🔗 GHA / 🪲 Godot CPP (push) Blocked by required conditions
[4.3] Cherry-picks for the 4.3 branch (future 4.3.1) - 1st batch
This commit is contained in:
commit
6225d39826
@ -1070,7 +1070,6 @@ if "check_c_headers" in env:
|
||||
env.AppendUnique(CPPDEFINES=[headers[header]])
|
||||
|
||||
|
||||
# FIXME: This method mixes both cosmetic progress stuff and cache handling...
|
||||
methods.show_progress(env)
|
||||
# TODO: replace this with `env.Dump(format="json")`
|
||||
# once we start requiring SCons 4.0 as min version.
|
||||
@ -1102,3 +1101,5 @@ def purge_flaky_files():
|
||||
|
||||
|
||||
atexit.register(purge_flaky_files)
|
||||
|
||||
methods.clean_cache(env)
|
||||
|
@ -140,7 +140,7 @@ if env["builtin_zstd"]:
|
||||
"decompress/zstd_decompress_block.c",
|
||||
"decompress/zstd_decompress.c",
|
||||
]
|
||||
if env["platform"] in ["android", "ios", "linuxbsd", "macos"]:
|
||||
if env["platform"] in ["android", "ios", "linuxbsd", "macos"] and env["arch"] == "x86_64":
|
||||
# Match platforms with ZSTD_ASM_SUPPORTED in common/portability_macros.h
|
||||
thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S")
|
||||
thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources]
|
||||
|
@ -1489,15 +1489,6 @@ ProjectSettings::ProjectSettings() {
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0);
|
||||
GLOBAL_DEF("audio/general/ios/mix_with_others", false);
|
||||
|
||||
PackedStringArray extensions;
|
||||
extensions.push_back("gd");
|
||||
if (ClassDB::class_exists("CSharpScript")) {
|
||||
extensions.push_back("cs");
|
||||
}
|
||||
extensions.push_back("gdshader");
|
||||
|
||||
GLOBAL_DEF(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions);
|
||||
|
||||
_add_builtin_input_map();
|
||||
|
||||
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
|
||||
|
@ -781,23 +781,14 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
|
||||
}
|
||||
}
|
||||
|
||||
String actual_lib_path;
|
||||
OS::GDExtensionData data = {
|
||||
true, // also_set_library_path
|
||||
&actual_lib_path, // r_resolved_path
|
||||
&library_path, // r_resolved_path
|
||||
Engine::get_singleton()->is_editor_hint(), // generate_temp_files
|
||||
&abs_dependencies_paths, // library_dependencies
|
||||
};
|
||||
Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data);
|
||||
|
||||
if (actual_lib_path.get_file() != abs_path.get_file()) {
|
||||
// If temporary files are generated, let's change the library path to point at the original,
|
||||
// because that's what we want to check to see if it's changed.
|
||||
library_path = actual_lib_path.get_base_dir().path_join(p_path.get_file());
|
||||
} else {
|
||||
library_path = actual_lib_path;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
|
||||
|
||||
|
@ -8,7 +8,7 @@ __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,guide:b10,back
|
||||
Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android,
|
||||
|
||||
# Web
|
||||
standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
|
||||
standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:+a4,righttrigger:+a5,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
|
||||
Linux24c6581a,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
|
||||
Linux0e6f0301,Logic 3 Controller (xbox compatible),a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
|
||||
Linux045e028e,Microsoft X-Box 360 pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
|
||||
|
@ -30,12 +30,7 @@
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/variant/variant_parser.h"
|
||||
|
||||
Error Expression::_get_token(Token &r_token) {
|
||||
while (true) {
|
||||
@ -392,7 +387,6 @@ Error Expression::_get_token(Token &r_token) {
|
||||
if (is_digit(c)) {
|
||||
} else if (c == 'e') {
|
||||
reading = READING_EXP;
|
||||
|
||||
} else {
|
||||
reading = READING_DONE;
|
||||
}
|
||||
@ -419,7 +413,9 @@ Error Expression::_get_token(Token &r_token) {
|
||||
is_first_char = false;
|
||||
}
|
||||
|
||||
str_ofs--;
|
||||
if (c != 0) {
|
||||
str_ofs--;
|
||||
}
|
||||
|
||||
r_token.type = TK_CONSTANT;
|
||||
|
||||
|
@ -105,6 +105,9 @@ public:
|
||||
static _ALWAYS_INLINE_ double fmod(double p_x, double p_y) { return ::fmod(p_x, p_y); }
|
||||
static _ALWAYS_INLINE_ float fmod(float p_x, float p_y) { return ::fmodf(p_x, p_y); }
|
||||
|
||||
static _ALWAYS_INLINE_ double modf(double p_x, double *r_y) { return ::modf(p_x, r_y); }
|
||||
static _ALWAYS_INLINE_ float modf(float p_x, float *r_y) { return ::modff(p_x, r_y); }
|
||||
|
||||
static _ALWAYS_INLINE_ double floor(double p_x) { return ::floor(p_x); }
|
||||
static _ALWAYS_INLINE_ float floor(float p_x) { return ::floorf(p_x); }
|
||||
|
||||
|
@ -60,6 +60,11 @@ int64_t RandomPCG::rand_weighted(const Vector<float> &p_weights) {
|
||||
}
|
||||
}
|
||||
|
||||
for (int64_t i = weights_size - 1; i >= 0; --i) {
|
||||
if (weights[i] > 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -45,14 +45,17 @@
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
struct _ObjectDebugLock {
|
||||
Object *obj;
|
||||
ObjectID obj_id;
|
||||
|
||||
_ObjectDebugLock(Object *p_obj) {
|
||||
obj = p_obj;
|
||||
obj->_lock_index.ref();
|
||||
obj_id = p_obj->get_instance_id();
|
||||
p_obj->_lock_index.ref();
|
||||
}
|
||||
~_ObjectDebugLock() {
|
||||
obj->_lock_index.unref();
|
||||
Object *obj_ptr = ObjectDB::get_instance(obj_id);
|
||||
if (likely(obj_ptr)) {
|
||||
obj_ptr->_lock_index.unref();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -2097,7 +2100,11 @@ Object::~Object() {
|
||||
// Disconnect signals that connect to this object.
|
||||
while (connections.size()) {
|
||||
Connection c = connections.front()->get();
|
||||
bool disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
|
||||
Object *obj = c.callable.get_object();
|
||||
bool disconnected = false;
|
||||
if (likely(obj)) {
|
||||
disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
|
||||
}
|
||||
if (unlikely(!disconnected)) {
|
||||
// If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
|
||||
connections.pop_front();
|
||||
|
@ -142,6 +142,7 @@ void ScriptLanguageExtension::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_debug_get_current_stack_info);
|
||||
|
||||
GDVIRTUAL_BIND(_reload_all_scripts);
|
||||
GDVIRTUAL_BIND(_reload_scripts, "scripts", "soft_reload");
|
||||
GDVIRTUAL_BIND(_reload_tool_script, "script", "soft_reload");
|
||||
|
||||
GDVIRTUAL_BIND(_get_recognized_extensions);
|
||||
|
@ -468,7 +468,10 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
|
||||
p_caller_pool_thread->signaled = false;
|
||||
|
||||
if (IS_WAIT_OVER) {
|
||||
p_caller_pool_thread->yield_is_over = false;
|
||||
if (unlikely(p_task == ThreadData::YIELDING)) {
|
||||
p_caller_pool_thread->yield_is_over = false;
|
||||
}
|
||||
|
||||
if (!exit_threads && was_signaled) {
|
||||
// This thread was awaken for some additional reason, but it's about to exit.
|
||||
// Let's find out what may be pending and forward the requests.
|
||||
|
@ -39,19 +39,10 @@ StaticCString StaticCString::create(const char *p_ptr) {
|
||||
return scs;
|
||||
}
|
||||
|
||||
StringName::_Data *StringName::_table[STRING_TABLE_LEN];
|
||||
|
||||
StringName _scs_create(const char *p_chr, bool p_static) {
|
||||
return (p_chr[0] ? StringName(StaticCString::create(p_chr), p_static) : StringName());
|
||||
}
|
||||
|
||||
bool StringName::configured = false;
|
||||
Mutex StringName::mutex;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool StringName::debug_stringname = false;
|
||||
#endif
|
||||
|
||||
void StringName::setup() {
|
||||
ERR_FAIL_COND(configured);
|
||||
for (int i = 0; i < STRING_TABLE_LEN; i++) {
|
||||
|
@ -67,7 +67,7 @@ class StringName {
|
||||
_Data() {}
|
||||
};
|
||||
|
||||
static _Data *_table[STRING_TABLE_LEN];
|
||||
static inline _Data *_table[STRING_TABLE_LEN];
|
||||
|
||||
_Data *_data = nullptr;
|
||||
|
||||
@ -75,10 +75,10 @@ class StringName {
|
||||
friend void register_core_types();
|
||||
friend void unregister_core_types();
|
||||
friend class Main;
|
||||
static Mutex mutex;
|
||||
static inline Mutex mutex;
|
||||
static void setup();
|
||||
static void cleanup();
|
||||
static bool configured;
|
||||
static inline bool configured = false;
|
||||
#ifdef DEBUG_ENABLED
|
||||
struct DebugSortReferences {
|
||||
bool operator()(const _Data *p_left, const _Data *p_right) const {
|
||||
@ -86,7 +86,7 @@ class StringName {
|
||||
}
|
||||
};
|
||||
|
||||
static bool debug_stringname;
|
||||
static inline bool debug_stringname = false;
|
||||
#endif
|
||||
|
||||
StringName(_Data *p_data) { _data = p_data; }
|
||||
|
@ -1537,13 +1537,16 @@ Vector<double> String::split_floats(const String &p_splitter, bool p_allow_empty
|
||||
int from = 0;
|
||||
int len = length();
|
||||
|
||||
String buffer = *this;
|
||||
while (true) {
|
||||
int end = find(p_splitter, from);
|
||||
if (end < 0) {
|
||||
end = len;
|
||||
}
|
||||
if (p_allow_empty || (end > from)) {
|
||||
ret.push_back(String::to_float(&get_data()[from]));
|
||||
buffer[end] = 0;
|
||||
ret.push_back(String::to_float(&buffer.get_data()[from]));
|
||||
buffer[end] = _cowdata.get(end);
|
||||
}
|
||||
|
||||
if (end == len) {
|
||||
@ -1561,6 +1564,7 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_
|
||||
int from = 0;
|
||||
int len = length();
|
||||
|
||||
String buffer = *this;
|
||||
while (true) {
|
||||
int idx;
|
||||
int end = findmk(p_splitters, from, &idx);
|
||||
@ -1572,7 +1576,9 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_
|
||||
}
|
||||
|
||||
if (p_allow_empty || (end > from)) {
|
||||
ret.push_back(String::to_float(&get_data()[from]));
|
||||
buffer[end] = 0;
|
||||
ret.push_back(String::to_float(&buffer.get_data()[from]));
|
||||
buffer[end] = _cowdata.get(end);
|
||||
}
|
||||
|
||||
if (end == len) {
|
||||
|
@ -169,7 +169,7 @@
|
||||
astar.ConnectPoints(2, 3, false);
|
||||
astar.ConnectPoints(4, 3, false);
|
||||
astar.ConnectPoints(1, 4, false);
|
||||
int[] res = astar.GetIdPath(1, 3); // Returns [1, 2, 3]
|
||||
long[] res = astar.GetIdPath(1, 3); // Returns [1, 2, 3]
|
||||
[/csharp]
|
||||
[/codeblocks]
|
||||
If you change the 2nd point's weight to 3, then the result will be [code][1, 4, 3][/code] instead, because now even though the distance is longer, it's "easier" to get through point 4 than through point 2.
|
||||
@ -209,7 +209,7 @@
|
||||
astar.ConnectPoints(1, 2, true);
|
||||
astar.ConnectPoints(1, 3, true);
|
||||
|
||||
int[] neighbors = astar.GetPointConnections(1); // Returns [2, 3]
|
||||
long[] neighbors = astar.GetPointConnections(1); // Returns [2, 3]
|
||||
[/csharp]
|
||||
[/codeblocks]
|
||||
</description>
|
||||
|
@ -197,7 +197,7 @@
|
||||
astar.ConnectPoints(2, 3, false);
|
||||
astar.ConnectPoints(4, 3, false);
|
||||
astar.ConnectPoints(1, 4, false);
|
||||
int[] res = astar.GetIdPath(1, 3); // Returns [1, 2, 3]
|
||||
long[] res = astar.GetIdPath(1, 3); // Returns [1, 2, 3]
|
||||
[/csharp]
|
||||
[/codeblocks]
|
||||
If you change the 2nd point's weight to 3, then the result will be [code][1, 4, 3][/code] instead, because now even though the distance is longer, it's "easier" to get through point 4 than through point 2.
|
||||
@ -236,7 +236,7 @@
|
||||
astar.ConnectPoints(1, 2, true);
|
||||
astar.ConnectPoints(1, 3, true);
|
||||
|
||||
int[] neighbors = astar.GetPointConnections(1); // Returns [2, 3]
|
||||
long[] neighbors = astar.GetPointConnections(1); // Returns [2, 3]
|
||||
[/csharp]
|
||||
[/codeblocks]
|
||||
</description>
|
||||
|
@ -5,7 +5,7 @@
|
||||
</brief_description>
|
||||
<description>
|
||||
A resource used by [AnimationNodeBlendTree].
|
||||
[AnimationNodeBlendSpace1D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value.
|
||||
[AnimationNodeBlendSpace2D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value.
|
||||
You can add vertices to the blend space with [method add_blend_point] and automatically triangulate it by setting [member auto_triangles] to [code]true[/code]. Otherwise, use [method add_triangle] and [method remove_triangle] to triangulate the blend space by hand.
|
||||
</description>
|
||||
<tutorials>
|
||||
@ -93,7 +93,7 @@
|
||||
<param index="0" name="point" type="int" />
|
||||
<param index="1" name="pos" type="Vector2" />
|
||||
<description>
|
||||
Updates the position of the point at index [param point] on the blend axis.
|
||||
Updates the position of the point at index [param point] in the blend space.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
@ -243,7 +243,7 @@
|
||||
var numbers = [1, 2, 3]
|
||||
var extra = [4, 5, 6]
|
||||
numbers.append_array(extra)
|
||||
print(nums) # Prints [1, 2, 3, 4, 5, 6]
|
||||
print(numbers) # Prints [1, 2, 3, 4, 5, 6]
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
@ -715,7 +715,7 @@
|
||||
<param index="0" name="func" type="Callable" />
|
||||
<description>
|
||||
Sorts the array using a custom [Callable].
|
||||
[param func] is called as many times as necessary, receiving two array elements as arguments. The function should return [code]true[/code] if the first element should be moved [i]behind[/i] the second one, otherwise it should return [code]false[/code].
|
||||
[param func] is called as many times as necessary, receiving two array elements as arguments. The function should return [code]true[/code] if the first element should be moved [i]before[/i] the second one, otherwise it should return [code]false[/code].
|
||||
[codeblock]
|
||||
func sort_ascending(a, b):
|
||||
if a[1] < b[1]:
|
||||
@ -728,7 +728,7 @@
|
||||
print(my_items) # Prints [["Rice", 4], ["Tomato", 5], ["Apple", 9]]
|
||||
|
||||
# Sort descending, using a lambda function.
|
||||
my_items.sort_custom(func(a, b): return a[0] > b[0])
|
||||
my_items.sort_custom(func(a, b): return a[1] > b[1])
|
||||
print(my_items) # Prints [["Apple", 9], ["Tomato", 5], ["Rice", 4]]
|
||||
[/codeblock]
|
||||
It may also be necessary to use this method to sort strings by natural order, with [method String.naturalnocasecmp_to], as in the following example:
|
||||
|
@ -88,7 +88,7 @@
|
||||
<param index="0" name="idx" type="int" />
|
||||
<param index="1" name="t" type="float" />
|
||||
<description>
|
||||
Returns the position between the vertex [param idx] and the vertex [code]idx + 1[/code], where [param t] controls if the point is the first vertex ([code]t = 0.0[/code]), the last vertex ([code]t = 1.0[/code]), or in between. Values of [param t] outside the range ([code]0.0 >= t <=1[/code]) give strange, but predictable results.
|
||||
Returns the position between the vertex [param idx] and the vertex [code]idx + 1[/code], where [param t] controls if the point is the first vertex ([code]t = 0.0[/code]), the last vertex ([code]t = 1.0[/code]), or in between. Values of [param t] outside the range ([code]0.0 <= t <= 1.0[/code]) give strange, but predictable results.
|
||||
If [param idx] is out of bounds it is truncated to the first or last vertex, and [param t] is ignored. If the curve has no points, the function sends an error to the console, and returns [code](0, 0)[/code].
|
||||
</description>
|
||||
</method>
|
||||
|
@ -29,6 +29,12 @@
|
||||
Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_navigation_map" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Returns the current navigation map [RID] used by this link.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_rid" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
@ -57,6 +63,13 @@
|
||||
Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_navigation_map">
|
||||
<return type="void" />
|
||||
<param index="0" name="navigation_map" type="RID" />
|
||||
<description>
|
||||
Sets the [RID] of the navigation map this link should use. By default the link will automatically join the [World2D] default navigation map so this function is only required to override the default map.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true">
|
||||
|
@ -29,6 +29,12 @@
|
||||
Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_navigation_map" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Returns the current navigation map [RID] used by this link.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_rid" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
@ -57,6 +63,13 @@
|
||||
Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_navigation_map">
|
||||
<return type="void" />
|
||||
<param index="0" name="navigation_map" type="RID" />
|
||||
<description>
|
||||
Sets the [RID] of the navigation map this link should use. By default the link will automatically join the [World3D] default navigation map so this function is only required to override the default map.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true">
|
||||
|
@ -136,7 +136,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
private List<int> _numbers = new();
|
||||
private Godot.Collections.Array<int> _numbers = new();
|
||||
|
||||
public override Godot.Collections.Array<Godot.Collections.Dictionary> _GetPropertyList()
|
||||
{
|
||||
@ -173,7 +173,7 @@
|
||||
if (propertyName.StartsWith("number_"))
|
||||
{
|
||||
int index = int.Parse(propertyName.Substring("number_".Length));
|
||||
numbers[index] = value.As<int>();
|
||||
_numbers[index] = value.As<int>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1000,7 +1000,7 @@
|
||||
prime-run %command%
|
||||
[/codeblock]
|
||||
</member>
|
||||
<member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="" default="PackedStringArray("gd", "gdshader")">
|
||||
<member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="">
|
||||
Text-based file extensions to include in the script editor's "Find in Files" feature. You can add e.g. [code]tscn[/code] if you wish to also parse your scene files, especially if you use built-in scripts which are serialized in the scene files.
|
||||
</member>
|
||||
<member name="editor/script/templates_search_path" type="String" setter="" getter="" default=""res://script_templates"">
|
||||
|
@ -944,7 +944,8 @@
|
||||
<param index="1" name="item" type="RID" />
|
||||
<param index="2" name="mirroring" type="Vector2" />
|
||||
<description>
|
||||
A copy of the canvas item will be drawn with a local offset of the mirroring [Vector2].
|
||||
A copy of the canvas item will be drawn with a local offset of the [param mirroring].
|
||||
[b]Note:[/b] This is equivalent to calling [method canvas_set_item_repeat] like [code]canvas_set_item_repeat(item, mirroring, 1)[/code], with an additional check ensuring [param canvas] is a parent of [param item].
|
||||
</description>
|
||||
</method>
|
||||
<method name="canvas_set_item_repeat">
|
||||
|
@ -40,7 +40,7 @@
|
||||
</member>
|
||||
<member name="kerning_pairs" type="PackedStringArray" setter="" getter="" default="PackedStringArray()">
|
||||
Kerning pairs for the font. Kerning pair adjust the spacing between two characters.
|
||||
Each string consist of three space separated values: "from" string, "to" string and integer offset. Each combination form the two string for a kerning pair, e.g, [code]ab cd -3[/code] will create kerning pairs [code]ac[/code], [code]ad[/code], [code]bc[/code], and [code]bd[/code] with offset [code]-3[/code].
|
||||
Each string consist of three space separated values: "from" string, "to" string and integer offset. Each combination form the two string for a kerning pair, e.g, [code]ab cd -3[/code] will create kerning pairs [code]ac[/code], [code]ad[/code], [code]bc[/code], and [code]bd[/code] with offset [code]-3[/code]. [code]\uXXXX[/code] escape sequences can be used to add Unicode characters.
|
||||
</member>
|
||||
<member name="rows" type="int" setter="" getter="" default="1">
|
||||
Number of rows in the font image. See also [member columns].
|
||||
|
@ -314,6 +314,13 @@
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="_reload_scripts" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="scripts" type="Array" />
|
||||
<param index="1" name="soft_reload" type="bool" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="_reload_tool_script" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="script" type="Script" />
|
||||
|
@ -551,7 +551,7 @@
|
||||
</member>
|
||||
<member name="default_font_size" type="int" setter="set_default_font_size" getter="get_default_font_size" default="-1">
|
||||
The default font size of this theme resource. Used as the default value when trying to fetch a font size value that doesn't exist in this theme or is in invalid state. If the default font size is also missing or invalid, the engine fallback value is used (see [member ThemeDB.fallback_font_size]).
|
||||
Values below [code]0[/code] are invalid and can be used to unset the property. Use [method has_default_font_size] to check if this value is valid.
|
||||
Values below [code]1[/code] are invalid and can be used to unset the property. Use [method has_default_font_size] to check if this value is valid.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
|
@ -2143,17 +2143,28 @@ void RenderingDeviceDriverD3D12::command_pipeline_barrier(CommandBufferID p_cmd_
|
||||
for (uint32_t i = 0; i < p_texture_barriers.size(); i++) {
|
||||
const TextureBarrier &texture_barrier_rd = p_texture_barriers[i];
|
||||
const TextureInfo *texture_info = (const TextureInfo *)(texture_barrier_rd.texture.id);
|
||||
if (texture_info->main_texture) {
|
||||
texture_info = texture_info->main_texture;
|
||||
}
|
||||
_rd_stages_and_access_to_d3d12(p_src_stages, texture_barrier_rd.prev_layout, texture_barrier_rd.src_access, texture_barrier_d3d12.SyncBefore, texture_barrier_d3d12.AccessBefore);
|
||||
_rd_stages_and_access_to_d3d12(p_dst_stages, texture_barrier_rd.next_layout, texture_barrier_rd.dst_access, texture_barrier_d3d12.SyncAfter, texture_barrier_d3d12.AccessAfter);
|
||||
texture_barrier_d3d12.LayoutBefore = _rd_texture_layout_to_d3d12_barrier_layout(texture_barrier_rd.prev_layout);
|
||||
texture_barrier_d3d12.LayoutAfter = _rd_texture_layout_to_d3d12_barrier_layout(texture_barrier_rd.next_layout);
|
||||
texture_barrier_d3d12.pResource = texture_info->resource;
|
||||
texture_barrier_d3d12.Subresources.IndexOrFirstMipLevel = texture_barrier_rd.subresources.base_mipmap;
|
||||
texture_barrier_d3d12.Subresources.NumMipLevels = texture_barrier_rd.subresources.mipmap_count;
|
||||
texture_barrier_d3d12.Subresources.FirstArraySlice = texture_barrier_rd.subresources.base_layer;
|
||||
texture_barrier_d3d12.Subresources.NumArraySlices = texture_barrier_rd.subresources.layer_count;
|
||||
texture_barrier_d3d12.Subresources.FirstPlane = _compute_plane_slice(texture_info->format, texture_barrier_rd.subresources.aspect);
|
||||
texture_barrier_d3d12.Subresources.NumPlanes = format_get_plane_count(texture_info->format);
|
||||
if (texture_barrier_rd.subresources.mipmap_count == texture_info->mipmaps && texture_barrier_rd.subresources.layer_count == texture_info->layers) {
|
||||
// So, all resources. Then, let's be explicit about it so D3D12 doesn't think
|
||||
// we are dealing with a subset of subresources.
|
||||
texture_barrier_d3d12.Subresources.IndexOrFirstMipLevel = 0xffffffff;
|
||||
texture_barrier_d3d12.Subresources.NumMipLevels = 0;
|
||||
// Because NumMipLevels == 0, all the other fields are ignored by D3D12.
|
||||
} else {
|
||||
texture_barrier_d3d12.Subresources.IndexOrFirstMipLevel = texture_barrier_rd.subresources.base_mipmap;
|
||||
texture_barrier_d3d12.Subresources.NumMipLevels = texture_barrier_rd.subresources.mipmap_count;
|
||||
texture_barrier_d3d12.Subresources.FirstArraySlice = texture_barrier_rd.subresources.base_layer;
|
||||
texture_barrier_d3d12.Subresources.NumArraySlices = texture_barrier_rd.subresources.layer_count;
|
||||
texture_barrier_d3d12.Subresources.FirstPlane = _compute_plane_slice(texture_info->format, texture_barrier_rd.subresources.aspect);
|
||||
texture_barrier_d3d12.Subresources.NumPlanes = format_get_plane_count(texture_info->format);
|
||||
}
|
||||
texture_barrier_d3d12.Flags = (texture_barrier_rd.prev_layout == RDD::TEXTURE_LAYOUT_UNDEFINED) ? D3D12_TEXTURE_BARRIER_FLAG_DISCARD : D3D12_TEXTURE_BARRIER_FLAG_NONE;
|
||||
texture_barriers.push_back(texture_barrier_d3d12);
|
||||
}
|
||||
|
@ -468,7 +468,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
|
||||
update_skeletons = false;
|
||||
}
|
||||
// Canvas group begins here, render until before this item
|
||||
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info);
|
||||
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info, material_screen_texture_mipmaps_cached);
|
||||
item_count = 0;
|
||||
|
||||
if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) {
|
||||
@ -499,7 +499,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
|
||||
mesh_storage->update_mesh_instances();
|
||||
update_skeletons = false;
|
||||
}
|
||||
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info);
|
||||
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true, r_render_info, material_screen_texture_mipmaps_cached);
|
||||
item_count = 0;
|
||||
|
||||
if (ci->canvas_group->blur_mipmaps) {
|
||||
@ -523,7 +523,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
|
||||
}
|
||||
//render anything pending, including clearing if no items
|
||||
|
||||
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info);
|
||||
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false, r_render_info, material_screen_texture_mipmaps_cached);
|
||||
item_count = 0;
|
||||
|
||||
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
|
||||
@ -553,7 +553,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
|
||||
mesh_storage->update_mesh_instances();
|
||||
update_skeletons = false;
|
||||
}
|
||||
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info);
|
||||
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr, r_render_info, material_screen_texture_mipmaps_cached);
|
||||
//then reset
|
||||
item_count = 0;
|
||||
}
|
||||
@ -573,10 +573,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
|
||||
state.current_instance_buffer_index = 0;
|
||||
}
|
||||
|
||||
void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info) {
|
||||
void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer, RenderingMethod::RenderInfo *r_render_info, bool p_backbuffer_has_mipmaps) {
|
||||
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
|
||||
|
||||
canvas_begin(p_to_render_target, p_to_backbuffer);
|
||||
canvas_begin(p_to_render_target, p_to_backbuffer, p_backbuffer_has_mipmaps);
|
||||
|
||||
if (p_item_count <= 0) {
|
||||
// Nothing to draw, just call canvas_begin() to clear the render target and return.
|
||||
@ -647,18 +647,17 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
|
||||
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, Point2());
|
||||
} else {
|
||||
Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
|
||||
Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos;
|
||||
Point2 pos = start_pos;
|
||||
Point2 offset;
|
||||
|
||||
do {
|
||||
do {
|
||||
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, pos);
|
||||
pos.y += ci->repeat_size.y;
|
||||
} while (pos.y < end_pos.y);
|
||||
|
||||
pos.x += ci->repeat_size.x;
|
||||
pos.y = start_pos.y;
|
||||
} while (pos.x < end_pos.x);
|
||||
int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0;
|
||||
int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0;
|
||||
for (int ry = 0; ry <= repeat_times_y; ry++) {
|
||||
offset.y = start_pos.y + ry * ci->repeat_size.y;
|
||||
for (int rx = 0; rx <= repeat_times_x; rx++) {
|
||||
offset.x = start_pos.x + rx * ci->repeat_size.x;
|
||||
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -809,7 +808,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
|
||||
state.last_item_index += index;
|
||||
}
|
||||
|
||||
void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_offset) {
|
||||
void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_repeat_offset) {
|
||||
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;
|
||||
|
||||
if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
|
||||
@ -826,11 +825,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat;
|
||||
}
|
||||
|
||||
Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
|
||||
|
||||
if (p_offset.x || p_offset.y) {
|
||||
base_transform *= Transform2D(0, p_offset / p_item->xform_curr.get_scale()); // TODO: Interpolate or explain why not needed.
|
||||
Transform2D base_transform = p_item->final_transform;
|
||||
if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) {
|
||||
base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset);
|
||||
}
|
||||
base_transform = p_canvas_transform_inverse * base_transform;
|
||||
|
||||
Transform2D draw_transform; // Used by transform command
|
||||
|
||||
@ -2170,7 +2169,7 @@ bool RasterizerCanvasGLES3::free(RID p_rid) {
|
||||
void RasterizerCanvasGLES3::update() {
|
||||
}
|
||||
|
||||
void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backbuffer) {
|
||||
void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backbuffer, bool p_backbuffer_has_mipmaps) {
|
||||
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
|
||||
GLES3::Config *config = GLES3::Config::get_singleton();
|
||||
|
||||
@ -2185,6 +2184,7 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, render_target->fbo);
|
||||
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4);
|
||||
glBindTexture(GL_TEXTURE_2D, render_target->backbuffer);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p_backbuffer_has_mipmaps ? render_target->mipmap_count - 1 : 0);
|
||||
}
|
||||
|
||||
if (render_target->is_transparent || p_to_backbuffer) {
|
||||
|
@ -335,7 +335,7 @@ public:
|
||||
|
||||
typedef void Texture;
|
||||
|
||||
void canvas_begin(RID p_to_render_target, bool p_to_backbuffer);
|
||||
void canvas_begin(RID p_to_render_target, bool p_to_backbuffer, bool p_backbuffer_has_mipmaps);
|
||||
|
||||
//virtual void draw_window_margins(int *black_margin, RID *black_image) override;
|
||||
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
|
||||
@ -361,8 +361,8 @@ public:
|
||||
void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size);
|
||||
|
||||
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
|
||||
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
|
||||
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_offset);
|
||||
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr, bool p_backbuffer_has_mipmaps = false);
|
||||
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_repeat_offset);
|
||||
void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr);
|
||||
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
|
||||
void _new_batch(bool &r_batch_broken);
|
||||
|
@ -62,6 +62,10 @@
|
||||
#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
|
||||
#define _EXT_DEBUG_OUTPUT 0x92E0
|
||||
|
||||
#ifndef GL_FRAMEBUFFER_SRGB
|
||||
#define GL_FRAMEBUFFER_SRGB 0x8DB9
|
||||
#endif
|
||||
|
||||
#ifndef GLAPIENTRY
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
#define GLAPIENTRY APIENTRY
|
||||
@ -345,6 +349,9 @@ RasterizerGLES3::RasterizerGLES3() {
|
||||
}
|
||||
}
|
||||
|
||||
// Disable OpenGL linear to sRGB conversion, because Godot will always do this conversion itself.
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
// OpenGL needs to be initialized before initializing the Rasterizers
|
||||
config = memnew(GLES3::Config);
|
||||
utilities = memnew(GLES3::Utilities);
|
||||
|
@ -777,7 +777,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
|
||||
ERR_FAIL_COND(p_env.is_null());
|
||||
|
||||
Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env));
|
||||
ERR_FAIL_NULL(sky);
|
||||
|
||||
GLES3::SkyMaterialData *material_data = nullptr;
|
||||
RID sky_material;
|
||||
@ -851,6 +850,15 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::SKY_ENERGY_MULTIPLIER, p_sky_energy_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
|
||||
Color fog_color = environment_get_fog_light_color(p_env).srgb_to_linear() * environment_get_fog_light_energy(p_env);
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_ENABLED, environment_get_fog_enabled(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_AERIAL_PERSPECTIVE, environment_get_fog_aerial_perspective(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_LIGHT_COLOR, fog_color, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_SUN_SCATTER, environment_get_fog_sun_scatter(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_DENSITY, environment_get_fog_density(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_SKY_AFFECT, environment_get_fog_sky_affect(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::DIRECTIONAL_LIGHT_COUNT, sky_globals.directional_light_count, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
|
||||
|
||||
if (p_use_multiview) {
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
@ -904,7 +912,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
|
||||
RS::SkyMode sky_mode = sky->mode;
|
||||
|
||||
if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
|
||||
if (shader_data->uses_time || shader_data->uses_position) {
|
||||
if ((shader_data->uses_time || shader_data->uses_position) && sky->radiance_size == 256) {
|
||||
update_single_frame = true;
|
||||
sky_mode = RS::SKY_MODE_REALTIME;
|
||||
} else if (shader_data->uses_light || shader_data->ubo_size > 0) {
|
||||
@ -925,7 +933,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
|
||||
int max_processing_layer = sky->mipmap_count;
|
||||
|
||||
// Update radiance cubemap
|
||||
if (sky->reflection_dirty && (sky->processing_layer > max_processing_layer || update_single_frame)) {
|
||||
if (sky->reflection_dirty && (sky->processing_layer >= max_processing_layer || update_single_frame)) {
|
||||
static const Vector3 view_normals[6] = {
|
||||
Vector3(+1, 0, 0),
|
||||
Vector3(-1, 0, 0),
|
||||
@ -2587,7 +2595,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
|
||||
|
||||
scene_state.enable_gl_depth_draw(false);
|
||||
|
||||
if (draw_sky) {
|
||||
if (draw_sky || draw_sky_fog_only) {
|
||||
RENDER_TIMESTAMP("Render Sky");
|
||||
|
||||
scene_state.enable_gl_depth_test(true);
|
||||
|
@ -698,7 +698,8 @@ void ShaderGLES3::_clear_version(Version *p_version) {
|
||||
|
||||
void ShaderGLES3::_initialize_version(Version *p_version) {
|
||||
ERR_FAIL_COND(p_version->variants.size() > 0);
|
||||
if (shader_cache_dir_valid && _load_from_cache(p_version)) {
|
||||
bool use_cache = shader_cache_dir_valid && !(feedback_count > 0 && GLES3::Config::get_singleton()->disable_transform_feedback_shader_cache);
|
||||
if (use_cache && _load_from_cache(p_version)) {
|
||||
return;
|
||||
}
|
||||
p_version->variants.reserve(variant_count);
|
||||
@ -709,7 +710,7 @@ void ShaderGLES3::_initialize_version(Version *p_version) {
|
||||
_compile_specialization(spec, i, p_version, specialization_default_mask);
|
||||
p_version->variants[i].insert(specialization_default_mask, spec);
|
||||
}
|
||||
if (shader_cache_dir_valid) {
|
||||
if (use_cache) {
|
||||
_save_to_cache(p_version);
|
||||
}
|
||||
}
|
||||
|
@ -583,6 +583,8 @@ void main() {
|
||||
|
||||
#define SHADER_IS_SRGB true
|
||||
|
||||
#define FLAGS_NON_UNIFORM_SCALE (1 << 4)
|
||||
|
||||
/* Varyings */
|
||||
|
||||
#if defined(COLOR_USED)
|
||||
@ -955,6 +957,7 @@ ivec2 multiview_uv(ivec2 uv) {
|
||||
|
||||
uniform highp mat4 world_transform;
|
||||
uniform mediump float opaque_prepass_threshold;
|
||||
uniform highp uint model_flags;
|
||||
|
||||
#if defined(RENDER_MATERIAL)
|
||||
layout(location = 0) out vec4 albedo_output_buffer;
|
||||
@ -1521,6 +1524,13 @@ void main() {
|
||||
vec3 light_vertex = vertex;
|
||||
#endif //LIGHT_VERTEX_USED
|
||||
|
||||
highp mat3 model_normal_matrix;
|
||||
if (bool(model_flags & uint(FLAGS_NON_UNIFORM_SCALE))) {
|
||||
model_normal_matrix = transpose(inverse(mat3(model_matrix)));
|
||||
} else {
|
||||
model_normal_matrix = mat3(model_matrix);
|
||||
}
|
||||
|
||||
{
|
||||
#CODE : FRAGMENT
|
||||
}
|
||||
@ -1728,16 +1738,10 @@ void main() {
|
||||
|
||||
vec3 n = normalize(lightmap_normal_xform * normal);
|
||||
|
||||
ambient_light += lm_light_l0 * 0.282095f;
|
||||
ambient_light += lm_light_l1n1 * 0.32573 * n.y * lightmap_exposure_normalization;
|
||||
ambient_light += lm_light_l1_0 * 0.32573 * n.z * lightmap_exposure_normalization;
|
||||
ambient_light += lm_light_l1p1 * 0.32573 * n.x * lightmap_exposure_normalization;
|
||||
if (metallic > 0.01) { // Since the more direct bounced light is lost, we can kind of fake it with this trick.
|
||||
vec3 r = reflect(normalize(-vertex), normal);
|
||||
specular_light += lm_light_l1n1 * 0.32573 * r.y * lightmap_exposure_normalization;
|
||||
specular_light += lm_light_l1_0 * 0.32573 * r.z * lightmap_exposure_normalization;
|
||||
specular_light += lm_light_l1p1 * 0.32573 * r.x * lightmap_exposure_normalization;
|
||||
}
|
||||
ambient_light += lm_light_l0 * lightmap_exposure_normalization;
|
||||
ambient_light += lm_light_l1n1 * n.y * lightmap_exposure_normalization;
|
||||
ambient_light += lm_light_l1_0 * n.z * lightmap_exposure_normalization;
|
||||
ambient_light += lm_light_l1p1 * n.x * lightmap_exposure_normalization;
|
||||
#else
|
||||
ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization;
|
||||
#endif
|
||||
@ -1871,7 +1875,7 @@ void main() {
|
||||
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
|
||||
|
||||
#if defined(ALPHA_SCISSOR_USED)
|
||||
if (alpha < alpha_scissor) {
|
||||
if (alpha < alpha_scissor_threshold) {
|
||||
discard;
|
||||
}
|
||||
#endif // !ALPHA_SCISSOR_USED
|
||||
|
@ -59,7 +59,7 @@ layout(location = 10) in highp uvec4 in_bone_attrib;
|
||||
layout(location = 11) in mediump vec4 in_weight_attrib;
|
||||
#endif
|
||||
|
||||
uniform mediump sampler2D skeleton_texture; // texunit:0
|
||||
uniform highp sampler2D skeleton_texture; // texunit:0
|
||||
#endif
|
||||
|
||||
/* clang-format on */
|
||||
|
@ -108,11 +108,11 @@ uniform float sky_energy_multiplier;
|
||||
uniform float luminance_multiplier;
|
||||
|
||||
uniform float fog_aerial_perspective;
|
||||
uniform vec3 fog_light_color;
|
||||
uniform vec4 fog_light_color;
|
||||
uniform float fog_sun_scatter;
|
||||
uniform bool fog_enabled;
|
||||
uniform float fog_density;
|
||||
uniform float z_far;
|
||||
uniform float fog_sky_affect;
|
||||
uniform uint directional_light_count;
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
@ -135,6 +135,24 @@ vec3 interleaved_gradient_noise(vec2 pos) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(DISABLE_FOG)
|
||||
vec4 fog_process(vec3 view, vec3 sky_color) {
|
||||
vec3 fog_color = mix(fog_light_color.rgb, sky_color, fog_aerial_perspective);
|
||||
|
||||
if (fog_sun_scatter > 0.001) {
|
||||
vec4 sun_scatter = vec4(0.0);
|
||||
float sun_total = 0.0;
|
||||
for (uint i = 0u; i < directional_light_count; i++) {
|
||||
vec3 light_color = directional_lights.data[i].color_size.xyz * directional_lights.data[i].direction_energy.w;
|
||||
float light_amount = pow(max(dot(view, directional_lights.data[i].direction_energy.xyz), 0.0), 8.0);
|
||||
fog_color += light_color * light_amount * fog_sun_scatter;
|
||||
}
|
||||
}
|
||||
|
||||
return vec4(fog_color, 1.0);
|
||||
}
|
||||
#endif // !DISABLE_FOG
|
||||
|
||||
void main() {
|
||||
vec3 cube_normal;
|
||||
#ifdef USE_MULTIVIEW
|
||||
@ -203,6 +221,21 @@ void main() {
|
||||
|
||||
// Convert to Linear for tonemapping so color matches scene shader better
|
||||
color = srgb_to_linear(color);
|
||||
|
||||
#if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS)
|
||||
|
||||
// Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
|
||||
if (fog_enabled) {
|
||||
vec4 fog = fog_process(cube_normal, color.rgb);
|
||||
color.rgb = mix(color.rgb, fog.rgb, fog.a * fog_sky_affect);
|
||||
}
|
||||
|
||||
if (custom_fog.a > 0.0) {
|
||||
color.rgb = mix(color.rgb, custom_fog.rgb, custom_fog.a);
|
||||
}
|
||||
|
||||
#endif // DISABLE_FOG
|
||||
|
||||
color *= exposure;
|
||||
#ifdef APPLY_TONEMAPPING
|
||||
color = apply_tonemapping(color, white);
|
||||
|
@ -121,7 +121,7 @@ Config::Config() {
|
||||
#ifdef WEB_ENABLED
|
||||
msaa_supported = (msaa_max_samples > 0);
|
||||
#else
|
||||
msaa_supported = extensions.has("GL_EXT_framebuffer_multisample");
|
||||
msaa_supported = true;
|
||||
#endif
|
||||
#ifndef IOS_ENABLED
|
||||
#ifdef WEB_ENABLED
|
||||
@ -218,6 +218,8 @@ Config::Config() {
|
||||
//https://github.com/godotengine/godot/issues/92662#issuecomment-2161199477
|
||||
//disable_particles_workaround = false;
|
||||
}
|
||||
} else if (rendering_device_name == "PowerVR Rogue GE8320") {
|
||||
disable_transform_feedback_shader_cache = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,9 @@ public:
|
||||
bool disable_particles_workaround = false; // set to 'true' to disable 'GPUParticles'
|
||||
bool flip_xy_workaround = false;
|
||||
|
||||
// PowerVR GE 8320 workaround
|
||||
bool disable_transform_feedback_shader_cache = false;
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
|
||||
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr;
|
||||
|
@ -1518,6 +1518,11 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i
|
||||
uint64_t min_pass = 0; // Pass of the existing one, try to use the least recently used one (LRU fashion).
|
||||
|
||||
for (int j = 0; j < sc; j++) {
|
||||
if (sarr[j].owner_is_omni != is_omni) {
|
||||
// Existing light instance type doesn't match new light instance type skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner);
|
||||
if (!sli) {
|
||||
// Found a released light instance.
|
||||
|
@ -301,7 +301,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
|
||||
Vector<uint8_t> ir = new_surface.index_data;
|
||||
wr = wf_indices.ptrw();
|
||||
|
||||
if (new_surface.vertex_count < (1 << 16)) {
|
||||
if (new_surface.vertex_count <= 65536) {
|
||||
// Read 16 bit indices.
|
||||
const uint16_t *src_idx = (const uint16_t *)ir.ptr();
|
||||
for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) {
|
||||
@ -743,6 +743,7 @@ String MeshStorage::mesh_get_path(RID p_mesh) const {
|
||||
}
|
||||
|
||||
void MeshStorage::mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) {
|
||||
ERR_FAIL_COND_MSG(p_mesh == p_shadow_mesh, "Cannot set a mesh as its own shadow mesh.");
|
||||
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
|
||||
ERR_FAIL_NULL(mesh);
|
||||
|
||||
|
@ -1039,7 +1039,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
|
||||
data.resize(data_size);
|
||||
|
||||
ERR_FAIL_COND_V(data.is_empty(), Ref<Image>());
|
||||
image = Image::create_from_data(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data);
|
||||
image = Image::create_from_data(texture->alloc_width, texture->alloc_height, texture->mipmaps > 1, texture->real_format, data);
|
||||
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
|
||||
if (texture->format != texture->real_format) {
|
||||
image->convert(texture->format);
|
||||
@ -1095,7 +1095,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
|
||||
data.resize(data_size);
|
||||
|
||||
ERR_FAIL_COND_V(data.is_empty(), Ref<Image>());
|
||||
image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data);
|
||||
image = Image::create_from_data(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data);
|
||||
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
|
||||
|
||||
if (texture->format != Image::FORMAT_RGBA8) {
|
||||
@ -1497,11 +1497,9 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image,
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
if (texture->target == GL_TEXTURE_2D_ARRAY) {
|
||||
if (p_initialize) {
|
||||
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0,
|
||||
size * texture->layers, &read[ofs]);
|
||||
} else {
|
||||
glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
|
||||
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0, size * texture->layers, nullptr);
|
||||
}
|
||||
glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
|
||||
} else {
|
||||
glCompressedTexImage2D(blit_target, i, internal_format, w, h, 0, size, &read[ofs]);
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
|
||||
if (!err) {
|
||||
return status.st_mtime;
|
||||
} else {
|
||||
print_verbose("Failed to get modified time for: " + p_file + "");
|
||||
WARN_PRINT("Failed to get modified time for: " + p_file);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -553,15 +553,40 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
|
||||
file = file.substr(0, file.length() - 1);
|
||||
}
|
||||
|
||||
struct _stat st;
|
||||
int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st);
|
||||
HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
|
||||
if (rv == 0) {
|
||||
return st.st_mtime;
|
||||
} else {
|
||||
print_verbose("Failed to get modified time for: " + p_file + "");
|
||||
return 0;
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
FILETIME ft_create, ft_write;
|
||||
|
||||
bool status = GetFileTime(handle, &ft_create, nullptr, &ft_write);
|
||||
|
||||
CloseHandle(handle);
|
||||
|
||||
if (status) {
|
||||
uint64_t ret = 0;
|
||||
|
||||
// If write time is invalid, fallback to creation time.
|
||||
if (ft_write.dwHighDateTime == 0 && ft_write.dwLowDateTime == 0) {
|
||||
ret = ft_create.dwHighDateTime;
|
||||
ret <<= 32;
|
||||
ret |= ft_create.dwLowDateTime;
|
||||
} else {
|
||||
ret = ft_write.dwHighDateTime;
|
||||
ret <<= 32;
|
||||
ret |= ft_write.dwLowDateTime;
|
||||
}
|
||||
|
||||
const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000;
|
||||
const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL;
|
||||
|
||||
if (ret >= TICKS_TO_UNIX_EPOCH) {
|
||||
return (ret - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_verbose("Failed to get modified time for: " + p_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BitField<FileAccess::UnixPermissionFlags> FileAccessWindows::_get_unix_permissions(const String &p_file) {
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_spin_slider.h"
|
||||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/view_panner.h"
|
||||
#include "scene/resources/text_line.h"
|
||||
@ -868,6 +869,11 @@ void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::Hand
|
||||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_out_handle(track_key_pair.first, track_key_pair.second));
|
||||
undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode", animation.ptr(), track_key_pair.first, track_key_pair.second, p_mode, p_auto ? Animation::HANDLE_SET_MODE_AUTO : Animation::HANDLE_SET_MODE_RESET);
|
||||
}
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
@ -1083,7 +1089,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (I.key == REMOVE_ICON) {
|
||||
if (!read_only) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action("Remove Bezier Track");
|
||||
undo_redo->create_action("Remove Bezier Track", UndoRedo::MERGE_DISABLE, animation.ptr());
|
||||
|
||||
undo_redo->add_do_method(this, "_update_locked_tracks_after", track);
|
||||
undo_redo->add_do_method(this, "_update_hidden_tracks_after", track);
|
||||
@ -1559,6 +1565,11 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||
undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, moving_handle_right, ratio);
|
||||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key), ratio);
|
||||
}
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
moving_handle = 0;
|
||||
queue_redraw();
|
||||
@ -1672,6 +1683,11 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
|
||||
undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point);
|
||||
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
queue_redraw();
|
||||
}
|
||||
@ -1772,6 +1788,11 @@ void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs, bool p_ofs_
|
||||
i++;
|
||||
}
|
||||
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->add_do_method(this, "queue_redraw");
|
||||
undo_redo->add_undo_method(this, "queue_redraw");
|
||||
undo_redo->commit_action();
|
||||
@ -1821,6 +1842,15 @@ void AnimationBezierTrackEdit::copy_selected_keys(bool p_cut) {
|
||||
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, E->value().pos, i == 0);
|
||||
i++;
|
||||
}
|
||||
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->add_do_method(this, "queue_redraw");
|
||||
undo_redo->add_undo_method(this, "queue_redraw");
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
}
|
||||
@ -1899,9 +1929,15 @@ void AnimationBezierTrackEdit::paste_keys(real_t p_ofs, bool p_ofs_valid) {
|
||||
i++;
|
||||
}
|
||||
|
||||
undo_redo->commit_action();
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->add_do_method(this, "queue_redraw");
|
||||
undo_redo->add_undo_method(this, "queue_redraw");
|
||||
|
||||
queue_redraw();
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1916,6 +1952,11 @@ void AnimationBezierTrackEdit::delete_selection() {
|
||||
}
|
||||
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
|
||||
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
|
||||
//selection.clear();
|
||||
@ -1925,6 +1966,15 @@ void AnimationBezierTrackEdit::delete_selection() {
|
||||
void AnimationBezierTrackEdit::_bezier_track_insert_key_at_anim(const Ref<Animation> &p_anim, int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode) {
|
||||
int idx = p_anim->bezier_track_insert_key(p_track, p_time, p_value, p_in_handle, p_out_handle);
|
||||
p_anim->bezier_track_set_key_handle_mode(p_track, idx, p_handle_mode);
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Animation Bezier Curve Change Call"));
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void AnimationBezierTrackEdit::_bind_methods() {
|
||||
|
@ -266,6 +266,11 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
|
||||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev);
|
||||
undo_redo->add_do_method(this, "_update_obj", animation);
|
||||
undo_redo->add_undo_method(this, "_update_obj", animation);
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
|
||||
setting = false;
|
||||
@ -282,6 +287,11 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
|
||||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, prev);
|
||||
undo_redo->add_do_method(this, "_update_obj", animation);
|
||||
undo_redo->add_undo_method(this, "_update_obj", animation);
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
|
||||
setting = false;
|
||||
@ -298,6 +308,11 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
|
||||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev);
|
||||
undo_redo->add_do_method(this, "_update_obj", animation);
|
||||
undo_redo->add_undo_method(this, "_update_obj", animation);
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
|
||||
setting = false;
|
||||
@ -318,6 +333,11 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
|
||||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, prev_in_handle);
|
||||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev_out_handle);
|
||||
undo_redo->add_undo_method(this, "_update_obj", animation);
|
||||
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
|
||||
if (ape) {
|
||||
undo_redo->add_do_method(ape, "_animation_update_key_frame");
|
||||
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
|
||||
setting = false;
|
||||
@ -7032,7 +7052,10 @@ void AnimationTrackEditor::_update_snap_unit() {
|
||||
if (timeline->is_using_fps()) {
|
||||
snap_unit = 1.0 / step->get_value();
|
||||
} else {
|
||||
snap_unit = 1.0 / Math::round(1.0 / step->get_value()); // Follow the snap behavior of the timeline editor.
|
||||
double integer;
|
||||
double fraction = Math::modf(step->get_value(), &integer);
|
||||
fraction = 1.0 / Math::round(1.0 / fraction);
|
||||
snap_unit = integer + fraction;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -994,6 +994,9 @@ Ref<Texture2D> CodeTextEditor::_get_completion_icon(const ScriptLanguage::CodeCo
|
||||
tex = get_editor_theme_icon(p_option.display);
|
||||
} else {
|
||||
tex = EditorNode::get_singleton()->get_class_icon(p_option.display);
|
||||
if (!tex.is_valid()) {
|
||||
tex = get_editor_theme_icon(SNAME("Object"));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case ScriptLanguage::CODE_COMPLETION_KIND_ENUM:
|
||||
|
@ -578,32 +578,34 @@ void DependencyRemoveDialog::ok_pressed() {
|
||||
}
|
||||
}
|
||||
|
||||
bool project_settings_modified = false;
|
||||
for (int i = 0; i < files_to_delete.size(); ++i) {
|
||||
// If the file we are deleting for e.g. the main scene, default environment,
|
||||
// or audio bus layout, we must clear its definition in Project Settings.
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("application/config/icon"))) {
|
||||
ProjectSettings::get_singleton()->set("application/config/icon", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) {
|
||||
ProjectSettings::get_singleton()->set("application/run/main_scene", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) {
|
||||
ProjectSettings::get_singleton()->set("application/boot_splash/image", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) {
|
||||
ProjectSettings::get_singleton()->set("rendering/environment/defaults/default_environment", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) {
|
||||
ProjectSettings::get_singleton()->set("display/mouse_cursor/custom_image", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) {
|
||||
ProjectSettings::get_singleton()->set("gui/theme/custom", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) {
|
||||
ProjectSettings::get_singleton()->set("gui/theme/custom_font", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) {
|
||||
ProjectSettings::get_singleton()->set("audio/buses/default_bus_layout", "");
|
||||
project_settings_modified = true;
|
||||
}
|
||||
|
||||
String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/");
|
||||
@ -615,6 +617,9 @@ void DependencyRemoveDialog::ok_pressed() {
|
||||
emit_signal(SNAME("file_removed"), files_to_delete[i]);
|
||||
}
|
||||
}
|
||||
if (project_settings_modified) {
|
||||
ProjectSettings::get_singleton()->save();
|
||||
}
|
||||
|
||||
if (dirs_to_delete.size() == 0) {
|
||||
// If we only deleted files we should only need to tell the file system about the files we touched.
|
||||
|
@ -457,7 +457,9 @@ void EditorAutoloadSettings::init_autoloads() {
|
||||
|
||||
for (const AutoloadInfo &info : autoload_cache) {
|
||||
if (info.node && info.in_editor) {
|
||||
callable_mp((Node *)get_tree()->get_root(), &Node::add_child).call_deferred(info.node, false, Node::INTERNAL_MODE_DISABLED);
|
||||
// It's important to add the node without deferring because code in plugins or tool scripts
|
||||
// could use the autoload node when they are enabled.
|
||||
get_tree()->get_root()->add_child(info.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -386,6 +386,8 @@ void EditorFileSystem::_scan_filesystem() {
|
||||
// On the first scan, the first_scan_root_dir is created in _first_scan_filesystem.
|
||||
if (first_scan) {
|
||||
sd = first_scan_root_dir;
|
||||
// Will be updated on scan.
|
||||
ResourceUID::get_singleton()->clear();
|
||||
} else {
|
||||
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
sd = memnew(ScannedDirectory);
|
||||
@ -1779,7 +1781,9 @@ String EditorFileSystem::_get_global_script_class(const String &p_type, const St
|
||||
|
||||
void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info) {
|
||||
String icon_path;
|
||||
if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) {
|
||||
if (file_info->resource_script_class != StringName()) {
|
||||
icon_path = EditorNode::get_editor_data().script_class_get_icon_path(file_info->resource_script_class);
|
||||
} else if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) {
|
||||
const String &script_dep = file_info->deps[0]; // Assuming the first dependency is a script.
|
||||
const String &script_path = script_dep.contains("::") ? script_dep.get_slice("::", 2) : script_dep;
|
||||
if (!script_path.is_empty()) {
|
||||
@ -3059,7 +3063,6 @@ EditorFileSystem::EditorFileSystem() {
|
||||
using_fat32_or_exfat = (da->get_filesystem_type() == "FAT32" || da->get_filesystem_type() == "exFAT");
|
||||
|
||||
scan_total = 0;
|
||||
callable_mp(ResourceUID::get_singleton(), &ResourceUID::clear).call_deferred(); // Will be updated on scan.
|
||||
ResourceSaver::set_get_resource_id_for_path(_resource_saver_get_resource_id_for_path);
|
||||
}
|
||||
|
||||
|
@ -174,6 +174,32 @@ static const String EDITOR_NODE_CONFIG_SECTION = "EditorNode";
|
||||
static const String REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE = "The Android build template is already installed in this project and it won't be overwritten.\nRemove the \"%s\" directory manually before attempting this operation again.";
|
||||
static const String INSTALL_ANDROID_BUILD_TEMPLATE_MESSAGE = "This will set up your project for gradle Android builds by installing the source template to \"%s\".\nNote that in order to make gradle builds instead of using pre-built APKs, the \"Use Gradle Build\" option should be enabled in the Android export preset.";
|
||||
|
||||
bool EditorProgress::step(const String &p_state, int p_step, bool p_force_refresh) {
|
||||
if (Thread::is_main_thread()) {
|
||||
return EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh);
|
||||
} else {
|
||||
EditorNode::progress_task_step_bg(task, p_step);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EditorProgress::EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel) {
|
||||
if (Thread::is_main_thread()) {
|
||||
EditorNode::progress_add_task(p_task, p_label, p_amount, p_can_cancel);
|
||||
} else {
|
||||
EditorNode::progress_add_task_bg(p_task, p_label, p_amount);
|
||||
}
|
||||
task = p_task;
|
||||
}
|
||||
|
||||
EditorProgress::~EditorProgress() {
|
||||
if (Thread::is_main_thread()) {
|
||||
EditorNode::progress_end_task(task);
|
||||
} else {
|
||||
EditorNode::progress_end_task_bg(task);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) {
|
||||
ERR_FAIL_COND_MSG(p_full_paths.size() != r_filenames.size(), vformat("disambiguate_filenames requires two string vectors of same length (%d != %d).", p_full_paths.size(), r_filenames.size()));
|
||||
|
||||
@ -670,7 +696,10 @@ void EditorNode::_notification(int p_what) {
|
||||
|
||||
callable_mp(this, &EditorNode::_begin_first_scan).call_deferred();
|
||||
|
||||
DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_update_theme).bind(false));
|
||||
last_dark_mode_state = DisplayServer::get_singleton()->is_dark_mode();
|
||||
last_system_accent_color = DisplayServer::get_singleton()->get_accent_color();
|
||||
last_system_base_color = DisplayServer::get_singleton()->get_base_color();
|
||||
DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_check_system_theme_changed));
|
||||
|
||||
/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
|
||||
} break;
|
||||
@ -1658,17 +1687,17 @@ void EditorNode::_find_node_types(Node *p_node, int &count_2d, int &count_3d) {
|
||||
}
|
||||
|
||||
void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
|
||||
EditorProgress save("save", TTR("Saving Scene"), 4);
|
||||
save_scene_progress = memnew(EditorProgress("save", TTR("Saving Scene"), 4));
|
||||
|
||||
if (editor_data.get_edited_scene_root() != nullptr) {
|
||||
save.step(TTR("Analyzing"), 0);
|
||||
save_scene_progress->step(TTR("Analyzing"), 0);
|
||||
|
||||
int c2d = 0;
|
||||
int c3d = 0;
|
||||
|
||||
_find_node_types(editor_data.get_edited_scene_root(), c2d, c3d);
|
||||
|
||||
save.step(TTR("Creating Thumbnail"), 1);
|
||||
save_scene_progress->step(TTR("Creating Thumbnail"), 1);
|
||||
// Current view?
|
||||
|
||||
Ref<Image> img;
|
||||
@ -1696,8 +1725,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
|
||||
if (img.is_valid() && img->get_width() > 0 && img->get_height() > 0) {
|
||||
img = img->duplicate();
|
||||
|
||||
save.step(TTR("Creating Thumbnail"), 2);
|
||||
save.step(TTR("Creating Thumbnail"), 3);
|
||||
save_scene_progress->step(TTR("Creating Thumbnail"), 3);
|
||||
|
||||
int preview_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
||||
preview_size *= EDSCALE;
|
||||
@ -1733,12 +1761,19 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
|
||||
}
|
||||
}
|
||||
|
||||
save.step(TTR("Saving Scene"), 4);
|
||||
save_scene_progress->step(TTR("Saving Scene"), 4);
|
||||
_save_scene(p_file, p_idx);
|
||||
|
||||
if (!singleton->cmdline_export_mode) {
|
||||
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
|
||||
}
|
||||
|
||||
_close_save_scene_progress();
|
||||
}
|
||||
|
||||
void EditorNode::_close_save_scene_progress() {
|
||||
memdelete_notnull(save_scene_progress);
|
||||
save_scene_progress = nullptr;
|
||||
}
|
||||
|
||||
bool EditorNode::_validate_scene_recursive(const String &p_filename, Node *p_node) {
|
||||
@ -5132,6 +5167,7 @@ bool EditorNode::is_project_exporting() const {
|
||||
void EditorNode::show_accept(const String &p_text, const String &p_title) {
|
||||
current_menu_option = -1;
|
||||
if (accept) {
|
||||
_close_save_scene_progress();
|
||||
accept->set_ok_button_text(p_title);
|
||||
accept->set_text(p_text);
|
||||
EditorInterface::get_singleton()->popup_dialog_centered(accept);
|
||||
@ -5141,6 +5177,7 @@ void EditorNode::show_accept(const String &p_text, const String &p_title) {
|
||||
void EditorNode::show_save_accept(const String &p_text, const String &p_title) {
|
||||
current_menu_option = -1;
|
||||
if (save_accept) {
|
||||
_close_save_scene_progress();
|
||||
save_accept->set_ok_button_text(p_title);
|
||||
save_accept->set_text(p_text);
|
||||
EditorInterface::get_singleton()->popup_dialog_centered(save_accept);
|
||||
@ -5149,6 +5186,7 @@ void EditorNode::show_save_accept(const String &p_text, const String &p_title) {
|
||||
|
||||
void EditorNode::show_warning(const String &p_text, const String &p_title) {
|
||||
if (warning) {
|
||||
_close_save_scene_progress();
|
||||
warning->set_text(p_text);
|
||||
warning->set_title(p_title);
|
||||
EditorInterface::get_singleton()->popup_dialog_centered(warning);
|
||||
@ -5296,14 +5334,18 @@ void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout) {
|
||||
|
||||
PackedStringArray scenes = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes");
|
||||
for (int i = 0; i < scenes.size(); i++) {
|
||||
load_scene(scenes[i]);
|
||||
if (FileAccess::exists(scenes[i])) {
|
||||
load_scene(scenes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_layout->has_section_key(EDITOR_NODE_CONFIG_SECTION, "current_scene")) {
|
||||
String current_scene = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "current_scene");
|
||||
int current_scene_idx = scenes.find(current_scene);
|
||||
if (current_scene_idx >= 0) {
|
||||
_set_current_scene(current_scene_idx);
|
||||
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
|
||||
if (editor_data.get_scene_path(i) == current_scene) {
|
||||
_set_current_scene(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6033,7 +6075,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
|
||||
base_packed_scene = current_packed_scene;
|
||||
}
|
||||
if (!local_scene_cache.find(path)) {
|
||||
current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP, &err);
|
||||
current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
|
||||
local_scene_cache[path] = current_packed_scene;
|
||||
} else {
|
||||
current_packed_scene = local_scene_cache[path];
|
||||
|
@ -122,6 +122,14 @@ class SurfaceUpgradeTool;
|
||||
class SurfaceUpgradeDialog;
|
||||
class WindowWrapper;
|
||||
|
||||
struct EditorProgress {
|
||||
String task;
|
||||
bool step(const String &p_state, int p_step = -1, bool p_force_refresh = true);
|
||||
|
||||
EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false);
|
||||
~EditorProgress();
|
||||
};
|
||||
|
||||
class EditorNode : public Node {
|
||||
GDCLASS(EditorNode, Node);
|
||||
|
||||
@ -470,6 +478,7 @@ private:
|
||||
String external_file;
|
||||
String open_navigate;
|
||||
String saving_scene;
|
||||
EditorProgress *save_scene_progress = nullptr;
|
||||
|
||||
DynamicFontImportSettingsDialog *fontdata_import_settings = nullptr;
|
||||
SceneImportSettingsDialog *scene_import_settings = nullptr;
|
||||
@ -624,6 +633,7 @@ private:
|
||||
|
||||
void _find_node_types(Node *p_node, int &count_2d, int &count_3d);
|
||||
void _save_scene_with_preview(String p_file, int p_idx = -1);
|
||||
void _close_save_scene_progress();
|
||||
|
||||
bool _find_scene_in_use(Node *p_node, const String &p_path) const;
|
||||
|
||||
@ -952,33 +962,6 @@ public:
|
||||
bool ensure_main_scene(bool p_from_native);
|
||||
};
|
||||
|
||||
struct EditorProgress {
|
||||
String task;
|
||||
bool step(const String &p_state, int p_step = -1, bool p_force_refresh = true) {
|
||||
if (Thread::is_main_thread()) {
|
||||
return EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh);
|
||||
} else {
|
||||
EditorNode::progress_task_step_bg(task, p_step);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false) {
|
||||
if (Thread::is_main_thread()) {
|
||||
EditorNode::progress_add_task(p_task, p_label, p_amount, p_can_cancel);
|
||||
} else {
|
||||
EditorNode::progress_add_task_bg(p_task, p_label, p_amount);
|
||||
}
|
||||
task = p_task;
|
||||
}
|
||||
~EditorProgress() {
|
||||
if (Thread::is_main_thread()) {
|
||||
EditorNode::progress_end_task(task);
|
||||
} else {
|
||||
EditorNode::progress_end_task_bg(task);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class EditorPluginList : public Object {
|
||||
private:
|
||||
Vector<EditorPlugin *> plugins_list;
|
||||
|
@ -2778,7 +2778,11 @@ void EditorPropertyNodePath::_update_menu() {
|
||||
void EditorPropertyNodePath::_menu_option(int p_idx) {
|
||||
switch (p_idx) {
|
||||
case ACTION_CLEAR: {
|
||||
emit_changed(get_edited_property(), NodePath());
|
||||
if (editing_node) {
|
||||
emit_changed(get_edited_property(), Variant());
|
||||
} else {
|
||||
emit_changed(get_edited_property(), NodePath());
|
||||
}
|
||||
update_property();
|
||||
} break;
|
||||
|
||||
|
@ -175,6 +175,13 @@ void EditorResourcePicker::_file_quick_selected() {
|
||||
_file_selected(quick_open->get_selected());
|
||||
}
|
||||
|
||||
void EditorResourcePicker::_resource_saved(Object *p_resource) {
|
||||
if (edited_resource.is_valid() && p_resource == edited_resource.ptr()) {
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorResourcePicker::_update_menu() {
|
||||
_update_menu_items();
|
||||
|
||||
@ -408,6 +415,10 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
||||
if (edited_resource.is_null()) {
|
||||
return;
|
||||
}
|
||||
Callable resource_saved = callable_mp(this, &EditorResourcePicker::_resource_saved);
|
||||
if (!EditorNode::get_singleton()->is_connected("resource_saved", resource_saved)) {
|
||||
EditorNode::get_singleton()->connect("resource_saved", resource_saved);
|
||||
}
|
||||
EditorNode::get_singleton()->save_resource_as(edited_resource);
|
||||
} break;
|
||||
|
||||
@ -833,6 +844,13 @@ void EditorResourcePicker::_notification(int p_what) {
|
||||
assign_button->queue_redraw();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
Callable resource_saved = callable_mp(this, &EditorResourcePicker::_resource_saved);
|
||||
if (EditorNode::get_singleton()->is_connected("resource_saved", resource_saved)) {
|
||||
EditorNode::get_singleton()->disconnect("resource_saved", resource_saved);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,8 @@ class EditorResourcePicker : public HBoxContainer {
|
||||
void _file_quick_selected();
|
||||
void _file_selected(const String &p_path);
|
||||
|
||||
void _resource_saved(Object *p_resource);
|
||||
|
||||
void _update_menu();
|
||||
void _update_menu_items();
|
||||
void _edit_menu_cbk(int p_which);
|
||||
|
@ -685,7 +685,15 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s
|
||||
}
|
||||
|
||||
Vector<String> FileSystemDock::get_selected_paths() const {
|
||||
return _tree_get_selected(false);
|
||||
if (display_mode == DISPLAY_MODE_TREE_ONLY) {
|
||||
return _tree_get_selected(false);
|
||||
} else {
|
||||
Vector<String> selected = _file_list_get_selected();
|
||||
if (selected.is_empty()) {
|
||||
selected.push_back(get_current_directory());
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
|
||||
String FileSystemDock::get_current_path() const {
|
||||
@ -953,7 +961,8 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
|
||||
files->set_max_columns(1);
|
||||
files->set_max_text_lines(1);
|
||||
files->set_fixed_column_width(0);
|
||||
files->set_fixed_icon_size(Size2());
|
||||
const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
||||
files->set_fixed_icon_size(Size2(icon_size, icon_size));
|
||||
}
|
||||
|
||||
Ref<Texture2D> folder_icon = (use_thumbnails) ? folder_thumbnail : get_theme_icon(SNAME("folder"), SNAME("FileDialog"));
|
||||
@ -2049,6 +2058,15 @@ Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion, bo
|
||||
return selected_strings;
|
||||
}
|
||||
|
||||
Vector<String> FileSystemDock::_file_list_get_selected() const {
|
||||
Vector<String> selected;
|
||||
|
||||
for (int idx : files->get_selected_items()) {
|
||||
selected.push_back(files->get_item_metadata(idx));
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
Vector<String> FileSystemDock::_remove_self_included_paths(Vector<String> selected_strings) {
|
||||
// Remove paths or files that are included into another.
|
||||
if (selected_strings.size() > 1) {
|
||||
@ -3392,6 +3410,11 @@ void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton
|
||||
|
||||
current_path = current_path_line_edit->get_text();
|
||||
|
||||
// Favorites isn't a directory so don't show menu.
|
||||
if (current_path == "Favorites") {
|
||||
return;
|
||||
}
|
||||
|
||||
file_list_popup->clear();
|
||||
file_list_popup->reset_size();
|
||||
|
||||
|
@ -359,6 +359,7 @@ private:
|
||||
void _update_display_mode(bool p_force = false);
|
||||
|
||||
Vector<String> _tree_get_selected(bool remove_self_inclusion = true, bool p_include_unselected_cursor = false) const;
|
||||
Vector<String> _file_list_get_selected() const;
|
||||
|
||||
bool _is_file_type_disabled_by_feature_profile(const StringName &p_class);
|
||||
|
||||
|
@ -115,6 +115,8 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_file
|
||||
file_name = ProjectSettings::get_singleton()->localize_path(file_name);
|
||||
}
|
||||
}
|
||||
selected_options = p_selected_options;
|
||||
|
||||
String f = files[0];
|
||||
if (mode == FILE_MODE_OPEN_FILES) {
|
||||
emit_signal(SNAME("files_selected"), files);
|
||||
@ -146,7 +148,6 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_file
|
||||
}
|
||||
file->set_text(f);
|
||||
dir->set_text(f.get_base_dir());
|
||||
selected_options = p_selected_options;
|
||||
filter->select(p_filter);
|
||||
}
|
||||
|
||||
|
@ -614,13 +614,13 @@ void EditorSpinSlider::_value_focus_exited() {
|
||||
// -> TAB was pressed
|
||||
// -> modal_close was not called
|
||||
// -> need to close/hide manually
|
||||
if (value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
|
||||
if (!is_visible_in_tree() || value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
|
||||
// Hidden or something else took focus.
|
||||
if (value_input_popup) {
|
||||
value_input_popup->hide();
|
||||
}
|
||||
//tab was pressed
|
||||
} else {
|
||||
//enter, click, esc
|
||||
// Enter or Esc was pressed.
|
||||
grab_focus();
|
||||
}
|
||||
|
||||
@ -664,6 +664,10 @@ bool EditorSpinSlider::is_grabbing() const {
|
||||
}
|
||||
|
||||
void EditorSpinSlider::_focus_entered() {
|
||||
if (is_read_only()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_ensure_input_popup();
|
||||
value_input->set_text(get_text_value());
|
||||
value_input_popup->set_size(get_size());
|
||||
|
@ -490,10 +490,14 @@ void SceneTreeEditor::_update_node_tooltip(Node *p_node, TreeItem *p_item) {
|
||||
String tooltip = p_node->get_name();
|
||||
|
||||
if (p_node == get_scene_node() && p_node->get_scene_inherited_state().is_valid()) {
|
||||
p_item->add_button(0, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_SUBSCENE, false, TTR("Open in Editor"));
|
||||
if (p_item->get_button_by_id(0, BUTTON_SUBSCENE) == -1) {
|
||||
p_item->add_button(0, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_SUBSCENE, false, TTR("Open in Editor"));
|
||||
}
|
||||
tooltip += String("\n" + TTR("Inherits:") + " " + p_node->get_scene_inherited_state()->get_path());
|
||||
} else if (p_node != get_scene_node() && !p_node->get_scene_file_path().is_empty() && can_open_instance) {
|
||||
p_item->add_button(0, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_SUBSCENE, false, TTR("Open in Editor"));
|
||||
if (p_item->get_button_by_id(0, BUTTON_SUBSCENE) == -1) {
|
||||
p_item->add_button(0, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_SUBSCENE, false, TTR("Open in Editor"));
|
||||
}
|
||||
tooltip += String("\n" + TTR("Instance:") + " " + p_node->get_scene_file_path());
|
||||
}
|
||||
|
||||
@ -1000,6 +1004,7 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) {
|
||||
TreeItem *item = p_node ? _find(tree->get_root(), p_node->get_path()) : nullptr;
|
||||
|
||||
if (item) {
|
||||
selected = p_node;
|
||||
if (auto_expand_selected) {
|
||||
// Make visible when it's collapsed.
|
||||
TreeItem *node = item->get_parent();
|
||||
@ -1009,8 +1014,24 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) {
|
||||
}
|
||||
item->select(0);
|
||||
item->set_as_cursor(0);
|
||||
selected = p_node;
|
||||
tree->ensure_cursor_is_visible();
|
||||
} else {
|
||||
// Ensure the node is selected and visible for the user if the node
|
||||
// is not collapsed.
|
||||
bool collapsed = false;
|
||||
TreeItem *node = item;
|
||||
while (node && node != tree->get_root()) {
|
||||
if (node->is_collapsed()) {
|
||||
collapsed = true;
|
||||
break;
|
||||
}
|
||||
node = node->get_parent();
|
||||
}
|
||||
if (!collapsed) {
|
||||
item->select(0);
|
||||
item->set_as_cursor(0);
|
||||
tree->ensure_cursor_is_visible();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!p_node) {
|
||||
|
@ -293,18 +293,20 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
|
||||
}
|
||||
String from_tokens;
|
||||
for (int i = 0; i < kp_tokens[0].length(); i++) {
|
||||
if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u') {
|
||||
if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u' && is_hex_digit(kp_tokens[0][i + 2]) && is_hex_digit(kp_tokens[0][i + 3]) && is_hex_digit(kp_tokens[0][i + 4]) && is_hex_digit(kp_tokens[0][i + 5])) {
|
||||
char32_t charcode = kp_tokens[0].substr(i + 2, 4).hex_to_int();
|
||||
from_tokens += charcode;
|
||||
i += 5;
|
||||
} else {
|
||||
from_tokens += kp_tokens[0][i];
|
||||
}
|
||||
}
|
||||
String to_tokens;
|
||||
for (int i = 0; i < kp_tokens[1].length(); i++) {
|
||||
if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u') {
|
||||
if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u' && is_hex_digit(kp_tokens[1][i + 2]) && is_hex_digit(kp_tokens[1][i + 3]) && is_hex_digit(kp_tokens[1][i + 4]) && is_hex_digit(kp_tokens[1][i + 5])) {
|
||||
char32_t charcode = kp_tokens[1].substr(i + 2, 4).hex_to_int();
|
||||
to_tokens += charcode;
|
||||
i += 5;
|
||||
} else {
|
||||
to_tokens += kp_tokens[1][i];
|
||||
}
|
||||
|
@ -428,10 +428,10 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
|
||||
loop_end = p_options["edit/loop_end"];
|
||||
// Wrap around to max frames, so `-1` can be used to select the end, etc.
|
||||
if (loop_begin < 0) {
|
||||
loop_begin = CLAMP(loop_begin + frames + 1, 0, frames);
|
||||
loop_begin = CLAMP(loop_begin + frames, 0, frames - 1);
|
||||
}
|
||||
if (loop_end < 0) {
|
||||
loop_end = CLAMP(loop_end + frames + 1, 0, frames);
|
||||
loop_end = CLAMP(loop_end + frames, 0, frames - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,16 +517,19 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
|
||||
Vector<uint8_t> dst_data;
|
||||
if (compression == 2) {
|
||||
dst_format = AudioStreamWAV::FORMAT_QOA;
|
||||
qoa_desc desc = { 0, 0, 0, { { { 0 }, { 0 } } } };
|
||||
qoa_desc desc = {};
|
||||
uint32_t qoa_len = 0;
|
||||
|
||||
desc.samplerate = rate;
|
||||
desc.samples = frames;
|
||||
desc.channels = format_channels;
|
||||
|
||||
void *encoded = qoa_encode((short *)pcm_data.ptrw(), &desc, &qoa_len);
|
||||
dst_data.resize(qoa_len);
|
||||
memcpy(dst_data.ptrw(), encoded, qoa_len);
|
||||
void *encoded = qoa_encode((short *)pcm_data.ptr(), &desc, &qoa_len);
|
||||
if (encoded) {
|
||||
dst_data.resize(qoa_len);
|
||||
memcpy(dst_data.ptrw(), encoded, qoa_len);
|
||||
QOA_FREE(encoded);
|
||||
}
|
||||
} else {
|
||||
dst_data = pcm_data;
|
||||
}
|
||||
|
@ -751,7 +751,9 @@ AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) {
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditorPlugin::edit(Object *p_object) {
|
||||
polygon_editor->edit(Object::cast_to<Node>(p_object));
|
||||
Node *polygon_node = Object::cast_to<Node>(p_object);
|
||||
polygon_editor->edit(polygon_node);
|
||||
make_visible(polygon_node != nullptr);
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditorPlugin::handles(Object *p_object) const {
|
||||
|
@ -561,7 +561,9 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int
|
||||
return;
|
||||
}
|
||||
|
||||
anim = anim->duplicate(); // Users simply dont care about referencing, so making a copy works better here.
|
||||
if (!anim->get_path().is_resource_file()) {
|
||||
anim = anim->duplicate(); // Users simply dont care about referencing, so making a copy works better here.
|
||||
}
|
||||
|
||||
String base_name;
|
||||
if (anim->get_name() != "") {
|
||||
|
@ -339,7 +339,17 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
|
||||
|
||||
track_editor->set_animation(anim, animation_is_readonly);
|
||||
Node *root = player->get_node_or_null(player->get_root_node());
|
||||
if (root) {
|
||||
|
||||
// Player shouldn't access parent if it's the scene root.
|
||||
if (!root || (player == get_tree()->get_edited_scene_root() && player->get_root_node() == SceneStringName(path_pp))) {
|
||||
NodePath cached_root_path = player->get_path_to(get_cached_root_node());
|
||||
if (player->get_node_or_null(cached_root_path) != nullptr) {
|
||||
player->set_root_node(cached_root_path);
|
||||
} else {
|
||||
player->set_root_node(SceneStringName(path_pp)); // No other choice, preventing crash.
|
||||
}
|
||||
} else {
|
||||
cached_root_node_id = root->get_instance_id(); // Caching as `track_editor` can lose track of player's root node.
|
||||
track_editor->set_root(root);
|
||||
}
|
||||
}
|
||||
@ -1824,6 +1834,10 @@ AnimationMixer *AnimationPlayerEditor::fetch_mixer_for_library() const {
|
||||
return original_node;
|
||||
}
|
||||
|
||||
Node *AnimationPlayerEditor::get_cached_root_node() const {
|
||||
return Object::cast_to<Node>(ObjectDB::get_instance(cached_root_node_id));
|
||||
}
|
||||
|
||||
bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
|
||||
bool is_valid = true;
|
||||
if (!p_anim.is_valid()) {
|
||||
|
@ -52,6 +52,7 @@ class AnimationPlayerEditor : public VBoxContainer {
|
||||
AnimationPlayerEditorPlugin *plugin = nullptr;
|
||||
AnimationMixer *original_node = nullptr; // For pinned mark in SceneTree.
|
||||
AnimationPlayer *player = nullptr; // For AnimationPlayerEditor, could be dummy.
|
||||
ObjectID cached_root_node_id;
|
||||
bool is_dummy = false;
|
||||
|
||||
enum {
|
||||
@ -251,6 +252,7 @@ public:
|
||||
AnimationMixer *get_editing_node() const;
|
||||
AnimationPlayer *get_player() const;
|
||||
AnimationMixer *fetch_mixer_for_library() const;
|
||||
Node *get_cached_root_node() const;
|
||||
|
||||
static AnimationPlayerEditor *get_singleton() { return singleton; }
|
||||
|
||||
|
@ -3999,6 +3999,8 @@ void CanvasItemEditor::_project_settings_changed() {
|
||||
void CanvasItemEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
_update_lock_and_group_button();
|
||||
|
||||
EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
|
||||
EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false));
|
||||
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CanvasItemEditor::_project_settings_changed));
|
||||
@ -4108,7 +4110,8 @@ void CanvasItemEditor::_notification(int p_what) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
||||
case NOTIFICATION_APPLICATION_FOCUS_OUT:
|
||||
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
|
||||
if (drag_type != DRAG_NONE) {
|
||||
_reset_drag();
|
||||
viewport->queue_redraw();
|
||||
|
@ -81,6 +81,8 @@ void EditorNode3DGizmo::redraw() {
|
||||
gizmo_plugin->redraw(this);
|
||||
}
|
||||
|
||||
_update_bvh();
|
||||
|
||||
if (Node3DEditor::get_singleton()->is_current_selected_gizmo(this)) {
|
||||
Node3DEditor::get_singleton()->update_transform_gizmo();
|
||||
}
|
||||
@ -244,6 +246,32 @@ void EditorNode3DGizmo::add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p
|
||||
instances.push_back(ins);
|
||||
}
|
||||
|
||||
void EditorNode3DGizmo::_update_bvh() {
|
||||
ERR_FAIL_NULL(spatial_node);
|
||||
|
||||
Transform3D transform = spatial_node->get_global_transform();
|
||||
|
||||
float effective_icon_size = selectable_icon_size > 0.0f ? selectable_icon_size : 0.0f;
|
||||
Vector3 icon_size_vector3 = Vector3(effective_icon_size, effective_icon_size, effective_icon_size);
|
||||
AABB aabb(spatial_node->get_position() - icon_size_vector3 * 100.0f, icon_size_vector3 * 200.0f);
|
||||
|
||||
for (const Vector3 &segment_end : collision_segments) {
|
||||
aabb.expand_to(transform.xform(segment_end));
|
||||
}
|
||||
|
||||
if (collision_mesh.is_valid()) {
|
||||
for (const Face3 &face : collision_mesh->get_faces()) {
|
||||
aabb.expand_to(transform.xform(face.vertex[0]));
|
||||
aabb.expand_to(transform.xform(face.vertex[1]));
|
||||
aabb.expand_to(transform.xform(face.vertex[2]));
|
||||
}
|
||||
}
|
||||
|
||||
Node3DEditor::get_singleton()->update_gizmo_bvh_node(
|
||||
bvh_node_id,
|
||||
aabb);
|
||||
}
|
||||
|
||||
void EditorNode3DGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard, const Color &p_modulate) {
|
||||
add_vertices(p_lines, p_material, Mesh::PRIMITIVE_LINES, p_billboard, p_modulate);
|
||||
}
|
||||
@ -403,12 +431,13 @@ void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<
|
||||
colors.resize(p_handles.size());
|
||||
Color *w = colors.ptrw();
|
||||
for (int i = 0; i < p_handles.size(); i++) {
|
||||
int id = p_ids.is_empty() ? i : p_ids[i];
|
||||
|
||||
Color col(1, 1, 1, 1);
|
||||
if (is_handle_highlighted(i, p_secondary)) {
|
||||
if (is_handle_highlighted(id, p_secondary)) {
|
||||
col = Color(0, 0, 1, 0.9);
|
||||
}
|
||||
|
||||
int id = p_ids.is_empty() ? i : p_ids[i];
|
||||
if (!is_current_hover_gizmo || current_hover_handle != id || p_secondary != current_hover_handle_secondary) {
|
||||
col.a = 0.8;
|
||||
}
|
||||
@ -765,6 +794,10 @@ void EditorNode3DGizmo::create() {
|
||||
instances.write[i].create_instance(spatial_node, hidden);
|
||||
}
|
||||
|
||||
bvh_node_id = Node3DEditor::get_singleton()->insert_gizmo_bvh_node(
|
||||
spatial_node,
|
||||
AABB(spatial_node->get_position(), Vector3(0, 0, 0)));
|
||||
|
||||
transform();
|
||||
}
|
||||
|
||||
@ -774,6 +807,8 @@ void EditorNode3DGizmo::transform() {
|
||||
for (int i = 0; i < instances.size(); i++) {
|
||||
RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform() * instances[i].xform);
|
||||
}
|
||||
|
||||
_update_bvh();
|
||||
}
|
||||
|
||||
void EditorNode3DGizmo::free() {
|
||||
@ -790,6 +825,9 @@ void EditorNode3DGizmo::free() {
|
||||
|
||||
clear();
|
||||
|
||||
Node3DEditor::get_singleton()->remove_gizmo_bvh_node(bvh_node_id);
|
||||
bvh_node_id = DynamicBVH::ID();
|
||||
|
||||
valid = false;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#ifndef NODE_3D_EDITOR_GIZMOS_H
|
||||
#define NODE_3D_EDITOR_GIZMOS_H
|
||||
|
||||
#include "core/math/dynamic_bvh.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
@ -72,8 +73,12 @@ class EditorNode3DGizmo : public Node3DGizmo {
|
||||
Vector<Instance> instances;
|
||||
Node3D *spatial_node = nullptr;
|
||||
|
||||
DynamicBVH::ID bvh_node_id;
|
||||
|
||||
void _set_node_3d(Node *p_node) { set_node_3d(Object::cast_to<Node3D>(p_node)); }
|
||||
|
||||
void _update_bvh();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
@ -770,7 +770,7 @@ void Node3DEditorViewport::_select_clicked(bool p_allow_locked) {
|
||||
}
|
||||
}
|
||||
|
||||
if (p_allow_locked || !_is_node_locked(selected)) {
|
||||
if (p_allow_locked || (selected != nullptr && !_is_node_locked(selected))) {
|
||||
if (clicked_wants_append) {
|
||||
if (editor_selection->is_selected(selected)) {
|
||||
editor_selection->remove_node(selected);
|
||||
@ -800,7 +800,6 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const {
|
||||
RS::get_singleton()->sdfgi_set_debug_probe_select(pos, ray);
|
||||
}
|
||||
|
||||
Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario());
|
||||
HashSet<Ref<EditorNode3DGizmo>> found_gizmos;
|
||||
|
||||
Node *edited_scene = get_tree()->get_edited_scene_root();
|
||||
@ -808,9 +807,9 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const {
|
||||
Node *item = nullptr;
|
||||
float closest_dist = 1e20;
|
||||
|
||||
for (int i = 0; i < instances.size(); i++) {
|
||||
Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
|
||||
Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far());
|
||||
|
||||
for (Node3D *spat : nodes_with_gizmos) {
|
||||
if (!spat) {
|
||||
continue;
|
||||
}
|
||||
@ -863,12 +862,11 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe
|
||||
Vector3 ray = get_ray(p_pos);
|
||||
Vector3 pos = get_ray_pos(p_pos);
|
||||
|
||||
Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario());
|
||||
Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far());
|
||||
|
||||
HashSet<Node3D *> found_nodes;
|
||||
|
||||
for (int i = 0; i < instances.size(); i++) {
|
||||
Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
|
||||
|
||||
for (Node3D *spat : nodes_with_gizmos) {
|
||||
if (!spat) {
|
||||
continue;
|
||||
}
|
||||
@ -1046,7 +1044,7 @@ void Node3DEditorViewport::_select_region() {
|
||||
_clear_selected();
|
||||
}
|
||||
|
||||
Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world_3d()->get_scenario());
|
||||
Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_frustum_query(frustum);
|
||||
HashSet<Node3D *> found_nodes;
|
||||
Vector<Node *> selected;
|
||||
|
||||
@ -1055,8 +1053,7 @@ void Node3DEditorViewport::_select_region() {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < instances.size(); i++) {
|
||||
Node3D *sp = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
|
||||
for (Node3D *sp : nodes_with_gizmos) {
|
||||
if (!sp || _is_node_locked(sp)) {
|
||||
continue;
|
||||
}
|
||||
@ -1064,21 +1061,23 @@ void Node3DEditorViewport::_select_region() {
|
||||
if (found_nodes.has(sp)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
found_nodes.insert(sp);
|
||||
|
||||
Node *node = Object::cast_to<Node>(sp);
|
||||
if (node != edited_scene) {
|
||||
node = edited_scene->get_deepest_editable_node(node);
|
||||
}
|
||||
|
||||
// Prevent selection of nodes not owned by the edited scene.
|
||||
while (node && node != edited_scene->get_parent()) {
|
||||
Node *node_owner = node->get_owner();
|
||||
if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
|
||||
break;
|
||||
// Selection requires that the node is the edited scene or its descendant, and has an owner.
|
||||
if (node != edited_scene) {
|
||||
if (!node->get_owner() || !edited_scene->is_ancestor_of(node)) {
|
||||
continue;
|
||||
}
|
||||
node = edited_scene->get_deepest_editable_node(node);
|
||||
while (node != edited_scene) {
|
||||
Node *node_owner = node->get_owner();
|
||||
if (node_owner == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
|
||||
break;
|
||||
}
|
||||
node = node->get_parent();
|
||||
}
|
||||
node = node->get_parent();
|
||||
}
|
||||
|
||||
// Replace the node by the group if grouped
|
||||
@ -1952,7 +1951,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
||||
}
|
||||
}
|
||||
|
||||
if (_edit.mode != TRANSFORM_NONE) {
|
||||
if (!_edit.instant && _edit.mode != TRANSFORM_NONE) {
|
||||
Node3D *selected = spatial_editor->get_single_selected_node();
|
||||
Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr;
|
||||
|
||||
@ -2252,6 +2251,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
||||
}
|
||||
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
if (_edit.gizmo.is_null() && is_freelook_active() && k->get_keycode() == Key::ESCAPE) {
|
||||
set_freelook_active(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_edit.gizmo.is_valid() && (k->get_keycode() == Key::ESCAPE || k->get_keycode() == Key::BACKSPACE)) {
|
||||
// Restore.
|
||||
_edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true);
|
||||
@ -2390,15 +2394,30 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
||||
if (ED_IS_SHORTCUT("spatial_editor/cancel_transform", p_event) && _edit.mode != TRANSFORM_NONE) {
|
||||
cancel_transform();
|
||||
}
|
||||
if (!is_freelook_active()) {
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_translate", p_event)) {
|
||||
begin_transform(TRANSFORM_TRANSLATE, true);
|
||||
if (!is_freelook_active() && !k->is_echo()) {
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_translate", p_event) && _edit.mode != TRANSFORM_TRANSLATE) {
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
begin_transform(TRANSFORM_TRANSLATE, true);
|
||||
} else if (_edit.instant) {
|
||||
commit_transform();
|
||||
begin_transform(TRANSFORM_TRANSLATE, true);
|
||||
}
|
||||
}
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_rotate", p_event)) {
|
||||
begin_transform(TRANSFORM_ROTATE, true);
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_rotate", p_event) && _edit.mode != TRANSFORM_ROTATE) {
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
begin_transform(TRANSFORM_ROTATE, true);
|
||||
} else if (_edit.instant) {
|
||||
commit_transform();
|
||||
begin_transform(TRANSFORM_ROTATE, true);
|
||||
}
|
||||
}
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_scale", p_event)) {
|
||||
begin_transform(TRANSFORM_SCALE, true);
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_scale", p_event) && _edit.mode != TRANSFORM_SCALE) {
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
begin_transform(TRANSFORM_SCALE, true);
|
||||
} else if (_edit.instant) {
|
||||
commit_transform();
|
||||
begin_transform(TRANSFORM_SCALE, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3030,8 +3049,11 @@ void Node3DEditorViewport::_notification(int p_what) {
|
||||
update_preview_node = false;
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_APPLICATION_FOCUS_OUT:
|
||||
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
|
||||
set_freelook_active(false);
|
||||
cursor.region_select = false;
|
||||
surface->queue_redraw();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
@ -3107,6 +3129,7 @@ void Node3DEditorViewport::_notification(int p_what) {
|
||||
case NOTIFICATION_DRAG_END: {
|
||||
// Clear preview material when dropped outside applicable object.
|
||||
if (spatial_editor->get_preview_material().is_valid() && !is_drag_successful()) {
|
||||
_reset_preview_material();
|
||||
_remove_preview_material();
|
||||
} else {
|
||||
_remove_preview_node();
|
||||
@ -4001,6 +4024,14 @@ void Node3DEditorViewport::set_state(const Dictionary &p_state) {
|
||||
_menu_option(VIEW_GIZMOS);
|
||||
}
|
||||
}
|
||||
if (p_state.has("grid")) {
|
||||
bool grid = p_state["grid"];
|
||||
|
||||
int idx = view_menu->get_popup()->get_item_index(VIEW_GRID);
|
||||
if (view_menu->get_popup()->is_item_checked(idx) != grid) {
|
||||
_menu_option(VIEW_GRID);
|
||||
}
|
||||
}
|
||||
if (p_state.has("information")) {
|
||||
bool information = p_state["information"];
|
||||
|
||||
@ -4079,6 +4110,7 @@ Dictionary Node3DEditorViewport::get_state() const {
|
||||
d["listener"] = viewport->is_audio_listener_3d();
|
||||
d["doppler"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER));
|
||||
d["gizmos"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS));
|
||||
d["grid"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GRID));
|
||||
d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION));
|
||||
d["frame_time"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FRAME_TIME));
|
||||
d["half_res"] = subviewport_container->get_stretch_shrink() > 1;
|
||||
@ -5001,14 +5033,24 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
|
||||
} break;
|
||||
|
||||
case TRANSFORM_ROTATE: {
|
||||
Plane plane = Plane(_get_camera_normal(), _edit.center);
|
||||
Plane plane;
|
||||
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
|
||||
Vector3 cam_to_obj = _edit.center - _get_camera_position();
|
||||
if (!cam_to_obj.is_zero_approx()) {
|
||||
plane = Plane(cam_to_obj.normalized(), _edit.center);
|
||||
} else {
|
||||
plane = Plane(_get_camera_normal(), _edit.center);
|
||||
}
|
||||
} else {
|
||||
plane = Plane(_get_camera_normal(), _edit.center);
|
||||
}
|
||||
|
||||
Vector3 local_axis;
|
||||
Vector3 global_axis;
|
||||
switch (_edit.plane) {
|
||||
case TRANSFORM_VIEW:
|
||||
// local_axis unused
|
||||
global_axis = _get_camera_normal();
|
||||
global_axis = plane.normal;
|
||||
break;
|
||||
case TRANSFORM_X_AXIS:
|
||||
local_axis = Vector3(1, 0, 0);
|
||||
@ -5039,7 +5081,7 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
|
||||
break;
|
||||
}
|
||||
|
||||
static const float orthogonal_threshold = Math::cos(Math::deg_to_rad(87.0f));
|
||||
static const float orthogonal_threshold = Math::cos(Math::deg_to_rad(85.0f));
|
||||
bool axis_is_orthogonal = ABS(plane.normal.dot(global_axis)) < orthogonal_threshold;
|
||||
|
||||
double angle = 0.0f;
|
||||
@ -7746,6 +7788,9 @@ void Node3DEditor::_sun_environ_settings_pressed() {
|
||||
sun_environ_popup->set_position(pos - Vector2(sun_environ_popup->get_contents_minimum_size().width / 2, 0));
|
||||
sun_environ_popup->reset_size();
|
||||
sun_environ_popup->popup();
|
||||
// Grabbing the focus is required for Shift modifier checking to be functional
|
||||
// (when the Add sun/environment buttons are pressed).
|
||||
sun_environ_popup->grab_focus();
|
||||
}
|
||||
|
||||
void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) {
|
||||
@ -9208,6 +9253,49 @@ void Node3DEditor::remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin) {
|
||||
_update_gizmos_menu();
|
||||
}
|
||||
|
||||
DynamicBVH::ID Node3DEditor::insert_gizmo_bvh_node(Node3D *p_node, const AABB &p_aabb) {
|
||||
return gizmo_bvh.insert(p_aabb, p_node);
|
||||
}
|
||||
|
||||
void Node3DEditor::update_gizmo_bvh_node(DynamicBVH::ID p_id, const AABB &p_aabb) {
|
||||
gizmo_bvh.update(p_id, p_aabb);
|
||||
gizmo_bvh.optimize_incremental(1);
|
||||
}
|
||||
|
||||
void Node3DEditor::remove_gizmo_bvh_node(DynamicBVH::ID p_id) {
|
||||
gizmo_bvh.remove(p_id);
|
||||
}
|
||||
|
||||
Vector<Node3D *> Node3DEditor::gizmo_bvh_ray_query(const Vector3 &p_ray_start, const Vector3 &p_ray_end) {
|
||||
struct Result {
|
||||
Vector<Node3D *> nodes;
|
||||
bool operator()(void *p_data) {
|
||||
nodes.append((Node3D *)p_data);
|
||||
return false;
|
||||
}
|
||||
} result;
|
||||
|
||||
gizmo_bvh.ray_query(p_ray_start, p_ray_end, result);
|
||||
|
||||
return result.nodes;
|
||||
}
|
||||
|
||||
Vector<Node3D *> Node3DEditor::gizmo_bvh_frustum_query(const Vector<Plane> &p_frustum) {
|
||||
Vector<Vector3> points = Geometry3D::compute_convex_mesh_points(&p_frustum[0], p_frustum.size());
|
||||
|
||||
struct Result {
|
||||
Vector<Node3D *> nodes;
|
||||
bool operator()(void *p_data) {
|
||||
nodes.append((Node3D *)p_data);
|
||||
return false;
|
||||
}
|
||||
} result;
|
||||
|
||||
gizmo_bvh.convex_query(p_frustum.ptr(), p_frustum.size(), points.ptr(), points.size(), result);
|
||||
|
||||
return result.nodes;
|
||||
}
|
||||
|
||||
Node3DEditorPlugin::Node3DEditorPlugin() {
|
||||
spatial_editor = memnew(Node3DEditor);
|
||||
spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
@ -31,6 +31,7 @@
|
||||
#ifndef NODE_3D_EDITOR_PLUGIN_H
|
||||
#define NODE_3D_EDITOR_PLUGIN_H
|
||||
|
||||
#include "core/math/dynamic_bvh.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
@ -629,6 +630,8 @@ private:
|
||||
int current_hover_gizmo_handle;
|
||||
bool current_hover_gizmo_handle_secondary;
|
||||
|
||||
DynamicBVH gizmo_bvh;
|
||||
|
||||
real_t snap_translate_value;
|
||||
real_t snap_rotate_value;
|
||||
real_t snap_scale_value;
|
||||
@ -933,6 +936,12 @@ public:
|
||||
void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin);
|
||||
void remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin);
|
||||
|
||||
DynamicBVH::ID insert_gizmo_bvh_node(Node3D *p_node, const AABB &p_aabb);
|
||||
void update_gizmo_bvh_node(DynamicBVH::ID p_id, const AABB &p_aabb);
|
||||
void remove_gizmo_bvh_node(DynamicBVH::ID p_id);
|
||||
Vector<Node3D *> gizmo_bvh_ray_query(const Vector3 &p_ray_start, const Vector3 &p_ray_end);
|
||||
Vector<Node3D *> gizmo_bvh_frustum_query(const Vector<Plane> &p_frustum);
|
||||
|
||||
void edit(Node3D *p_spatial);
|
||||
void clear();
|
||||
|
||||
|
@ -83,9 +83,8 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
|
||||
|
||||
int idx_last = atr_owners.size() - 1;
|
||||
if (idx_last > 0 && !parent_path.begins_with(atr_owners[idx_last].first)) {
|
||||
// Switch to the previous auto translation owner this was nested in, if that was the case.
|
||||
// Exit from the current owner nesting into the previous one.
|
||||
atr_owners.remove_at(idx_last);
|
||||
idx_last -= 1;
|
||||
}
|
||||
|
||||
if (property == "auto_translate_mode") {
|
||||
@ -106,7 +105,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
|
||||
// If `auto_translate_mode` wasn't found, that means it is set to its default value (`AUTO_TRANSLATE_MODE_INHERIT`).
|
||||
if (!auto_translate_mode_found) {
|
||||
int idx_last = atr_owners.size() - 1;
|
||||
if (idx_last > 0 && atr_owners[idx_last].first == parent_path) {
|
||||
if (idx_last > 0 && parent_path.begins_with(atr_owners[idx_last].first)) {
|
||||
auto_translating = atr_owners[idx_last].second;
|
||||
} else {
|
||||
atr_owners.push_back(Pair(state->get_node_path(i), true));
|
||||
|
@ -197,12 +197,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
|
||||
const Vector2 new_point = xform.affine_inverse().xform(gpoint2);
|
||||
curve->add_point(new_point, Vector2(0, 0), Vector2(0, 0), insertion_point + 1);
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Split Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "add_point", new_point, Vector2(0, 0), Vector2(0, 0), insertion_point + 1);
|
||||
undo_redo->add_undo_method(curve.ptr(), "remove_point", insertion_point + 1);
|
||||
|
||||
action = ACTION_MOVING_NEW_POINT;
|
||||
action = ACTION_MOVING_NEW_POINT_FROM_SPLIT;
|
||||
action_point = insertion_point + 1;
|
||||
moving_from = curve->get_point_position(action_point);
|
||||
moving_screen_from = gpoint2;
|
||||
@ -245,6 +240,16 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
|
||||
undo_redo->commit_action(false);
|
||||
} break;
|
||||
|
||||
case ACTION_MOVING_NEW_POINT_FROM_SPLIT: {
|
||||
undo_redo->create_action(TTR("Split Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "add_point", Vector2(), Vector2(), Vector2(), action_point);
|
||||
undo_redo->add_do_method(curve.ptr(), "set_point_position", action_point, cpoint);
|
||||
undo_redo->add_undo_method(curve.ptr(), "remove_point", action_point);
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action(false);
|
||||
} break;
|
||||
|
||||
case ACTION_MOVING_IN: {
|
||||
if (original_mouse_pos != gpoint) {
|
||||
undo_redo->create_action(TTR("Move In-Control in Curve"));
|
||||
@ -350,7 +355,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
|
||||
break;
|
||||
|
||||
case ACTION_MOVING_POINT:
|
||||
case ACTION_MOVING_NEW_POINT: {
|
||||
case ACTION_MOVING_NEW_POINT:
|
||||
case ACTION_MOVING_NEW_POINT_FROM_SPLIT: {
|
||||
curve->set_point_position(action_point, cpoint);
|
||||
} break;
|
||||
|
||||
@ -573,6 +579,10 @@ void Path2DEditor::_cancel_current_action() {
|
||||
curve->remove_point(curve->get_point_count() - 1);
|
||||
} break;
|
||||
|
||||
case ACTION_MOVING_NEW_POINT_FROM_SPLIT: {
|
||||
curve->remove_point(action_point);
|
||||
} break;
|
||||
|
||||
case ACTION_MOVING_IN: {
|
||||
curve->set_point_in(action_point, moving_from);
|
||||
curve->set_point_out(action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_out_length));
|
||||
|
@ -81,6 +81,7 @@ class Path2DEditor : public HBoxContainer {
|
||||
ACTION_NONE,
|
||||
ACTION_MOVING_POINT,
|
||||
ACTION_MOVING_NEW_POINT,
|
||||
ACTION_MOVING_NEW_POINT_FROM_SPLIT,
|
||||
ACTION_MOVING_IN,
|
||||
ACTION_MOVING_OUT,
|
||||
};
|
||||
|
@ -181,7 +181,7 @@ void Polygon2DEditor::_sync_bones() {
|
||||
}
|
||||
|
||||
if (weights.size() == 0) { //create them
|
||||
weights.resize(node->get_polygon().size());
|
||||
weights.resize(wc);
|
||||
float *w = weights.ptrw();
|
||||
for (int j = 0; j < wc; j++) {
|
||||
w[j] = 0.0;
|
||||
@ -850,8 +850,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
|
||||
if (mm.is_valid()) {
|
||||
if (uv_drag) {
|
||||
Vector2 uv_drag_to = mm->get_position();
|
||||
uv_drag_to = snap_point(uv_drag_to); // FIXME: Only works correctly with 'UV_MODE_EDIT_POINT', it's imprecise with the rest.
|
||||
Vector2 drag = mtx.affine_inverse().xform(uv_drag_to) - mtx.affine_inverse().xform(uv_drag_from);
|
||||
uv_drag_to = snap_point(uv_drag_to);
|
||||
Vector2 drag = mtx.affine_inverse().basis_xform(uv_drag_to - uv_drag_from);
|
||||
|
||||
switch (uv_move_current) {
|
||||
case UV_MODE_CREATE: {
|
||||
@ -1166,12 +1166,8 @@ void Polygon2DEditor::_uv_draw() {
|
||||
poly_line_color.a *= 0.25;
|
||||
}
|
||||
Color polygon_line_color = Color(0.5, 0.5, 0.9);
|
||||
Vector<Color> polygon_fill_color;
|
||||
{
|
||||
Color pf = polygon_line_color;
|
||||
pf.a *= 0.5;
|
||||
polygon_fill_color.push_back(pf);
|
||||
}
|
||||
Color polygon_fill_color = polygon_line_color;
|
||||
polygon_fill_color.a *= 0.5;
|
||||
Color prev_color = Color(0.5, 0.5, 0.5);
|
||||
|
||||
int uv_draw_max = uvs.size();
|
||||
@ -1216,7 +1212,7 @@ void Polygon2DEditor::_uv_draw() {
|
||||
uv_edit_draw->draw_line(mtx.xform(uvs[idx]), mtx.xform(uvs[idx_next]), polygon_line_color, Math::round(EDSCALE));
|
||||
}
|
||||
if (points.size() >= 3) {
|
||||
uv_edit_draw->draw_polygon(polypoints, polygon_fill_color);
|
||||
uv_edit_draw->draw_colored_polygon(polypoints, polygon_fill_color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1309,8 +1305,8 @@ void Polygon2DEditor::_bind_methods() {
|
||||
|
||||
Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
|
||||
if (use_snap) {
|
||||
p_target.x = Math::snap_scalar(snap_offset.x * uv_draw_zoom - uv_draw_ofs.x, snap_step.x * uv_draw_zoom, p_target.x);
|
||||
p_target.y = Math::snap_scalar(snap_offset.y * uv_draw_zoom - uv_draw_ofs.y, snap_step.y * uv_draw_zoom, p_target.y);
|
||||
p_target.x = Math::snap_scalar((snap_offset.x - uv_draw_ofs.x) * uv_draw_zoom, snap_step.x * uv_draw_zoom, p_target.x);
|
||||
p_target.y = Math::snap_scalar((snap_offset.y - uv_draw_ofs.y) * uv_draw_zoom, snap_step.y * uv_draw_zoom, p_target.y);
|
||||
}
|
||||
|
||||
return p_target;
|
||||
@ -1486,7 +1482,7 @@ Polygon2DEditor::Polygon2DEditor() {
|
||||
|
||||
grid_settings = memnew(AcceptDialog);
|
||||
grid_settings->set_title(TTR("Configure Grid:"));
|
||||
add_child(grid_settings);
|
||||
uv_edit->add_child(grid_settings);
|
||||
VBoxContainer *grid_settings_vb = memnew(VBoxContainer);
|
||||
grid_settings->add_child(grid_settings_vb);
|
||||
|
||||
|
@ -2711,9 +2711,11 @@ void ScriptEditor::apply_scripts() const {
|
||||
}
|
||||
|
||||
void ScriptEditor::reload_scripts(bool p_refresh_only) {
|
||||
if (external_editor_active) {
|
||||
return;
|
||||
}
|
||||
// Call deferred to make sure it runs on the main thread.
|
||||
callable_mp(this, &ScriptEditor::_reload_scripts).call_deferred(p_refresh_only);
|
||||
}
|
||||
|
||||
void ScriptEditor::_reload_scripts(bool p_refresh_only) {
|
||||
for (int i = 0; i < tab_container->get_tab_count(); i++) {
|
||||
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
|
||||
if (!se) {
|
||||
|
@ -436,6 +436,7 @@ class ScriptEditor : public PanelContainer {
|
||||
void _file_removed(const String &p_file);
|
||||
void _autosave_scripts();
|
||||
void _update_autosave_timer();
|
||||
void _reload_scripts(bool p_refresh_only = false);
|
||||
|
||||
void _update_members_overview_visibility();
|
||||
void _update_members_overview();
|
||||
|
@ -372,7 +372,7 @@ void TileAtlasView::_draw_base_tiles_shape_grid() {
|
||||
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
|
||||
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin();
|
||||
if (tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, -tile_shape_size / 2) && tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, tile_shape_size / 2 - Vector2(1, 1))) {
|
||||
if (tile_set_atlas_source->is_rect_in_tile_texture_region(tile_id, 0, Rect2(Vector2(-tile_shape_size) / 2, tile_shape_size))) {
|
||||
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
|
||||
Color color = grid_color;
|
||||
if (frame > 0) {
|
||||
|
@ -165,10 +165,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
|
||||
base_control->draw_set_transform_matrix(xform);
|
||||
|
||||
// Draw fill rect under texture region.
|
||||
Rect2 texture_rect(-background_region.size / 2, background_region.size);
|
||||
Rect2 texture_rect(Vector2(), background_region.size);
|
||||
if (tile_data) {
|
||||
texture_rect.position -= tile_data->get_texture_origin();
|
||||
if (tile_data->get_transpose()) {
|
||||
texture_rect.size = Size2(texture_rect.size.y, texture_rect.size.x);
|
||||
}
|
||||
}
|
||||
texture_rect.position -= texture_rect.size / 2; // Half-size offset must be applied after transposing.
|
||||
base_control->draw_rect(texture_rect, Color(1, 1, 1, 0.3));
|
||||
|
||||
// Draw the background.
|
||||
@ -180,18 +184,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
|
||||
if (tile_data->get_flip_v()) {
|
||||
region_size.y = -region_size.y;
|
||||
}
|
||||
base_control->draw_texture_rect_region(background_atlas_source->get_texture(), Rect2(-background_region.size / 2 - tile_data->get_texture_origin(), region_size), background_region, tile_data->get_modulate(), tile_data->get_transpose());
|
||||
// Destination rect position must account for transposing, size must not.
|
||||
base_control->draw_texture_rect_region(background_atlas_source->get_texture(), Rect2(texture_rect.position, region_size), background_region, tile_data->get_modulate(), tile_data->get_transpose());
|
||||
}
|
||||
|
||||
// Compute and draw the grid area.
|
||||
Rect2 grid_area = Rect2(-base_tile_size / 2, base_tile_size);
|
||||
if (tile_data) {
|
||||
grid_area.expand_to(-background_region.get_size() / 2 - tile_data->get_texture_origin());
|
||||
grid_area.expand_to(background_region.get_size() / 2 - tile_data->get_texture_origin());
|
||||
} else {
|
||||
grid_area.expand_to(-background_region.get_size() / 2);
|
||||
grid_area.expand_to(background_region.get_size() / 2);
|
||||
}
|
||||
grid_area.expand_to(texture_rect.position);
|
||||
grid_area.expand_to(texture_rect.get_end());
|
||||
base_control->draw_rect(grid_area, Color(1, 1, 1, 0.3), false);
|
||||
|
||||
// Draw grid.
|
||||
@ -1385,10 +1385,8 @@ void TileDataTextureOriginEditor::draw_over_tile(CanvasItem *p_canvas_item, Tran
|
||||
|
||||
TileSetSource *source = *(tile_set->get_source(p_cell.source_id));
|
||||
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
|
||||
if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, -tile_set_tile_size / 2) && atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, tile_set_tile_size / 2 - Vector2(1, 1))) {
|
||||
Transform2D tile_xform;
|
||||
tile_xform.set_scale(tile_set_tile_size);
|
||||
tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color);
|
||||
if (atlas_source->is_rect_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Rect2(Vector2(-tile_set_tile_size) / 2, tile_set_tile_size))) {
|
||||
tile_set->draw_tile_shape(p_canvas_item, p_transform.scaled_local(tile_set_tile_size), color);
|
||||
}
|
||||
|
||||
if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2())) {
|
||||
|
@ -1366,11 +1366,13 @@ void TileMapLayerEditorTilesPlugin::_stop_dragging() {
|
||||
Vector2i coords;
|
||||
HashMap<Vector2i, TileMapCell> cells_undo;
|
||||
for (int i = 0; i < selection_used_cells.size(); i++) {
|
||||
coords = tile_set->map_pattern(top_left, selection_used_cells[i], selection_pattern);
|
||||
cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
|
||||
coords = tile_set->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
|
||||
cells_undo[coords] = TileMapCell(edited_layer->get_cell_source_id(coords), edited_layer->get_cell_atlas_coords(coords), edited_layer->get_cell_alternative_tile(coords));
|
||||
}
|
||||
for (int i = 0; i < selection_used_cells.size(); i++) {
|
||||
coords = tile_set->map_pattern(top_left, selection_used_cells[i], selection_pattern);
|
||||
cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
|
||||
}
|
||||
|
||||
// Build the list of cells to do.
|
||||
HashMap<Vector2i, TileMapCell> cells_do;
|
||||
|
@ -115,8 +115,8 @@ void VSGraphNode::_draw_port(int p_slot_index, Point2i p_pos, bool p_left, const
|
||||
icon_offset = -port_icon->get_size() * 0.5;
|
||||
|
||||
// Draw "shadow"/outline in the connection rim color.
|
||||
draw_texture_rect(port_icon, Rect2(p_pos + icon_offset - Size2(2, 2), port_icon->get_size() + Size2(4, 4)), false, p_rim_color);
|
||||
draw_texture(port_icon, p_pos + icon_offset, p_color);
|
||||
draw_texture_rect(port_icon, Rect2(p_pos + (icon_offset - Size2(2, 2)) * EDSCALE, (port_icon->get_size() + Size2(4, 4)) * EDSCALE), false, p_rim_color);
|
||||
draw_texture_rect(port_icon, Rect2(p_pos + icon_offset * EDSCALE, port_icon->get_size() * EDSCALE), false, p_color);
|
||||
}
|
||||
|
||||
void VSGraphNode::draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Color &p_color) {
|
||||
@ -154,7 +154,6 @@ VSRerouteNode::VSRerouteNode() {
|
||||
title_lbl->hide();
|
||||
|
||||
const Size2 size = Size2(32, 32) * EDSCALE;
|
||||
print_line("VSRerouteNode size: " + size);
|
||||
|
||||
Control *slot_area = memnew(Control);
|
||||
slot_area->set_custom_minimum_size(size);
|
||||
@ -2888,8 +2887,8 @@ void VisualShaderEditor::_frame_title_popup_show(const Point2 &p_position, int p
|
||||
}
|
||||
frame_title_change_edit->set_text(node->get_title());
|
||||
frame_title_change_popup->set_meta("id", p_node_id);
|
||||
frame_title_change_popup->popup();
|
||||
frame_title_change_popup->set_position(p_position);
|
||||
frame_title_change_popup->popup();
|
||||
|
||||
// Select current text.
|
||||
frame_title_change_edit->grab_focus();
|
||||
|
@ -162,7 +162,7 @@ void ProjectDialog::_validate_path() {
|
||||
}
|
||||
}
|
||||
|
||||
if (target_path.is_empty() || target_path.is_relative_path()) {
|
||||
if (target_path.is_relative_path()) {
|
||||
_set_message(TTR("The path specified is invalid."), MESSAGE_ERROR, target_path_input_type);
|
||||
return;
|
||||
}
|
||||
@ -352,7 +352,7 @@ void ProjectDialog::_install_path_changed() {
|
||||
|
||||
void ProjectDialog::_browse_project_path() {
|
||||
String path = project_path->get_text();
|
||||
if (path.is_empty()) {
|
||||
if (path.is_relative_path()) {
|
||||
path = EDITOR_GET("filesystem/directories/default_project_path");
|
||||
}
|
||||
if (mode == MODE_IMPORT && install_path->is_visible_in_tree()) {
|
||||
@ -382,12 +382,16 @@ void ProjectDialog::_browse_project_path() {
|
||||
void ProjectDialog::_browse_install_path() {
|
||||
ERR_FAIL_COND_MSG(mode != MODE_IMPORT, "Install path is only used for MODE_IMPORT.");
|
||||
|
||||
String path = install_path->get_text();
|
||||
if (path.is_relative_path() || !DirAccess::dir_exists_absolute(path)) {
|
||||
path = EDITOR_GET("filesystem/directories/default_project_path");
|
||||
}
|
||||
if (create_dir->is_pressed()) {
|
||||
// Select parent directory of install path.
|
||||
fdialog_install->set_current_dir(install_path->get_text().get_base_dir());
|
||||
fdialog_install->set_current_dir(path.get_base_dir());
|
||||
} else {
|
||||
// Select install path.
|
||||
fdialog_install->set_current_dir(install_path->get_text());
|
||||
fdialog_install->set_current_dir(path);
|
||||
}
|
||||
|
||||
fdialog_install->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
|
||||
|
@ -760,7 +760,7 @@ void ProjectList::_create_project_item_control(int p_index) {
|
||||
hb->set_tags(item.tags, this);
|
||||
hb->set_unsupported_features(item.unsupported_features.duplicate());
|
||||
hb->set_project_version(item.project_version);
|
||||
hb->set_last_edited_info(Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true));
|
||||
hb->set_last_edited_info(!item.missing ? Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true) : TTR("Missing Date"));
|
||||
|
||||
hb->set_is_favorite(item.favorite);
|
||||
hb->set_is_missing(item.missing);
|
||||
|
@ -1181,7 +1181,16 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
||||
case TOOL_OPEN_DOCUMENTATION: {
|
||||
List<Node *> selection = editor_selection->get_selected_node_list();
|
||||
for (const Node *node : selection) {
|
||||
ScriptEditor::get_singleton()->goto_help("class_name:" + node->get_class());
|
||||
String class_name;
|
||||
Ref<Script> script_base = node->get_script();
|
||||
if (script_base.is_valid()) {
|
||||
class_name = script_base->get_global_name();
|
||||
}
|
||||
if (class_name.is_empty()) {
|
||||
class_name = node->get_class();
|
||||
}
|
||||
|
||||
ScriptEditor::get_singleton()->goto_help("class_name:" + class_name);
|
||||
}
|
||||
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
|
||||
} break;
|
||||
@ -2226,8 +2235,7 @@ void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) {
|
||||
}
|
||||
|
||||
void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform) {
|
||||
Node *new_parent = p_new_parent;
|
||||
ERR_FAIL_NULL(new_parent);
|
||||
ERR_FAIL_NULL(p_new_parent);
|
||||
|
||||
if (p_nodes.size() == 0) {
|
||||
return; // Nothing to reparent.
|
||||
@ -2267,7 +2275,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
||||
// Prevent selecting the hovered node and keep the reparented node(s) selected instead.
|
||||
hovered_but_reparenting = true;
|
||||
|
||||
Node *validate = new_parent;
|
||||
Node *validate = p_new_parent;
|
||||
while (validate) {
|
||||
ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent.");
|
||||
validate = validate->get_parent();
|
||||
@ -2289,7 +2297,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
||||
// No undo implemented for this yet.
|
||||
Node *node = p_nodes[ni];
|
||||
|
||||
fill_path_renames(node, new_parent, &path_renames);
|
||||
fill_path_renames(node, p_new_parent, &path_renames);
|
||||
former_names.push_back(node->get_name());
|
||||
|
||||
List<Node *> owned;
|
||||
@ -2299,7 +2307,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
||||
owners.push_back(E);
|
||||
}
|
||||
|
||||
bool same_parent = new_parent == node->get_parent();
|
||||
bool same_parent = p_new_parent == node->get_parent();
|
||||
if (same_parent && node->get_index(false) < p_position_in_parent + ni) {
|
||||
inc--; // If the child will generate a gap when moved, adjust.
|
||||
}
|
||||
@ -2310,17 +2318,17 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
||||
need_edit = select_node_hovered_at_end_of_drag;
|
||||
} else {
|
||||
undo_redo->add_do_method(node->get_parent(), "remove_child", node);
|
||||
undo_redo->add_do_method(new_parent, "add_child", node, true);
|
||||
undo_redo->add_do_method(p_new_parent, "add_child", node, true);
|
||||
}
|
||||
|
||||
int new_position_in_parent = p_position_in_parent == -1 ? -1 : p_position_in_parent + inc;
|
||||
if (new_position_in_parent >= 0 || same_parent) {
|
||||
undo_redo->add_do_method(new_parent, "move_child", node, new_position_in_parent);
|
||||
undo_redo->add_do_method(p_new_parent, "move_child", node, new_position_in_parent);
|
||||
}
|
||||
|
||||
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||
String old_name = former_names[ni];
|
||||
String new_name = new_parent->validate_child_name(node);
|
||||
String new_name = p_new_parent->validate_child_name(node);
|
||||
|
||||
// Name was modified, fix the path renames.
|
||||
if (old_name.casecmp_to(new_name) != 0) {
|
||||
@ -2345,8 +2353,12 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
||||
}
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, new_position_in_parent);
|
||||
undo_redo->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).path_join(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index(false));
|
||||
// FIXME: Live editing for "Reparent to New Node" option is broken.
|
||||
// We must get the path to `p_new_parent` *after* it was added to the scene.
|
||||
if (p_new_parent->is_inside_tree()) {
|
||||
undo_redo->add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(p_new_parent), new_name, new_position_in_parent);
|
||||
undo_redo->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(p_new_parent)).path_join(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index(false));
|
||||
}
|
||||
|
||||
if (p_keep_global_xform) {
|
||||
if (Object::cast_to<Node2D>(node)) {
|
||||
@ -2366,7 +2378,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
||||
undo_redo->add_do_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", node);
|
||||
}
|
||||
|
||||
undo_redo->add_undo_method(new_parent, "remove_child", node);
|
||||
undo_redo->add_undo_method(p_new_parent, "remove_child", node);
|
||||
undo_redo->add_undo_method(node, "set_name", former_names[ni]);
|
||||
|
||||
inc++;
|
||||
@ -2384,7 +2396,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
||||
}
|
||||
|
||||
int child_pos = node->get_index(false);
|
||||
bool reparented_to_container = Object::cast_to<Container>(new_parent) && Object::cast_to<Control>(node);
|
||||
bool reparented_to_container = Object::cast_to<Container>(p_new_parent) && Object::cast_to<Control>(node);
|
||||
|
||||
undo_redo->add_undo_method(node->get_parent(), "add_child", node, true);
|
||||
undo_redo->add_undo_method(node->get_parent(), "move_child", node, child_pos);
|
||||
@ -2641,6 +2653,13 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!entire_scene) {
|
||||
for (const Node *E : remove_list) {
|
||||
// `move_child` + `get_index` doesn't really work for internal nodes.
|
||||
ERR_FAIL_COND_MSG(E->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to remove internal node, this is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
EditorNode::get_singleton()->hide_unused_editors(this);
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
@ -2653,10 +2672,6 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
|
||||
undo_redo->add_undo_method(scene_tree, "update_tree");
|
||||
undo_redo->add_undo_reference(edited_scene);
|
||||
} else {
|
||||
for (const Node *E : remove_list) {
|
||||
// `move_child` + `get_index` doesn't really work for internal nodes.
|
||||
ERR_FAIL_COND_MSG(E->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to remove internal node, this is not supported.");
|
||||
}
|
||||
if (delete_tracks_checkbox->is_pressed() || p_cut) {
|
||||
remove_list.sort_custom<Node::Comparator>(); // Sort nodes to keep positions.
|
||||
HashMap<Node *, NodePath> path_renames;
|
||||
@ -2761,10 +2776,10 @@ void SceneTreeDock::_selection_changed() {
|
||||
_update_script_button();
|
||||
}
|
||||
|
||||
void SceneTreeDock::_do_create(Node *p_parent) {
|
||||
Node *SceneTreeDock::_do_create(Node *p_parent) {
|
||||
Variant c = create_dialog->instantiate_selected();
|
||||
Node *child = Object::cast_to<Node>(c);
|
||||
ERR_FAIL_NULL(child);
|
||||
ERR_FAIL_NULL_V(child, nullptr);
|
||||
|
||||
String new_name = p_parent->validate_child_name(child);
|
||||
if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
|
||||
@ -2778,8 +2793,6 @@ void SceneTreeDock::_do_create(Node *p_parent) {
|
||||
if (edited_scene) {
|
||||
undo_redo->add_do_method(p_parent, "add_child", child, true);
|
||||
undo_redo->add_do_method(child, "set_owner", edited_scene);
|
||||
undo_redo->add_do_method(editor_selection, "clear");
|
||||
undo_redo->add_do_method(editor_selection, "add_node", child);
|
||||
undo_redo->add_do_reference(child);
|
||||
undo_redo->add_undo_method(p_parent, "remove_child", child);
|
||||
|
||||
@ -2794,28 +2807,34 @@ void SceneTreeDock::_do_create(Node *p_parent) {
|
||||
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(this, "_post_do_create", child);
|
||||
undo_redo->commit_action();
|
||||
_push_item(c);
|
||||
editor_selection->clear();
|
||||
editor_selection->add_node(child);
|
||||
if (Object::cast_to<Control>(c)) {
|
||||
//make editor more comfortable, so some controls don't appear super shrunk
|
||||
Control *ct = Object::cast_to<Control>(c);
|
||||
|
||||
Size2 ms = ct->get_minimum_size();
|
||||
return child;
|
||||
}
|
||||
|
||||
void SceneTreeDock::_post_do_create(Node *p_child) {
|
||||
editor_selection->clear();
|
||||
editor_selection->add_node(p_child);
|
||||
_push_item(p_child);
|
||||
|
||||
// Make editor more comfortable, so some controls don't appear super shrunk.
|
||||
Control *control = Object::cast_to<Control>(p_child);
|
||||
if (control) {
|
||||
Size2 ms = control->get_minimum_size();
|
||||
if (ms.width < 4) {
|
||||
ms.width = 40;
|
||||
}
|
||||
if (ms.height < 4) {
|
||||
ms.height = 40;
|
||||
}
|
||||
if (ct->is_layout_rtl()) {
|
||||
ct->set_position(ct->get_position() - Vector2(ms.x, 0));
|
||||
if (control->is_layout_rtl()) {
|
||||
control->set_position(control->get_position() - Vector2(ms.x, 0));
|
||||
}
|
||||
ct->set_size(ms);
|
||||
control->set_size(ms);
|
||||
}
|
||||
|
||||
emit_signal(SNAME("node_created"), c);
|
||||
emit_signal(SNAME("node_created"), p_child);
|
||||
}
|
||||
|
||||
void SceneTreeDock::_create() {
|
||||
@ -2898,22 +2917,24 @@ void SceneTreeDock::_create() {
|
||||
}
|
||||
|
||||
Node *parent = nullptr;
|
||||
int original_position = -1;
|
||||
if (only_one_top_node) {
|
||||
parent = top_node->get_parent();
|
||||
original_position = top_node->get_index(false);
|
||||
} else {
|
||||
parent = top_node->get_parent()->get_parent();
|
||||
}
|
||||
|
||||
_do_create(parent);
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action_for_history(TTR("Reparent to New Node"), editor_data->get_current_edited_scene_history_id());
|
||||
|
||||
Node *last_created = _do_create(parent);
|
||||
|
||||
Vector<Node *> nodes;
|
||||
for (Node *E : selection) {
|
||||
nodes.push_back(E);
|
||||
}
|
||||
|
||||
// This works because editor_selection was cleared and populated with last created node in _do_create()
|
||||
Node *last_created = editor_selection->get_selected_node_list().front()->get();
|
||||
|
||||
if (center_parent) {
|
||||
// Find parent type and only average positions of relevant nodes.
|
||||
Node3D *parent_node_3d = Object::cast_to<Node3D>(last_created);
|
||||
@ -2952,6 +2973,11 @@ void SceneTreeDock::_create() {
|
||||
}
|
||||
|
||||
_do_reparent(last_created, -1, nodes, true);
|
||||
|
||||
if (only_one_top_node) {
|
||||
undo_redo->add_do_method(parent, "move_child", last_created, original_position);
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
scene_tree->get_scene_tree()->grab_focus();
|
||||
@ -4354,6 +4380,7 @@ void SceneTreeDock::_edit_subresource(int p_idx, const PopupMenu *p_from_menu) {
|
||||
}
|
||||
|
||||
void SceneTreeDock::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_post_do_create"), &SceneTreeDock::_post_do_create);
|
||||
ClassDB::bind_method(D_METHOD("_set_owners"), &SceneTreeDock::_set_owners);
|
||||
ClassDB::bind_method(D_METHOD("_reparent_nodes_to_root"), &SceneTreeDock::_reparent_nodes_to_root);
|
||||
ClassDB::bind_method(D_METHOD("_reparent_nodes_to_paths_with_transform_and_name"), &SceneTreeDock::_reparent_nodes_to_paths_with_transform_and_name);
|
||||
|
@ -179,7 +179,8 @@ class SceneTreeDock : public VBoxContainer {
|
||||
bool first_enter = true;
|
||||
|
||||
void _create();
|
||||
void _do_create(Node *p_parent);
|
||||
Node *_do_create(Node *p_parent);
|
||||
void _post_do_create(Node *p_child);
|
||||
Node *scene_root = nullptr;
|
||||
Node *edited_scene = nullptr;
|
||||
Node *pending_click_select = nullptr;
|
||||
|
@ -1419,21 +1419,24 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
|
||||
p_theme->set_icon("decrement_highlight", "VScrollBar", empty_icon);
|
||||
p_theme->set_icon("decrement_pressed", "VScrollBar", empty_icon);
|
||||
|
||||
// Slider
|
||||
const int background_margin = MAX(2, p_config.base_margin / 2);
|
||||
|
||||
// HSlider.
|
||||
p_theme->set_icon("grabber_highlight", "HSlider", p_theme->get_icon(SNAME("GuiSliderGrabberHl"), EditorStringName(EditorIcons)));
|
||||
p_theme->set_icon("grabber", "HSlider", p_theme->get_icon(SNAME("GuiSliderGrabber"), EditorStringName(EditorIcons)));
|
||||
p_theme->set_stylebox("slider", "HSlider", make_flat_stylebox(p_config.dark_color_3, 0, p_config.base_margin / 2, 0, p_config.base_margin / 2, p_config.corner_radius));
|
||||
p_theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(p_config.contrast_color_1, 0, p_config.base_margin / 2, 0, p_config.base_margin / 2, p_config.corner_radius));
|
||||
p_theme->set_stylebox("grabber_area_highlight", "HSlider", make_flat_stylebox(p_config.contrast_color_1, 0, p_config.base_margin / 2, 0, p_config.base_margin / 2));
|
||||
p_theme->set_stylebox("slider", "HSlider", make_flat_stylebox(p_config.dark_color_3, 0, background_margin, 0, background_margin, p_config.corner_radius));
|
||||
p_theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(p_config.contrast_color_1, 0, background_margin, 0, background_margin, p_config.corner_radius));
|
||||
p_theme->set_stylebox("grabber_area_highlight", "HSlider", make_flat_stylebox(p_config.contrast_color_1, 0, background_margin, 0, background_margin));
|
||||
p_theme->set_constant("center_grabber", "HSlider", 0);
|
||||
p_theme->set_constant("grabber_offset", "HSlider", 0);
|
||||
|
||||
// VSlider.
|
||||
p_theme->set_icon("grabber", "VSlider", p_theme->get_icon(SNAME("GuiSliderGrabber"), EditorStringName(EditorIcons)));
|
||||
p_theme->set_icon("grabber_highlight", "VSlider", p_theme->get_icon(SNAME("GuiSliderGrabberHl"), EditorStringName(EditorIcons)));
|
||||
p_theme->set_stylebox("slider", "VSlider", make_flat_stylebox(p_config.dark_color_3, p_config.base_margin / 2, 0, p_config.base_margin / 2, 0, p_config.corner_radius));
|
||||
p_theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(p_config.contrast_color_1, p_config.base_margin / 2, 0, p_config.base_margin / 2, 0, p_config.corner_radius));
|
||||
p_theme->set_stylebox("grabber_area_highlight", "VSlider", make_flat_stylebox(p_config.contrast_color_1, p_config.base_margin / 2, 0, p_config.base_margin / 2, 0));
|
||||
p_theme->set_stylebox("slider", "VSlider", make_flat_stylebox(p_config.dark_color_3, background_margin, 0, background_margin, 0, p_config.corner_radius));
|
||||
p_theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(p_config.contrast_color_1, background_margin, 0, background_margin, 0, p_config.corner_radius));
|
||||
p_theme->set_stylebox("grabber_area_highlight", "VSlider", make_flat_stylebox(p_config.contrast_color_1, background_margin, 0, background_margin, 0));
|
||||
p_theme->set_constant("center_grabber", "VSlider", 0);
|
||||
p_theme->set_constant("grabber_offset", "VSlider", 0);
|
||||
}
|
||||
@ -1768,7 +1771,9 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
|
||||
p_theme->set_color("background", EditorStringName(Editor), background_color_opaque);
|
||||
p_theme->set_stylebox("Background", EditorStringName(EditorStyles), make_flat_stylebox(background_color_opaque, p_config.base_margin, p_config.base_margin, p_config.base_margin, p_config.base_margin));
|
||||
|
||||
p_theme->set_stylebox("PanelForeground", EditorStringName(EditorStyles), p_config.base_style);
|
||||
Ref<StyleBoxFlat> editor_panel_foreground = p_config.base_style->duplicate();
|
||||
editor_panel_foreground->set_corner_radius_all(0);
|
||||
p_theme->set_stylebox("PanelForeground", EditorStringName(EditorStyles), editor_panel_foreground);
|
||||
|
||||
// Editor focus.
|
||||
p_theme->set_stylebox("Focus", EditorStringName(EditorStyles), p_config.button_style_focus);
|
||||
|
@ -1511,6 +1511,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
main_args.push_back(arg);
|
||||
main_args.push_back(N->get());
|
||||
N = N->next();
|
||||
// GDScript docgen requires Autoloads, but loading those also creates a main loop.
|
||||
// This forces main loop to quit without adding more GDScript-specific exceptions to setup.
|
||||
quit_after = 1;
|
||||
} else {
|
||||
OS::get_singleton()->print("Missing relative or absolute path to project for --gdscript-docs, aborting.\n");
|
||||
goto error;
|
||||
@ -2465,7 +2468,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
|
||||
|
||||
if (p_second_phase) {
|
||||
return setup2();
|
||||
exit_err = setup2();
|
||||
if (exit_err != OK) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
@ -2763,6 +2769,30 @@ Error Main::setup2(bool p_show_boot_logo) {
|
||||
|
||||
if (err != OK || display_server == nullptr) {
|
||||
ERR_PRINT("Unable to create DisplayServer, all display drivers failed.\nUse \"--headless\" command line argument to run the engine in headless mode if this is desired (e.g. for continuous integration).");
|
||||
|
||||
if (display_server) {
|
||||
memdelete(display_server);
|
||||
}
|
||||
|
||||
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
|
||||
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
|
||||
unregister_server_types();
|
||||
|
||||
if (input) {
|
||||
memdelete(input);
|
||||
}
|
||||
if (tsman) {
|
||||
memdelete(tsman);
|
||||
}
|
||||
#ifndef _3D_DISABLED
|
||||
if (physics_server_3d_manager) {
|
||||
memdelete(physics_server_3d_manager);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
if (physics_server_2d_manager) {
|
||||
memdelete(physics_server_2d_manager);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -3062,6 +3092,14 @@ Error Main::setup2(bool p_show_boot_logo) {
|
||||
OS::get_singleton()->benchmark_end_measure("Scene", "Modules and Extensions");
|
||||
}
|
||||
|
||||
PackedStringArray extensions;
|
||||
extensions.push_back("gd");
|
||||
if (ClassDB::class_exists("CSharpScript")) {
|
||||
extensions.push_back("cs");
|
||||
}
|
||||
extensions.push_back("gdshader");
|
||||
GLOBAL_DEF_NOVAL(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions); // Note: should be defined after Scene level modules init to see .NET.
|
||||
|
||||
OS::get_singleton()->benchmark_end_measure("Startup", "Scene");
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
@ -3333,13 +3371,16 @@ int Main::start() {
|
||||
gdscript_docs_path = E->next()->get();
|
||||
#endif
|
||||
} else if (E->get() == "--export-release") {
|
||||
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
|
||||
editor = true; //needs editor
|
||||
_export_preset = E->next()->get();
|
||||
} else if (E->get() == "--export-debug") {
|
||||
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
|
||||
editor = true; //needs editor
|
||||
_export_preset = E->next()->get();
|
||||
export_debug = true;
|
||||
} else if (E->get() == "--export-pack") {
|
||||
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
|
||||
editor = true;
|
||||
_export_preset = E->next()->get();
|
||||
export_pack_only = true;
|
||||
@ -3351,6 +3392,8 @@ int Main::start() {
|
||||
if (parsed_pair) {
|
||||
E = E->next();
|
||||
}
|
||||
} else if (E->get().begins_with("--export-")) {
|
||||
ERR_FAIL_V_MSG(EXIT_FAILURE, "Missing export preset name, aborting.");
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Handle case where no path is given to --doctool.
|
||||
@ -4189,7 +4232,7 @@ bool Main::iteration() {
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (wait_for_import && EditorFileSystem::get_singleton()->doing_first_scan()) {
|
||||
if (wait_for_import && EditorFileSystem::get_singleton() && EditorFileSystem::get_singleton()->doing_first_scan()) {
|
||||
exit = false;
|
||||
}
|
||||
#endif
|
||||
|
100
methods.py
100
methods.py
@ -905,21 +905,18 @@ def show_progress(env):
|
||||
node_count_fname = str(env.Dir("#")) + "/.scons_node_count"
|
||||
|
||||
import math
|
||||
import time
|
||||
|
||||
class cache_progress:
|
||||
# The default is 1 GB cache and 12 hours half life
|
||||
def __init__(self, path=None, limit=1073741824, half_life=43200):
|
||||
# The default is 1 GB cache
|
||||
def __init__(self, path=None, limit=pow(1024, 3)):
|
||||
self.path = path
|
||||
self.limit = limit
|
||||
self.exponent_scale = math.log(2) / half_life
|
||||
if env["verbose"] and path is not None:
|
||||
screen.write(
|
||||
"Current cache limit is {} (used: {})\n".format(
|
||||
self.convert_size(limit), self.convert_size(self.get_size(path))
|
||||
)
|
||||
)
|
||||
self.delete(self.file_list())
|
||||
|
||||
def __call__(self, node, *args, **kw):
|
||||
nonlocal node_count, node_count_max, node_count_interval, node_count_fname, show_progress
|
||||
@ -936,12 +933,66 @@ def show_progress(env):
|
||||
screen.write("\r[Initial build] ")
|
||||
screen.flush()
|
||||
|
||||
def convert_size(self, size_bytes):
|
||||
if size_bytes == 0:
|
||||
return "0 bytes"
|
||||
size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
|
||||
i = int(math.floor(math.log(size_bytes, 1024)))
|
||||
p = math.pow(1024, i)
|
||||
s = round(size_bytes / p, 2)
|
||||
return "%s %s" % (int(s) if i == 0 else s, size_name[i])
|
||||
|
||||
def get_size(self, start_path="."):
|
||||
total_size = 0
|
||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
||||
for f in filenames:
|
||||
fp = os.path.join(dirpath, f)
|
||||
total_size += os.path.getsize(fp)
|
||||
return total_size
|
||||
|
||||
def progress_finish(target, source, env):
|
||||
nonlocal node_count, progressor
|
||||
try:
|
||||
with open(node_count_fname, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write("%d\n" % node_count)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
with open(node_count_fname, "r", encoding="utf-8") as f:
|
||||
node_count_max = int(f.readline())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cache_directory = os.environ.get("SCONS_CACHE")
|
||||
# Simple cache pruning, attached to SCons' progress callback. Trim the
|
||||
# cache directory to a size not larger than cache_limit.
|
||||
cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024
|
||||
progressor = cache_progress(cache_directory, cache_limit)
|
||||
Progress(progressor, interval=node_count_interval)
|
||||
|
||||
progress_finish_command = Command("progress_finish", [], progress_finish)
|
||||
AlwaysBuild(progress_finish_command)
|
||||
|
||||
|
||||
def clean_cache(env):
|
||||
import atexit
|
||||
import time
|
||||
|
||||
class cache_clean:
|
||||
def __init__(self, path=None, limit=pow(1024, 3)):
|
||||
self.path = path
|
||||
self.limit = limit
|
||||
|
||||
def clean(self):
|
||||
self.delete(self.file_list())
|
||||
|
||||
def delete(self, files):
|
||||
if len(files) == 0:
|
||||
return
|
||||
if env["verbose"]:
|
||||
# Utter something
|
||||
screen.write("\rPurging %d %s from cache...\n" % (len(files), len(files) > 1 and "files" or "file"))
|
||||
print("Purging %d %s from cache..." % (len(files), "files" if len(files) > 1 else "file"))
|
||||
[os.remove(f) for f in files]
|
||||
|
||||
def file_list(self):
|
||||
@ -975,47 +1026,20 @@ def show_progress(env):
|
||||
else:
|
||||
return [x[0] for x in file_stat[mark:]]
|
||||
|
||||
def convert_size(self, size_bytes):
|
||||
if size_bytes == 0:
|
||||
return "0 bytes"
|
||||
size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
|
||||
i = int(math.floor(math.log(size_bytes, 1024)))
|
||||
p = math.pow(1024, i)
|
||||
s = round(size_bytes / p, 2)
|
||||
return "%s %s" % (int(s) if i == 0 else s, size_name[i])
|
||||
|
||||
def get_size(self, start_path="."):
|
||||
total_size = 0
|
||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
||||
for f in filenames:
|
||||
fp = os.path.join(dirpath, f)
|
||||
total_size += os.path.getsize(fp)
|
||||
return total_size
|
||||
|
||||
def progress_finish(target, source, env):
|
||||
nonlocal node_count, progressor
|
||||
def cache_finally():
|
||||
nonlocal cleaner
|
||||
try:
|
||||
with open(node_count_fname, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write("%d\n" % node_count)
|
||||
progressor.delete(progressor.file_list())
|
||||
cleaner.clean()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
with open(node_count_fname, "r", encoding="utf-8") as f:
|
||||
node_count_max = int(f.readline())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cache_directory = os.environ.get("SCONS_CACHE")
|
||||
# Simple cache pruning, attached to SCons' progress callback. Trim the
|
||||
# cache directory to a size not larger than cache_limit.
|
||||
cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024
|
||||
progressor = cache_progress(cache_directory, cache_limit)
|
||||
Progress(progressor, interval=node_count_interval)
|
||||
cleaner = cache_clean(cache_directory, cache_limit)
|
||||
|
||||
progress_finish_command = Command("progress_finish", [], progress_finish)
|
||||
AlwaysBuild(progress_finish_command)
|
||||
atexit.register(cache_finally)
|
||||
|
||||
|
||||
def dump(env):
|
||||
|
@ -3225,6 +3225,26 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
||||
push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
// Consider `Signal(self, "my_signal")` as an implicit use of the signal.
|
||||
if (builtin_type == Variant::SIGNAL && p_call->arguments.size() >= 2) {
|
||||
const GDScriptParser::ExpressionNode *object_arg = p_call->arguments[0];
|
||||
if (object_arg && object_arg->type == GDScriptParser::Node::SELF) {
|
||||
const GDScriptParser::ExpressionNode *signal_arg = p_call->arguments[1];
|
||||
if (signal_arg && signal_arg->is_constant) {
|
||||
const StringName &signal_name = signal_arg->reduced_value;
|
||||
if (parser->current_class->has_member(signal_name)) {
|
||||
const GDScriptParser::ClassNode::Member &member = parser->current_class->get_member(signal_name);
|
||||
if (member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
|
||||
member.signal->usages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
p_call->set_datatype(call_type);
|
||||
return;
|
||||
} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
|
||||
@ -3459,6 +3479,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
||||
|
||||
parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, caller_type);
|
||||
}
|
||||
|
||||
// Consider `emit_signal()`, `connect()`, and `disconnect()` as implicit uses of the signal.
|
||||
if (is_self && (p_call->function_name == SNAME("emit_signal") || p_call->function_name == SNAME("connect") || p_call->function_name == SNAME("disconnect")) && !p_call->arguments.is_empty()) {
|
||||
const GDScriptParser::ExpressionNode *signal_arg = p_call->arguments[0];
|
||||
if (signal_arg && signal_arg->is_constant) {
|
||||
const StringName &signal_name = signal_arg->reduced_value;
|
||||
if (parser->current_class->has_member(signal_name)) {
|
||||
const GDScriptParser::ClassNode::Member &member = parser->current_class->get_member(signal_name);
|
||||
if (member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
|
||||
member.signal->usages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
call_type = return_type;
|
||||
|
@ -296,6 +296,7 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code,
|
||||
encode_uint32(identifier_map.size(), &contents.write[0]);
|
||||
encode_uint32(constant_map.size(), &contents.write[4]);
|
||||
encode_uint32(token_lines.size(), &contents.write[8]);
|
||||
encode_uint32(0, &contents.write[12]); // Unused, kept for compatibility. Please remove at next `TOKENIZER_VERSION` increment.
|
||||
encode_uint32(token_counter, &contents.write[16]);
|
||||
|
||||
int buf_pos = 20;
|
||||
|
@ -229,19 +229,6 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
|
||||
arr[i] = item.to_json();
|
||||
i++;
|
||||
}
|
||||
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
|
||||
arr = native_member_completions.duplicate();
|
||||
|
||||
for (KeyValue<String, ExtendGDScriptParser *> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts) {
|
||||
ExtendGDScriptParser *scr = E.value;
|
||||
const Array &items = scr->get_member_completions();
|
||||
|
||||
const int start_size = arr.size();
|
||||
arr.resize(start_size + items.size());
|
||||
for (int i = start_size; i < arr.size(); i++) {
|
||||
arr[i] = items[i - start_size];
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
@ -958,28 +958,30 @@ struct CompletionItem {
|
||||
|
||||
/**
|
||||
* A string that should be used when comparing this item
|
||||
* with other items. When `falsy` the label is used.
|
||||
* with other items. When omitted the label is used
|
||||
* as the filter text for this item.
|
||||
*/
|
||||
String sortText;
|
||||
|
||||
/**
|
||||
* A string that should be used when filtering a set of
|
||||
* completion items. When `falsy` the label is used.
|
||||
* completion items. When omitted the label is used as the
|
||||
* filter text for this item.
|
||||
*/
|
||||
String filterText;
|
||||
|
||||
/**
|
||||
* A string that should be inserted into a document when selecting
|
||||
* this completion. When `falsy` the label is used.
|
||||
* this completion. When omitted the label is used as the insert text
|
||||
* for this item.
|
||||
*
|
||||
* The `insertText` is subject to interpretation by the client side.
|
||||
* Some tools might not take the string literally. For example
|
||||
* VS Code when code complete is requested in this example `con<cursor position>`
|
||||
* and a completion item with an `insertText` of `console` is provided it
|
||||
* will only insert `sole`. Therefore it is recommended to use `textEdit` instead
|
||||
* since it avoids additional client side interpretation.
|
||||
*
|
||||
* @deprecated Use textEdit instead.
|
||||
* VS Code when code complete is requested in this example
|
||||
* `con<cursor position>` and a completion item with an `insertText` of
|
||||
* `console` is provided it will only insert `sole`. Therefore it is
|
||||
* recommended to use `textEdit` instead since it avoids additional client
|
||||
* side interpretation.
|
||||
*/
|
||||
String insertText;
|
||||
|
||||
@ -1034,14 +1036,20 @@ struct CompletionItem {
|
||||
dict["label"] = label;
|
||||
dict["kind"] = kind;
|
||||
dict["data"] = data;
|
||||
dict["insertText"] = insertText;
|
||||
if (!insertText.is_empty()) {
|
||||
dict["insertText"] = insertText;
|
||||
}
|
||||
if (resolved) {
|
||||
dict["detail"] = detail;
|
||||
dict["documentation"] = documentation.to_json();
|
||||
dict["deprecated"] = deprecated;
|
||||
dict["preselect"] = preselect;
|
||||
dict["sortText"] = sortText;
|
||||
dict["filterText"] = filterText;
|
||||
if (!sortText.is_empty()) {
|
||||
dict["sortText"] = sortText;
|
||||
}
|
||||
if (!filterText.is_empty()) {
|
||||
dict["filterText"] = filterText;
|
||||
}
|
||||
if (commitCharacters.size()) {
|
||||
dict["commitCharacters"] = commitCharacters;
|
||||
}
|
||||
|
@ -1,12 +1,29 @@
|
||||
signal s1()
|
||||
signal s2()
|
||||
signal s3()
|
||||
# Doesn't produce the warning:
|
||||
signal used_as_first_class_signal()
|
||||
signal used_with_signal_constructor()
|
||||
signal used_with_signal_emit()
|
||||
signal used_with_object_emit_signal()
|
||||
signal used_with_object_connect()
|
||||
signal used_with_object_disconnect()
|
||||
signal used_with_self_prefix()
|
||||
|
||||
# Produce the warning:
|
||||
signal used_with_dynamic_name()
|
||||
signal just_unused()
|
||||
@warning_ignore("unused_signal")
|
||||
signal s4()
|
||||
signal unused_but_ignored()
|
||||
|
||||
func no_exec():
|
||||
s1.emit()
|
||||
print(s2)
|
||||
print(used_as_first_class_signal)
|
||||
print(Signal(self, "used_with_signal_constructor"))
|
||||
used_with_signal_emit.emit()
|
||||
print(emit_signal("used_with_object_emit_signal"))
|
||||
print(connect("used_with_object_connect", Callable()))
|
||||
disconnect("used_with_object_disconnect", Callable())
|
||||
print(self.emit_signal("used_with_self_prefix"))
|
||||
|
||||
var dynamic_name := "used_with_dynamic_name"
|
||||
print(emit_signal(dynamic_name))
|
||||
|
||||
func test():
|
||||
pass
|
||||
|
@ -1,5 +1,9 @@
|
||||
GDTEST_OK
|
||||
>> WARNING
|
||||
>> Line: 3
|
||||
>> Line: 11
|
||||
>> UNUSED_SIGNAL
|
||||
>> The signal "s3" is declared but never explicitly used in the class.
|
||||
>> The signal "used_with_dynamic_name" is declared but never explicitly used in the class.
|
||||
>> WARNING
|
||||
>> Line: 12
|
||||
>> UNUSED_SIGNAL
|
||||
>> The signal "just_unused" is declared but never explicitly used in the class.
|
||||
|
@ -0,0 +1,50 @@
|
||||
# https://github.com/godotengine/godot/issues/75658
|
||||
|
||||
class MyObj:
|
||||
var callable: Callable
|
||||
|
||||
func run():
|
||||
callable.call()
|
||||
|
||||
var prop:
|
||||
set(value):
|
||||
callable.call()
|
||||
get:
|
||||
callable.call()
|
||||
return 0
|
||||
|
||||
func _on_some_signal():
|
||||
callable.call()
|
||||
|
||||
func _init(p_callable: Callable):
|
||||
self.callable = p_callable
|
||||
|
||||
signal some_signal
|
||||
|
||||
var obj: MyObj
|
||||
|
||||
func test():
|
||||
# Call.
|
||||
obj = MyObj.new(nullify_obj)
|
||||
obj.run()
|
||||
print(obj)
|
||||
|
||||
# Get.
|
||||
obj = MyObj.new(nullify_obj)
|
||||
var _aux = obj.prop
|
||||
print(obj)
|
||||
|
||||
# Set.
|
||||
obj = MyObj.new(nullify_obj)
|
||||
obj.prop = 1
|
||||
print(obj)
|
||||
|
||||
# Signal handling.
|
||||
obj = MyObj.new(nullify_obj)
|
||||
@warning_ignore("return_value_discarded")
|
||||
some_signal.connect(obj._on_some_signal)
|
||||
some_signal.emit()
|
||||
print(obj)
|
||||
|
||||
func nullify_obj():
|
||||
obj = null
|
@ -0,0 +1,5 @@
|
||||
GDTEST_OK
|
||||
<null>
|
||||
<null>
|
||||
<null>
|
||||
<null>
|
@ -115,8 +115,15 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
|
||||
List<String> *r_missing_deps, Error *r_err) {
|
||||
String blender_path = EDITOR_GET("filesystem/import/blender/blender_path");
|
||||
|
||||
if (blender_major_version == -1 || blender_minor_version == -1) {
|
||||
_get_blender_version(blender_path, blender_major_version, blender_minor_version, nullptr);
|
||||
ERR_FAIL_COND_V_MSG(blender_path.is_empty(), nullptr, "Blender path is empty, check your Editor Settings.");
|
||||
ERR_FAIL_COND_V_MSG(!FileAccess::exists(blender_path), nullptr, vformat("Invalid Blender path: %s, check your Editor Settings.", blender_path));
|
||||
|
||||
if (blender_major_version == -1 || blender_minor_version == -1 || last_tested_blender_path != blender_path) {
|
||||
String error;
|
||||
if (!_get_blender_version(blender_path, blender_major_version, blender_minor_version, &error)) {
|
||||
ERR_FAIL_V_MSG(nullptr, error);
|
||||
}
|
||||
last_tested_blender_path = blender_path;
|
||||
}
|
||||
|
||||
// Get global paths for source and sink.
|
||||
|
@ -45,6 +45,7 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter {
|
||||
|
||||
int blender_major_version = -1;
|
||||
int blender_minor_version = -1;
|
||||
String last_tested_blender_path;
|
||||
|
||||
public:
|
||||
enum {
|
||||
|
@ -5658,6 +5658,15 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
|
||||
if (p_scene_root == nullptr) {
|
||||
// If the root node argument is null, this is the root node.
|
||||
p_scene_root = current_node;
|
||||
// If multiple nodes were generated under the root node, ensure they have the owner set.
|
||||
if (unlikely(current_node->get_child_count() > 0)) {
|
||||
Array args;
|
||||
args.append(p_scene_root);
|
||||
for (int i = 0; i < current_node->get_child_count(); i++) {
|
||||
Node *child = current_node->get_child(i);
|
||||
child->propagate_call(StringName("set_owner"), args);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Add the node we generated and set the owner to the scene root.
|
||||
p_scene_parent->add_child(current_node, true);
|
||||
|
@ -643,6 +643,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
|
||||
_do_paste();
|
||||
input_action = INPUT_NONE;
|
||||
_update_paste_indicator();
|
||||
return EditorPlugin::AFTER_GUI_INPUT_STOP;
|
||||
} else if (mb->is_shift_pressed() && can_edit) {
|
||||
input_action = INPUT_SELECT;
|
||||
last_selection = selection;
|
||||
|
@ -777,7 +777,7 @@ void AudioStreamPlaybackInteractive::_queue(int p_to_clip_index, bool p_is_auto_
|
||||
|
||||
if (stream->clips[p_to_clip_index].auto_advance == AudioStreamInteractive::AUTO_ADVANCE_ENABLED) {
|
||||
int next_clip = stream->clips[p_to_clip_index].auto_advance_next_clip;
|
||||
if (next_clip >= 0 && next_clip < (int)stream->clip_count && states[next_clip].playback.is_valid() && next_clip != p_to_clip_index && next_clip != playback_current && (!transition.use_filler_clip || next_clip != transition.filler_clip)) {
|
||||
if (next_clip >= 0 && next_clip < (int)stream->clip_count && states[next_clip].playback.is_valid() && next_clip != p_to_clip_index && (!transition.use_filler_clip || next_clip != transition.filler_clip)) {
|
||||
auto_advance_to = next_clip;
|
||||
}
|
||||
}
|
||||
@ -905,7 +905,9 @@ void AudioStreamPlaybackInteractive::_mix_internal_state(int p_state_idx, int p_
|
||||
// time to start!
|
||||
from_frame = state.fade_wait * mix_rate;
|
||||
state.fade_wait = 0;
|
||||
queue_next = state.auto_advance;
|
||||
if (state.fade_speed == 0.0) {
|
||||
queue_next = state.auto_advance;
|
||||
}
|
||||
playback_current = p_state_idx;
|
||||
state.first_mix = false;
|
||||
} else {
|
||||
@ -919,7 +921,6 @@ void AudioStreamPlaybackInteractive::_mix_internal_state(int p_state_idx, int p_
|
||||
state.playback->mix(temp_buffer + from_frame, 1.0, p_frames - from_frame);
|
||||
|
||||
double frame_fade_inc = state.fade_speed * frame_inc;
|
||||
|
||||
for (int i = from_frame; i < p_frames; i++) {
|
||||
if (state.fade_wait) {
|
||||
// This is for fade out of existing stream;
|
||||
@ -933,6 +934,7 @@ void AudioStreamPlaybackInteractive::_mix_internal_state(int p_state_idx, int p_
|
||||
state.fade_speed = 0.0;
|
||||
frame_fade_inc = 0.0;
|
||||
state.fade_volume = 1.0;
|
||||
queue_next = state.auto_advance;
|
||||
}
|
||||
} else if (frame_fade_inc < 0.0) {
|
||||
state.fade_volume += frame_fade_inc;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user