mirror of
https://github.com/godotengine/godot.git
synced 2024-09-20 06:22:24 +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]])
|
env.AppendUnique(CPPDEFINES=[headers[header]])
|
||||||
|
|
||||||
|
|
||||||
# FIXME: This method mixes both cosmetic progress stuff and cache handling...
|
|
||||||
methods.show_progress(env)
|
methods.show_progress(env)
|
||||||
# TODO: replace this with `env.Dump(format="json")`
|
# TODO: replace this with `env.Dump(format="json")`
|
||||||
# once we start requiring SCons 4.0 as min version.
|
# once we start requiring SCons 4.0 as min version.
|
||||||
|
@ -1102,3 +1101,5 @@ def purge_flaky_files():
|
||||||
|
|
||||||
|
|
||||||
atexit.register(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_block.c",
|
||||||
"decompress/zstd_decompress.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
|
# Match platforms with ZSTD_ASM_SUPPORTED in common/portability_macros.h
|
||||||
thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S")
|
thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S")
|
||||||
thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources]
|
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(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);
|
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();
|
_add_builtin_input_map();
|
||||||
|
|
||||||
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
|
// 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 = {
|
OS::GDExtensionData data = {
|
||||||
true, // also_set_library_path
|
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
|
Engine::get_singleton()->is_editor_hint(), // generate_temp_files
|
||||||
&abs_dependencies_paths, // library_dependencies
|
&abs_dependencies_paths, // library_dependencies
|
||||||
};
|
};
|
||||||
Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data);
|
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 == 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);
|
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,
|
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
|
# 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,
|
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,
|
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,
|
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 "expression.h"
|
||||||
|
|
||||||
#include "core/io/marshalls.h"
|
|
||||||
#include "core/math/math_funcs.h"
|
|
||||||
#include "core/object/class_db.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) {
|
Error Expression::_get_token(Token &r_token) {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -392,7 +387,6 @@ Error Expression::_get_token(Token &r_token) {
|
||||||
if (is_digit(c)) {
|
if (is_digit(c)) {
|
||||||
} else if (c == 'e') {
|
} else if (c == 'e') {
|
||||||
reading = READING_EXP;
|
reading = READING_EXP;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
reading = READING_DONE;
|
reading = READING_DONE;
|
||||||
}
|
}
|
||||||
|
@ -419,7 +413,9 @@ Error Expression::_get_token(Token &r_token) {
|
||||||
is_first_char = false;
|
is_first_char = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
str_ofs--;
|
if (c != 0) {
|
||||||
|
str_ofs--;
|
||||||
|
}
|
||||||
|
|
||||||
r_token.type = TK_CONSTANT;
|
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_ 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_ 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_ double floor(double p_x) { return ::floor(p_x); }
|
||||||
static _ALWAYS_INLINE_ float floor(float p_x) { return ::floorf(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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,14 +45,17 @@
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
|
|
||||||
struct _ObjectDebugLock {
|
struct _ObjectDebugLock {
|
||||||
Object *obj;
|
ObjectID obj_id;
|
||||||
|
|
||||||
_ObjectDebugLock(Object *p_obj) {
|
_ObjectDebugLock(Object *p_obj) {
|
||||||
obj = p_obj;
|
obj_id = p_obj->get_instance_id();
|
||||||
obj->_lock_index.ref();
|
p_obj->_lock_index.ref();
|
||||||
}
|
}
|
||||||
~_ObjectDebugLock() {
|
~_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.
|
// Disconnect signals that connect to this object.
|
||||||
while (connections.size()) {
|
while (connections.size()) {
|
||||||
Connection c = connections.front()->get();
|
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 (unlikely(!disconnected)) {
|
||||||
// If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
|
// If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
|
||||||
connections.pop_front();
|
connections.pop_front();
|
||||||
|
|
|
@ -142,6 +142,7 @@ void ScriptLanguageExtension::_bind_methods() {
|
||||||
GDVIRTUAL_BIND(_debug_get_current_stack_info);
|
GDVIRTUAL_BIND(_debug_get_current_stack_info);
|
||||||
|
|
||||||
GDVIRTUAL_BIND(_reload_all_scripts);
|
GDVIRTUAL_BIND(_reload_all_scripts);
|
||||||
|
GDVIRTUAL_BIND(_reload_scripts, "scripts", "soft_reload");
|
||||||
GDVIRTUAL_BIND(_reload_tool_script, "script", "soft_reload");
|
GDVIRTUAL_BIND(_reload_tool_script, "script", "soft_reload");
|
||||||
|
|
||||||
GDVIRTUAL_BIND(_get_recognized_extensions);
|
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;
|
p_caller_pool_thread->signaled = false;
|
||||||
|
|
||||||
if (IS_WAIT_OVER) {
|
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) {
|
if (!exit_threads && was_signaled) {
|
||||||
// This thread was awaken for some additional reason, but it's about to exit.
|
// 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.
|
// 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;
|
return scs;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringName::_Data *StringName::_table[STRING_TABLE_LEN];
|
|
||||||
|
|
||||||
StringName _scs_create(const char *p_chr, bool p_static) {
|
StringName _scs_create(const char *p_chr, bool p_static) {
|
||||||
return (p_chr[0] ? StringName(StaticCString::create(p_chr), p_static) : StringName());
|
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() {
|
void StringName::setup() {
|
||||||
ERR_FAIL_COND(configured);
|
ERR_FAIL_COND(configured);
|
||||||
for (int i = 0; i < STRING_TABLE_LEN; i++) {
|
for (int i = 0; i < STRING_TABLE_LEN; i++) {
|
||||||
|
|
|
@ -67,7 +67,7 @@ class StringName {
|
||||||
_Data() {}
|
_Data() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
static _Data *_table[STRING_TABLE_LEN];
|
static inline _Data *_table[STRING_TABLE_LEN];
|
||||||
|
|
||||||
_Data *_data = nullptr;
|
_Data *_data = nullptr;
|
||||||
|
|
||||||
|
@ -75,10 +75,10 @@ class StringName {
|
||||||
friend void register_core_types();
|
friend void register_core_types();
|
||||||
friend void unregister_core_types();
|
friend void unregister_core_types();
|
||||||
friend class Main;
|
friend class Main;
|
||||||
static Mutex mutex;
|
static inline Mutex mutex;
|
||||||
static void setup();
|
static void setup();
|
||||||
static void cleanup();
|
static void cleanup();
|
||||||
static bool configured;
|
static inline bool configured = false;
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
struct DebugSortReferences {
|
struct DebugSortReferences {
|
||||||
bool operator()(const _Data *p_left, const _Data *p_right) const {
|
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
|
#endif
|
||||||
|
|
||||||
StringName(_Data *p_data) { _data = p_data; }
|
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 from = 0;
|
||||||
int len = length();
|
int len = length();
|
||||||
|
|
||||||
|
String buffer = *this;
|
||||||
while (true) {
|
while (true) {
|
||||||
int end = find(p_splitter, from);
|
int end = find(p_splitter, from);
|
||||||
if (end < 0) {
|
if (end < 0) {
|
||||||
end = len;
|
end = len;
|
||||||
}
|
}
|
||||||
if (p_allow_empty || (end > from)) {
|
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) {
|
if (end == len) {
|
||||||
|
@ -1561,6 +1564,7 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_
|
||||||
int from = 0;
|
int from = 0;
|
||||||
int len = length();
|
int len = length();
|
||||||
|
|
||||||
|
String buffer = *this;
|
||||||
while (true) {
|
while (true) {
|
||||||
int idx;
|
int idx;
|
||||||
int end = findmk(p_splitters, from, &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)) {
|
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) {
|
if (end == len) {
|
||||||
|
|
|
@ -169,7 +169,7 @@
|
||||||
astar.ConnectPoints(2, 3, false);
|
astar.ConnectPoints(2, 3, false);
|
||||||
astar.ConnectPoints(4, 3, false);
|
astar.ConnectPoints(4, 3, false);
|
||||||
astar.ConnectPoints(1, 4, 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]
|
[/csharp]
|
||||||
[/codeblocks]
|
[/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.
|
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, 2, true);
|
||||||
astar.ConnectPoints(1, 3, true);
|
astar.ConnectPoints(1, 3, true);
|
||||||
|
|
||||||
int[] neighbors = astar.GetPointConnections(1); // Returns [2, 3]
|
long[] neighbors = astar.GetPointConnections(1); // Returns [2, 3]
|
||||||
[/csharp]
|
[/csharp]
|
||||||
[/codeblocks]
|
[/codeblocks]
|
||||||
</description>
|
</description>
|
||||||
|
|
|
@ -197,7 +197,7 @@
|
||||||
astar.ConnectPoints(2, 3, false);
|
astar.ConnectPoints(2, 3, false);
|
||||||
astar.ConnectPoints(4, 3, false);
|
astar.ConnectPoints(4, 3, false);
|
||||||
astar.ConnectPoints(1, 4, 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]
|
[/csharp]
|
||||||
[/codeblocks]
|
[/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.
|
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, 2, true);
|
||||||
astar.ConnectPoints(1, 3, true);
|
astar.ConnectPoints(1, 3, true);
|
||||||
|
|
||||||
int[] neighbors = astar.GetPointConnections(1); // Returns [2, 3]
|
long[] neighbors = astar.GetPointConnections(1); // Returns [2, 3]
|
||||||
[/csharp]
|
[/csharp]
|
||||||
[/codeblocks]
|
[/codeblocks]
|
||||||
</description>
|
</description>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</brief_description>
|
</brief_description>
|
||||||
<description>
|
<description>
|
||||||
A resource used by [AnimationNodeBlendTree].
|
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.
|
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>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
<param index="0" name="point" type="int" />
|
<param index="0" name="point" type="int" />
|
||||||
<param index="1" name="pos" type="Vector2" />
|
<param index="1" name="pos" type="Vector2" />
|
||||||
<description>
|
<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>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
|
|
|
@ -243,7 +243,7 @@
|
||||||
var numbers = [1, 2, 3]
|
var numbers = [1, 2, 3]
|
||||||
var extra = [4, 5, 6]
|
var extra = [4, 5, 6]
|
||||||
numbers.append_array(extra)
|
numbers.append_array(extra)
|
||||||
print(nums) # Prints [1, 2, 3, 4, 5, 6]
|
print(numbers) # Prints [1, 2, 3, 4, 5, 6]
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
@ -715,7 +715,7 @@
|
||||||
<param index="0" name="func" type="Callable" />
|
<param index="0" name="func" type="Callable" />
|
||||||
<description>
|
<description>
|
||||||
Sorts the array using a custom [Callable].
|
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]
|
[codeblock]
|
||||||
func sort_ascending(a, b):
|
func sort_ascending(a, b):
|
||||||
if a[1] < b[1]:
|
if a[1] < b[1]:
|
||||||
|
@ -728,7 +728,7 @@
|
||||||
print(my_items) # Prints [["Rice", 4], ["Tomato", 5], ["Apple", 9]]
|
print(my_items) # Prints [["Rice", 4], ["Tomato", 5], ["Apple", 9]]
|
||||||
|
|
||||||
# Sort descending, using a lambda function.
|
# 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]]
|
print(my_items) # Prints [["Apple", 9], ["Tomato", 5], ["Rice", 4]]
|
||||||
[/codeblock]
|
[/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:
|
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="0" name="idx" type="int" />
|
||||||
<param index="1" name="t" type="float" />
|
<param index="1" name="t" type="float" />
|
||||||
<description>
|
<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].
|
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>
|
</description>
|
||||||
</method>
|
</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.
|
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>
|
</description>
|
||||||
</method>
|
</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">
|
<method name="get_rid" qualifiers="const">
|
||||||
<return type="RID" />
|
<return type="RID" />
|
||||||
<description>
|
<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.
|
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>
|
</description>
|
||||||
</method>
|
</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>
|
</methods>
|
||||||
<members>
|
<members>
|
||||||
<member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true">
|
<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.
|
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>
|
</description>
|
||||||
</method>
|
</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">
|
<method name="get_rid" qualifiers="const">
|
||||||
<return type="RID" />
|
<return type="RID" />
|
||||||
<description>
|
<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.
|
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>
|
</description>
|
||||||
</method>
|
</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>
|
</methods>
|
||||||
<members>
|
<members>
|
||||||
<member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true">
|
<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()
|
public override Godot.Collections.Array<Godot.Collections.Dictionary> _GetPropertyList()
|
||||||
{
|
{
|
||||||
|
@ -173,7 +173,7 @@
|
||||||
if (propertyName.StartsWith("number_"))
|
if (propertyName.StartsWith("number_"))
|
||||||
{
|
{
|
||||||
int index = int.Parse(propertyName.Substring("number_".Length));
|
int index = int.Parse(propertyName.Substring("number_".Length));
|
||||||
numbers[index] = value.As<int>();
|
_numbers[index] = value.As<int>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1000,7 +1000,7 @@
|
||||||
prime-run %command%
|
prime-run %command%
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
</member>
|
</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.
|
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>
|
||||||
<member name="editor/script/templates_search_path" type="String" setter="" getter="" default=""res://script_templates"">
|
<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="1" name="item" type="RID" />
|
||||||
<param index="2" name="mirroring" type="Vector2" />
|
<param index="2" name="mirroring" type="Vector2" />
|
||||||
<description>
|
<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>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="canvas_set_item_repeat">
|
<method name="canvas_set_item_repeat">
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
</member>
|
</member>
|
||||||
<member name="kerning_pairs" type="PackedStringArray" setter="" getter="" default="PackedStringArray()">
|
<member name="kerning_pairs" type="PackedStringArray" setter="" getter="" default="PackedStringArray()">
|
||||||
Kerning pairs for the font. Kerning pair adjust the spacing between two characters.
|
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>
|
||||||
<member name="rows" type="int" setter="" getter="" default="1">
|
<member name="rows" type="int" setter="" getter="" default="1">
|
||||||
Number of rows in the font image. See also [member columns].
|
Number of rows in the font image. See also [member columns].
|
||||||
|
|
|
@ -314,6 +314,13 @@
|
||||||
<description>
|
<description>
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</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">
|
<method name="_reload_tool_script" qualifiers="virtual">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="script" type="Script" />
|
<param index="0" name="script" type="Script" />
|
||||||
|
|
|
@ -551,7 +551,7 @@
|
||||||
</member>
|
</member>
|
||||||
<member name="default_font_size" type="int" setter="set_default_font_size" getter="get_default_font_size" default="-1">
|
<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]).
|
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>
|
</member>
|
||||||
</members>
|
</members>
|
||||||
<constants>
|
<constants>
|
||||||
|
|
|
@ -2143,17 +2143,28 @@ void RenderingDeviceDriverD3D12::command_pipeline_barrier(CommandBufferID p_cmd_
|
||||||
for (uint32_t i = 0; i < p_texture_barriers.size(); i++) {
|
for (uint32_t i = 0; i < p_texture_barriers.size(); i++) {
|
||||||
const TextureBarrier &texture_barrier_rd = p_texture_barriers[i];
|
const TextureBarrier &texture_barrier_rd = p_texture_barriers[i];
|
||||||
const TextureInfo *texture_info = (const TextureInfo *)(texture_barrier_rd.texture.id);
|
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_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);
|
_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.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.LayoutAfter = _rd_texture_layout_to_d3d12_barrier_layout(texture_barrier_rd.next_layout);
|
||||||
texture_barrier_d3d12.pResource = texture_info->resource;
|
texture_barrier_d3d12.pResource = texture_info->resource;
|
||||||
texture_barrier_d3d12.Subresources.IndexOrFirstMipLevel = texture_barrier_rd.subresources.base_mipmap;
|
if (texture_barrier_rd.subresources.mipmap_count == texture_info->mipmaps && texture_barrier_rd.subresources.layer_count == texture_info->layers) {
|
||||||
texture_barrier_d3d12.Subresources.NumMipLevels = texture_barrier_rd.subresources.mipmap_count;
|
// So, all resources. Then, let's be explicit about it so D3D12 doesn't think
|
||||||
texture_barrier_d3d12.Subresources.FirstArraySlice = texture_barrier_rd.subresources.base_layer;
|
// we are dealing with a subset of subresources.
|
||||||
texture_barrier_d3d12.Subresources.NumArraySlices = texture_barrier_rd.subresources.layer_count;
|
texture_barrier_d3d12.Subresources.IndexOrFirstMipLevel = 0xffffffff;
|
||||||
texture_barrier_d3d12.Subresources.FirstPlane = _compute_plane_slice(texture_info->format, texture_barrier_rd.subresources.aspect);
|
texture_barrier_d3d12.Subresources.NumMipLevels = 0;
|
||||||
texture_barrier_d3d12.Subresources.NumPlanes = format_get_plane_count(texture_info->format);
|
// 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_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);
|
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;
|
update_skeletons = false;
|
||||||
}
|
}
|
||||||
// Canvas group begins here, render until before this item
|
// 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;
|
item_count = 0;
|
||||||
|
|
||||||
if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) {
|
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();
|
mesh_storage->update_mesh_instances();
|
||||||
update_skeletons = false;
|
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;
|
item_count = 0;
|
||||||
|
|
||||||
if (ci->canvas_group->blur_mipmaps) {
|
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 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;
|
item_count = 0;
|
||||||
|
|
||||||
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
|
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();
|
mesh_storage->update_mesh_instances();
|
||||||
update_skeletons = false;
|
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
|
//then reset
|
||||||
item_count = 0;
|
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;
|
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();
|
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) {
|
if (p_item_count <= 0) {
|
||||||
// Nothing to draw, just call canvas_begin() to clear the render target and return.
|
// 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());
|
_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 {
|
} else {
|
||||||
Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
|
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 offset;
|
||||||
Point2 pos = start_pos;
|
|
||||||
|
|
||||||
do {
|
int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0;
|
||||||
do {
|
int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0;
|
||||||
_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);
|
for (int ry = 0; ry <= repeat_times_y; ry++) {
|
||||||
pos.y += ci->repeat_size.y;
|
offset.y = start_pos.y + ry * ci->repeat_size.y;
|
||||||
} while (pos.y < end_pos.y);
|
for (int rx = 0; rx <= repeat_times_x; rx++) {
|
||||||
|
offset.x = start_pos.x + rx * ci->repeat_size.x;
|
||||||
pos.x += 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);
|
||||||
pos.y = start_pos.y;
|
}
|
||||||
} while (pos.x < end_pos.x);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,7 +808,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
|
||||||
state.last_item_index += index;
|
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;
|
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) {
|
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;
|
state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
|
Transform2D base_transform = p_item->final_transform;
|
||||||
|
if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) {
|
||||||
if (p_offset.x || p_offset.y) {
|
base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset);
|
||||||
base_transform *= Transform2D(0, p_offset / p_item->xform_curr.get_scale()); // TODO: Interpolate or explain why not needed.
|
|
||||||
}
|
}
|
||||||
|
base_transform = p_canvas_transform_inverse * base_transform;
|
||||||
|
|
||||||
Transform2D draw_transform; // Used by transform command
|
Transform2D draw_transform; // Used by transform command
|
||||||
|
|
||||||
|
@ -2170,7 +2169,7 @@ bool RasterizerCanvasGLES3::free(RID p_rid) {
|
||||||
void RasterizerCanvasGLES3::update() {
|
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::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
|
||||||
GLES3::Config *config = GLES3::Config::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);
|
glBindFramebuffer(GL_FRAMEBUFFER, render_target->fbo);
|
||||||
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4);
|
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4);
|
||||||
glBindTexture(GL_TEXTURE_2D, render_target->backbuffer);
|
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) {
|
if (render_target->is_transparent || p_to_backbuffer) {
|
||||||
|
|
|
@ -335,7 +335,7 @@ public:
|
||||||
|
|
||||||
typedef void Texture;
|
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;
|
//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);
|
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 _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 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 _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_offset);
|
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);
|
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);
|
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
|
||||||
void _new_batch(bool &r_batch_broken);
|
void _new_batch(bool &r_batch_broken);
|
||||||
|
|
|
@ -62,6 +62,10 @@
|
||||||
#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
|
#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
|
||||||
#define _EXT_DEBUG_OUTPUT 0x92E0
|
#define _EXT_DEBUG_OUTPUT 0x92E0
|
||||||
|
|
||||||
|
#ifndef GL_FRAMEBUFFER_SRGB
|
||||||
|
#define GL_FRAMEBUFFER_SRGB 0x8DB9
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef GLAPIENTRY
|
#ifndef GLAPIENTRY
|
||||||
#if defined(WINDOWS_ENABLED)
|
#if defined(WINDOWS_ENABLED)
|
||||||
#define GLAPIENTRY APIENTRY
|
#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
|
// OpenGL needs to be initialized before initializing the Rasterizers
|
||||||
config = memnew(GLES3::Config);
|
config = memnew(GLES3::Config);
|
||||||
utilities = memnew(GLES3::Utilities);
|
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());
|
ERR_FAIL_COND(p_env.is_null());
|
||||||
|
|
||||||
Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env));
|
Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env));
|
||||||
ERR_FAIL_NULL(sky);
|
|
||||||
|
|
||||||
GLES3::SkyMaterialData *material_data = nullptr;
|
GLES3::SkyMaterialData *material_data = nullptr;
|
||||||
RID sky_material;
|
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::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);
|
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) {
|
if (p_use_multiview) {
|
||||||
glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
|
glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
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;
|
RS::SkyMode sky_mode = sky->mode;
|
||||||
|
|
||||||
if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
|
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;
|
update_single_frame = true;
|
||||||
sky_mode = RS::SKY_MODE_REALTIME;
|
sky_mode = RS::SKY_MODE_REALTIME;
|
||||||
} else if (shader_data->uses_light || shader_data->ubo_size > 0) {
|
} 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;
|
int max_processing_layer = sky->mipmap_count;
|
||||||
|
|
||||||
// Update radiance cubemap
|
// 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] = {
|
static const Vector3 view_normals[6] = {
|
||||||
Vector3(+1, 0, 0),
|
Vector3(+1, 0, 0),
|
||||||
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);
|
scene_state.enable_gl_depth_draw(false);
|
||||||
|
|
||||||
if (draw_sky) {
|
if (draw_sky || draw_sky_fog_only) {
|
||||||
RENDER_TIMESTAMP("Render Sky");
|
RENDER_TIMESTAMP("Render Sky");
|
||||||
|
|
||||||
scene_state.enable_gl_depth_test(true);
|
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) {
|
void ShaderGLES3::_initialize_version(Version *p_version) {
|
||||||
ERR_FAIL_COND(p_version->variants.size() > 0);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
p_version->variants.reserve(variant_count);
|
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);
|
_compile_specialization(spec, i, p_version, specialization_default_mask);
|
||||||
p_version->variants[i].insert(specialization_default_mask, spec);
|
p_version->variants[i].insert(specialization_default_mask, spec);
|
||||||
}
|
}
|
||||||
if (shader_cache_dir_valid) {
|
if (use_cache) {
|
||||||
_save_to_cache(p_version);
|
_save_to_cache(p_version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -583,6 +583,8 @@ void main() {
|
||||||
|
|
||||||
#define SHADER_IS_SRGB true
|
#define SHADER_IS_SRGB true
|
||||||
|
|
||||||
|
#define FLAGS_NON_UNIFORM_SCALE (1 << 4)
|
||||||
|
|
||||||
/* Varyings */
|
/* Varyings */
|
||||||
|
|
||||||
#if defined(COLOR_USED)
|
#if defined(COLOR_USED)
|
||||||
|
@ -955,6 +957,7 @@ ivec2 multiview_uv(ivec2 uv) {
|
||||||
|
|
||||||
uniform highp mat4 world_transform;
|
uniform highp mat4 world_transform;
|
||||||
uniform mediump float opaque_prepass_threshold;
|
uniform mediump float opaque_prepass_threshold;
|
||||||
|
uniform highp uint model_flags;
|
||||||
|
|
||||||
#if defined(RENDER_MATERIAL)
|
#if defined(RENDER_MATERIAL)
|
||||||
layout(location = 0) out vec4 albedo_output_buffer;
|
layout(location = 0) out vec4 albedo_output_buffer;
|
||||||
|
@ -1521,6 +1524,13 @@ void main() {
|
||||||
vec3 light_vertex = vertex;
|
vec3 light_vertex = vertex;
|
||||||
#endif //LIGHT_VERTEX_USED
|
#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
|
#CODE : FRAGMENT
|
||||||
}
|
}
|
||||||
|
@ -1728,16 +1738,10 @@ void main() {
|
||||||
|
|
||||||
vec3 n = normalize(lightmap_normal_xform * normal);
|
vec3 n = normalize(lightmap_normal_xform * normal);
|
||||||
|
|
||||||
ambient_light += lm_light_l0 * 0.282095f;
|
ambient_light += lm_light_l0 * lightmap_exposure_normalization;
|
||||||
ambient_light += lm_light_l1n1 * 0.32573 * n.y * lightmap_exposure_normalization;
|
ambient_light += lm_light_l1n1 * n.y * lightmap_exposure_normalization;
|
||||||
ambient_light += lm_light_l1_0 * 0.32573 * n.z * lightmap_exposure_normalization;
|
ambient_light += lm_light_l1_0 * n.z * lightmap_exposure_normalization;
|
||||||
ambient_light += lm_light_l1p1 * 0.32573 * n.x * lightmap_exposure_normalization;
|
ambient_light += lm_light_l1p1 * 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;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization;
|
ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1871,7 +1875,7 @@ void main() {
|
||||||
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
|
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
|
||||||
|
|
||||||
#if defined(ALPHA_SCISSOR_USED)
|
#if defined(ALPHA_SCISSOR_USED)
|
||||||
if (alpha < alpha_scissor) {
|
if (alpha < alpha_scissor_threshold) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
#endif // !ALPHA_SCISSOR_USED
|
#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;
|
layout(location = 11) in mediump vec4 in_weight_attrib;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uniform mediump sampler2D skeleton_texture; // texunit:0
|
uniform highp sampler2D skeleton_texture; // texunit:0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
|
|
|
@ -108,11 +108,11 @@ uniform float sky_energy_multiplier;
|
||||||
uniform float luminance_multiplier;
|
uniform float luminance_multiplier;
|
||||||
|
|
||||||
uniform float fog_aerial_perspective;
|
uniform float fog_aerial_perspective;
|
||||||
uniform vec3 fog_light_color;
|
uniform vec4 fog_light_color;
|
||||||
uniform float fog_sun_scatter;
|
uniform float fog_sun_scatter;
|
||||||
uniform bool fog_enabled;
|
uniform bool fog_enabled;
|
||||||
uniform float fog_density;
|
uniform float fog_density;
|
||||||
uniform float z_far;
|
uniform float fog_sky_affect;
|
||||||
uniform uint directional_light_count;
|
uniform uint directional_light_count;
|
||||||
|
|
||||||
#ifdef USE_MULTIVIEW
|
#ifdef USE_MULTIVIEW
|
||||||
|
@ -135,6 +135,24 @@ vec3 interleaved_gradient_noise(vec2 pos) {
|
||||||
}
|
}
|
||||||
#endif
|
#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() {
|
void main() {
|
||||||
vec3 cube_normal;
|
vec3 cube_normal;
|
||||||
#ifdef USE_MULTIVIEW
|
#ifdef USE_MULTIVIEW
|
||||||
|
@ -203,6 +221,21 @@ void main() {
|
||||||
|
|
||||||
// Convert to Linear for tonemapping so color matches scene shader better
|
// Convert to Linear for tonemapping so color matches scene shader better
|
||||||
color = srgb_to_linear(color);
|
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;
|
color *= exposure;
|
||||||
#ifdef APPLY_TONEMAPPING
|
#ifdef APPLY_TONEMAPPING
|
||||||
color = apply_tonemapping(color, white);
|
color = apply_tonemapping(color, white);
|
||||||
|
|
|
@ -121,7 +121,7 @@ Config::Config() {
|
||||||
#ifdef WEB_ENABLED
|
#ifdef WEB_ENABLED
|
||||||
msaa_supported = (msaa_max_samples > 0);
|
msaa_supported = (msaa_max_samples > 0);
|
||||||
#else
|
#else
|
||||||
msaa_supported = extensions.has("GL_EXT_framebuffer_multisample");
|
msaa_supported = true;
|
||||||
#endif
|
#endif
|
||||||
#ifndef IOS_ENABLED
|
#ifndef IOS_ENABLED
|
||||||
#ifdef WEB_ENABLED
|
#ifdef WEB_ENABLED
|
||||||
|
@ -218,6 +218,8 @@ Config::Config() {
|
||||||
//https://github.com/godotengine/godot/issues/92662#issuecomment-2161199477
|
//https://github.com/godotengine/godot/issues/92662#issuecomment-2161199477
|
||||||
//disable_particles_workaround = false;
|
//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 disable_particles_workaround = false; // set to 'true' to disable 'GPUParticles'
|
||||||
bool flip_xy_workaround = false;
|
bool flip_xy_workaround = false;
|
||||||
|
|
||||||
|
// PowerVR GE 8320 workaround
|
||||||
|
bool disable_transform_feedback_shader_cache = false;
|
||||||
|
|
||||||
#ifdef ANDROID_ENABLED
|
#ifdef ANDROID_ENABLED
|
||||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
|
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
|
||||||
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = 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).
|
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++) {
|
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);
|
LightInstance *sli = light_instance_owner.get_or_null(sarr[j].owner);
|
||||||
if (!sli) {
|
if (!sli) {
|
||||||
// Found a released light instance.
|
// 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;
|
Vector<uint8_t> ir = new_surface.index_data;
|
||||||
wr = wf_indices.ptrw();
|
wr = wf_indices.ptrw();
|
||||||
|
|
||||||
if (new_surface.vertex_count < (1 << 16)) {
|
if (new_surface.vertex_count <= 65536) {
|
||||||
// Read 16 bit indices.
|
// Read 16 bit indices.
|
||||||
const uint16_t *src_idx = (const uint16_t *)ir.ptr();
|
const uint16_t *src_idx = (const uint16_t *)ir.ptr();
|
||||||
for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) {
|
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) {
|
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);
|
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
|
||||||
ERR_FAIL_NULL(mesh);
|
ERR_FAIL_NULL(mesh);
|
||||||
|
|
||||||
|
|
|
@ -1039,7 +1039,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
|
||||||
data.resize(data_size);
|
data.resize(data_size);
|
||||||
|
|
||||||
ERR_FAIL_COND_V(data.is_empty(), Ref<Image>());
|
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>());
|
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
|
||||||
if (texture->format != texture->real_format) {
|
if (texture->format != texture->real_format) {
|
||||||
image->convert(texture->format);
|
image->convert(texture->format);
|
||||||
|
@ -1095,7 +1095,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
|
||||||
data.resize(data_size);
|
data.resize(data_size);
|
||||||
|
|
||||||
ERR_FAIL_COND_V(data.is_empty(), Ref<Image>());
|
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>());
|
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
|
||||||
|
|
||||||
if (texture->format != Image::FORMAT_RGBA8) {
|
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);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
if (texture->target == GL_TEXTURE_2D_ARRAY) {
|
if (texture->target == GL_TEXTURE_2D_ARRAY) {
|
||||||
if (p_initialize) {
|
if (p_initialize) {
|
||||||
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0,
|
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0, size * texture->layers, nullptr);
|
||||||
size * texture->layers, &read[ofs]);
|
|
||||||
} else {
|
|
||||||
glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
|
|
||||||
}
|
}
|
||||||
|
glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
|
||||||
} else {
|
} else {
|
||||||
glCompressedTexImage2D(blit_target, i, internal_format, w, h, 0, size, &read[ofs]);
|
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) {
|
if (!err) {
|
||||||
return status.st_mtime;
|
return status.st_mtime;
|
||||||
} else {
|
} else {
|
||||||
print_verbose("Failed to get modified time for: " + p_file + "");
|
WARN_PRINT("Failed to get modified time for: " + p_file);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -553,15 +553,40 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
|
||||||
file = file.substr(0, file.length() - 1);
|
file = file.substr(0, file.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct _stat st;
|
HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||||
int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st);
|
|
||||||
|
|
||||||
if (rv == 0) {
|
if (handle != INVALID_HANDLE_VALUE) {
|
||||||
return st.st_mtime;
|
FILETIME ft_create, ft_write;
|
||||||
} else {
|
|
||||||
print_verbose("Failed to get modified time for: " + p_file + "");
|
bool status = GetFileTime(handle, &ft_create, nullptr, &ft_write);
|
||||||
return 0;
|
|
||||||
|
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) {
|
BitField<FileAccess::UnixPermissionFlags> FileAccessWindows::_get_unix_permissions(const String &p_file) {
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "editor/editor_string_names.h"
|
#include "editor/editor_string_names.h"
|
||||||
#include "editor/editor_undo_redo_manager.h"
|
#include "editor/editor_undo_redo_manager.h"
|
||||||
#include "editor/gui/editor_spin_slider.h"
|
#include "editor/gui/editor_spin_slider.h"
|
||||||
|
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
#include "scene/gui/view_panner.h"
|
#include "scene/gui/view_panner.h"
|
||||||
#include "scene/resources/text_line.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_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);
|
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();
|
undo_redo->commit_action();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1083,7 +1089,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||||
if (I.key == REMOVE_ICON) {
|
if (I.key == REMOVE_ICON) {
|
||||||
if (!read_only) {
|
if (!read_only) {
|
||||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
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_locked_tracks_after", track);
|
||||||
undo_redo->add_do_method(this, "_update_hidden_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_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);
|
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();
|
undo_redo->commit_action();
|
||||||
moving_handle = 0;
|
moving_handle = 0;
|
||||||
queue_redraw();
|
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_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(this, "_clear_selection_for_anim", animation);
|
||||||
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
|
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();
|
undo_redo->commit_action();
|
||||||
queue_redraw();
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
@ -1772,6 +1788,11 @@ void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs, bool p_ofs_
|
||||||
i++;
|
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_do_method(this, "queue_redraw");
|
||||||
undo_redo->add_undo_method(this, "queue_redraw");
|
undo_redo->add_undo_method(this, "queue_redraw");
|
||||||
undo_redo->commit_action();
|
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);
|
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, E->value().pos, i == 0);
|
||||||
i++;
|
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();
|
undo_redo->commit_action();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1899,9 +1929,15 @@ void AnimationBezierTrackEdit::paste_keys(real_t p_ofs, bool p_ofs_valid) {
|
||||||
i++;
|
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_do_method(this, "_clear_selection_for_anim", animation);
|
||||||
undo_redo->add_undo_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();
|
undo_redo->commit_action();
|
||||||
|
|
||||||
//selection.clear();
|
//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) {
|
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);
|
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);
|
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() {
|
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_undo_method(animation.ptr(), "bezier_track_set_key_value", track, key, prev);
|
||||||
undo_redo->add_do_method(this, "_update_obj", animation);
|
undo_redo->add_do_method(this, "_update_obj", animation);
|
||||||
undo_redo->add_undo_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();
|
undo_redo->commit_action();
|
||||||
|
|
||||||
setting = false;
|
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_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_do_method(this, "_update_obj", animation);
|
||||||
undo_redo->add_undo_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();
|
undo_redo->commit_action();
|
||||||
|
|
||||||
setting = false;
|
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_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_do_method(this, "_update_obj", animation);
|
||||||
undo_redo->add_undo_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();
|
undo_redo->commit_action();
|
||||||
|
|
||||||
setting = false;
|
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_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(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev_out_handle);
|
||||||
undo_redo->add_undo_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();
|
undo_redo->commit_action();
|
||||||
|
|
||||||
setting = false;
|
setting = false;
|
||||||
|
@ -7032,7 +7052,10 @@ void AnimationTrackEditor::_update_snap_unit() {
|
||||||
if (timeline->is_using_fps()) {
|
if (timeline->is_using_fps()) {
|
||||||
snap_unit = 1.0 / step->get_value();
|
snap_unit = 1.0 / step->get_value();
|
||||||
} else {
|
} 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);
|
tex = get_editor_theme_icon(p_option.display);
|
||||||
} else {
|
} else {
|
||||||
tex = EditorNode::get_singleton()->get_class_icon(p_option.display);
|
tex = EditorNode::get_singleton()->get_class_icon(p_option.display);
|
||||||
|
if (!tex.is_valid()) {
|
||||||
|
tex = get_editor_theme_icon(SNAME("Object"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case ScriptLanguage::CODE_COMPLETION_KIND_ENUM:
|
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) {
|
for (int i = 0; i < files_to_delete.size(); ++i) {
|
||||||
// If the file we are deleting for e.g. the main scene, default environment,
|
// 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.
|
// or audio bus layout, we must clear its definition in Project Settings.
|
||||||
if (files_to_delete[i] == String(GLOBAL_GET("application/config/icon"))) {
|
if (files_to_delete[i] == String(GLOBAL_GET("application/config/icon"))) {
|
||||||
ProjectSettings::get_singleton()->set("application/config/icon", "");
|
ProjectSettings::get_singleton()->set("application/config/icon", "");
|
||||||
}
|
project_settings_modified = true;
|
||||||
if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) {
|
} else if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) {
|
||||||
ProjectSettings::get_singleton()->set("application/run/main_scene", "");
|
ProjectSettings::get_singleton()->set("application/run/main_scene", "");
|
||||||
}
|
project_settings_modified = true;
|
||||||
if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) {
|
} else if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) {
|
||||||
ProjectSettings::get_singleton()->set("application/boot_splash/image", "");
|
ProjectSettings::get_singleton()->set("application/boot_splash/image", "");
|
||||||
}
|
project_settings_modified = true;
|
||||||
if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) {
|
} else if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) {
|
||||||
ProjectSettings::get_singleton()->set("rendering/environment/defaults/default_environment", "");
|
ProjectSettings::get_singleton()->set("rendering/environment/defaults/default_environment", "");
|
||||||
}
|
project_settings_modified = true;
|
||||||
if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) {
|
} else if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) {
|
||||||
ProjectSettings::get_singleton()->set("display/mouse_cursor/custom_image", "");
|
ProjectSettings::get_singleton()->set("display/mouse_cursor/custom_image", "");
|
||||||
}
|
project_settings_modified = true;
|
||||||
if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) {
|
} else if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) {
|
||||||
ProjectSettings::get_singleton()->set("gui/theme/custom", "");
|
ProjectSettings::get_singleton()->set("gui/theme/custom", "");
|
||||||
}
|
project_settings_modified = true;
|
||||||
if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) {
|
} else if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) {
|
||||||
ProjectSettings::get_singleton()->set("gui/theme/custom_font", "");
|
ProjectSettings::get_singleton()->set("gui/theme/custom_font", "");
|
||||||
}
|
project_settings_modified = true;
|
||||||
if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) {
|
} else if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) {
|
||||||
ProjectSettings::get_singleton()->set("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://", "/");
|
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]);
|
emit_signal(SNAME("file_removed"), files_to_delete[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (project_settings_modified) {
|
||||||
|
ProjectSettings::get_singleton()->save();
|
||||||
|
}
|
||||||
|
|
||||||
if (dirs_to_delete.size() == 0) {
|
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.
|
// 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) {
|
for (const AutoloadInfo &info : autoload_cache) {
|
||||||
if (info.node && info.in_editor) {
|
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.
|
// On the first scan, the first_scan_root_dir is created in _first_scan_filesystem.
|
||||||
if (first_scan) {
|
if (first_scan) {
|
||||||
sd = first_scan_root_dir;
|
sd = first_scan_root_dir;
|
||||||
|
// Will be updated on scan.
|
||||||
|
ResourceUID::get_singleton()->clear();
|
||||||
} else {
|
} else {
|
||||||
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||||
sd = memnew(ScannedDirectory);
|
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) {
|
void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info) {
|
||||||
String icon_path;
|
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_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;
|
const String &script_path = script_dep.contains("::") ? script_dep.get_slice("::", 2) : script_dep;
|
||||||
if (!script_path.is_empty()) {
|
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");
|
using_fat32_or_exfat = (da->get_filesystem_type() == "FAT32" || da->get_filesystem_type() == "exFAT");
|
||||||
|
|
||||||
scan_total = 0;
|
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);
|
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 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.";
|
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) {
|
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()));
|
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();
|
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 */
|
/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
|
||||||
} break;
|
} 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) {
|
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) {
|
if (editor_data.get_edited_scene_root() != nullptr) {
|
||||||
save.step(TTR("Analyzing"), 0);
|
save_scene_progress->step(TTR("Analyzing"), 0);
|
||||||
|
|
||||||
int c2d = 0;
|
int c2d = 0;
|
||||||
int c3d = 0;
|
int c3d = 0;
|
||||||
|
|
||||||
_find_node_types(editor_data.get_edited_scene_root(), c2d, c3d);
|
_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?
|
// Current view?
|
||||||
|
|
||||||
Ref<Image> img;
|
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) {
|
if (img.is_valid() && img->get_width() > 0 && img->get_height() > 0) {
|
||||||
img = img->duplicate();
|
img = img->duplicate();
|
||||||
|
|
||||||
save.step(TTR("Creating Thumbnail"), 2);
|
save_scene_progress->step(TTR("Creating Thumbnail"), 3);
|
||||||
save.step(TTR("Creating Thumbnail"), 3);
|
|
||||||
|
|
||||||
int preview_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
int preview_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
||||||
preview_size *= EDSCALE;
|
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);
|
_save_scene(p_file, p_idx);
|
||||||
|
|
||||||
if (!singleton->cmdline_export_mode) {
|
if (!singleton->cmdline_export_mode) {
|
||||||
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
|
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) {
|
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) {
|
void EditorNode::show_accept(const String &p_text, const String &p_title) {
|
||||||
current_menu_option = -1;
|
current_menu_option = -1;
|
||||||
if (accept) {
|
if (accept) {
|
||||||
|
_close_save_scene_progress();
|
||||||
accept->set_ok_button_text(p_title);
|
accept->set_ok_button_text(p_title);
|
||||||
accept->set_text(p_text);
|
accept->set_text(p_text);
|
||||||
EditorInterface::get_singleton()->popup_dialog_centered(accept);
|
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) {
|
void EditorNode::show_save_accept(const String &p_text, const String &p_title) {
|
||||||
current_menu_option = -1;
|
current_menu_option = -1;
|
||||||
if (save_accept) {
|
if (save_accept) {
|
||||||
|
_close_save_scene_progress();
|
||||||
save_accept->set_ok_button_text(p_title);
|
save_accept->set_ok_button_text(p_title);
|
||||||
save_accept->set_text(p_text);
|
save_accept->set_text(p_text);
|
||||||
EditorInterface::get_singleton()->popup_dialog_centered(save_accept);
|
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) {
|
void EditorNode::show_warning(const String &p_text, const String &p_title) {
|
||||||
if (warning) {
|
if (warning) {
|
||||||
|
_close_save_scene_progress();
|
||||||
warning->set_text(p_text);
|
warning->set_text(p_text);
|
||||||
warning->set_title(p_title);
|
warning->set_title(p_title);
|
||||||
EditorInterface::get_singleton()->popup_dialog_centered(warning);
|
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");
|
PackedStringArray scenes = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes");
|
||||||
for (int i = 0; i < scenes.size(); i++) {
|
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")) {
|
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");
|
String current_scene = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "current_scene");
|
||||||
int current_scene_idx = scenes.find(current_scene);
|
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
|
||||||
if (current_scene_idx >= 0) {
|
if (editor_data.get_scene_path(i) == current_scene) {
|
||||||
_set_current_scene(current_scene_idx);
|
_set_current_scene(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6033,7 +6075,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
|
||||||
base_packed_scene = current_packed_scene;
|
base_packed_scene = current_packed_scene;
|
||||||
}
|
}
|
||||||
if (!local_scene_cache.find(path)) {
|
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;
|
local_scene_cache[path] = current_packed_scene;
|
||||||
} else {
|
} else {
|
||||||
current_packed_scene = local_scene_cache[path];
|
current_packed_scene = local_scene_cache[path];
|
||||||
|
|
|
@ -122,6 +122,14 @@ class SurfaceUpgradeTool;
|
||||||
class SurfaceUpgradeDialog;
|
class SurfaceUpgradeDialog;
|
||||||
class WindowWrapper;
|
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 {
|
class EditorNode : public Node {
|
||||||
GDCLASS(EditorNode, Node);
|
GDCLASS(EditorNode, Node);
|
||||||
|
|
||||||
|
@ -470,6 +478,7 @@ private:
|
||||||
String external_file;
|
String external_file;
|
||||||
String open_navigate;
|
String open_navigate;
|
||||||
String saving_scene;
|
String saving_scene;
|
||||||
|
EditorProgress *save_scene_progress = nullptr;
|
||||||
|
|
||||||
DynamicFontImportSettingsDialog *fontdata_import_settings = nullptr;
|
DynamicFontImportSettingsDialog *fontdata_import_settings = nullptr;
|
||||||
SceneImportSettingsDialog *scene_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 _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 _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;
|
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);
|
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 {
|
class EditorPluginList : public Object {
|
||||||
private:
|
private:
|
||||||
Vector<EditorPlugin *> plugins_list;
|
Vector<EditorPlugin *> plugins_list;
|
||||||
|
|
|
@ -2778,7 +2778,11 @@ void EditorPropertyNodePath::_update_menu() {
|
||||||
void EditorPropertyNodePath::_menu_option(int p_idx) {
|
void EditorPropertyNodePath::_menu_option(int p_idx) {
|
||||||
switch (p_idx) {
|
switch (p_idx) {
|
||||||
case ACTION_CLEAR: {
|
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();
|
update_property();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
|
@ -175,6 +175,13 @@ void EditorResourcePicker::_file_quick_selected() {
|
||||||
_file_selected(quick_open->get_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() {
|
void EditorResourcePicker::_update_menu() {
|
||||||
_update_menu_items();
|
_update_menu_items();
|
||||||
|
|
||||||
|
@ -408,6 +415,10 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
||||||
if (edited_resource.is_null()) {
|
if (edited_resource.is_null()) {
|
||||||
return;
|
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);
|
EditorNode::get_singleton()->save_resource_as(edited_resource);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -833,6 +844,13 @@ void EditorResourcePicker::_notification(int p_what) {
|
||||||
assign_button->queue_redraw();
|
assign_button->queue_redraw();
|
||||||
}
|
}
|
||||||
} break;
|
} 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_quick_selected();
|
||||||
void _file_selected(const String &p_path);
|
void _file_selected(const String &p_path);
|
||||||
|
|
||||||
|
void _resource_saved(Object *p_resource);
|
||||||
|
|
||||||
void _update_menu();
|
void _update_menu();
|
||||||
void _update_menu_items();
|
void _update_menu_items();
|
||||||
void _edit_menu_cbk(int p_which);
|
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 {
|
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 {
|
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_columns(1);
|
||||||
files->set_max_text_lines(1);
|
files->set_max_text_lines(1);
|
||||||
files->set_fixed_column_width(0);
|
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"));
|
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;
|
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) {
|
Vector<String> FileSystemDock::_remove_self_included_paths(Vector<String> selected_strings) {
|
||||||
// Remove paths or files that are included into another.
|
// Remove paths or files that are included into another.
|
||||||
if (selected_strings.size() > 1) {
|
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();
|
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->clear();
|
||||||
file_list_popup->reset_size();
|
file_list_popup->reset_size();
|
||||||
|
|
||||||
|
|
|
@ -359,6 +359,7 @@ private:
|
||||||
void _update_display_mode(bool p_force = false);
|
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> _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);
|
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);
|
file_name = ProjectSettings::get_singleton()->localize_path(file_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selected_options = p_selected_options;
|
||||||
|
|
||||||
String f = files[0];
|
String f = files[0];
|
||||||
if (mode == FILE_MODE_OPEN_FILES) {
|
if (mode == FILE_MODE_OPEN_FILES) {
|
||||||
emit_signal(SNAME("files_selected"), 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);
|
file->set_text(f);
|
||||||
dir->set_text(f.get_base_dir());
|
dir->set_text(f.get_base_dir());
|
||||||
selected_options = p_selected_options;
|
|
||||||
filter->select(p_filter);
|
filter->select(p_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -614,13 +614,13 @@ void EditorSpinSlider::_value_focus_exited() {
|
||||||
// -> TAB was pressed
|
// -> TAB was pressed
|
||||||
// -> modal_close was not called
|
// -> modal_close was not called
|
||||||
// -> need to close/hide manually
|
// -> 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) {
|
if (value_input_popup) {
|
||||||
value_input_popup->hide();
|
value_input_popup->hide();
|
||||||
}
|
}
|
||||||
//tab was pressed
|
|
||||||
} else {
|
} else {
|
||||||
//enter, click, esc
|
// Enter or Esc was pressed.
|
||||||
grab_focus();
|
grab_focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,6 +664,10 @@ bool EditorSpinSlider::is_grabbing() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorSpinSlider::_focus_entered() {
|
void EditorSpinSlider::_focus_entered() {
|
||||||
|
if (is_read_only()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_ensure_input_popup();
|
_ensure_input_popup();
|
||||||
value_input->set_text(get_text_value());
|
value_input->set_text(get_text_value());
|
||||||
value_input_popup->set_size(get_size());
|
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();
|
String tooltip = p_node->get_name();
|
||||||
|
|
||||||
if (p_node == get_scene_node() && p_node->get_scene_inherited_state().is_valid()) {
|
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());
|
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) {
|
} 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());
|
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;
|
TreeItem *item = p_node ? _find(tree->get_root(), p_node->get_path()) : nullptr;
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
|
selected = p_node;
|
||||||
if (auto_expand_selected) {
|
if (auto_expand_selected) {
|
||||||
// Make visible when it's collapsed.
|
// Make visible when it's collapsed.
|
||||||
TreeItem *node = item->get_parent();
|
TreeItem *node = item->get_parent();
|
||||||
|
@ -1009,8 +1014,24 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) {
|
||||||
}
|
}
|
||||||
item->select(0);
|
item->select(0);
|
||||||
item->set_as_cursor(0);
|
item->set_as_cursor(0);
|
||||||
selected = p_node;
|
|
||||||
tree->ensure_cursor_is_visible();
|
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 {
|
} else {
|
||||||
if (!p_node) {
|
if (!p_node) {
|
||||||
|
|
|
@ -293,18 +293,20 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
|
||||||
}
|
}
|
||||||
String from_tokens;
|
String from_tokens;
|
||||||
for (int i = 0; i < kp_tokens[0].length(); i++) {
|
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();
|
char32_t charcode = kp_tokens[0].substr(i + 2, 4).hex_to_int();
|
||||||
from_tokens += charcode;
|
from_tokens += charcode;
|
||||||
|
i += 5;
|
||||||
} else {
|
} else {
|
||||||
from_tokens += kp_tokens[0][i];
|
from_tokens += kp_tokens[0][i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String to_tokens;
|
String to_tokens;
|
||||||
for (int i = 0; i < kp_tokens[1].length(); i++) {
|
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();
|
char32_t charcode = kp_tokens[1].substr(i + 2, 4).hex_to_int();
|
||||||
to_tokens += charcode;
|
to_tokens += charcode;
|
||||||
|
i += 5;
|
||||||
} else {
|
} else {
|
||||||
to_tokens += kp_tokens[1][i];
|
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"];
|
loop_end = p_options["edit/loop_end"];
|
||||||
// Wrap around to max frames, so `-1` can be used to select the end, etc.
|
// Wrap around to max frames, so `-1` can be used to select the end, etc.
|
||||||
if (loop_begin < 0) {
|
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) {
|
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;
|
Vector<uint8_t> dst_data;
|
||||||
if (compression == 2) {
|
if (compression == 2) {
|
||||||
dst_format = AudioStreamWAV::FORMAT_QOA;
|
dst_format = AudioStreamWAV::FORMAT_QOA;
|
||||||
qoa_desc desc = { 0, 0, 0, { { { 0 }, { 0 } } } };
|
qoa_desc desc = {};
|
||||||
uint32_t qoa_len = 0;
|
uint32_t qoa_len = 0;
|
||||||
|
|
||||||
desc.samplerate = rate;
|
desc.samplerate = rate;
|
||||||
desc.samples = frames;
|
desc.samples = frames;
|
||||||
desc.channels = format_channels;
|
desc.channels = format_channels;
|
||||||
|
|
||||||
void *encoded = qoa_encode((short *)pcm_data.ptrw(), &desc, &qoa_len);
|
void *encoded = qoa_encode((short *)pcm_data.ptr(), &desc, &qoa_len);
|
||||||
dst_data.resize(qoa_len);
|
if (encoded) {
|
||||||
memcpy(dst_data.ptrw(), encoded, qoa_len);
|
dst_data.resize(qoa_len);
|
||||||
|
memcpy(dst_data.ptrw(), encoded, qoa_len);
|
||||||
|
QOA_FREE(encoded);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dst_data = pcm_data;
|
dst_data = pcm_data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -751,7 +751,9 @@ AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractPolygon2DEditorPlugin::edit(Object *p_object) {
|
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 {
|
bool AbstractPolygon2DEditorPlugin::handles(Object *p_object) const {
|
||||||
|
|
|
@ -561,7 +561,9 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int
|
||||||
return;
|
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;
|
String base_name;
|
||||||
if (anim->get_name() != "") {
|
if (anim->get_name() != "") {
|
||||||
|
|
|
@ -339,7 +339,17 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
|
||||||
|
|
||||||
track_editor->set_animation(anim, animation_is_readonly);
|
track_editor->set_animation(anim, animation_is_readonly);
|
||||||
Node *root = player->get_node_or_null(player->get_root_node());
|
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);
|
track_editor->set_root(root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1824,6 +1834,10 @@ AnimationMixer *AnimationPlayerEditor::fetch_mixer_for_library() const {
|
||||||
return original_node;
|
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 AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
|
||||||
bool is_valid = true;
|
bool is_valid = true;
|
||||||
if (!p_anim.is_valid()) {
|
if (!p_anim.is_valid()) {
|
||||||
|
|
|
@ -52,6 +52,7 @@ class AnimationPlayerEditor : public VBoxContainer {
|
||||||
AnimationPlayerEditorPlugin *plugin = nullptr;
|
AnimationPlayerEditorPlugin *plugin = nullptr;
|
||||||
AnimationMixer *original_node = nullptr; // For pinned mark in SceneTree.
|
AnimationMixer *original_node = nullptr; // For pinned mark in SceneTree.
|
||||||
AnimationPlayer *player = nullptr; // For AnimationPlayerEditor, could be dummy.
|
AnimationPlayer *player = nullptr; // For AnimationPlayerEditor, could be dummy.
|
||||||
|
ObjectID cached_root_node_id;
|
||||||
bool is_dummy = false;
|
bool is_dummy = false;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -251,6 +252,7 @@ public:
|
||||||
AnimationMixer *get_editing_node() const;
|
AnimationMixer *get_editing_node() const;
|
||||||
AnimationPlayer *get_player() const;
|
AnimationPlayer *get_player() const;
|
||||||
AnimationMixer *fetch_mixer_for_library() const;
|
AnimationMixer *fetch_mixer_for_library() const;
|
||||||
|
Node *get_cached_root_node() const;
|
||||||
|
|
||||||
static AnimationPlayerEditor *get_singleton() { return singleton; }
|
static AnimationPlayerEditor *get_singleton() { return singleton; }
|
||||||
|
|
||||||
|
|
|
@ -3999,6 +3999,8 @@ void CanvasItemEditor::_project_settings_changed() {
|
||||||
void CanvasItemEditor::_notification(int p_what) {
|
void CanvasItemEditor::_notification(int p_what) {
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case NOTIFICATION_READY: {
|
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("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));
|
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));
|
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CanvasItemEditor::_project_settings_changed));
|
||||||
|
@ -4108,7 +4110,8 @@ void CanvasItemEditor::_notification(int p_what) {
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
case NOTIFICATION_APPLICATION_FOCUS_OUT:
|
||||||
|
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
|
||||||
if (drag_type != DRAG_NONE) {
|
if (drag_type != DRAG_NONE) {
|
||||||
_reset_drag();
|
_reset_drag();
|
||||||
viewport->queue_redraw();
|
viewport->queue_redraw();
|
||||||
|
|
|
@ -81,6 +81,8 @@ void EditorNode3DGizmo::redraw() {
|
||||||
gizmo_plugin->redraw(this);
|
gizmo_plugin->redraw(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_update_bvh();
|
||||||
|
|
||||||
if (Node3DEditor::get_singleton()->is_current_selected_gizmo(this)) {
|
if (Node3DEditor::get_singleton()->is_current_selected_gizmo(this)) {
|
||||||
Node3DEditor::get_singleton()->update_transform_gizmo();
|
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);
|
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) {
|
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);
|
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());
|
colors.resize(p_handles.size());
|
||||||
Color *w = colors.ptrw();
|
Color *w = colors.ptrw();
|
||||||
for (int i = 0; i < p_handles.size(); i++) {
|
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);
|
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);
|
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) {
|
if (!is_current_hover_gizmo || current_hover_handle != id || p_secondary != current_hover_handle_secondary) {
|
||||||
col.a = 0.8;
|
col.a = 0.8;
|
||||||
}
|
}
|
||||||
|
@ -765,6 +794,10 @@ void EditorNode3DGizmo::create() {
|
||||||
instances.write[i].create_instance(spatial_node, hidden);
|
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();
|
transform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,6 +807,8 @@ void EditorNode3DGizmo::transform() {
|
||||||
for (int i = 0; i < instances.size(); i++) {
|
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);
|
RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform() * instances[i].xform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_update_bvh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorNode3DGizmo::free() {
|
void EditorNode3DGizmo::free() {
|
||||||
|
@ -790,6 +825,9 @@ void EditorNode3DGizmo::free() {
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
|
Node3DEditor::get_singleton()->remove_gizmo_bvh_node(bvh_node_id);
|
||||||
|
bvh_node_id = DynamicBVH::ID();
|
||||||
|
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#ifndef NODE_3D_EDITOR_GIZMOS_H
|
#ifndef NODE_3D_EDITOR_GIZMOS_H
|
||||||
#define 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/hash_map.h"
|
||||||
#include "core/templates/local_vector.h"
|
#include "core/templates/local_vector.h"
|
||||||
#include "scene/3d/camera_3d.h"
|
#include "scene/3d/camera_3d.h"
|
||||||
|
@ -72,8 +73,12 @@ class EditorNode3DGizmo : public Node3DGizmo {
|
||||||
Vector<Instance> instances;
|
Vector<Instance> instances;
|
||||||
Node3D *spatial_node = nullptr;
|
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 _set_node_3d(Node *p_node) { set_node_3d(Object::cast_to<Node3D>(p_node)); }
|
||||||
|
|
||||||
|
void _update_bvh();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
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 (clicked_wants_append) {
|
||||||
if (editor_selection->is_selected(selected)) {
|
if (editor_selection->is_selected(selected)) {
|
||||||
editor_selection->remove_node(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);
|
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;
|
HashSet<Ref<EditorNode3DGizmo>> found_gizmos;
|
||||||
|
|
||||||
Node *edited_scene = get_tree()->get_edited_scene_root();
|
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;
|
Node *item = nullptr;
|
||||||
float closest_dist = 1e20;
|
float closest_dist = 1e20;
|
||||||
|
|
||||||
for (int i = 0; i < instances.size(); i++) {
|
Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far());
|
||||||
Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
|
|
||||||
|
|
||||||
|
for (Node3D *spat : nodes_with_gizmos) {
|
||||||
if (!spat) {
|
if (!spat) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -863,12 +862,11 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe
|
||||||
Vector3 ray = get_ray(p_pos);
|
Vector3 ray = get_ray(p_pos);
|
||||||
Vector3 pos = get_ray_pos(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;
|
HashSet<Node3D *> found_nodes;
|
||||||
|
|
||||||
for (int i = 0; i < instances.size(); i++) {
|
for (Node3D *spat : nodes_with_gizmos) {
|
||||||
Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
|
|
||||||
|
|
||||||
if (!spat) {
|
if (!spat) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1044,7 @@ void Node3DEditorViewport::_select_region() {
|
||||||
_clear_selected();
|
_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;
|
HashSet<Node3D *> found_nodes;
|
||||||
Vector<Node *> selected;
|
Vector<Node *> selected;
|
||||||
|
|
||||||
|
@ -1055,8 +1053,7 @@ void Node3DEditorViewport::_select_region() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < instances.size(); i++) {
|
for (Node3D *sp : nodes_with_gizmos) {
|
||||||
Node3D *sp = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
|
|
||||||
if (!sp || _is_node_locked(sp)) {
|
if (!sp || _is_node_locked(sp)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1064,21 +1061,23 @@ void Node3DEditorViewport::_select_region() {
|
||||||
if (found_nodes.has(sp)) {
|
if (found_nodes.has(sp)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
found_nodes.insert(sp);
|
found_nodes.insert(sp);
|
||||||
|
|
||||||
Node *node = Object::cast_to<Node>(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.
|
// Selection requires that the node is the edited scene or its descendant, and has an owner.
|
||||||
while (node && node != edited_scene->get_parent()) {
|
if (node != edited_scene) {
|
||||||
Node *node_owner = node->get_owner();
|
if (!node->get_owner() || !edited_scene->is_ancestor_of(node)) {
|
||||||
if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
|
continue;
|
||||||
break;
|
}
|
||||||
|
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
|
// 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();
|
Node3D *selected = spatial_editor->get_single_selected_node();
|
||||||
Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr;
|
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.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)) {
|
if (_edit.gizmo.is_valid() && (k->get_keycode() == Key::ESCAPE || k->get_keycode() == Key::BACKSPACE)) {
|
||||||
// Restore.
|
// Restore.
|
||||||
_edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true);
|
_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) {
|
if (ED_IS_SHORTCUT("spatial_editor/cancel_transform", p_event) && _edit.mode != TRANSFORM_NONE) {
|
||||||
cancel_transform();
|
cancel_transform();
|
||||||
}
|
}
|
||||||
if (!is_freelook_active()) {
|
if (!is_freelook_active() && !k->is_echo()) {
|
||||||
if (ED_IS_SHORTCUT("spatial_editor/instant_translate", p_event)) {
|
if (ED_IS_SHORTCUT("spatial_editor/instant_translate", p_event) && _edit.mode != TRANSFORM_TRANSLATE) {
|
||||||
begin_transform(TRANSFORM_TRANSLATE, true);
|
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)) {
|
if (ED_IS_SHORTCUT("spatial_editor/instant_rotate", p_event) && _edit.mode != TRANSFORM_ROTATE) {
|
||||||
begin_transform(TRANSFORM_ROTATE, true);
|
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)) {
|
if (ED_IS_SHORTCUT("spatial_editor/instant_scale", p_event) && _edit.mode != TRANSFORM_SCALE) {
|
||||||
begin_transform(TRANSFORM_SCALE, true);
|
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;
|
update_preview_node = false;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NOTIFICATION_APPLICATION_FOCUS_OUT:
|
||||||
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
|
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
|
||||||
set_freelook_active(false);
|
set_freelook_active(false);
|
||||||
|
cursor.region_select = false;
|
||||||
|
surface->queue_redraw();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NOTIFICATION_ENTER_TREE: {
|
case NOTIFICATION_ENTER_TREE: {
|
||||||
|
@ -3107,6 +3129,7 @@ void Node3DEditorViewport::_notification(int p_what) {
|
||||||
case NOTIFICATION_DRAG_END: {
|
case NOTIFICATION_DRAG_END: {
|
||||||
// Clear preview material when dropped outside applicable object.
|
// Clear preview material when dropped outside applicable object.
|
||||||
if (spatial_editor->get_preview_material().is_valid() && !is_drag_successful()) {
|
if (spatial_editor->get_preview_material().is_valid() && !is_drag_successful()) {
|
||||||
|
_reset_preview_material();
|
||||||
_remove_preview_material();
|
_remove_preview_material();
|
||||||
} else {
|
} else {
|
||||||
_remove_preview_node();
|
_remove_preview_node();
|
||||||
|
@ -4001,6 +4024,14 @@ void Node3DEditorViewport::set_state(const Dictionary &p_state) {
|
||||||
_menu_option(VIEW_GIZMOS);
|
_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")) {
|
if (p_state.has("information")) {
|
||||||
bool information = p_state["information"];
|
bool information = p_state["information"];
|
||||||
|
|
||||||
|
@ -4079,6 +4110,7 @@ Dictionary Node3DEditorViewport::get_state() const {
|
||||||
d["listener"] = viewport->is_audio_listener_3d();
|
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["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["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["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["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;
|
d["half_res"] = subviewport_container->get_stretch_shrink() > 1;
|
||||||
|
@ -5001,14 +5033,24 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case TRANSFORM_ROTATE: {
|
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 local_axis;
|
||||||
Vector3 global_axis;
|
Vector3 global_axis;
|
||||||
switch (_edit.plane) {
|
switch (_edit.plane) {
|
||||||
case TRANSFORM_VIEW:
|
case TRANSFORM_VIEW:
|
||||||
// local_axis unused
|
// local_axis unused
|
||||||
global_axis = _get_camera_normal();
|
global_axis = plane.normal;
|
||||||
break;
|
break;
|
||||||
case TRANSFORM_X_AXIS:
|
case TRANSFORM_X_AXIS:
|
||||||
local_axis = Vector3(1, 0, 0);
|
local_axis = Vector3(1, 0, 0);
|
||||||
|
@ -5039,7 +5081,7 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
|
||||||
break;
|
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;
|
bool axis_is_orthogonal = ABS(plane.normal.dot(global_axis)) < orthogonal_threshold;
|
||||||
|
|
||||||
double angle = 0.0f;
|
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->set_position(pos - Vector2(sun_environ_popup->get_contents_minimum_size().width / 2, 0));
|
||||||
sun_environ_popup->reset_size();
|
sun_environ_popup->reset_size();
|
||||||
sun_environ_popup->popup();
|
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) {
|
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();
|
_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() {
|
Node3DEditorPlugin::Node3DEditorPlugin() {
|
||||||
spatial_editor = memnew(Node3DEditor);
|
spatial_editor = memnew(Node3DEditor);
|
||||||
spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#ifndef NODE_3D_EDITOR_PLUGIN_H
|
#ifndef NODE_3D_EDITOR_PLUGIN_H
|
||||||
#define 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/editor_plugin.h"
|
||||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
|
@ -629,6 +630,8 @@ private:
|
||||||
int current_hover_gizmo_handle;
|
int current_hover_gizmo_handle;
|
||||||
bool current_hover_gizmo_handle_secondary;
|
bool current_hover_gizmo_handle_secondary;
|
||||||
|
|
||||||
|
DynamicBVH gizmo_bvh;
|
||||||
|
|
||||||
real_t snap_translate_value;
|
real_t snap_translate_value;
|
||||||
real_t snap_rotate_value;
|
real_t snap_rotate_value;
|
||||||
real_t snap_scale_value;
|
real_t snap_scale_value;
|
||||||
|
@ -933,6 +936,12 @@ public:
|
||||||
void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin);
|
void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin);
|
||||||
void remove_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 edit(Node3D *p_spatial);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
|
|
@ -83,9 +83,8 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
|
||||||
|
|
||||||
int idx_last = atr_owners.size() - 1;
|
int idx_last = atr_owners.size() - 1;
|
||||||
if (idx_last > 0 && !parent_path.begins_with(atr_owners[idx_last].first)) {
|
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);
|
atr_owners.remove_at(idx_last);
|
||||||
idx_last -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property == "auto_translate_mode") {
|
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` wasn't found, that means it is set to its default value (`AUTO_TRANSLATE_MODE_INHERIT`).
|
||||||
if (!auto_translate_mode_found) {
|
if (!auto_translate_mode_found) {
|
||||||
int idx_last = atr_owners.size() - 1;
|
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;
|
auto_translating = atr_owners[idx_last].second;
|
||||||
} else {
|
} else {
|
||||||
atr_owners.push_back(Pair(state->get_node_path(i), true));
|
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);
|
const Vector2 new_point = xform.affine_inverse().xform(gpoint2);
|
||||||
curve->add_point(new_point, Vector2(0, 0), Vector2(0, 0), insertion_point + 1);
|
curve->add_point(new_point, Vector2(0, 0), Vector2(0, 0), insertion_point + 1);
|
||||||
|
|
||||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
action = ACTION_MOVING_NEW_POINT_FROM_SPLIT;
|
||||||
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_point = insertion_point + 1;
|
action_point = insertion_point + 1;
|
||||||
moving_from = curve->get_point_position(action_point);
|
moving_from = curve->get_point_position(action_point);
|
||||||
moving_screen_from = gpoint2;
|
moving_screen_from = gpoint2;
|
||||||
|
@ -245,6 +240,16 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
|
||||||
undo_redo->commit_action(false);
|
undo_redo->commit_action(false);
|
||||||
} break;
|
} 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: {
|
case ACTION_MOVING_IN: {
|
||||||
if (original_mouse_pos != gpoint) {
|
if (original_mouse_pos != gpoint) {
|
||||||
undo_redo->create_action(TTR("Move In-Control in Curve"));
|
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;
|
break;
|
||||||
|
|
||||||
case ACTION_MOVING_POINT:
|
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);
|
curve->set_point_position(action_point, cpoint);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -573,6 +579,10 @@ void Path2DEditor::_cancel_current_action() {
|
||||||
curve->remove_point(curve->get_point_count() - 1);
|
curve->remove_point(curve->get_point_count() - 1);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case ACTION_MOVING_NEW_POINT_FROM_SPLIT: {
|
||||||
|
curve->remove_point(action_point);
|
||||||
|
} break;
|
||||||
|
|
||||||
case ACTION_MOVING_IN: {
|
case ACTION_MOVING_IN: {
|
||||||
curve->set_point_in(action_point, moving_from);
|
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));
|
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_NONE,
|
||||||
ACTION_MOVING_POINT,
|
ACTION_MOVING_POINT,
|
||||||
ACTION_MOVING_NEW_POINT,
|
ACTION_MOVING_NEW_POINT,
|
||||||
|
ACTION_MOVING_NEW_POINT_FROM_SPLIT,
|
||||||
ACTION_MOVING_IN,
|
ACTION_MOVING_IN,
|
||||||
ACTION_MOVING_OUT,
|
ACTION_MOVING_OUT,
|
||||||
};
|
};
|
||||||
|
|
|
@ -181,7 +181,7 @@ void Polygon2DEditor::_sync_bones() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weights.size() == 0) { //create them
|
if (weights.size() == 0) { //create them
|
||||||
weights.resize(node->get_polygon().size());
|
weights.resize(wc);
|
||||||
float *w = weights.ptrw();
|
float *w = weights.ptrw();
|
||||||
for (int j = 0; j < wc; j++) {
|
for (int j = 0; j < wc; j++) {
|
||||||
w[j] = 0.0;
|
w[j] = 0.0;
|
||||||
|
@ -850,8 +850,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
|
||||||
if (mm.is_valid()) {
|
if (mm.is_valid()) {
|
||||||
if (uv_drag) {
|
if (uv_drag) {
|
||||||
Vector2 uv_drag_to = mm->get_position();
|
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.
|
uv_drag_to = snap_point(uv_drag_to);
|
||||||
Vector2 drag = mtx.affine_inverse().xform(uv_drag_to) - mtx.affine_inverse().xform(uv_drag_from);
|
Vector2 drag = mtx.affine_inverse().basis_xform(uv_drag_to - uv_drag_from);
|
||||||
|
|
||||||
switch (uv_move_current) {
|
switch (uv_move_current) {
|
||||||
case UV_MODE_CREATE: {
|
case UV_MODE_CREATE: {
|
||||||
|
@ -1166,12 +1166,8 @@ void Polygon2DEditor::_uv_draw() {
|
||||||
poly_line_color.a *= 0.25;
|
poly_line_color.a *= 0.25;
|
||||||
}
|
}
|
||||||
Color polygon_line_color = Color(0.5, 0.5, 0.9);
|
Color polygon_line_color = Color(0.5, 0.5, 0.9);
|
||||||
Vector<Color> polygon_fill_color;
|
Color polygon_fill_color = polygon_line_color;
|
||||||
{
|
polygon_fill_color.a *= 0.5;
|
||||||
Color pf = polygon_line_color;
|
|
||||||
pf.a *= 0.5;
|
|
||||||
polygon_fill_color.push_back(pf);
|
|
||||||
}
|
|
||||||
Color prev_color = Color(0.5, 0.5, 0.5);
|
Color prev_color = Color(0.5, 0.5, 0.5);
|
||||||
|
|
||||||
int uv_draw_max = uvs.size();
|
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));
|
uv_edit_draw->draw_line(mtx.xform(uvs[idx]), mtx.xform(uvs[idx_next]), polygon_line_color, Math::round(EDSCALE));
|
||||||
}
|
}
|
||||||
if (points.size() >= 3) {
|
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 {
|
Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
|
||||||
if (use_snap) {
|
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.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_zoom - uv_draw_ofs.y, snap_step.y * uv_draw_zoom, p_target.y);
|
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;
|
return p_target;
|
||||||
|
@ -1486,7 +1482,7 @@ Polygon2DEditor::Polygon2DEditor() {
|
||||||
|
|
||||||
grid_settings = memnew(AcceptDialog);
|
grid_settings = memnew(AcceptDialog);
|
||||||
grid_settings->set_title(TTR("Configure Grid:"));
|
grid_settings->set_title(TTR("Configure Grid:"));
|
||||||
add_child(grid_settings);
|
uv_edit->add_child(grid_settings);
|
||||||
VBoxContainer *grid_settings_vb = memnew(VBoxContainer);
|
VBoxContainer *grid_settings_vb = memnew(VBoxContainer);
|
||||||
grid_settings->add_child(grid_settings_vb);
|
grid_settings->add_child(grid_settings_vb);
|
||||||
|
|
||||||
|
|
|
@ -2711,9 +2711,11 @@ void ScriptEditor::apply_scripts() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEditor::reload_scripts(bool p_refresh_only) {
|
void ScriptEditor::reload_scripts(bool p_refresh_only) {
|
||||||
if (external_editor_active) {
|
// Call deferred to make sure it runs on the main thread.
|
||||||
return;
|
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++) {
|
for (int i = 0; i < tab_container->get_tab_count(); i++) {
|
||||||
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
|
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
|
||||||
if (!se) {
|
if (!se) {
|
||||||
|
|
|
@ -436,6 +436,7 @@ class ScriptEditor : public PanelContainer {
|
||||||
void _file_removed(const String &p_file);
|
void _file_removed(const String &p_file);
|
||||||
void _autosave_scripts();
|
void _autosave_scripts();
|
||||||
void _update_autosave_timer();
|
void _update_autosave_timer();
|
||||||
|
void _reload_scripts(bool p_refresh_only = false);
|
||||||
|
|
||||||
void _update_members_overview_visibility();
|
void _update_members_overview_visibility();
|
||||||
void _update_members_overview();
|
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++) {
|
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||||
Vector2i tile_id = tile_set_atlas_source->get_tile_id(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();
|
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++) {
|
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
|
||||||
Color color = grid_color;
|
Color color = grid_color;
|
||||||
if (frame > 0) {
|
if (frame > 0) {
|
||||||
|
|
|
@ -165,10 +165,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
|
||||||
base_control->draw_set_transform_matrix(xform);
|
base_control->draw_set_transform_matrix(xform);
|
||||||
|
|
||||||
// Draw fill rect under texture region.
|
// 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) {
|
if (tile_data) {
|
||||||
texture_rect.position -= tile_data->get_texture_origin();
|
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));
|
base_control->draw_rect(texture_rect, Color(1, 1, 1, 0.3));
|
||||||
|
|
||||||
// Draw the background.
|
// Draw the background.
|
||||||
|
@ -180,18 +184,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
|
||||||
if (tile_data->get_flip_v()) {
|
if (tile_data->get_flip_v()) {
|
||||||
region_size.y = -region_size.y;
|
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.
|
// Compute and draw the grid area.
|
||||||
Rect2 grid_area = Rect2(-base_tile_size / 2, base_tile_size);
|
Rect2 grid_area = Rect2(-base_tile_size / 2, base_tile_size);
|
||||||
if (tile_data) {
|
grid_area.expand_to(texture_rect.position);
|
||||||
grid_area.expand_to(-background_region.get_size() / 2 - tile_data->get_texture_origin());
|
grid_area.expand_to(texture_rect.get_end());
|
||||||
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);
|
|
||||||
}
|
|
||||||
base_control->draw_rect(grid_area, Color(1, 1, 1, 0.3), false);
|
base_control->draw_rect(grid_area, Color(1, 1, 1, 0.3), false);
|
||||||
|
|
||||||
// Draw grid.
|
// 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));
|
TileSetSource *source = *(tile_set->get_source(p_cell.source_id));
|
||||||
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
|
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))) {
|
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))) {
|
||||||
Transform2D tile_xform;
|
tile_set->draw_tile_shape(p_canvas_item, p_transform.scaled_local(tile_set_tile_size), color);
|
||||||
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_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2())) {
|
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;
|
Vector2i coords;
|
||||||
HashMap<Vector2i, TileMapCell> cells_undo;
|
HashMap<Vector2i, TileMapCell> cells_undo;
|
||||||
for (int i = 0; i < selection_used_cells.size(); i++) {
|
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);
|
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));
|
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.
|
// Build the list of cells to do.
|
||||||
HashMap<Vector2i, TileMapCell> cells_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;
|
icon_offset = -port_icon->get_size() * 0.5;
|
||||||
|
|
||||||
// Draw "shadow"/outline in the connection rim color.
|
// 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_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(port_icon, p_pos + icon_offset, p_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) {
|
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();
|
title_lbl->hide();
|
||||||
|
|
||||||
const Size2 size = Size2(32, 32) * EDSCALE;
|
const Size2 size = Size2(32, 32) * EDSCALE;
|
||||||
print_line("VSRerouteNode size: " + size);
|
|
||||||
|
|
||||||
Control *slot_area = memnew(Control);
|
Control *slot_area = memnew(Control);
|
||||||
slot_area->set_custom_minimum_size(size);
|
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_edit->set_text(node->get_title());
|
||||||
frame_title_change_popup->set_meta("id", p_node_id);
|
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->set_position(p_position);
|
||||||
|
frame_title_change_popup->popup();
|
||||||
|
|
||||||
// Select current text.
|
// Select current text.
|
||||||
frame_title_change_edit->grab_focus();
|
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);
|
_set_message(TTR("The path specified is invalid."), MESSAGE_ERROR, target_path_input_type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -352,7 +352,7 @@ void ProjectDialog::_install_path_changed() {
|
||||||
|
|
||||||
void ProjectDialog::_browse_project_path() {
|
void ProjectDialog::_browse_project_path() {
|
||||||
String path = project_path->get_text();
|
String path = project_path->get_text();
|
||||||
if (path.is_empty()) {
|
if (path.is_relative_path()) {
|
||||||
path = EDITOR_GET("filesystem/directories/default_project_path");
|
path = EDITOR_GET("filesystem/directories/default_project_path");
|
||||||
}
|
}
|
||||||
if (mode == MODE_IMPORT && install_path->is_visible_in_tree()) {
|
if (mode == MODE_IMPORT && install_path->is_visible_in_tree()) {
|
||||||
|
@ -382,12 +382,16 @@ void ProjectDialog::_browse_project_path() {
|
||||||
void ProjectDialog::_browse_install_path() {
|
void ProjectDialog::_browse_install_path() {
|
||||||
ERR_FAIL_COND_MSG(mode != MODE_IMPORT, "Install path is only used for MODE_IMPORT.");
|
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()) {
|
if (create_dir->is_pressed()) {
|
||||||
// Select parent directory of install path.
|
// 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 {
|
} else {
|
||||||
// Select install path.
|
// 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);
|
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_tags(item.tags, this);
|
||||||
hb->set_unsupported_features(item.unsupported_features.duplicate());
|
hb->set_unsupported_features(item.unsupported_features.duplicate());
|
||||||
hb->set_project_version(item.project_version);
|
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_favorite(item.favorite);
|
||||||
hb->set_is_missing(item.missing);
|
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: {
|
case TOOL_OPEN_DOCUMENTATION: {
|
||||||
List<Node *> selection = editor_selection->get_selected_node_list();
|
List<Node *> selection = editor_selection->get_selected_node_list();
|
||||||
for (const Node *node : selection) {
|
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);
|
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
|
||||||
} break;
|
} 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) {
|
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(p_new_parent);
|
||||||
ERR_FAIL_NULL(new_parent);
|
|
||||||
|
|
||||||
if (p_nodes.size() == 0) {
|
if (p_nodes.size() == 0) {
|
||||||
return; // Nothing to reparent.
|
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.
|
// Prevent selecting the hovered node and keep the reparented node(s) selected instead.
|
||||||
hovered_but_reparenting = true;
|
hovered_but_reparenting = true;
|
||||||
|
|
||||||
Node *validate = new_parent;
|
Node *validate = p_new_parent;
|
||||||
while (validate) {
|
while (validate) {
|
||||||
ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent.");
|
ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent.");
|
||||||
validate = validate->get_parent();
|
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.
|
// No undo implemented for this yet.
|
||||||
Node *node = p_nodes[ni];
|
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());
|
former_names.push_back(node->get_name());
|
||||||
|
|
||||||
List<Node *> owned;
|
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);
|
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) {
|
if (same_parent && node->get_index(false) < p_position_in_parent + ni) {
|
||||||
inc--; // If the child will generate a gap when moved, adjust.
|
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;
|
need_edit = select_node_hovered_at_end_of_drag;
|
||||||
} else {
|
} else {
|
||||||
undo_redo->add_do_method(node->get_parent(), "remove_child", node);
|
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;
|
int new_position_in_parent = p_position_in_parent == -1 ? -1 : p_position_in_parent + inc;
|
||||||
if (new_position_in_parent >= 0 || same_parent) {
|
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();
|
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
|
||||||
String old_name = former_names[ni];
|
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.
|
// Name was modified, fix the path renames.
|
||||||
if (old_name.casecmp_to(new_name) != 0) {
|
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);
|
// FIXME: Live editing for "Reparent to New Node" option is broken.
|
||||||
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));
|
// 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 (p_keep_global_xform) {
|
||||||
if (Object::cast_to<Node2D>(node)) {
|
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_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]);
|
undo_redo->add_undo_method(node, "set_name", former_names[ni]);
|
||||||
|
|
||||||
inc++;
|
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);
|
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(), "add_child", node, true);
|
||||||
undo_redo->add_undo_method(node->get_parent(), "move_child", node, child_pos);
|
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);
|
EditorNode::get_singleton()->hide_unused_editors(this);
|
||||||
|
|
||||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
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_method(scene_tree, "update_tree");
|
||||||
undo_redo->add_undo_reference(edited_scene);
|
undo_redo->add_undo_reference(edited_scene);
|
||||||
} else {
|
} 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) {
|
if (delete_tracks_checkbox->is_pressed() || p_cut) {
|
||||||
remove_list.sort_custom<Node::Comparator>(); // Sort nodes to keep positions.
|
remove_list.sort_custom<Node::Comparator>(); // Sort nodes to keep positions.
|
||||||
HashMap<Node *, NodePath> path_renames;
|
HashMap<Node *, NodePath> path_renames;
|
||||||
|
@ -2761,10 +2776,10 @@ void SceneTreeDock::_selection_changed() {
|
||||||
_update_script_button();
|
_update_script_button();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneTreeDock::_do_create(Node *p_parent) {
|
Node *SceneTreeDock::_do_create(Node *p_parent) {
|
||||||
Variant c = create_dialog->instantiate_selected();
|
Variant c = create_dialog->instantiate_selected();
|
||||||
Node *child = Object::cast_to<Node>(c);
|
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);
|
String new_name = p_parent->validate_child_name(child);
|
||||||
if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
|
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) {
|
if (edited_scene) {
|
||||||
undo_redo->add_do_method(p_parent, "add_child", child, true);
|
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(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_do_reference(child);
|
||||||
undo_redo->add_undo_method(p_parent, "remove_child", 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_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
undo_redo->add_do_method(this, "_post_do_create", child);
|
||||||
undo_redo->commit_action();
|
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) {
|
if (ms.width < 4) {
|
||||||
ms.width = 40;
|
ms.width = 40;
|
||||||
}
|
}
|
||||||
if (ms.height < 4) {
|
if (ms.height < 4) {
|
||||||
ms.height = 40;
|
ms.height = 40;
|
||||||
}
|
}
|
||||||
if (ct->is_layout_rtl()) {
|
if (control->is_layout_rtl()) {
|
||||||
ct->set_position(ct->get_position() - Vector2(ms.x, 0));
|
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() {
|
void SceneTreeDock::_create() {
|
||||||
|
@ -2898,22 +2917,24 @@ void SceneTreeDock::_create() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *parent = nullptr;
|
Node *parent = nullptr;
|
||||||
|
int original_position = -1;
|
||||||
if (only_one_top_node) {
|
if (only_one_top_node) {
|
||||||
parent = top_node->get_parent();
|
parent = top_node->get_parent();
|
||||||
|
original_position = top_node->get_index(false);
|
||||||
} else {
|
} else {
|
||||||
parent = top_node->get_parent()->get_parent();
|
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;
|
Vector<Node *> nodes;
|
||||||
for (Node *E : selection) {
|
for (Node *E : selection) {
|
||||||
nodes.push_back(E);
|
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) {
|
if (center_parent) {
|
||||||
// Find parent type and only average positions of relevant nodes.
|
// Find parent type and only average positions of relevant nodes.
|
||||||
Node3D *parent_node_3d = Object::cast_to<Node3D>(last_created);
|
Node3D *parent_node_3d = Object::cast_to<Node3D>(last_created);
|
||||||
|
@ -2952,6 +2973,11 @@ void SceneTreeDock::_create() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_do_reparent(last_created, -1, nodes, true);
|
_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();
|
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() {
|
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("_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_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);
|
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;
|
bool first_enter = true;
|
||||||
|
|
||||||
void _create();
|
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 *scene_root = nullptr;
|
||||||
Node *edited_scene = nullptr;
|
Node *edited_scene = nullptr;
|
||||||
Node *pending_click_select = 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_highlight", "VScrollBar", empty_icon);
|
||||||
p_theme->set_icon("decrement_pressed", "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.
|
// HSlider.
|
||||||
p_theme->set_icon("grabber_highlight", "HSlider", p_theme->get_icon(SNAME("GuiSliderGrabberHl"), EditorStringName(EditorIcons)));
|
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_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("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, 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, 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, p_config.base_margin / 2, 0, p_config.base_margin / 2));
|
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("center_grabber", "HSlider", 0);
|
||||||
p_theme->set_constant("grabber_offset", "HSlider", 0);
|
p_theme->set_constant("grabber_offset", "HSlider", 0);
|
||||||
|
|
||||||
// VSlider.
|
// VSlider.
|
||||||
p_theme->set_icon("grabber", "VSlider", p_theme->get_icon(SNAME("GuiSliderGrabber"), EditorStringName(EditorIcons)));
|
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_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("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, 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, 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, p_config.base_margin / 2, 0, p_config.base_margin / 2, 0));
|
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("center_grabber", "VSlider", 0);
|
||||||
p_theme->set_constant("grabber_offset", "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_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("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.
|
// Editor focus.
|
||||||
p_theme->set_stylebox("Focus", EditorStringName(EditorStyles), p_config.button_style_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(arg);
|
||||||
main_args.push_back(N->get());
|
main_args.push_back(N->get());
|
||||||
N = N->next();
|
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 {
|
} else {
|
||||||
OS::get_singleton()->print("Missing relative or absolute path to project for --gdscript-docs, aborting.\n");
|
OS::get_singleton()->print("Missing relative or absolute path to project for --gdscript-docs, aborting.\n");
|
||||||
goto error;
|
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");
|
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
|
||||||
|
|
||||||
if (p_second_phase) {
|
if (p_second_phase) {
|
||||||
return setup2();
|
exit_err = setup2();
|
||||||
|
if (exit_err != OK) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -2763,6 +2769,30 @@ Error Main::setup2(bool p_show_boot_logo) {
|
||||||
|
|
||||||
if (err != OK || display_server == nullptr) {
|
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).");
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3062,6 +3092,14 @@ Error Main::setup2(bool p_show_boot_logo) {
|
||||||
OS::get_singleton()->benchmark_end_measure("Scene", "Modules and Extensions");
|
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");
|
OS::get_singleton()->benchmark_end_measure("Startup", "Scene");
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
@ -3333,13 +3371,16 @@ int Main::start() {
|
||||||
gdscript_docs_path = E->next()->get();
|
gdscript_docs_path = E->next()->get();
|
||||||
#endif
|
#endif
|
||||||
} else if (E->get() == "--export-release") {
|
} 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
|
editor = true; //needs editor
|
||||||
_export_preset = E->next()->get();
|
_export_preset = E->next()->get();
|
||||||
} else if (E->get() == "--export-debug") {
|
} 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
|
editor = true; //needs editor
|
||||||
_export_preset = E->next()->get();
|
_export_preset = E->next()->get();
|
||||||
export_debug = true;
|
export_debug = true;
|
||||||
} else if (E->get() == "--export-pack") {
|
} 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;
|
editor = true;
|
||||||
_export_preset = E->next()->get();
|
_export_preset = E->next()->get();
|
||||||
export_pack_only = true;
|
export_pack_only = true;
|
||||||
|
@ -3351,6 +3392,8 @@ int Main::start() {
|
||||||
if (parsed_pair) {
|
if (parsed_pair) {
|
||||||
E = E->next();
|
E = E->next();
|
||||||
}
|
}
|
||||||
|
} else if (E->get().begins_with("--export-")) {
|
||||||
|
ERR_FAIL_V_MSG(EXIT_FAILURE, "Missing export preset name, aborting.");
|
||||||
}
|
}
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
// Handle case where no path is given to --doctool.
|
// Handle case where no path is given to --doctool.
|
||||||
|
@ -4189,7 +4232,7 @@ bool Main::iteration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#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;
|
exit = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
100
methods.py
100
methods.py
|
@ -905,21 +905,18 @@ def show_progress(env):
|
||||||
node_count_fname = str(env.Dir("#")) + "/.scons_node_count"
|
node_count_fname = str(env.Dir("#")) + "/.scons_node_count"
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import time
|
|
||||||
|
|
||||||
class cache_progress:
|
class cache_progress:
|
||||||
# The default is 1 GB cache and 12 hours half life
|
# The default is 1 GB cache
|
||||||
def __init__(self, path=None, limit=1073741824, half_life=43200):
|
def __init__(self, path=None, limit=pow(1024, 3)):
|
||||||
self.path = path
|
self.path = path
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
self.exponent_scale = math.log(2) / half_life
|
|
||||||
if env["verbose"] and path is not None:
|
if env["verbose"] and path is not None:
|
||||||
screen.write(
|
screen.write(
|
||||||
"Current cache limit is {} (used: {})\n".format(
|
"Current cache limit is {} (used: {})\n".format(
|
||||||
self.convert_size(limit), self.convert_size(self.get_size(path))
|
self.convert_size(limit), self.convert_size(self.get_size(path))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.delete(self.file_list())
|
|
||||||
|
|
||||||
def __call__(self, node, *args, **kw):
|
def __call__(self, node, *args, **kw):
|
||||||
nonlocal node_count, node_count_max, node_count_interval, node_count_fname, show_progress
|
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.write("\r[Initial build] ")
|
||||||
screen.flush()
|
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):
|
def delete(self, files):
|
||||||
if len(files) == 0:
|
if len(files) == 0:
|
||||||
return
|
return
|
||||||
if env["verbose"]:
|
if env["verbose"]:
|
||||||
# Utter something
|
# 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]
|
[os.remove(f) for f in files]
|
||||||
|
|
||||||
def file_list(self):
|
def file_list(self):
|
||||||
|
@ -975,47 +1026,20 @@ def show_progress(env):
|
||||||
else:
|
else:
|
||||||
return [x[0] for x in file_stat[mark:]]
|
return [x[0] for x in file_stat[mark:]]
|
||||||
|
|
||||||
def convert_size(self, size_bytes):
|
def cache_finally():
|
||||||
if size_bytes == 0:
|
nonlocal cleaner
|
||||||
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:
|
try:
|
||||||
with open(node_count_fname, "w", encoding="utf-8", newline="\n") as f:
|
cleaner.clean()
|
||||||
f.write("%d\n" % node_count)
|
|
||||||
progressor.delete(progressor.file_list())
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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")
|
cache_directory = os.environ.get("SCONS_CACHE")
|
||||||
# Simple cache pruning, attached to SCons' progress callback. Trim the
|
# Simple cache pruning, attached to SCons' progress callback. Trim the
|
||||||
# cache directory to a size not larger than cache_limit.
|
# cache directory to a size not larger than cache_limit.
|
||||||
cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024
|
cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024
|
||||||
progressor = cache_progress(cache_directory, cache_limit)
|
cleaner = cache_clean(cache_directory, cache_limit)
|
||||||
Progress(progressor, interval=node_count_interval)
|
|
||||||
|
|
||||||
progress_finish_command = Command("progress_finish", [], progress_finish)
|
atexit.register(cache_finally)
|
||||||
AlwaysBuild(progress_finish_command)
|
|
||||||
|
|
||||||
|
|
||||||
def dump(env):
|
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);
|
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);
|
p_call->set_datatype(call_type);
|
||||||
return;
|
return;
|
||||||
} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
|
} 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);
|
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
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
call_type = return_type;
|
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(identifier_map.size(), &contents.write[0]);
|
||||||
encode_uint32(constant_map.size(), &contents.write[4]);
|
encode_uint32(constant_map.size(), &contents.write[4]);
|
||||||
encode_uint32(token_lines.size(), &contents.write[8]);
|
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]);
|
encode_uint32(token_counter, &contents.write[16]);
|
||||||
|
|
||||||
int buf_pos = 20;
|
int buf_pos = 20;
|
||||||
|
|
|
@ -229,19 +229,6 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
|
||||||
arr[i] = item.to_json();
|
arr[i] = item.to_json();
|
||||||
i++;
|
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;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -958,28 +958,30 @@ struct CompletionItem {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string that should be used when comparing this item
|
* 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;
|
String sortText;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string that should be used when filtering a set of
|
* 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;
|
String filterText;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string that should be inserted into a document when selecting
|
* 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.
|
* The `insertText` is subject to interpretation by the client side.
|
||||||
* Some tools might not take the string literally. For example
|
* Some tools might not take the string literally. For example
|
||||||
* VS Code when code complete is requested in this example `con<cursor position>`
|
* VS Code when code complete is requested in this example
|
||||||
* and a completion item with an `insertText` of `console` is provided it
|
* `con<cursor position>` and a completion item with an `insertText` of
|
||||||
* will only insert `sole`. Therefore it is recommended to use `textEdit` instead
|
* `console` is provided it will only insert `sole`. Therefore it is
|
||||||
* since it avoids additional client side interpretation.
|
* recommended to use `textEdit` instead since it avoids additional client
|
||||||
*
|
* side interpretation.
|
||||||
* @deprecated Use textEdit instead.
|
|
||||||
*/
|
*/
|
||||||
String insertText;
|
String insertText;
|
||||||
|
|
||||||
|
@ -1034,14 +1036,20 @@ struct CompletionItem {
|
||||||
dict["label"] = label;
|
dict["label"] = label;
|
||||||
dict["kind"] = kind;
|
dict["kind"] = kind;
|
||||||
dict["data"] = data;
|
dict["data"] = data;
|
||||||
dict["insertText"] = insertText;
|
if (!insertText.is_empty()) {
|
||||||
|
dict["insertText"] = insertText;
|
||||||
|
}
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
dict["detail"] = detail;
|
dict["detail"] = detail;
|
||||||
dict["documentation"] = documentation.to_json();
|
dict["documentation"] = documentation.to_json();
|
||||||
dict["deprecated"] = deprecated;
|
dict["deprecated"] = deprecated;
|
||||||
dict["preselect"] = preselect;
|
dict["preselect"] = preselect;
|
||||||
dict["sortText"] = sortText;
|
if (!sortText.is_empty()) {
|
||||||
dict["filterText"] = filterText;
|
dict["sortText"] = sortText;
|
||||||
|
}
|
||||||
|
if (!filterText.is_empty()) {
|
||||||
|
dict["filterText"] = filterText;
|
||||||
|
}
|
||||||
if (commitCharacters.size()) {
|
if (commitCharacters.size()) {
|
||||||
dict["commitCharacters"] = commitCharacters;
|
dict["commitCharacters"] = commitCharacters;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,29 @@
|
||||||
signal s1()
|
# Doesn't produce the warning:
|
||||||
signal s2()
|
signal used_as_first_class_signal()
|
||||||
signal s3()
|
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")
|
@warning_ignore("unused_signal")
|
||||||
signal s4()
|
signal unused_but_ignored()
|
||||||
|
|
||||||
func no_exec():
|
func no_exec():
|
||||||
s1.emit()
|
print(used_as_first_class_signal)
|
||||||
print(s2)
|
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():
|
func test():
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
GDTEST_OK
|
GDTEST_OK
|
||||||
>> WARNING
|
>> WARNING
|
||||||
>> Line: 3
|
>> Line: 11
|
||||||
>> UNUSED_SIGNAL
|
>> 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) {
|
List<String> *r_missing_deps, Error *r_err) {
|
||||||
String blender_path = EDITOR_GET("filesystem/import/blender/blender_path");
|
String blender_path = EDITOR_GET("filesystem/import/blender/blender_path");
|
||||||
|
|
||||||
if (blender_major_version == -1 || blender_minor_version == -1) {
|
ERR_FAIL_COND_V_MSG(blender_path.is_empty(), nullptr, "Blender path is empty, check your Editor Settings.");
|
||||||
_get_blender_version(blender_path, blender_major_version, blender_minor_version, nullptr);
|
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.
|
// Get global paths for source and sink.
|
||||||
|
|
|
@ -45,6 +45,7 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter {
|
||||||
|
|
||||||
int blender_major_version = -1;
|
int blender_major_version = -1;
|
||||||
int blender_minor_version = -1;
|
int blender_minor_version = -1;
|
||||||
|
String last_tested_blender_path;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -5658,6 +5658,15 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
|
||||||
if (p_scene_root == nullptr) {
|
if (p_scene_root == nullptr) {
|
||||||
// If the root node argument is null, this is the root node.
|
// If the root node argument is null, this is the root node.
|
||||||
p_scene_root = current_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 {
|
} else {
|
||||||
// Add the node we generated and set the owner to the scene root.
|
// Add the node we generated and set the owner to the scene root.
|
||||||
p_scene_parent->add_child(current_node, true);
|
p_scene_parent->add_child(current_node, true);
|
||||||
|
|
|
@ -643,6 +643,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
|
||||||
_do_paste();
|
_do_paste();
|
||||||
input_action = INPUT_NONE;
|
input_action = INPUT_NONE;
|
||||||
_update_paste_indicator();
|
_update_paste_indicator();
|
||||||
|
return EditorPlugin::AFTER_GUI_INPUT_STOP;
|
||||||
} else if (mb->is_shift_pressed() && can_edit) {
|
} else if (mb->is_shift_pressed() && can_edit) {
|
||||||
input_action = INPUT_SELECT;
|
input_action = INPUT_SELECT;
|
||||||
last_selection = selection;
|
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) {
|
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;
|
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;
|
auto_advance_to = next_clip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -905,7 +905,9 @@ void AudioStreamPlaybackInteractive::_mix_internal_state(int p_state_idx, int p_
|
||||||
// time to start!
|
// time to start!
|
||||||
from_frame = state.fade_wait * mix_rate;
|
from_frame = state.fade_wait * mix_rate;
|
||||||
state.fade_wait = 0;
|
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;
|
playback_current = p_state_idx;
|
||||||
state.first_mix = false;
|
state.first_mix = false;
|
||||||
} else {
|
} 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);
|
state.playback->mix(temp_buffer + from_frame, 1.0, p_frames - from_frame);
|
||||||
|
|
||||||
double frame_fade_inc = state.fade_speed * frame_inc;
|
double frame_fade_inc = state.fade_speed * frame_inc;
|
||||||
|
|
||||||
for (int i = from_frame; i < p_frames; i++) {
|
for (int i = from_frame; i < p_frames; i++) {
|
||||||
if (state.fade_wait) {
|
if (state.fade_wait) {
|
||||||
// This is for fade out of existing stream;
|
// 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;
|
state.fade_speed = 0.0;
|
||||||
frame_fade_inc = 0.0;
|
frame_fade_inc = 0.0;
|
||||||
state.fade_volume = 1.0;
|
state.fade_volume = 1.0;
|
||||||
|
queue_next = state.auto_advance;
|
||||||
}
|
}
|
||||||
} else if (frame_fade_inc < 0.0) {
|
} else if (frame_fade_inc < 0.0) {
|
||||||
state.fade_volume += frame_fade_inc;
|
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