summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct16
-rw-r--r--core/input/input_event.cpp2
-rw-r--r--core/math/vector4.cpp94
-rw-r--r--core/math/vector4.h25
-rw-r--r--core/register_core_types.cpp1
-rw-r--r--core/variant/variant_call.cpp6
-rw-r--r--core/variant/variant_utility.cpp154
-rw-r--r--doc/classes/@GlobalScope.xml95
-rw-r--r--doc/classes/HTTPClient.xml3
-rw-r--r--doc/classes/Light3D.xml18
-rw-r--r--doc/classes/RenderingServer.xml12
-rw-r--r--doc/classes/Vector4.xml45
-rw-r--r--doc/classes/VisualShaderNodeVectorRefract.xml4
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h2
-rw-r--r--drivers/gles3/shaders/scene.glsl2
-rw-r--r--drivers/gles3/storage/light_storage.cpp1
-rw-r--r--editor/editor_properties.cpp42
-rw-r--r--editor/editor_properties.h2
-rw-r--r--editor/editor_resource_picker.cpp43
-rw-r--r--editor/editor_resource_picker.h1
-rw-r--r--editor/filesystem_dock.cpp3
-rw-r--r--editor/import/resource_importer_layered_texture.cpp6
-rw-r--r--editor/import/resource_importer_layered_texture.h2
-rw-r--r--editor/import/resource_importer_texture.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp4
-rw-r--r--editor/project_manager.cpp253
-rw-r--r--editor/project_manager.h2
-rw-r--r--methods.py46
-rw-r--r--modules/gdscript/config.py1
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs2
-rw-r--r--modules/msdfgen/config.py3
-rw-r--r--modules/multiplayer/editor/replication_editor_plugin.cpp112
-rw-r--r--modules/multiplayer/editor/replication_editor_plugin.h18
-rw-r--r--modules/text_server_adv/SCsub4
-rw-r--r--modules/text_server_fb/SCsub4
-rw-r--r--modules/theora/config.py3
-rw-r--r--modules/vorbis/config.py3
-rw-r--r--modules/websocket/emws_server.cpp92
-rw-r--r--modules/websocket/emws_server.h64
-rw-r--r--modules/websocket/register_types.cpp5
-rw-r--r--scene/3d/light_3d.cpp5
-rw-r--r--scene/3d/light_3d.h1
-rw-r--r--scene/resources/visual_shader_nodes.cpp47
-rw-r--r--scene/resources/visual_shader_nodes.h7
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp21
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h4
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/light_data_inc.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl10
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.cpp1
-rw-r--r--servers/rendering/shader_language.cpp4
-rw-r--r--servers/rendering_server.cpp1
-rw-r--r--servers/rendering_server.h1
-rw-r--r--tests/core/math/test_vector4.h312
-rw-r--r--tests/core/math/test_vector4i.h148
-rw-r--r--tests/test_main.cpp2
63 files changed, 1215 insertions, 590 deletions
diff --git a/SConstruct b/SConstruct
index 17184c2e36..d0d03bd8dc 100644
--- a/SConstruct
+++ b/SConstruct
@@ -138,6 +138,7 @@ env_base.__class__.CommandNoCache = methods.CommandNoCache
env_base.__class__.Run = methods.Run
env_base.__class__.disable_warnings = methods.disable_warnings
env_base.__class__.force_optimization_on_debug = methods.force_optimization_on_debug
+env_base.__class__.module_add_dependencies = methods.module_add_dependencies
env_base.__class__.module_check_dependencies = methods.module_check_dependencies
env_base["x86_libtheora_opt_gcc"] = False
@@ -705,6 +706,7 @@ if selected_platform in platform_list:
sys.modules.pop("detect")
modules_enabled = OrderedDict()
+ env.module_dependencies = {}
env.module_icons_paths = []
env.doc_class_path = {}
@@ -716,6 +718,10 @@ if selected_platform in platform_list:
import config
if config.can_build(env, selected_platform):
+ # Disable it if a required dependency is missing.
+ if not env.module_check_dependencies(name):
+ continue
+
config.configure(env)
# Get doc classes paths (if present)
try:
@@ -738,6 +744,7 @@ if selected_platform in platform_list:
sys.modules.pop("config")
env.module_list = modules_enabled
+ methods.sort_module_list(env)
methods.update_version(env.module_version_string)
@@ -800,15 +807,6 @@ if selected_platform in platform_list:
if env["minizip"]:
env.Append(CPPDEFINES=["MINIZIP_ENABLED"])
- editor_module_list = []
- if env["tools"] and not env.module_check_dependencies("tools", editor_module_list):
- print(
- "Build option 'module_"
- + x
- + "_enabled=no' cannot be used with 'tools=yes' (editor), only with 'tools=no' (export template)."
- )
- Exit(255)
-
if not env["verbose"]:
methods.no_verbose(sys, env)
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index 3c104c2c86..8ad2193fca 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -816,7 +816,7 @@ String InputEventMouseMotion::to_string() {
// Work around the fact vformat can only take 5 substitutions but 7 need to be passed.
String mask_and_position_and_relative = vformat("button_mask=%s, position=(%s), relative=(%s)", button_mask_string, String(get_position()), String(get_relative()));
- return vformat("InputEventMouseMotion: %s, velocity=(%s), pressure=%.2f, tilt=(%s), pen_inverted=(%d)", mask_and_position_and_relative, String(get_velocity()), get_pressure(), String(get_tilt()), get_pen_inverted());
+ return vformat("InputEventMouseMotion: %s, velocity=(%s), pressure=%.2f, tilt=(%s), pen_inverted=(%s)", mask_and_position_and_relative, String(get_velocity()), get_pressure(), String(get_tilt()), get_pen_inverted());
}
bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) {
diff --git a/core/math/vector4.cpp b/core/math/vector4.cpp
index cc0d0dcf72..4697c311b4 100644
--- a/core/math/vector4.cpp
+++ b/core/math/vector4.cpp
@@ -33,6 +33,40 @@
#include "core/math/basis.h"
#include "core/string/print_string.h"
+void Vector4::set_axis(const int p_axis, const real_t p_value) {
+ ERR_FAIL_INDEX(p_axis, 4);
+ components[p_axis] = p_value;
+}
+
+real_t Vector4::get_axis(const int p_axis) const {
+ ERR_FAIL_INDEX_V(p_axis, 4, 0);
+ return operator[](p_axis);
+}
+
+Vector4::Axis Vector4::min_axis_index() const {
+ uint32_t min_index = 0;
+ real_t min_value = x;
+ for (uint32_t i = 1; i < 4; i++) {
+ if (operator[](i) <= min_value) {
+ min_index = i;
+ min_value = operator[](i);
+ }
+ }
+ return Vector4::Axis(min_index);
+}
+
+Vector4::Axis Vector4::max_axis_index() const {
+ uint32_t max_index = 0;
+ real_t max_value = x;
+ for (uint32_t i = 1; i < 4; i++) {
+ if (operator[](i) > max_value) {
+ max_index = i;
+ max_value = operator[](i);
+ }
+ }
+ return Vector4::Axis(max_index);
+}
+
bool Vector4::is_equal_approx(const Vector4 &p_vec4) const {
return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w);
}
@@ -53,6 +87,16 @@ bool Vector4::is_normalized() const {
return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); // Use less epsilon.
}
+real_t Vector4::distance_to(const Vector4 &p_to) const {
+ return (p_to - *this).length();
+}
+
+Vector4 Vector4::direction_to(const Vector4 &p_to) const {
+ Vector4 ret(p_to.x - x, p_to.y - y, p_to.z - z, p_to.w - w);
+ ret.normalize();
+ return ret;
+}
+
Vector4 Vector4::abs() const {
return Vector4(Math::abs(x), Math::abs(y), Math::abs(z), Math::abs(w));
}
@@ -81,32 +125,38 @@ Vector4 Vector4::lerp(const Vector4 &p_to, const real_t p_weight) const {
w + (p_weight * (p_to.w - w)));
}
-Vector4 Vector4::inverse() const {
- return Vector4(1.0f / x, 1.0f / y, 1.0f / z, 1.0f / w);
+Vector4 Vector4::cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight) const {
+ Vector4 res = *this;
+ res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
+ res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
+ res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight);
+ res.w = Math::cubic_interpolate(res.w, p_b.w, p_pre_a.w, p_post_b.w, p_weight);
+ return res;
}
-Vector4::Axis Vector4::min_axis_index() const {
- uint32_t min_index = 0;
- real_t min_value = x;
- for (uint32_t i = 1; i < 4; i++) {
- if (operator[](i) <= min_value) {
- min_index = i;
- min_value = operator[](i);
- }
- }
- return Vector4::Axis(min_index);
+Vector4 Vector4::posmod(const real_t p_mod) const {
+ return Vector4(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod), Math::fposmod(z, p_mod), Math::fposmod(w, p_mod));
}
-Vector4::Axis Vector4::max_axis_index() const {
- uint32_t max_index = 0;
- real_t max_value = x;
- for (uint32_t i = 1; i < 4; i++) {
- if (operator[](i) > max_value) {
- max_index = i;
- max_value = operator[](i);
- }
- }
- return Vector4::Axis(max_index);
+Vector4 Vector4::posmodv(const Vector4 &p_modv) const {
+ return Vector4(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y), Math::fposmod(z, p_modv.z), Math::fposmod(w, p_modv.w));
+}
+
+void Vector4::snap(const Vector4 &p_step) {
+ x = Math::snapped(x, p_step.x);
+ y = Math::snapped(y, p_step.y);
+ z = Math::snapped(z, p_step.z);
+ w = Math::snapped(w, p_step.w);
+}
+
+Vector4 Vector4::snapped(const Vector4 &p_step) const {
+ Vector4 v = *this;
+ v.snap(p_step);
+ return v;
+}
+
+Vector4 Vector4::inverse() const {
+ return Vector4(1.0f / x, 1.0f / y, 1.0f / z, 1.0f / w);
}
Vector4 Vector4::clamp(const Vector4 &p_min, const Vector4 &p_max) const {
diff --git a/core/math/vector4.h b/core/math/vector4.h
index 37ddb509d6..373a6a1218 100644
--- a/core/math/vector4.h
+++ b/core/math/vector4.h
@@ -62,6 +62,15 @@ struct _NO_DISCARD_ Vector4 {
DEV_ASSERT((unsigned int)p_axis < 4);
return components[p_axis];
}
+
+ _FORCE_INLINE_ void set_all(const real_t p_value);
+
+ void set_axis(const int p_axis, const real_t p_value);
+ real_t get_axis(const int p_axis) const;
+
+ Vector4::Axis min_axis_index() const;
+ Vector4::Axis max_axis_index() const;
+
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Vector4 &p_vec4) const;
real_t length() const;
@@ -69,15 +78,21 @@ struct _NO_DISCARD_ Vector4 {
Vector4 normalized() const;
bool is_normalized() const;
+ real_t distance_to(const Vector4 &p_to) const;
+ Vector4 direction_to(const Vector4 &p_to) const;
+
Vector4 abs() const;
Vector4 sign() const;
Vector4 floor() const;
Vector4 ceil() const;
Vector4 round() const;
Vector4 lerp(const Vector4 &p_to, const real_t p_weight) const;
+ Vector4 cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight) const;
- Vector4::Axis min_axis_index() const;
- Vector4::Axis max_axis_index() const;
+ Vector4 posmod(const real_t p_mod) const;
+ Vector4 posmodv(const Vector4 &p_modv) const;
+ void snap(const Vector4 &p_step);
+ Vector4 snapped(const Vector4 &p_step) const;
Vector4 clamp(const Vector4 &p_min, const Vector4 &p_max) const;
Vector4 inverse() const;
@@ -130,6 +145,10 @@ struct _NO_DISCARD_ Vector4 {
}
};
+void Vector4::set_all(const real_t p_value) {
+ x = y = z = p_value;
+}
+
real_t Vector4::dot(const Vector4 &p_vec4) const {
return x * p_vec4.x + y * p_vec4.y + z * p_vec4.z + w * p_vec4.w;
}
@@ -193,7 +212,7 @@ Vector4 Vector4::operator/(const Vector4 &p_vec4) const {
}
Vector4 Vector4::operator-() const {
- return Vector4(x, y, z, w);
+ return Vector4(-x, -y, -z, -w);
}
Vector4 Vector4::operator*(const real_t &s) const {
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 1ae5d1c83f..9ad6b0ca68 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -269,6 +269,7 @@ void register_core_types() {
_marshalls = memnew(core_bind::Marshalls);
_engine_debugger = memnew(core_bind::EngineDebugger);
+ GDREGISTER_NATIVE_STRUCT(ObjectID, "uint64_t id = 0");
GDREGISTER_NATIVE_STRUCT(AudioFrame, "float left;float right");
GDREGISTER_NATIVE_STRUCT(ScriptLanguageExtensionProfilingInfo, "StringName signature;uint64_t call_count;uint64_t total_time;uint64_t self_time");
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index d1f1b83457..5c298f9b3b 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1737,9 +1737,15 @@ static void _register_variant_builtin_methods() {
bind_method(Vector4, ceil, sarray(), varray());
bind_method(Vector4, round, sarray(), varray());
bind_method(Vector4, lerp, sarray("to", "weight"), varray());
+ bind_method(Vector4, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray());
+ bind_method(Vector4, posmod, sarray("mod"), varray());
+ bind_method(Vector4, posmodv, sarray("modv"), varray());
+ bind_method(Vector4, snapped, sarray("step"), varray());
bind_method(Vector4, clamp, sarray("min", "max"), varray());
bind_method(Vector4, normalized, sarray(), varray());
bind_method(Vector4, is_normalized, sarray(), varray());
+ bind_method(Vector4, direction_to, sarray("to"), varray());
+ bind_method(Vector4, distance_to, sarray("to"), varray());
bind_method(Vector4, dot, sarray("with"), varray());
bind_method(Vector4, inverse, sarray(), varray());
bind_method(Vector4, is_equal_approx, sarray("with"), varray());
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index c1a0ad73b0..1f1439ab24 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -99,18 +99,105 @@ struct VariantUtilityFunctions {
return Math::posmod(b, r);
}
- static inline double floor(double x) {
+ static inline Variant floor(Variant x, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ switch (x.get_type()) {
+ case Variant::INT: {
+ return VariantInternalAccessor<int64_t>::get(&x);
+ } break;
+ case Variant::FLOAT: {
+ return Math::floor(VariantInternalAccessor<double>::get(&x));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).floor();
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).floor();
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).floor();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+ }
+ }
+
+ static inline double floorf(double x) {
return Math::floor(x);
}
- static inline double ceil(double x) {
+ static inline int floori(double x) {
+ return int(x);
+ }
+
+ static inline Variant ceil(Variant x, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ switch (x.get_type()) {
+ case Variant::INT: {
+ return VariantInternalAccessor<int64_t>::get(&x);
+ } break;
+ case Variant::FLOAT: {
+ return Math::ceil(VariantInternalAccessor<double>::get(&x));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).ceil();
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).ceil();
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).ceil();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+ }
+ }
+
+ static inline double ceilf(double x) {
return Math::ceil(x);
}
- static inline double round(double x) {
+ static inline int ceili(double x) {
+ return int(Math::ceil(x));
+ }
+
+ static inline Variant round(Variant x, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ switch (x.get_type()) {
+ case Variant::INT: {
+ return VariantInternalAccessor<int64_t>::get(&x);
+ } break;
+ case Variant::FLOAT: {
+ return Math::round(VariantInternalAccessor<double>::get(&x));
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&x).round();
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&x).round();
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&x).round();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+ }
+ }
+
+ static inline double roundf(double x) {
return Math::round(x);
}
+ static inline int roundi(double x) {
+ return int(Math::round(x));
+ }
+
static inline Variant abs(const Variant &x, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;
switch (x.get_type()) {
@@ -235,7 +322,44 @@ struct VariantUtilityFunctions {
return Math::snapped(value, step);
}
- static inline double lerp(double from, double to, double weight) {
+ static inline Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+ if (from.get_type() != to.get_type()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ return Variant();
+ }
+
+ switch (from.get_type()) {
+ case Variant::FLOAT: {
+ return lerpf(VariantInternalAccessor<double>::get(&from), to, weight);
+ } break;
+ case Variant::VECTOR2: {
+ return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight);
+ } break;
+ case Variant::VECTOR3: {
+ return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight);
+ } break;
+ case Variant::VECTOR4: {
+ return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight);
+ } break;
+ case Variant::QUATERNION: {
+ return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight);
+ } break;
+ case Variant::BASIS: {
+ return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight);
+ } break;
+ case Variant::COLOR: {
+ return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight);
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+ }
+ }
+
+ static inline double lerpf(double from, double to, double weight) {
return Math::lerp(from, to, weight);
}
@@ -1252,17 +1376,24 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(fmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(fposmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(posmod, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDR(floor, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDR(ceil, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDR(round, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDVR(abs, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDVR(floor, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(floorf, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(floori, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+
+ FUNCBINDVR(ceil, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(ceilf, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(ceili, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDVR(round, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(roundf, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(roundi, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
+
+ FUNCBINDVR(abs, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(absf, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(absi, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDVR(sign, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
-
FUNCBINDR(signf, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(signi, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
@@ -1280,7 +1411,8 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(step_decimals, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(snapped, sarray("x", "step"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDR(lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDVR3(lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(lerpf, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(cubic_interpolate, sarray("from", "to", "pre", "post", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(bezier_interpolate, sarray("start", "control_1", "control_2", "end", "t"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(lerp_angle, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
@@ -1300,12 +1432,10 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(wrapf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDVARARG(max, sarray(), Variant::UTILITY_FUNC_TYPE_MATH);
-
FUNCBINDR(maxi, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(maxf, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDVARARG(min, sarray(), Variant::UTILITY_FUNC_TYPE_MATH);
-
FUNCBINDR(mini, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(minf, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_MATH);
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 4a16244235..658a13d05b 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -134,15 +134,32 @@
</description>
</method>
<method name="ceil">
- <return type="float" />
- <argument index="0" name="x" type="float" />
+ <return type="Variant" />
+ <argument index="0" name="x" type="Variant" />
<description>
- Rounds [code]x[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]x[/code].
+ Rounds [code]x[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]x[/code]. Supported types: [int], [float], [Vector2], [Vector3], [Vector4].
[codeblock]
var i = ceil(1.45) # i is 2.0
i = ceil(1.001) # i is 2.0
[/codeblock]
See also [method floor], [method round], and [method snapped].
+ [b]Note:[/b] For better type safety, you can use [method ceilf], [method ceili], [method Vector2.ceil], [method Vector3.ceil] or [method Vector4.ceil] instead.
+ </description>
+ </method>
+ <method name="ceilf">
+ <return type="float" />
+ <argument index="0" name="x" type="float" />
+ <description>
+ Rounds [code]x[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]x[/code].
+ A type-safe version of [method ceil], specialzied in floats.
+ </description>
+ </method>
+ <method name="ceili">
+ <return type="int" />
+ <argument index="0" name="x" type="float" />
+ <description>
+ Rounds [code]x[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]x[/code].
+ A type-safe version of [method ceil] that returns integer.
</description>
</method>
<method name="clamp">
@@ -300,10 +317,10 @@
</description>
</method>
<method name="floor">
- <return type="float" />
- <argument index="0" name="x" type="float" />
+ <return type="Variant" />
+ <argument index="0" name="x" type="Variant" />
<description>
- Rounds [code]x[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]x[/code].
+ Rounds [code]x[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]x[/code]. Supported types: [int], [float], [Vector2], [Vector3], [Vector4].
[codeblock]
# a is 2.0
var a = floor(2.99)
@@ -311,7 +328,23 @@
a = floor(-2.99)
[/codeblock]
See also [method ceil], [method round], and [method snapped].
- [b]Note:[/b] This method returns a float. If you need an integer, you can use [code]int(x)[/code] directly.
+ [b]Note:[/b] For better type safety, you can use [method floorf], [method floori], [method Vector2.floor], [method Vector3.floor] or [method Vector4.floor] instead.
+ </description>
+ </method>
+ <method name="floorf">
+ <return type="float" />
+ <argument index="0" name="x" type="float" />
+ <description>
+ Rounds [code]x[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]x[/code].
+ A type-safe version of [method floor], specialzied in floats.
+ </description>
+ </method>
+ <method name="floori">
+ <return type="int" />
+ <argument index="0" name="x" type="float" />
+ <description>
+ Rounds [code]x[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]x[/code].
+ Equivalent of doing [code]int(x)[/code].
</description>
</method>
<method name="fmod">
@@ -439,16 +472,18 @@
</description>
</method>
<method name="lerp">
- <return type="float" />
- <argument index="0" name="from" type="float" />
- <argument index="1" name="to" type="float" />
- <argument index="2" name="weight" type="float" />
+ <return type="Variant" />
+ <argument index="0" name="from" type="Variant" />
+ <argument index="1" name="to" type="Variant" />
+ <argument index="2" name="weight" type="Variant" />
<description>
Linearly interpolates between two values by the factor defined in [code]weight[/code]. To perform interpolation, [code]weight[/code] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. Use [method clamp] on the result of [method lerp] if this is not desired.
+ Both [code]from[/code] and [code]to[/code] must have matching types. Supported types: [float], [Vector2], [Vector3], [Vector4], [Color], [Quaternion], [Basis].
[codeblock]
lerp(0, 4, 0.75) # Returns 3.0
[/codeblock]
See also [method inverse_lerp] which performs the reverse of this operation. To perform eased interpolation with [method lerp], combine it with [method ease] or [method smoothstep]. See also [method range_lerp] to map a continuous series of values to another.
+ [b]Note:[/b] For better type safety, you can use [method lerpf], [method Vector2.lerp], [method Vector3.lerp], [method Vector4.lerp], [method Color.lerp], [method Quaternion.slerp] or [method Basis.slerp] instead.
</description>
</method>
<method name="lerp_angle">
@@ -471,6 +506,19 @@
[b]Note:[/b] This method lerps through the shortest path between [code]from[/code] and [code]to[/code]. However, when these two angles are approximately [code]PI + k * TAU[/code] apart for any integer [code]k[/code], it's not obvious which way they lerp due to floating-point precision errors. For example, [code]lerp_angle(0, PI, weight)[/code] lerps counter-clockwise, while [code]lerp_angle(0, PI + 5 * TAU, weight)[/code] lerps clockwise.
</description>
</method>
+ <method name="lerpf">
+ <return type="float" />
+ <argument index="0" name="from" type="float" />
+ <argument index="1" name="to" type="float" />
+ <argument index="2" name="weight" type="float" />
+ <description>
+ Linearly interpolates between two values by the factor defined in [code]weight[/code]. To perform interpolation, [code]weight[/code] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i].
+ [codeblock]
+ lerp(0, 4, 0.75) # Returns 3.0
+ [/codeblock]
+ See also [method inverse_lerp] which performs the reverse of this operation. To perform eased interpolation with [method lerp], combine it with [method ease] or [method smoothstep].
+ </description>
+ </method>
<method name="linear2db">
<return type="float" />
<argument index="0" name="lin" type="float" />
@@ -828,14 +876,33 @@
</description>
</method>
<method name="round">
- <return type="float" />
- <argument index="0" name="x" type="float" />
+ <return type="Variant" />
+ <argument index="0" name="x" type="Variant" />
<description>
- Rounds [code]x[/code] to the nearest whole number, with halfway cases rounded away from zero.
+ Rounds [code]x[/code] to the nearest whole number, with halfway cases rounded away from zero. Supported types: [int], [float], [Vector2], [Vector3], [Vector4].
[codeblock]
+ round(2.4) # Returns 2
+ round(2.5) # Returns 3
round(2.6) # Returns 3
[/codeblock]
See also [method floor], [method ceil], and [method snapped].
+ [b]Note:[/b] For better type safety, you can use [method roundf], [method roundi], [method Vector2.round], [method Vector3.round] or [method Vector4.round] instead.
+ </description>
+ </method>
+ <method name="roundf">
+ <return type="float" />
+ <argument index="0" name="x" type="float" />
+ <description>
+ Rounds [code]x[/code] to the nearest whole number, with halfway cases rounded away from zero.
+ A type-safe version of [method round], specialzied in floats.
+ </description>
+ </method>
+ <method name="roundi">
+ <return type="int" />
+ <argument index="0" name="x" type="float" />
+ <description>
+ Rounds [code]x[/code] to the nearest whole number, with halfway cases rounded away from zero.
+ A type-safe version of [method round] that returns integer.
</description>
</method>
<method name="seed">
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 5a9d12d01b..fb437b6d8e 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -4,7 +4,8 @@
Low-level hyper-text transfer protocol client.
</brief_description>
<description>
- Hyper-text transfer protocol client (sometimes called "User Agent"). Used to make HTTP requests to download web content, upload files and other data or to communicate with various services, among other use cases. [b]See the [HTTPRequest] node for a higher-level alternative.[/b]
+ Hyper-text transfer protocol client (sometimes called "User Agent"). Used to make HTTP requests to download web content, upload files and other data or to communicate with various services, among other use cases.
+ See the [HTTPRequest] node for a higher-level alternative.
[b]Note:[/b] This client only needs to connect to a host once (see [method connect_to_host]) to send multiple requests. Because of this, methods that take URLs usually take just the part after the host instead of the full URL, as the client is already connected to a host. See [method request] for a full example and to get started.
A [HTTPClient] should be reused between multiple requests or to connect to different hosts instead of creating one client per request. Supports SSL and SSL server certificate verification. HTTP status codes in the 2xx range indicate success, 3xx redirection (i.e. "try again, but over here"), 4xx something was wrong with the request, and 5xx something went wrong on the server's side.
For more information on HTTP, see https://developer.mozilla.org/en-US/docs/Web/HTTP (or read RFC 2616 to get it straight from the source: https://tools.ietf.org/html/rfc2616).
diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml
index 0ebd83c882..46c3e57547 100644
--- a/doc/classes/Light3D.xml
+++ b/doc/classes/Light3D.xml
@@ -37,13 +37,12 @@
[b]Note:[/b] Only effective for [OmniLight3D] and [SpotLight3D].
</member>
<member name="distance_fade_length" type="float" setter="set_distance_fade_length" getter="get_distance_fade_length" default="10.0">
- Distance over which the light fades. The light's energy is progressively reduced over this distance and is completely invisible at the end.
+ Distance over which the light and its shadow fades. The light's energy and shadow's opacity is progressively reduced over this distance and is completely invisible at the end.
[b]Note:[/b] Only effective for [OmniLight3D] and [SpotLight3D].
</member>
<member name="distance_fade_shadow" type="float" setter="set_distance_fade_shadow" getter="get_distance_fade_shadow" default="50.0">
The distance from the camera at which the light's shadow cuts off (in 3D units). Set this to a value lower than [member distance_fade_begin] + [member distance_fade_length] to further improve performance, as shadow rendering is often more expensive than light rendering itself.
[b]Note:[/b] Only effective for [OmniLight3D] and [SpotLight3D], and only when [member shadow_enabled] is [code]true[/code].
- [b]Note:[/b] Due to a rendering engine limitation, shadows will be disabled instantly instead of fading smoothly according to [member distance_fade_length]. This may result in visible pop-in depending on the scene topography.
</member>
<member name="editor_only" type="bool" setter="set_editor_only" getter="is_editor_only" default="false">
If [code]true[/code], the light only appears in the editor and will not be visible at runtime.
@@ -95,6 +94,9 @@
<member name="shadow_normal_bias" type="float" setter="set_param" getter="get_param" default="1.0">
Offsets the lookup into the shadow map by the object's normal. This can be used to reduce self-shadowing artifacts without using [member shadow_bias]. In practice, this value should be tweaked along with [member shadow_bias] to reduce artifacts as much as possible.
</member>
+ <member name="shadow_opacity" type="float" setter="set_param" getter="get_param" default="1.0">
+ The opacity to use when rendering the light's shadow map. Values lower than [code]1.0[/code] make the light appear through shadows. This can be used to fake global illumination at a low performance cost.
+ </member>
<member name="shadow_reverse_cull_face" type="bool" setter="set_shadow_reverse_cull_face" getter="get_shadow_reverse_cull_face" default="false">
If [code]true[/code], reverses the backface culling of the mesh. This can be useful when you have a flat mesh that has a light behind it. If you need to cast a shadow on both sides of the mesh, set the mesh to use double-sided shadows with [constant GeometryInstance3D.SHADOW_CASTING_SETTING_DOUBLE_SIDED].
</member>
@@ -150,15 +152,19 @@
<constant name="PARAM_SHADOW_PANCAKE_SIZE" value="15" enum="Param">
Constant for accessing [member DirectionalLight3D.directional_shadow_pancake_size].
</constant>
- <constant name="PARAM_SHADOW_BLUR" value="16" enum="Param">
+ <constant name="PARAM_SHADOW_OPACITY" value="16" enum="Param">
+ Constant for accessing [member shadow_opacity].
+ </constant>
+ <constant name="PARAM_SHADOW_BLUR" value="17" enum="Param">
Constant for accessing [member shadow_blur].
</constant>
- <constant name="PARAM_SHADOW_VOLUMETRIC_FOG_FADE" value="17" enum="Param">
+ <constant name="PARAM_SHADOW_VOLUMETRIC_FOG_FADE" value="18" enum="Param">
+ Constant for accessing [member shadow_fog_fade].
</constant>
- <constant name="PARAM_TRANSMITTANCE_BIAS" value="18" enum="Param">
+ <constant name="PARAM_TRANSMITTANCE_BIAS" value="19" enum="Param">
Constant for accessing [member shadow_transmittance_bias].
</constant>
- <constant name="PARAM_MAX" value="19" enum="Param">
+ <constant name="PARAM_MAX" value="20" enum="Param">
Represents the size of the [enum Param] enum.
</constant>
<constant name="BAKE_DISABLED" value="0" enum="BakeMode">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 1b58b50bc7..c06b2e1d98 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3777,6 +3777,7 @@
The light's energy.
</constant>
<constant name="LIGHT_PARAM_INDIRECT_ENERGY" value="1" enum="LightParam">
+ The light's indirect energy multiplier (final indirect energy is [constant LIGHT_PARAM_ENERGY] * [constant LIGHT_PARAM_INDIRECT_ENERGY]).
</constant>
<constant name="LIGHT_PARAM_SPECULAR" value="2" enum="LightParam">
The light's influence on specularity.
@@ -3820,14 +3821,17 @@
<constant name="LIGHT_PARAM_SHADOW_PANCAKE_SIZE" value="15" enum="LightParam">
Sets the size of the directional shadow pancake. The pancake offsets the start of the shadow's camera frustum to provide a higher effective depth resolution for the shadow. However, a high pancake size can cause artifacts in the shadows of large objects that are close to the edge of the frustum. Reducing the pancake size can help. Setting the size to [code]0[/code] turns off the pancaking effect.
</constant>
- <constant name="LIGHT_PARAM_SHADOW_BLUR" value="16" enum="LightParam">
+ <constant name="LIGHT_PARAM_SHADOW_OPACITY" value="16" enum="LightParam">
+ The light's shadow opacity. Values lower than [code]1.0[/code] make the light appear through shadows. This can be used to fake global illumination at a low performance cost.
+ </constant>
+ <constant name="LIGHT_PARAM_SHADOW_BLUR" value="17" enum="LightParam">
Blurs the edges of the shadow. Can be used to hide pixel artifacts in low resolution shadow maps. A high value can make shadows appear grainy and can cause other unwanted artifacts. Try to keep as near default as possible.
</constant>
- <constant name="LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE" value="17" enum="LightParam">
+ <constant name="LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE" value="18" enum="LightParam">
</constant>
- <constant name="LIGHT_PARAM_TRANSMITTANCE_BIAS" value="18" enum="LightParam">
+ <constant name="LIGHT_PARAM_TRANSMITTANCE_BIAS" value="19" enum="LightParam">
</constant>
- <constant name="LIGHT_PARAM_MAX" value="19" enum="LightParam">
+ <constant name="LIGHT_PARAM_MAX" value="20" enum="LightParam">
Represents the size of the [enum LightParam] enum.
</constant>
<constant name="LIGHT_BAKE_DISABLED" value="0" enum="LightBakeMode">
diff --git a/doc/classes/Vector4.xml b/doc/classes/Vector4.xml
index da0df2672e..ee9d97019e 100644
--- a/doc/classes/Vector4.xml
+++ b/doc/classes/Vector4.xml
@@ -63,6 +63,30 @@
Returns a new vector with all components clamped between the components of [code]min[/code] and [code]max[/code], by running [method @GlobalScope.clamp] on each component.
</description>
</method>
+ <method name="cubic_interpolate" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="b" type="Vector4" />
+ <argument index="1" name="pre_a" type="Vector4" />
+ <argument index="2" name="post_b" type="Vector4" />
+ <argument index="3" name="weight" type="float" />
+ <description>
+ Performs a cubic interpolation between this vector and [code]b[/code] using [code]pre_a[/code] and [code]post_b[/code] as handles, and returns the result at position [code]weight[/code]. [code]weight[/code] is on the range of 0.0 to 1.0, representing the amount of interpolation.
+ </description>
+ </method>
+ <method name="direction_to" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="to" type="Vector4" />
+ <description>
+ Returns the normalized vector pointing from this vector to [code]to[/code]. This is equivalent to using [code](b - a).normalized()[/code].
+ </description>
+ </method>
+ <method name="distance_to" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="to" type="Vector4" />
+ <description>
+ Returns the distance between this vector and [code]to[/code].
+ </description>
+ </method>
<method name="dot" qualifiers="const">
<return type="float" />
<argument index="0" name="with" type="Vector4" />
@@ -133,6 +157,20 @@
Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code].
</description>
</method>
+ <method name="posmod" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="mod" type="float" />
+ <description>
+ Returns a vector composed of the [method @GlobalScope.fposmod] of this vector's components and [code]mod[/code].
+ </description>
+ </method>
+ <method name="posmodv" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="modv" type="Vector4" />
+ <description>
+ Returns a vector composed of the [method @GlobalScope.fposmod] of this vector's components and [code]modv[/code]'s components.
+ </description>
+ </method>
<method name="round" qualifiers="const">
<return type="Vector4" />
<description>
@@ -145,6 +183,13 @@
Returns a new vector with each component set to one or negative one, depending on the signs of the components, or zero if the component is zero, by calling [method @GlobalScope.sign] on each component.
</description>
</method>
+ <method name="snapped" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="step" type="Vector4" />
+ <description>
+ Returns this vector with each component snapped to the nearest multiple of [code]step[/code]. This can also be used to round to an arbitrary number of decimals.
+ </description>
+ </method>
</methods>
<members>
<member name="w" type="float" setter="" getter="" default="0.0">
diff --git a/doc/classes/VisualShaderNodeVectorRefract.xml b/doc/classes/VisualShaderNodeVectorRefract.xml
index d12188ea55..003b505671 100644
--- a/doc/classes/VisualShaderNodeVectorRefract.xml
+++ b/doc/classes/VisualShaderNodeVectorRefract.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorRefract" inherits="VisualShaderNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="VisualShaderNodeVectorRefract" inherits="VisualShaderNodeVectorBase" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Returns the [Vector3] that points in the direction of refraction. For use within the visual shader graph.
+ Returns the vector that points in the direction of refraction. For use within the visual shader graph.
</brief_description>
<description>
Translated to [code]refract(I, N, eta)[/code] in the shader language, where [code]I[/code] is the incident vector, [code]N[/code] is the normal vector and [code]eta[/code] is the ratio of the indices of the refraction.
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 410cd376a7..7207a6efbb 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1650,7 +1650,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b
light_data.specular_amount = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR) * 2.0;
- light_data.shadow_enabled = false;
+ light_data.shadow_opacity = 0.0;
}
// TODO, to avoid stalls, should rotate between 3 buffers based on frame index.
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index e227b2df82..a54d87a3a3 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -165,7 +165,7 @@ private:
float inv_spot_attenuation;
float cos_spot_angle;
float specular_amount;
- uint32_t shadow_enabled;
+ float shadow_opacity;
};
static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes");
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 4081d73ab0..2550b3a8a1 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -495,7 +495,7 @@ struct LightData { //this structure needs to be as packed as possible
mediump float cone_attenuation;
mediump float cone_angle;
mediump float specular_amount;
- bool shadow_enabled;
+ mediump float shadow_opacity;
};
#ifndef DISABLE_LIGHT_OMNI
layout(std140) uniform OmniLightData { // ubo:5
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index 22578c9e91..2e4bfdc15b 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -70,6 +70,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) {
light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6;
light.param[RS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8;
light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0;
+ light.param[RS::LIGHT_PARAM_SHADOW_OPACITY] = 1.0;
light.param[RS::LIGHT_PARAM_SHADOW_BIAS] = 0.02;
light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0;
light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0;
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index c713fe3df0..5e66d436fc 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -507,13 +507,55 @@ void EditorPropertyPath::_path_focus_exited() {
_path_selected(path->get_text());
}
+void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+ const Dictionary drag_data = p_data;
+ if (!drag_data.has("type")) {
+ return;
+ }
+ if (String(drag_data["type"]) != "files") {
+ return;
+ }
+ const Vector<String> filesPaths = drag_data["files"];
+ if (filesPaths.size() == 0) {
+ return;
+ }
+
+ emit_changed(get_edited_property(), filesPaths[0]);
+ update_property();
+}
+
+bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+ const Dictionary drag_data = p_data;
+ if (!drag_data.has("type")) {
+ return false;
+ }
+ if (String(drag_data["type"]) != "files") {
+ return false;
+ }
+ const Vector<String> filesPaths = drag_data["files"];
+ if (filesPaths.size() == 0) {
+ return false;
+ }
+
+ for (const String &extension : extensions) {
+ if (filesPaths[0].ends_with(extension.substr(1, extension.size() - 1))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void EditorPropertyPath::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw", "position", "data", "from"), &EditorPropertyPath::_can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw", "position", "data", "from"), &EditorPropertyPath::_drop_data_fw);
}
EditorPropertyPath::EditorPropertyPath() {
HBoxContainer *path_hb = memnew(HBoxContainer);
add_child(path_hb);
path = memnew(LineEdit);
+ path->set_drag_forwarding(this);
path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
path_hb->add_child(path);
path->connect("text_submitted", callable_mp(this, &EditorPropertyPath::_path_selected));
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 6a1360d2ca..5c73a53184 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -142,6 +142,8 @@ class EditorPropertyPath : public EditorProperty {
void _path_selected(const String &p_path);
void _path_pressed();
void _path_focus_exited();
+ void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
protected:
virtual void _set_read_only(bool p_read_only) override;
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index d850773a6d..e5c1836205 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -184,6 +184,21 @@ void EditorResourcePicker::_update_menu_items() {
edit_menu->add_icon_item(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")), TTR("Edit"), OBJ_MENU_EDIT);
edit_menu->add_icon_item(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")), TTR("Clear"), OBJ_MENU_CLEAR);
edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
+
+ // Check whether the resource has subresources.
+ List<PropertyInfo> property_list;
+ edited_resource->get_property_list(&property_list);
+ bool has_subresources = false;
+ for (PropertyInfo &p : property_list) {
+ if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script")) {
+ has_subresources = true;
+ break;
+ }
+ }
+ if (has_subresources) {
+ edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique (Recursive)"), OBJ_MENU_MAKE_UNIQUE_RECURSIVE);
+ }
+
edit_menu->add_icon_item(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")), TTR("Save"), OBJ_MENU_SAVE);
if (edited_resource->get_path().is_resource_file()) {
@@ -297,28 +312,22 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
return;
}
- List<PropertyInfo> property_list;
- edited_resource->get_property_list(&property_list);
- List<Pair<String, Variant>> propvalues;
- for (const PropertyInfo &pi : property_list) {
- Pair<String, Variant> p;
- if (pi.usage & PROPERTY_USAGE_STORAGE) {
- p.first = pi.name;
- p.second = edited_resource->get(pi.name);
- }
+ Ref<Resource> unique_resource = edited_resource->duplicate();
+ ERR_FAIL_COND(unique_resource.is_null());
- propvalues.push_back(p);
+ edited_resource = unique_resource;
+ emit_signal(SNAME("resource_changed"), edited_resource);
+ _update_resource();
+ } break;
+
+ case OBJ_MENU_MAKE_UNIQUE_RECURSIVE: {
+ if (edited_resource.is_null()) {
+ return;
}
- String orig_type = edited_resource->get_class();
- Object *inst = ClassDB::instantiate(orig_type);
- Ref<Resource> unique_resource = Ref<Resource>(Object::cast_to<Resource>(inst));
+ Ref<Resource> unique_resource = edited_resource->duplicate(true);
ERR_FAIL_COND(unique_resource.is_null());
- for (const Pair<String, Variant> &p : propvalues) {
- unique_resource->set(p.first, p.second);
- }
-
edited_resource = unique_resource;
emit_signal(SNAME("resource_changed"), edited_resource);
_update_resource();
diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h
index d36e742bcd..3a4d5985bd 100644
--- a/editor/editor_resource_picker.h
+++ b/editor/editor_resource_picker.h
@@ -66,6 +66,7 @@ class EditorResourcePicker : public HBoxContainer {
OBJ_MENU_EDIT,
OBJ_MENU_CLEAR,
OBJ_MENU_MAKE_UNIQUE,
+ OBJ_MENU_MAKE_UNIQUE_RECURSIVE,
OBJ_MENU_SAVE,
OBJ_MENU_COPY,
OBJ_MENU_PASTE,
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 6638e2f42f..b823db68f0 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -1531,14 +1531,13 @@ void FileSystemDock::_folder_removed(String p_folder) {
void FileSystemDock::_rename_operation_confirm() {
String new_name = rename_dialog_text->get_text().strip_edges();
- String old_name = tree->get_selected()->get_text(0);
if (new_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
return;
} else if (new_name.contains("/") || new_name.contains("\\") || new_name.contains(":")) {
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
return;
- } else if (to_rename.is_file && old_name.get_extension() != new_name.get_extension()) {
+ } else if (to_rename.is_file && to_rename.path.get_extension() != new_name.get_extension()) {
if (!EditorFileSystem::get_singleton()->get_valid_extensions().find(new_name.get_extension())) {
EditorNode::get_singleton()->show_warning(TTR("This file extension is not recognized by the editor.\nIf you want to rename it anyway, use your operating system's file manager.\nAfter renaming to an unknown extension, the file won't be shown in the editor anymore."));
return;
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index a5dfd67d18..2e6de95a46 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -391,7 +391,7 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
texture_import->bptc_ldr = bptc_ldr;
texture_import->mipmaps = mipmaps;
texture_import->used_channels = used_channels;
- _check_compress_ctex(texture_import);
+ _check_compress_ctex(p_source_file, texture_import);
if (r_metadata) {
Dictionary metadata;
metadata["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED;
@@ -472,7 +472,7 @@ ResourceImporterLayeredTexture::ResourceImporterLayeredTexture() {
ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() {
}
-void ResourceImporterLayeredTexture::_check_compress_ctex(Ref<LayeredTextureImport> r_texture_import) {
+void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source_file, Ref<LayeredTextureImport> r_texture_import) {
String extension = get_save_extension();
ERR_FAIL_NULL(r_texture_import->csource);
if (r_texture_import->compress_mode != COMPRESS_VRAM_COMPRESSED) {
@@ -542,5 +542,5 @@ void ResourceImporterLayeredTexture::_check_compress_ctex(Ref<LayeredTextureImpo
}
return;
}
- EditorNode::add_io_error(TTR("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC."));
+ EditorNode::add_io_error(vformat(TTR("%s: No suitable PC VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms."), p_source_file));
}
diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h
index 5a29010c3b..e292390fb3 100644
--- a/editor/import/resource_importer_layered_texture.h
+++ b/editor/import/resource_importer_layered_texture.h
@@ -87,7 +87,7 @@ protected:
static ResourceImporterLayeredTexture *singleton;
public:
- void _check_compress_ctex(Ref<LayeredTextureImport> r_texture_import);
+ void _check_compress_ctex(const String &p_source_file, Ref<LayeredTextureImport> r_texture_import);
static ResourceImporterLayeredTexture *get_singleton() { return singleton; }
virtual String get_importer_name() const override;
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index deb3047864..0eed6184c0 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -597,7 +597,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
}
if (!ok_on_pc) {
- EditorNode::add_io_error(TTR("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC."));
+ EditorNode::add_io_error(vformat(TTR("%s: No suitable desktop VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms."), p_source_file));
}
} else {
//import normally
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 1c0e879a0a..4a144bc391 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -5577,8 +5577,12 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), { VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), { VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), { VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 8395fa996a..21891e381b 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -42,6 +42,7 @@
#include "core/string/translation.h"
#include "core/version.h"
#include "editor/editor_file_dialog.h"
+#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_themes.h"
@@ -57,10 +58,6 @@
#include "servers/navigation_server_3d.h"
#include "servers/physics_server_2d.h"
-static inline String get_project_key_from_path(const String &dir) {
- return dir.replace("/", "::");
-}
-
class ProjectDialog : public ConfirmationDialog {
GDCLASS(ProjectDialog, ConfirmationDialog);
@@ -600,9 +597,6 @@ private:
if (dir.ends_with("/")) {
dir = dir.substr(0, dir.length() - 1);
}
- String proj = get_project_key_from_path(dir);
- EditorSettings::get_singleton()->set("projects/" + proj, dir);
- EditorSettings::get_singleton()->save();
hide();
emit_signal(SNAME("project_created"), dir);
@@ -989,7 +983,6 @@ public:
// Can often be passed by copy
struct Item {
- String project_key;
String project_name;
String description;
String path;
@@ -1006,8 +999,7 @@ public:
Item() {}
- Item(const String &p_project,
- const String &p_name,
+ Item(const String &p_name,
const String &p_description,
const String &p_path,
const String &p_icon,
@@ -1018,7 +1010,6 @@ public:
bool p_grayed,
bool p_missing,
int p_version) {
- project_key = p_project;
project_name = p_name;
description = p_description;
path = p_path;
@@ -1034,7 +1025,7 @@ public:
}
_FORCE_INLINE_ bool operator==(const Item &l) const {
- return project_key == l.project_key;
+ return path == l.path;
}
};
@@ -1047,6 +1038,7 @@ public:
void _global_menu_open_project(const Variant &p_tag);
void update_dock_menu();
+ void migrate_config();
void load_projects();
void set_search_term(String p_search_term);
void set_order_option(int p_option);
@@ -1062,6 +1054,8 @@ public:
bool is_any_project_missing() const;
void erase_missing_projects();
int refresh_project(const String &dir_path);
+ void add_project(const String &dir_path, bool favorite);
+ void save_config();
private:
static void _bind_methods();
@@ -1082,12 +1076,15 @@ private:
String _search_term;
FilterOption _order_option;
- HashSet<String> _selected_project_keys;
+ HashSet<String> _selected_project_paths;
String _last_clicked; // Project key
VBoxContainer *_scroll_children;
int _icon_load_index;
Vector<Item> _projects;
+
+ ConfigFile _config;
+ String _config_path;
};
struct ProjectListComparator {
@@ -1103,7 +1100,7 @@ struct ProjectListComparator {
}
switch (order_option) {
case PATH:
- return a.project_key < b.project_key;
+ return a.path < b.path;
case EDIT_DATE:
return a.last_edited > b.last_edited;
default:
@@ -1120,6 +1117,7 @@ ProjectList::ProjectList() {
_icon_load_index = 0;
project_opening_initiated = false;
+ _config_path = EditorPaths::get_singleton()->get_data_dir().plus_file("projects.cfg");
}
ProjectList::~ProjectList() {
@@ -1171,9 +1169,8 @@ void ProjectList::load_project_icon(int p_index) {
}
// Load project data from p_property_key and return it in a ProjectList::Item. p_favorite is passed directly into the Item.
-ProjectList::Item ProjectList::load_project_data(const String &p_property_key, bool p_favorite) {
- String path = EditorSettings::get_singleton()->get(p_property_key);
- String conf = path.plus_file("project.godot");
+ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_favorite) {
+ String conf = p_path.plus_file("project.godot");
bool grayed = false;
bool missing = false;
@@ -1209,7 +1206,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_property_key, b
// when editing a project (but not when running it).
last_edited = FileAccess::get_modified_time(conf);
- String fscache = path.plus_file(".fscache");
+ String fscache = p_path.plus_file(".fscache");
if (FileAccess::exists(fscache)) {
uint64_t cache_modified = FileAccess::get_modified_time(fscache);
if (cache_modified > last_edited) {
@@ -1222,9 +1219,38 @@ ProjectList::Item ProjectList::load_project_data(const String &p_property_key, b
print_line("Project is missing: " + conf);
}
- const String project_key = p_property_key.get_slice("/", 1);
+ return Item(project_name, description, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
+}
+
+void ProjectList::migrate_config() {
+ // Proposal #1637 moved the project list from editor settings to a separate config file
+ // If the new config file doesn't exist, populate it from EditorSettings
+ if (FileAccess::exists(_config_path)) {
+ return;
+ }
+ print_line("Migrating legacy project list");
+
+ List<PropertyInfo> properties;
+ EditorSettings::get_singleton()->get_property_list(&properties);
- return Item(project_key, project_name, description, path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
+ for (const PropertyInfo &E : properties) {
+ // This is actually something like "projects/C:::Documents::Godot::Projects::MyGame"
+ String property_key = E.name;
+ if (!property_key.begins_with("projects/")) {
+ continue;
+ }
+
+ String path = EditorSettings::get_singleton()->get(property_key);
+ String favoriteKey = "favorite_projects/" + property_key.get_slice("/", 1);
+ bool favorite = EditorSettings::get_singleton()->has_setting(favoriteKey);
+ add_project(path, favorite);
+ if (favorite) {
+ EditorSettings::get_singleton()->erase(favoriteKey);
+ }
+ EditorSettings::get_singleton()->erase(property_key);
+ }
+
+ save_config();
}
void ProjectList::load_projects() {
@@ -1239,37 +1265,15 @@ void ProjectList::load_projects() {
}
_projects.clear();
_last_clicked = "";
- _selected_project_keys.clear();
-
- // Load data
- // TODO Would be nice to change how projects and favourites are stored... it complicates things a bit.
- // Use a dictionary associating project path to metadata (like is_favorite).
-
- List<PropertyInfo> properties;
- EditorSettings::get_singleton()->get_property_list(&properties);
-
- HashSet<String> favorites;
- // Find favourites...
- for (const PropertyInfo &E : properties) {
- String property_key = E.name;
- if (property_key.begins_with("favorite_projects/")) {
- favorites.insert(property_key);
- }
- }
-
- for (const PropertyInfo &E : properties) {
- // This is actually something like "projects/C:::Documents::Godot::Projects::MyGame"
- String property_key = E.name;
- if (!property_key.begins_with("projects/")) {
- continue;
- }
-
- String project_key = property_key.get_slice("/", 1);
- bool favorite = favorites.has("favorite_projects/" + project_key);
+ _selected_project_paths.clear();
- Item item = load_project_data(property_key, favorite);
+ List<String> sections;
+ _config.load(_config_path);
+ _config.get_sections(&sections);
- _projects.push_back(item);
+ for (const String &path : sections) {
+ bool favorite = _config.get_value(path, "favorite", false);
+ _projects.push_back(load_project_data(path, favorite));
}
// Create controls
@@ -1496,19 +1500,19 @@ void ProjectList::sort_projects() {
const HashSet<String> &ProjectList::get_selected_project_keys() const {
// Faster if that's all you need
- return _selected_project_keys;
+ return _selected_project_paths;
}
Vector<ProjectList::Item> ProjectList::get_selected_projects() const {
Vector<Item> items;
- if (_selected_project_keys.size() == 0) {
+ if (_selected_project_paths.size() == 0) {
return items;
}
- items.resize(_selected_project_keys.size());
+ items.resize(_selected_project_paths.size());
int j = 0;
for (int i = 0; i < _projects.size(); ++i) {
const Item &item = _projects[i];
- if (_selected_project_keys.has(item.project_key)) {
+ if (_selected_project_paths.has(item.path)) {
items.write[j++] = item;
}
}
@@ -1522,41 +1526,40 @@ void ProjectList::ensure_project_visible(int p_index) {
}
int ProjectList::get_single_selected_index() const {
- if (_selected_project_keys.size() == 0) {
+ if (_selected_project_paths.size() == 0) {
// Default selection
return 0;
}
String key;
- if (_selected_project_keys.size() == 1) {
+ if (_selected_project_paths.size() == 1) {
// Only one selected
- key = *_selected_project_keys.begin();
+ key = *_selected_project_paths.begin();
} else {
// Multiple selected, consider the last clicked one as "main"
key = _last_clicked;
}
for (int i = 0; i < _projects.size(); ++i) {
- if (_projects[i].project_key == key) {
+ if (_projects[i].path == key) {
return i;
}
}
return 0;
}
-void ProjectList::remove_project(int p_index, bool p_update_settings) {
+void ProjectList::remove_project(int p_index, bool p_update_config) {
const Item item = _projects[p_index]; // Take a copy
- _selected_project_keys.erase(item.project_key);
+ _selected_project_paths.erase(item.path);
- if (_last_clicked == item.project_key) {
+ if (_last_clicked == item.path) {
_last_clicked = "";
}
memdelete(item.control);
_projects.remove_at(p_index);
- if (p_update_settings) {
- EditorSettings::get_singleton()->erase("projects/" + item.project_key);
- EditorSettings::get_singleton()->erase("favorite_projects/" + item.project_key);
+ if (p_update_config) {
+ _config.erase_section(item.path);
// Not actually saving the file, in case you are doing more changes to settings
}
@@ -1594,41 +1597,19 @@ void ProjectList::erase_missing_projects() {
}
print_line("Removed " + itos(deleted_count) + " projects from the list, remaining " + itos(remaining_count) + " projects");
-
- EditorSettings::get_singleton()->save();
+ save_config();
}
int ProjectList::refresh_project(const String &dir_path) {
- // Reads editor settings and reloads information about a specific project.
+ // Reloads information about a specific project.
// If it wasn't loaded and should be in the list, it is added (i.e new project).
// If it isn't in the list anymore, it is removed.
// If it is in the list but doesn't exist anymore, it is marked as missing.
- String project_key = get_project_key_from_path(dir_path);
-
- // Read project manager settings
- bool is_favourite = false;
- bool should_be_in_list = false;
- String property_key = "projects/" + project_key;
- {
- List<PropertyInfo> properties;
- EditorSettings::get_singleton()->get_property_list(&properties);
- String favorite_property_key = "favorite_projects/" + project_key;
-
- bool found = false;
- for (const PropertyInfo &E : properties) {
- String prop = E.name;
- if (!found && prop == property_key) {
- found = true;
- } else if (!is_favourite && prop == favorite_property_key) {
- is_favourite = true;
- }
- }
-
- should_be_in_list = found;
- }
+ bool should_be_in_list = _config.has_section(dir_path);
+ bool is_favourite = _config.get_value(dir_path, "favorite", false);
- bool was_selected = _selected_project_keys.has(project_key);
+ bool was_selected = _selected_project_paths.has(dir_path);
// Remove item in any case
for (int i = 0; i < _projects.size(); ++i) {
@@ -1643,7 +1624,7 @@ int ProjectList::refresh_project(const String &dir_path) {
if (should_be_in_list) {
// Recreate it with updated info
- Item item = load_project_data(property_key, is_favourite);
+ Item item = load_project_data(dir_path, is_favourite);
_projects.push_back(item);
create_project_item_control(_projects.size() - 1);
@@ -1651,7 +1632,7 @@ int ProjectList::refresh_project(const String &dir_path) {
sort_projects();
for (int i = 0; i < _projects.size(); ++i) {
- if (_projects[i].project_key == project_key) {
+ if (_projects[i].path == dir_path) {
if (was_selected) {
select_project(i);
ensure_project_visible(i);
@@ -1667,13 +1648,23 @@ int ProjectList::refresh_project(const String &dir_path) {
return index;
}
+void ProjectList::add_project(const String &dir_path, bool favorite) {
+ if (!_config.has_section(dir_path)) {
+ _config.set_value(dir_path, "favorite", favorite);
+ }
+}
+
+void ProjectList::save_config() {
+ _config.save(_config_path);
+}
+
int ProjectList::get_project_count() const {
return _projects.size();
}
void ProjectList::select_project(int p_index) {
Vector<Item> previous_selected_items = get_selected_projects();
- _selected_project_keys.clear();
+ _selected_project_paths.clear();
for (int i = 0; i < previous_selected_items.size(); ++i) {
previous_selected_items[i].control->update();
@@ -1695,7 +1686,7 @@ void ProjectList::select_first_visible_project() {
if (!found) {
// Deselect all projects if there are no visible projects in the list.
- _selected_project_keys.clear();
+ _selected_project_paths.clear();
}
}
@@ -1717,24 +1708,23 @@ void ProjectList::select_range(int p_begin, int p_end) {
void ProjectList::toggle_select(int p_index) {
Item &item = _projects.write[p_index];
- if (_selected_project_keys.has(item.project_key)) {
- _selected_project_keys.erase(item.project_key);
+ if (_selected_project_paths.has(item.path)) {
+ _selected_project_paths.erase(item.path);
} else {
- _selected_project_keys.insert(item.project_key);
+ _selected_project_paths.insert(item.path);
}
item.control->update();
}
void ProjectList::erase_selected_projects(bool p_delete_project_contents) {
- if (_selected_project_keys.size() == 0) {
+ if (_selected_project_paths.size() == 0) {
return;
}
for (int i = 0; i < _projects.size(); ++i) {
Item &item = _projects.write[i];
- if (_selected_project_keys.has(item.project_key) && item.control->is_visible()) {
- EditorSettings::get_singleton()->erase("projects/" + item.project_key);
- EditorSettings::get_singleton()->erase("favorite_projects/" + item.project_key);
+ if (_selected_project_paths.has(item.path) && item.control->is_visible()) {
+ _config.erase_section(item.path);
if (p_delete_project_contents) {
OS::get_singleton()->move_to_trash(item.path);
@@ -1746,9 +1736,8 @@ void ProjectList::erase_selected_projects(bool p_delete_project_contents) {
}
}
- EditorSettings::get_singleton()->save();
-
- _selected_project_keys.clear();
+ save_config();
+ _selected_project_paths.clear();
_last_clicked = "";
update_dock_menu();
@@ -1764,9 +1753,9 @@ void ProjectList::_panel_draw(Node *p_hb) {
hb->draw_line(Point2(0, hb->get_size().y + 1), Point2(hb->get_size().x, hb->get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
}
- String key = _projects[p_hb->get_index()].project_key;
+ String key = _projects[p_hb->get_index()].path;
- if (_selected_project_keys.has(key)) {
+ if (_selected_project_paths.has(key)) {
hb->draw_style_box(get_theme_stylebox(SNAME("selected"), SNAME("Tree")), Rect2(Point2(), hb->get_size()));
}
}
@@ -1778,11 +1767,11 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
const Item &clicked_project = _projects[clicked_index];
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
- if (mb->is_shift_pressed() && _selected_project_keys.size() > 0 && !_last_clicked.is_empty() && clicked_project.project_key != _last_clicked) {
+ if (mb->is_shift_pressed() && _selected_project_paths.size() > 0 && !_last_clicked.is_empty() && clicked_project.path != _last_clicked) {
int anchor_index = -1;
for (int i = 0; i < _projects.size(); ++i) {
const Item &p = _projects[i];
- if (p.project_key == _last_clicked) {
+ if (p.path == _last_clicked) {
anchor_index = p.control->get_index();
break;
}
@@ -1794,7 +1783,7 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
toggle_select(clicked_index);
} else {
- _last_clicked = clicked_project.project_key;
+ _last_clicked = clicked_project.path;
select_project(clicked_index);
}
@@ -1816,12 +1805,8 @@ void ProjectList::_favorite_pressed(Node *p_hb) {
item.favorite = !item.favorite;
- if (item.favorite) {
- EditorSettings::get_singleton()->set("favorite_projects/" + item.project_key, item.path);
- } else {
- EditorSettings::get_singleton()->erase("favorite_projects/" + item.project_key);
- }
- EditorSettings::get_singleton()->save();
+ _config.set_value(item.path, "favorite", item.favorite);
+ save_config();
_projects.write[index] = item;
@@ -1831,7 +1816,7 @@ void ProjectList::_favorite_pressed(Node *p_hb) {
if (item.favorite) {
for (int i = 0; i < _projects.size(); ++i) {
- if (_projects[i].project_key == item.project_key) {
+ if (_projects[i].path == item.path) {
ensure_project_visible(i);
break;
}
@@ -2089,6 +2074,8 @@ void ProjectManager::_on_projects_updated() {
}
void ProjectManager::_on_project_created(const String &dir) {
+ _project_list->add_project(dir, false);
+ _project_list->save_config();
search_box->clear();
int i = _project_list->refresh_project(dir);
_project_list->select_project(i);
@@ -2109,9 +2096,7 @@ void ProjectManager::_open_selected_projects() {
const HashSet<String> &selected_list = _project_list->get_selected_project_keys();
- for (const String &E : selected_list) {
- const String &selected = E;
- String path = EditorSettings::get_singleton()->get("projects/" + selected);
+ for (const String &path : selected_list) {
String conf = path.plus_file("project.godot");
if (!FileAccess::exists(conf)) {
@@ -2120,7 +2105,7 @@ void ProjectManager::_open_selected_projects() {
return;
}
- print_line("Editing project: " + path + " (" + selected + ")");
+ print_line("Editing project: " + path);
List<String> args;
@@ -2244,8 +2229,7 @@ void ProjectManager::_run_project_confirm() {
continue;
}
- const String &selected = selected_list[i].project_key;
- String path = EditorSettings::get_singleton()->get("projects/" + selected);
+ const String &path = selected_list[i].path;
// `.substr(6)` on `ProjectSettings::get_singleton()->get_imported_files_path()` strips away the leading "res://".
if (!DirAccess::exists(path.plus_file(ProjectSettings::get_singleton()->get_imported_files_path().substr(6)))) {
@@ -2254,7 +2238,7 @@ void ProjectManager::_run_project_confirm() {
continue;
}
- print_line("Running project: " + path + " (" + selected + ")");
+ print_line("Running project: " + path);
List<String> args;
@@ -2285,7 +2269,7 @@ void ProjectManager::_run_project() {
}
}
-void ProjectManager::_scan_dir(const String &path, List<String> *r_projects) {
+void ProjectManager::_scan_dir(const String &path) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
Error error = da->change_dir(path);
ERR_FAIL_COND_MSG(error != OK, "Could not scan directory at: " + path);
@@ -2293,26 +2277,18 @@ void ProjectManager::_scan_dir(const String &path, List<String> *r_projects) {
String n = da->get_next();
while (!n.is_empty()) {
if (da->current_is_dir() && !n.begins_with(".")) {
- _scan_dir(da->get_current_dir().plus_file(n), r_projects);
+ _scan_dir(da->get_current_dir().plus_file(n));
} else if (n == "project.godot") {
- r_projects->push_back(da->get_current_dir());
+ _project_list->add_project(da->get_current_dir(), false);
}
n = da->get_next();
}
da->list_dir_end();
}
-
void ProjectManager::_scan_begin(const String &p_base) {
print_line("Scanning projects at: " + p_base);
- List<String> projects;
- _scan_dir(p_base, &projects);
- print_line("Found " + itos(projects.size()) + " projects.");
-
- for (const String &E : projects) {
- String proj = get_project_key_from_path(E);
- EditorSettings::get_singleton()->set("projects/" + proj, E);
- }
- EditorSettings::get_singleton()->save();
+ _scan_dir(p_base);
+ _project_list->save_config();
_load_recent_projects();
}
@@ -2338,9 +2314,7 @@ void ProjectManager::_rename_project() {
}
for (const String &E : selected_list) {
- const String &selected = E;
- String path = EditorSettings::get_singleton()->get("projects/" + selected);
- npdialog->set_project_path(path);
+ npdialog->set_project_path(E);
npdialog->set_mode(ProjectDialog::MODE_RENAME);
npdialog->show_dialog();
}
@@ -2871,6 +2845,7 @@ ProjectManager::ProjectManager() {
_build_icon_type_cache(get_theme());
}
+ _project_list->migrate_config();
_load_recent_projects();
Ref<DirAccess> dir_access = DirAccess::create(DirAccess::AccessType::ACCESS_FILESYSTEM);
diff --git a/editor/project_manager.h b/editor/project_manager.h
index 28383e4142..10bf25c048 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -125,7 +125,7 @@ class ProjectManager : public Control {
void _on_projects_updated();
void _scan_multiple_folders(PackedStringArray p_files);
void _scan_begin(const String &p_base);
- void _scan_dir(const String &path, List<String> *r_projects);
+ void _scan_dir(const String &path);
void _install_project(const String &p_zip_path, const String &p_title);
diff --git a/methods.py b/methods.py
index b719395ae0..82c19f09e3 100644
--- a/methods.py
+++ b/methods.py
@@ -1,5 +1,6 @@
import os
import re
+import sys
import glob
import subprocess
from collections import OrderedDict
@@ -336,7 +337,20 @@ def disable_module(self):
self.disabled_modules.append(self.current_module)
-def module_check_dependencies(self, module, dependencies, silent=False):
+def module_add_dependencies(self, module, dependencies, optional=False):
+ """
+ Adds dependencies for a given module.
+ Meant to be used in module `can_build` methods.
+ """
+ if module not in self.module_dependencies:
+ self.module_dependencies[module] = [[], []]
+ if optional:
+ self.module_dependencies[module][1].extend(dependencies)
+ else:
+ self.module_dependencies[module][0].extend(dependencies)
+
+
+def module_check_dependencies(self, module):
"""
Checks if module dependencies are enabled for a given module,
and prints a warning if they aren't.
@@ -344,23 +358,41 @@ def module_check_dependencies(self, module, dependencies, silent=False):
Returns a boolean (True if dependencies are satisfied).
"""
missing_deps = []
- for dep in dependencies:
+ required_deps = self.module_dependencies[module][0] if module in self.module_dependencies else []
+ for dep in required_deps:
opt = "module_{}_enabled".format(dep)
if not opt in self or not self[opt]:
missing_deps.append(dep)
if missing_deps != []:
- if not silent:
- print(
- "Disabling '{}' module as the following dependencies are not satisfied: {}".format(
- module, ", ".join(missing_deps)
- )
+ print(
+ "Disabling '{}' module as the following dependencies are not satisfied: {}".format(
+ module, ", ".join(missing_deps)
)
+ )
return False
else:
return True
+def sort_module_list(env):
+ out = OrderedDict()
+ deps = {k: v[0] + list(filter(lambda x: x in env.module_list, v[1])) for k, v in env.module_dependencies.items()}
+
+ frontier = list(env.module_list.keys())
+ explored = []
+ while len(frontier):
+ cur = frontier.pop()
+ deps_list = deps[cur] if cur in deps else []
+ if len(deps_list) and any([d not in explored for d in deps_list]):
+ # Will explore later, after its dependencies
+ frontier.insert(0, cur)
+ continue
+ explored.append(cur)
+ for k in explored:
+ env.module_list.move_to_end(k)
+
+
def use_windows_spawn_fix(self, platform=None):
if os.name != "nt":
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
index 61ce6185a5..a7d5c406e9 100644
--- a/modules/gdscript/config.py
+++ b/modules/gdscript/config.py
@@ -1,4 +1,5 @@
def can_build(env, platform):
+ env.module_add_dependencies("gdscript", ["jsonrpc", "websocket"], True)
return True
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index fc9d40ca48..a6324504fc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -916,7 +916,7 @@ namespace Godot
/// <c>new Color(1 - c.r, 1 - c.g, 1 - c.b, 1 - c.a)</c>.
/// </summary>
/// <param name="color">The color to invert.</param>
- /// <returns>The inverted color</returns>
+ /// <returns>The inverted color.</returns>
public static Color operator -(Color color)
{
return Colors.White - color;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index bb076a9633..236d0666bc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -239,11 +239,20 @@ namespace Godot
}
/// <summary>
- /// Converts one or more arguments of any type to string in the best way possible and prints them to the console. The following BBCode tags are supported: b, i, u, s, indent, code, url, center, right, color, bgcolor, fgcolor. Color tags only support named colors such as [code]red[/code], [i]not[/i] hexadecimal color codes. Unsupported tags will be left as-is in standard output.
- /// When printing to standard output, the supported subset of BBCode is converted to ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes is currently only supported on Linux and macOS. Support for ANSI escape codes may vary across terminal emulators, especially for italic and strikethrough.
+ /// Converts one or more arguments of any type to string in the best way possible
+ /// and prints them to the console.
+ /// The following BBCode tags are supported: b, i, u, s, indent, code, url, center,
+ /// right, color, bgcolor, fgcolor.
+ /// Color tags only support named colors such as <c>red</c>, not hexadecimal color codes.
+ /// Unsupported tags will be left as-is in standard output.
+ /// When printing to standard output, the supported subset of BBCode is converted to
+ /// ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes
+ /// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary
+ /// across terminal emulators, especially for italic and strikethrough.
///
/// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
- /// to print error and warning messages instead of <see cref="Print(object[])"/> or <see cref="PrintRich(object[])"/>.
+ /// to print error and warning messages instead of <see cref="Print(object[])"/> or
+ /// <see cref="PrintRich(object[])"/>.
/// This distinguishes them from print messages used for debugging purposes,
/// while also displaying a stack trace when an error or warning is printed.
/// </summary>
@@ -253,7 +262,6 @@ namespace Godot
/// </code>
/// </example>
/// <param name="what">Arguments that will be printed.</param>
- /// </summary>
public static void PrintRich(params object[] what)
{
godot_icall_GD_print_rich(GetPrintParams(what));
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
index 5680c9d55a..da01300586 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
@@ -18,7 +18,7 @@ namespace Godot
public StringName Name => _signalName;
/// <summary>
- /// Creates a new <see cref="Signal"/> with the name <paramref name="name"/>
+ /// Creates a new <see cref="SignalInfo"/> with the name <paramref name="name"/>
/// in the specified <paramref name="owner"/>.
/// </summary>
/// <param name="owner">Object that contains the signal.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 9c80dd0217..67f70390dd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -534,7 +534,7 @@ namespace Godot
///
/// This method also handles interpolating the lengths if the input vectors
/// have different lengths. For the special case of one or both input vectors
- /// having zero length, this method behaves like <see cref="Lerp"/>.
+ /// having zero length, this method behaves like <see cref="Lerp(Vector2, real_t)"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index c3d5fbfdef..67a98efc2d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -574,7 +574,7 @@ namespace Godot
///
/// This method also handles interpolating the lengths if the input vectors
/// have different lengths. For the special case of one or both input vectors
- /// having zero length, this method behaves like <see cref="Lerp"/>.
+ /// having zero length, this method behaves like <see cref="Lerp(Vector3, real_t)"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
diff --git a/modules/msdfgen/config.py b/modules/msdfgen/config.py
index 653e466a74..631894400d 100644
--- a/modules/msdfgen/config.py
+++ b/modules/msdfgen/config.py
@@ -1,5 +1,6 @@
def can_build(env, platform):
- return env.module_check_dependencies("msdfgen", ["freetype"])
+ env.module_add_dependencies("msdfgen", ["freetype"])
+ return True
def configure(env):
diff --git a/modules/multiplayer/editor/replication_editor_plugin.cpp b/modules/multiplayer/editor/replication_editor_plugin.cpp
index 1f79b8c3e3..50f1434ad8 100644
--- a/modules/multiplayer/editor/replication_editor_plugin.cpp
+++ b/modules/multiplayer/editor/replication_editor_plugin.cpp
@@ -255,8 +255,6 @@ void ReplicationEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_checked", "property", "column", "checked"), &ReplicationEditor::_update_checked);
ClassDB::bind_method("_can_drop_data_fw", &ReplicationEditor::_can_drop_data_fw);
ClassDB::bind_method("_drop_data_fw", &ReplicationEditor::_drop_data_fw);
-
- ADD_SIGNAL(MethodInfo("keying_changed"));
}
bool ReplicationEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
@@ -322,10 +320,6 @@ void ReplicationEditor::_notification(int p_what) {
add_pick_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
pin->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")));
} break;
-
- case NOTIFICATION_VISIBILITY_CHANGED: {
- update_keying();
- } break;
}
}
@@ -341,28 +335,15 @@ void ReplicationEditor::_add_pressed() {
return;
}
String np_text = np_line_edit->get_text();
- if (np_text.find(":") == -1) {
- np_text = ":" + np_text;
- }
- NodePath prop = NodePath(np_text);
- if (prop.is_empty()) {
- return;
- }
- UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
- undo_redo->create_action(TTR("Add property"));
- config = current->get_replication_config();
- if (config.is_null()) {
- config.instantiate();
- current->set_replication_config(config);
- undo_redo->add_do_method(current, "set_replication_config", config);
- undo_redo->add_undo_method(current, "set_replication_config", Ref<SceneReplicationConfig>());
- _update_config();
+ int idx = np_text.find(":");
+ if (idx == -1) {
+ np_text = ".:" + np_text;
+ } else if (idx == 0) {
+ np_text = "." + np_text;
}
- undo_redo->add_do_method(config.ptr(), "add_property", prop);
- undo_redo->add_undo_method(config.ptr(), "remove_property", prop);
- undo_redo->add_do_method(this, "_update_config");
- undo_redo->add_undo_method(this, "_update_config");
- undo_redo->commit_action();
+ NodePath path = NodePath(np_text);
+
+ _add_sync_property(path);
}
void ReplicationEditor::_tree_item_edited() {
@@ -440,32 +421,12 @@ void ReplicationEditor::_update_checked(const NodePath &p_prop, int p_column, bo
}
}
-void ReplicationEditor::update_keying() {
- /// TODO make keying usable.
-#if 0
- bool keying_enabled = false;
- EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
- if (is_visible_in_tree() && config.is_valid() && editor_history->get_path_size() > 0) {
- Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
- keying_enabled = Object::cast_to<Node>(obj) != nullptr;
- }
-
- if (keying_enabled == keying) {
- return;
- }
-
- keying = keying_enabled;
- emit_signal(SNAME("keying_changed"));
-#endif
-}
-
void ReplicationEditor::_update_config() {
deleting = NodePath();
tree->clear();
tree->create_item();
drop_label->set_visible(true);
if (!config.is_valid()) {
- update_keying();
return;
}
TypedArray<NodePath> props = config->get_properties();
@@ -476,7 +437,6 @@ void ReplicationEditor::_update_config() {
const NodePath path = props[i];
_add_property(path, config->property_get_spawn(path), config->property_get_sync(path));
}
- update_keying();
}
void ReplicationEditor::edit(MultiplayerSynchronizer *p_sync) {
@@ -532,43 +492,6 @@ void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn,
item->set_editable(2, true);
}
-void ReplicationEditor::property_keyed(const String &p_property) {
- ERR_FAIL_COND(!current || config.is_null());
- Node *root = current->get_node(current->get_root_path());
- ERR_FAIL_COND(!root);
- EditorSelectionHistory *history = EditorNode::get_singleton()->get_editor_selection_history();
- ERR_FAIL_COND(history->get_path_size() == 0);
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(history->get_path_object(0)));
- ERR_FAIL_COND(!node);
- if (node->is_class("MultiplayerSynchronizer")) {
- error_dialog->set_text(TTR("Properties of 'MultiplayerSynchronizer' cannot be configured for replication."));
- error_dialog->popup_centered();
- return;
- }
- if (history->get_path_size() > 1 || p_property.get_slice_count(":") > 1) {
- error_dialog->set_text(TTR("Subresources cannot yet be configured for replication."));
- error_dialog->popup_centered();
- return;
- }
-
- String path = root->get_path_to(node);
- for (int i = 1; i < history->get_path_size(); i++) {
- String prop = history->get_path_property(i);
- ERR_FAIL_COND(prop == "");
- path += ":" + prop;
- }
- path += ":" + p_property;
-
- NodePath prop = path;
- UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
- undo_redo->create_action(TTR("Add property"));
- undo_redo->add_do_method(config.ptr(), "add_property", prop);
- undo_redo->add_undo_method(config.ptr(), "remove_property", prop);
- undo_redo->add_do_method(this, "_update_config");
- undo_redo->add_undo_method(this, "_update_config");
- undo_redo->commit_action();
-}
-
/// ReplicationEditorPlugin
ReplicationEditorPlugin::ReplicationEditorPlugin() {
repl_editor = memnew(ReplicationEditor);
@@ -580,26 +503,9 @@ ReplicationEditorPlugin::ReplicationEditorPlugin() {
ReplicationEditorPlugin::~ReplicationEditorPlugin() {
}
-void ReplicationEditorPlugin::_keying_changed() {
- // TODO make lock usable.
- //InspectorDock::get_inspector_singleton()->set_keying(repl_editor->has_keying(), this);
-}
-
-void ReplicationEditorPlugin::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
- if (!repl_editor->has_keying()) {
- return;
- }
- repl_editor->property_keyed(p_keyed);
-}
-
void ReplicationEditorPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- //Node3DEditor::get_singleton()->connect("transform_key_request", callable_mp(this, &AnimationPlayerEditorPlugin::_transform_key_request));
- InspectorDock::get_inspector_singleton()->connect("property_keyed", callable_mp(this, &ReplicationEditorPlugin::_property_keyed));
- repl_editor->connect("keying_changed", callable_mp(this, &ReplicationEditorPlugin::_keying_changed));
- // TODO make lock usable.
- //InspectorDock::get_inspector_singleton()->connect("object_inspected", callable_mp(repl_editor, &ReplicationEditor::update_keying));
get_tree()->connect("node_removed", callable_mp(this, &ReplicationEditorPlugin::_node_removed));
} break;
}
@@ -635,8 +541,6 @@ bool ReplicationEditorPlugin::handles(Object *p_object) const {
void ReplicationEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- //editor->hide_animation_player_editors();
- //editor->animation_panel_make_visible(true);
button->show();
EditorNode::get_singleton()->make_bottom_panel_item_visible(repl_editor);
} else if (!repl_editor->get_pin()->is_pressed()) {
diff --git a/modules/multiplayer/editor/replication_editor_plugin.h b/modules/multiplayer/editor/replication_editor_plugin.h
index 5cc2bbe937..e60e49cc25 100644
--- a/modules/multiplayer/editor/replication_editor_plugin.h
+++ b/modules/multiplayer/editor/replication_editor_plugin.h
@@ -61,7 +61,6 @@ private:
Ref<SceneReplicationConfig> config;
NodePath deleting;
Tree *tree = nullptr;
- bool keying = false;
PropertySelector *prop_selector = nullptr;
SceneTreeDialog *pick_node = nullptr;
@@ -98,11 +97,8 @@ protected:
void _notification(int p_what);
public:
- void update_keying();
void edit(MultiplayerSynchronizer *p_object);
- bool has_keying() const { return keying; }
MultiplayerSynchronizer *get_current() const { return current; }
- void property_keyed(const String &p_property);
Button *get_pin() { return pin; }
ReplicationEditor();
@@ -117,8 +113,6 @@ private:
ReplicationEditor *repl_editor = nullptr;
void _node_removed(Node *p_node);
- void _keying_changed();
- void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
void _pinned();
@@ -133,17 +127,5 @@ public:
ReplicationEditorPlugin();
~ReplicationEditorPlugin();
};
-#else
-class ReplicationEditorPlugin : public EditorPlugin {
- GDCLASS(ReplicationEditorPlugin, EditorPlugin);
-
-public:
- virtual void edit(Object *p_object) override {}
- virtual bool handles(Object *p_object) const override { return false; }
- virtual void make_visible(bool p_visible) override {}
-
- ReplicationEditorPlugin() {}
- ~ReplicationEditorPlugin() {}
-};
#endif // REPLICATION_EDITOR_PLUGIN_H
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 81b8e1ea5d..c6678307af 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -36,8 +36,8 @@ def make_icu_data(target, source, env):
# Thirdparty source files
thirdparty_obj = []
-freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"], True)
-msdfgen_enabled = env.module_check_dependencies("text_server_adv", ["msdfgen"], True)
+freetype_enabled = "freetype" in env.module_list
+msdfgen_enabled = "msdfgen" in env.module_list
if env["builtin_harfbuzz"]:
env_harfbuzz = env_modules.Clone()
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index 8f626f02b8..429d2e1fdc 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -3,8 +3,8 @@
Import("env")
Import("env_modules")
-freetype_enabled = env.module_check_dependencies("text_server_fb", ["freetype"], True)
-msdfgen_enabled = env.module_check_dependencies("text_server_fb", ["msdfgen"], True)
+freetype_enabled = "freetype" in env.module_list
+msdfgen_enabled = "msdfgen" in env.module_list
env_text_server_fb = env_modules.Clone()
diff --git a/modules/theora/config.py b/modules/theora/config.py
index 7f354a8fda..9a27e8e132 100644
--- a/modules/theora/config.py
+++ b/modules/theora/config.py
@@ -1,7 +1,8 @@
def can_build(env, platform):
if env["arch"].startswith("rv"):
return False
- return env.module_check_dependencies("theora", ["ogg", "vorbis"])
+ env.module_add_dependencies("theora", ["ogg", "vorbis"])
+ return True
def configure(env):
diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py
index 7ce885a37a..a231ef179d 100644
--- a/modules/vorbis/config.py
+++ b/modules/vorbis/config.py
@@ -1,5 +1,6 @@
def can_build(env, platform):
- return env.module_check_dependencies("vorbis", ["ogg"])
+ env.module_add_dependencies("vorbis", ["ogg"])
+ return True
def configure(env):
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
deleted file mode 100644
index 2033098cad..0000000000
--- a/modules/websocket/emws_server.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*************************************************************************/
-/* emws_server.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "emws_server.h"
-#include "core/os/os.h"
-
-void EMWSServer::set_extra_headers(const Vector<String> &p_headers) {
-}
-
-Error EMWSServer::listen(int p_port, Vector<String> p_protocols, bool gd_mp_api) {
- return FAILED;
-}
-
-bool EMWSServer::is_listening() const {
- return false;
-}
-
-void EMWSServer::stop() {
-}
-
-bool EMWSServer::has_peer(int p_id) const {
- return false;
-}
-
-Ref<WebSocketPeer> EMWSServer::get_peer(int p_id) const {
- return nullptr;
-}
-
-Vector<String> EMWSServer::get_protocols() const {
- Vector<String> out;
-
- return out;
-}
-
-IPAddress EMWSServer::get_peer_address(int p_peer_id) const {
- return IPAddress();
-}
-
-int EMWSServer::get_peer_port(int p_peer_id) const {
- return 0;
-}
-
-void EMWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
-}
-
-void EMWSServer::poll() {
-}
-
-int EMWSServer::get_max_packet_size() const {
- return 0;
-}
-
-Error EMWSServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
- return OK;
-}
-
-EMWSServer::EMWSServer() {
-}
-
-EMWSServer::~EMWSServer() {
-}
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
deleted file mode 100644
index 14a9449605..0000000000
--- a/modules/websocket/emws_server.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*************************************************************************/
-/* emws_server.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef EMWS_SERVER_H
-#define EMWS_SERVER_H
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "core/object/ref_counted.h"
-#include "emws_peer.h"
-#include "websocket_server.h"
-
-class EMWSServer : public WebSocketServer {
- GDCIIMPL(EMWSServer, WebSocketServer);
-
-public:
- Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override;
- void set_extra_headers(const Vector<String> &p_headers) override;
- Error listen(int p_port, Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) override;
- void stop() override;
- bool is_listening() const override;
- bool has_peer(int p_id) const override;
- Ref<WebSocketPeer> get_peer(int p_id) const override;
- IPAddress get_peer_address(int p_peer_id) const override;
- int get_peer_port(int p_peer_id) const override;
- void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") override;
- int get_max_packet_size() const override;
- virtual void poll() override;
- virtual Vector<String> get_protocols() const;
-
- EMWSServer();
- ~EMWSServer();
-};
-
-#endif
-
-#endif // EMWS_SERVER_H
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index f562de111f..056111ec92 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -33,11 +33,13 @@
#include "core/config/project_settings.h"
#include "core/error/error_macros.h"
+#include "websocket_client.h"
+#include "websocket_server.h"
+
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
#include "emws_client.h"
#include "emws_peer.h"
-#include "emws_server.h"
#else
#include "wsl_client.h"
#include "wsl_server.h"
@@ -60,7 +62,6 @@ void initialize_websocket_module(ModuleInitializationLevel p_level) {
#ifdef JAVASCRIPT_ENABLED
EMWSPeer::make_default();
EMWSClient::make_default();
- EMWSServer::make_default();
#else
WSLPeer::make_default();
WSLClient::make_default();
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 678c217676..53c072c318 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -224,7 +224,7 @@ bool Light3D::is_editor_only() const {
}
void Light3D::_validate_property(PropertyInfo &property) const {
- if (!shadow && (property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur" || property.name == "distance_fade_shadow")) {
+ if (!shadow && (property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_opacity" || property.name == "shadow_blur" || property.name == "distance_fade_shadow")) {
property.usage = PROPERTY_USAGE_NO_EDITOR;
}
@@ -299,6 +299,7 @@ void Light3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_OPACITY);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BLUR);
ADD_GROUP("Distance Fade", "distance_fade_");
@@ -328,6 +329,7 @@ void Light3D::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_SHADOW_NORMAL_BIAS);
BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS);
BIND_ENUM_CONSTANT(PARAM_SHADOW_PANCAKE_SIZE);
+ BIND_ENUM_CONSTANT(PARAM_SHADOW_OPACITY);
BIND_ENUM_CONSTANT(PARAM_SHADOW_BLUR);
BIND_ENUM_CONSTANT(PARAM_SHADOW_VOLUMETRIC_FOG_FADE);
BIND_ENUM_CONSTANT(PARAM_TRANSMITTANCE_BIAS);
@@ -375,6 +377,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) {
set_param(PARAM_SHADOW_SPLIT_3_OFFSET, 0.5);
set_param(PARAM_SHADOW_FADE_START, 0.8);
set_param(PARAM_SHADOW_PANCAKE_SIZE, 20.0);
+ set_param(PARAM_SHADOW_OPACITY, 1.0);
set_param(PARAM_SHADOW_BLUR, 1.0);
set_param(PARAM_SHADOW_BIAS, 0.03);
set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0);
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index 6ff332df5a..ef003e133d 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -54,6 +54,7 @@ public:
PARAM_SHADOW_NORMAL_BIAS = RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS,
PARAM_SHADOW_BIAS = RS::LIGHT_PARAM_SHADOW_BIAS,
PARAM_SHADOW_PANCAKE_SIZE = RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE,
+ PARAM_SHADOW_OPACITY = RS::LIGHT_PARAM_SHADOW_OPACITY,
PARAM_SHADOW_BLUR = RS::LIGHT_PARAM_SHADOW_BLUR,
PARAM_SHADOW_VOLUMETRIC_FOG_FADE = RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE,
PARAM_TRANSMITTANCE_BIAS = RS::LIGHT_PARAM_TRANSMITTANCE_BIAS,
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 5cc2073ca5..b422d298b2 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -1901,13 +1901,7 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
code += "atan(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
case OP_REFLECT:
- if (op_type == OP_TYPE_VECTOR_2D) { // Not supported.
- code += "vec2(0.0);\n";
- } else if (op_type == OP_TYPE_VECTOR_4D) { // Not supported.
- code += "vec4(0.0);\n";
- } else {
- code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
- }
+ code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
@@ -1967,7 +1961,7 @@ String VisualShaderNodeVectorOp::get_warning(Shader::Mode p_mode, VisualShader::
bool invalid_type = false;
if (op_type == OP_TYPE_VECTOR_2D || op_type == OP_TYPE_VECTOR_4D) {
- if (op == OP_CROSS || op == OP_REFLECT) {
+ if (op == OP_CROSS) {
invalid_type = true;
}
}
@@ -4006,14 +4000,6 @@ int VisualShaderNodeVectorRefract::get_input_port_count() const {
return 3;
}
-VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_input_port_type(int p_port) const {
- if (p_port == 2) {
- return PORT_TYPE_SCALAR;
- }
-
- return PORT_TYPE_VECTOR_3D;
-}
-
String VisualShaderNodeVectorRefract::get_input_port_name(int p_port) const {
switch (p_port) {
case 0:
@@ -4030,10 +4016,6 @@ int VisualShaderNodeVectorRefract::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR_3D;
-}
-
String VisualShaderNodeVectorRefract::get_output_port_name(int p_port) const {
return "";
}
@@ -4042,6 +4024,31 @@ String VisualShaderNodeVectorRefract::generate_code(Shader::Mode p_mode, VisualS
return " " + p_output_vars[0] + " = refract(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
+void VisualShaderNodeVectorRefract::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
VisualShaderNodeVectorRefract::VisualShaderNodeVectorRefract() {
set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index f770156d14..ffcb41072d 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -1564,21 +1564,20 @@ public:
/// REFRACT
///////////////////////////////////////
-class VisualShaderNodeVectorRefract : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorRefract, VisualShaderNode);
+class VisualShaderNodeVectorRefract : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorRefract, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+ virtual void set_op_type(OpType p_op_type) override;
VisualShaderNodeVectorRefract();
};
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index c6f38012a6..8b3b7050d2 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -2869,7 +2869,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
WARN_PRINT_ONCE("The DirectionalLight3D PSSM splits debug draw mode is not reimplemented yet.");
}
- light_data.shadow_enabled = p_using_shadows && light_storage->light_has_shadow(base);
+ light_data.shadow_opacity = p_using_shadows && light_storage->light_has_shadow(base) ? light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_OPACITY) : 0.0;
float angular_diameter = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE);
if (angular_diameter > 0.0) {
@@ -2886,7 +2886,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
angular_diameter = 0.0;
}
- if (light_data.shadow_enabled) {
+ if (light_data.shadow_opacity > 0.001) {
RS::LightDirectionalShadowMode smode = light_storage->light_directional_get_shadow_mode(base);
int limit = smode == RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL ? 0 : (smode == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS ? 1 : 3);
@@ -3040,19 +3040,26 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
// Reuse fade begin, fade length and distance for shadow LOD determination later.
float fade_begin = 0.0;
+ float fade_shadow = 0.0;
float fade_length = 0.0;
real_t distance = 0.0;
float fade = 1.0;
+ float shadow_opacity_fade = 1.0;
if (light_storage->light_is_distance_fade_enabled(li->light)) {
fade_begin = light_storage->light_get_distance_fade_begin(li->light);
+ fade_shadow = light_storage->light_get_distance_fade_shadow(li->light);
fade_length = light_storage->light_get_distance_fade_length(li->light);
distance = camera_plane.distance_to(li->transform.origin);
+ // Use `smoothstep()` to make opacity changes more gradual and less noticeable to the player.
if (distance > fade_begin) {
- // Use `smoothstep()` to make opacity changes more gradual and less noticeable to the player.
fade = Math::smoothstep(0.0f, 1.0f, 1.0f - float(distance - fade_begin) / fade_length);
}
+
+ if (distance > fade_shadow) {
+ shadow_opacity_fade = Math::smoothstep(0.0f, 1.0f, 1.0f - float(distance - fade_shadow) / fade_length);
+ }
}
float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade;
@@ -3120,7 +3127,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
bool in_shadow_range = true;
if (needs_shadow && light_storage->light_is_distance_fade_enabled(li->light)) {
- if (distance > light_storage->light_get_distance_fade_shadow(li->light)) {
+ if (distance > light_storage->light_get_distance_fade_shadow(li->light) + light_storage->light_get_distance_fade_length(li->light)) {
// Out of range, don't draw shadows to improve performance.
in_shadow_range = false;
}
@@ -3129,7 +3136,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
if (needs_shadow && in_shadow_range) {
// fill in the shadow information
- light_data.shadow_enabled = true;
+ light_data.shadow_opacity = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_OPACITY) * shadow_opacity_fade;
float shadow_texel_size = light_instance_get_shadow_texel_size(li->self, p_shadow_atlas);
light_data.shadow_normal_bias = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * shadow_texel_size * 10.0;
@@ -3189,7 +3196,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
}
}
} else {
- light_data.shadow_enabled = false;
+ light_data.shadow_opacity = 0.0;
}
li->cull_mask = light_storage->light_get_cull_mask(base);
@@ -3637,7 +3644,7 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool
if (p_render_data->render_buffers.is_valid()) {
bool directional_shadows = false;
for (uint32_t i = 0; i < directional_light_count; i++) {
- if (cluster.directional_lights[i].shadow_enabled) {
+ if (cluster.directional_lights[i].shadow_opacity > 0.001) {
directional_shadows = true;
break;
}
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 09ec2a9efd..22e9ead243 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -608,7 +608,7 @@ private:
float inv_spot_attenuation;
float cos_spot_angle;
float specular_amount;
- uint32_t shadow_enabled;
+ float shadow_opacity;
float atlas_rect[4]; // in omni, used for atlas uv, in spot, used for projector uv
float shadow_matrix[16];
@@ -633,7 +633,7 @@ private:
float softshadow_angle;
float soft_shadow_scale;
uint32_t blend_splits;
- uint32_t shadow_enabled;
+ float shadow_opacity;
float fade_from;
float fade_to;
uint32_t pad[2];
diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
index e74cfad65c..6f79b9e771 100644
--- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
@@ -377,7 +377,7 @@ void main() {
for (uint i = 0; i < params.directional_light_count; i++) {
vec3 shadow_attenuation = vec3(1.0);
- if (directional_lights.data[i].shadow_enabled) {
+ if (directional_lights.data[i].shadow_opacity > 0.001) {
float depth_z = -view_pos.z;
vec4 pssm_coord;
@@ -486,7 +486,7 @@ void main() {
vec3 light = omni_lights.data[light_index].color;
- if (omni_lights.data[light_index].shadow_enabled) {
+ if (omni_lights.data[light_index].shadow_opacity > 0.001) {
//has shadow
vec4 uv_rect = omni_lights.data[light_index].atlas_rect;
vec2 flip_offset = omni_lights.data[light_index].direction.xy;
@@ -572,7 +572,7 @@ void main() {
vec3 light = spot_lights.data[light_index].color;
- if (spot_lights.data[light_index].shadow_enabled) {
+ if (spot_lights.data[light_index].shadow_opacity > 0.001) {
//has shadow
vec4 uv_rect = spot_lights.data[light_index].atlas_rect;
vec2 flip_offset = spot_lights.data[light_index].direction.xy;
diff --git a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl
index 61c8488a05..799f7087b6 100644
--- a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl
@@ -15,7 +15,7 @@ struct LightData { //this structure needs to be as packed as possible
mediump float cone_attenuation;
mediump float cone_angle;
mediump float specular_amount;
- bool shadow_enabled;
+ mediump float shadow_opacity;
highp vec4 atlas_rect; // rect in the shadow atlas
highp mat4 shadow_matrix;
@@ -60,7 +60,7 @@ struct DirectionalLightData {
highp float softshadow_angle;
highp float soft_shadow_scale;
bool blend_splits;
- bool shadow_enabled;
+ mediump float shadow_opacity;
highp float fade_from;
highp float fade_to;
uvec2 pad;
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
index 22058b3a06..dff24b7144 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
@@ -1417,7 +1417,7 @@ void fragment_shader(in SceneData scene_data) {
float shadow = 1.0;
- if (directional_lights.data[i].shadow_enabled) {
+ if (directional_lights.data[i].shadow_opacity > 0.001) {
float depth_z = -vertex.z;
vec3 light_dir = directional_lights.data[i].direction;
vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp))));
@@ -1626,7 +1626,7 @@ void fragment_shader(in SceneData scene_data) {
#ifdef LIGHT_TRANSMITTANCE_USED
float transmittance_z = transmittance_depth;
- if (directional_lights.data[i].shadow_enabled) {
+ if (directional_lights.data[i].shadow_opacity > 0.001) {
float depth_z = -vertex.z;
if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
@@ -1681,6 +1681,8 @@ void fragment_shader(in SceneData scene_data) {
} else {
shadow = float(shadow1 >> ((i - 4) * 8) & 0xFF) / 255.0;
}
+
+ shadow = shadow * directional_lights.data[i].shadow_opacity + 1.0 - directional_lights.data[i].shadow_opacity;
#endif
blur_shadow(shadow);
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index e27c81eaea..50a13de11a 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -395,7 +395,7 @@ float get_omni_attenuation(float distance, float inv_range, float decay) {
float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
#ifndef SHADOWS_DISABLED
- if (omni_lights.data[idx].shadow_enabled) {
+ if (omni_lights.data[idx].shadow_opacity > 0.001) {
// there is a shadowmap
vec2 texel_size = scene_data_block.data.shadow_atlas_pixel_size;
vec4 base_uv_rect = omni_lights.data[idx].atlas_rect;
@@ -498,6 +498,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
}
shadow /= float(sc_penumbra_shadow_samples);
+ shadow = mix(1.0, shadow, omni_lights.data[idx].shadow_opacity);
} else {
//no blockers found, so no shadow
@@ -516,7 +517,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
vec2 pos = shadow_sample.xy / shadow_sample.z;
float depth = shadow_len - omni_lights.data[idx].shadow_bias;
depth *= omni_lights.data[idx].inv_radius;
- shadow = sample_omni_pcf_shadow(shadow_atlas, omni_lights.data[idx].soft_shadow_scale / shadow_sample.z, pos, uv_rect, flip_offset, depth);
+ shadow = mix(1.0, sample_omni_pcf_shadow(shadow_atlas, omni_lights.data[idx].soft_shadow_scale / shadow_sample.z, pos, uv_rect, flip_offset, depth), omni_lights.data[idx].shadow_opacity);
}
return shadow;
@@ -674,7 +675,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
#ifndef SHADOWS_DISABLED
- if (spot_lights.data[idx].shadow_enabled) {
+ if (spot_lights.data[idx].shadow_opacity > 0.001) {
vec3 light_rel_vec = spot_lights.data[idx].position - vertex;
float light_length = length(light_rel_vec);
vec3 spot_dir = spot_lights.data[idx].direction;
@@ -735,6 +736,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
}
shadow /= float(sc_penumbra_shadow_samples);
+ shadow = mix(1.0, shadow, spot_lights.data[idx].shadow_opacity);
} else {
//no blockers found, so no shadow
@@ -743,7 +745,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
} else {
//hard shadow
vec3 shadow_uv = vec3(splane.xy * spot_lights.data[idx].atlas_rect.zw + spot_lights.data[idx].atlas_rect.xy, splane.z);
- shadow = sample_pcf_shadow(shadow_atlas, spot_lights.data[idx].soft_shadow_scale * scene_data_block.data.shadow_atlas_pixel_size, shadow_uv);
+ shadow = mix(1.0, sample_pcf_shadow(shadow_atlas, spot_lights.data[idx].soft_shadow_scale * scene_data_block.data.shadow_atlas_pixel_size, shadow_uv), spot_lights.data[idx].shadow_opacity);
}
return shadow;
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
index 26d0de46c2..eb73a33558 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
@@ -1134,7 +1134,7 @@ void main() {
#ifdef USE_SOFT_SHADOWS
//version with soft shadows, more expensive
- if (directional_lights.data[i].shadow_enabled) {
+ if (directional_lights.data[i].shadow_opacity > 0.001) {
float depth_z = -vertex.z;
vec4 pssm_coord;
@@ -1286,7 +1286,7 @@ void main() {
#else
// Soft shadow disabled version
- if (directional_lights.data[i].shadow_enabled) {
+ if (directional_lights.data[i].shadow_opacity > 0.001) {
float depth_z = -vertex.z;
vec4 pssm_coord;
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index d3831b4618..882afdfa54 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -88,6 +88,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) {
light.param[RS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8;
light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0;
light.param[RS::LIGHT_PARAM_SHADOW_BIAS] = 0.02;
+ light.param[RS::LIGHT_PARAM_SHADOW_OPACITY] = 1.0;
light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0;
light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0;
light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 0.1;
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 019f10fe38..839f5b8eea 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -2463,11 +2463,15 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
// reflect
+ { "reflect", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "I", "N" }, TAG_GLOBAL, false },
{ "reflect", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "I", "N" }, TAG_GLOBAL, false },
+ { "reflect", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "I", "N" }, TAG_GLOBAL, false },
// refract
+ { "refract", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "I", "N", "eta" }, TAG_GLOBAL, false },
{ "refract", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "I", "N", "eta" }, TAG_GLOBAL, false },
+ { "refract", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "I", "N", "eta" }, TAG_GLOBAL, false },
// faceforward
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 1ac9b5854d..a54d4f0384 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -1911,6 +1911,7 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_NORMAL_BIAS);
BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_BIAS);
BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_PANCAKE_SIZE);
+ BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_OPACITY);
BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_BLUR);
BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE);
BIND_ENUM_CONSTANT(LIGHT_PARAM_TRANSMITTANCE_BIAS);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 297736aec7..a2fe9caf19 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -425,6 +425,7 @@ public:
LIGHT_PARAM_SHADOW_NORMAL_BIAS,
LIGHT_PARAM_SHADOW_BIAS,
LIGHT_PARAM_SHADOW_PANCAKE_SIZE,
+ LIGHT_PARAM_SHADOW_OPACITY,
LIGHT_PARAM_SHADOW_BLUR,
LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE,
LIGHT_PARAM_TRANSMITTANCE_BIAS,
diff --git a/tests/core/math/test_vector4.h b/tests/core/math/test_vector4.h
new file mode 100644
index 0000000000..4b8759c0ca
--- /dev/null
+++ b/tests/core/math/test_vector4.h
@@ -0,0 +1,312 @@
+/*************************************************************************/
+/* test_vector4.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VECTOR4_H
+#define TEST_VECTOR4_H
+
+#include "core/math/vector4.h"
+#include "tests/test_macros.h"
+
+#define Math_SQRT3 1.7320508075688772935274463415059
+
+namespace TestVector4 {
+
+TEST_CASE("[Vector4] Axis methods") {
+ Vector4 vector = Vector4(1.2, 3.4, 5.6, -0.9);
+ CHECK_MESSAGE(
+ vector.max_axis_index() == Vector4::Axis::AXIS_Z,
+ "Vector4 max_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.min_axis_index() == Vector4::Axis::AXIS_W,
+ "Vector4 min_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.get_axis(vector.max_axis_index()) == (real_t)5.6,
+ "Vector4 get_axis should work as expected.");
+ CHECK_MESSAGE(
+ vector[vector.min_axis_index()] == (real_t)-0.9,
+ "Vector4 array operator should work as expected.");
+
+ vector.set_axis(Vector4::Axis::AXIS_Y, 4.7);
+ CHECK_MESSAGE(
+ vector.get_axis(Vector4::Axis::AXIS_Y) == (real_t)4.7,
+ "Vector4 set_axis should work as expected.");
+ vector[Vector4::Axis::AXIS_Y] = 3.7;
+ CHECK_MESSAGE(
+ vector[Vector4::Axis::AXIS_Y] == (real_t)3.7,
+ "Vector4 array operator setter should work as expected.");
+}
+
+TEST_CASE("[Vector4] Interpolation methods") {
+ const Vector4 vector1 = Vector4(1, 2, 3, 4);
+ const Vector4 vector2 = Vector4(4, 5, 6, 7);
+ CHECK_MESSAGE(
+ vector1.lerp(vector2, 0.5) == Vector4(2.5, 3.5, 4.5, 5.5),
+ "Vector4 lerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.lerp(vector2, 1.0 / 3.0).is_equal_approx(Vector4(2, 3, 4, 5)),
+ "Vector4 lerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.cubic_interpolate(vector2, Vector4(), Vector4(7, 7, 7, 7), 0.5) == Vector4(2.375, 3.5, 4.625, 5.75),
+ "Vector4 cubic_interpolate should work as expected.");
+ CHECK_MESSAGE(
+ vector1.cubic_interpolate(vector2, Vector4(), Vector4(7, 7, 7, 7), 1.0 / 3.0).is_equal_approx(Vector4(1.851851940155029297, 2.962963104248046875, 4.074074268341064453, 5.185185185185)),
+ "Vector4 cubic_interpolate should work as expected.");
+}
+
+TEST_CASE("[Vector4] Length methods") {
+ const Vector4 vector1 = Vector4(10, 10, 10, 10);
+ const Vector4 vector2 = Vector4(20, 30, 40, 50);
+ CHECK_MESSAGE(
+ vector1.length_squared() == 400,
+ "Vector4 length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.length(), 20),
+ "Vector4 length should work as expected.");
+ CHECK_MESSAGE(
+ vector2.length_squared() == 5400,
+ "Vector4 length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector2.length(), (real_t)73.484692283495),
+ "Vector4 length should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.distance_to(vector2), (real_t)54.772255750517),
+ "Vector4 distance_to should work as expected.");
+}
+
+TEST_CASE("[Vector4] Limiting methods") {
+ const Vector4 vector = Vector4(10, 10, 10, 10);
+ CHECK_MESSAGE(
+ Vector4(-5, 5, 15, -15).clamp(Vector4(), vector) == Vector4(0, 5, 10, 0),
+ "Vector4 clamp should work as expected.");
+ CHECK_MESSAGE(
+ vector.clamp(Vector4(0, 10, 15, 18), Vector4(5, 10, 20, 25)) == Vector4(5, 10, 15, 18),
+ "Vector4 clamp should work as expected.");
+}
+
+TEST_CASE("[Vector4] Normalization methods") {
+ CHECK_MESSAGE(
+ Vector4(1, 0, 0, 0).is_normalized() == true,
+ "Vector4 is_normalized should return true for a normalized vector.");
+ CHECK_MESSAGE(
+ Vector4(1, 1, 1, 1).is_normalized() == false,
+ "Vector4 is_normalized should return false for a non-normalized vector.");
+ CHECK_MESSAGE(
+ Vector4(1, 0, 0, 0).normalized() == Vector4(1, 0, 0, 0),
+ "Vector4 normalized should return the same vector for a normalized vector.");
+ CHECK_MESSAGE(
+ Vector4(1, 1, 0, 0).normalized().is_equal_approx(Vector4(Math_SQRT12, Math_SQRT12, 0, 0)),
+ "Vector4 normalized should work as expected.");
+ CHECK_MESSAGE(
+ Vector4(1, 1, 1, 1).normalized().is_equal_approx(Vector4(0.5, 0.5, 0.5, 0.5)),
+ "Vector4 normalized should work as expected.");
+}
+
+TEST_CASE("[Vector4] Operators") {
+ const Vector4 decimal1 = Vector4(2.3, 4.9, 7.8, 3.2);
+ const Vector4 decimal2 = Vector4(1.2, 3.4, 5.6, 1.7);
+ const Vector4 power1 = Vector4(0.75, 1.5, 0.625, 0.125);
+ const Vector4 power2 = Vector4(0.5, 0.125, 0.25, 0.75);
+ const Vector4 int1 = Vector4(4, 5, 9, 2);
+ const Vector4 int2 = Vector4(1, 2, 3, 1);
+
+ CHECK_MESSAGE(
+ -decimal1 == Vector4(-2.3, -4.9, -7.8, -3.2),
+ "Vector4 change of sign should work as expected.");
+ CHECK_MESSAGE(
+ (decimal1 + decimal2).is_equal_approx(Vector4(3.5, 8.3, 13.4, 4.9)),
+ "Vector4 addition should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 + power2) == Vector4(1.25, 1.625, 0.875, 0.875),
+ "Vector4 addition with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 + int2) == Vector4(5, 7, 12, 3),
+ "Vector4 addition with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 - decimal2).is_equal_approx(Vector4(1.1, 1.5, 2.2, 1.5)),
+ "Vector4 subtraction should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 - power2) == Vector4(0.25, 1.375, 0.375, -0.625),
+ "Vector4 subtraction with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 - int2) == Vector4(3, 3, 6, 1),
+ "Vector4 subtraction with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 * decimal2).is_equal_approx(Vector4(2.76, 16.66, 43.68, 5.44)),
+ "Vector4 multiplication should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 * power2) == Vector4(0.375, 0.1875, 0.15625, 0.09375),
+ "Vector4 multiplication with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 * int2) == Vector4(4, 10, 27, 2),
+ "Vector4 multiplication with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 / decimal2).is_equal_approx(Vector4(1.91666666666666666, 1.44117647058823529, 1.39285714285714286, 1.88235294118)),
+ "Vector4 division should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 / power2) == Vector4(1.5, 12.0, 2.5, 1.0 / 6.0),
+ "Vector4 division with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 / int2) == Vector4(4, 2.5, 3, 2),
+ "Vector4 division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 * 2).is_equal_approx(Vector4(4.6, 9.8, 15.6, 6.4)),
+ "Vector4 multiplication should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 * 2) == Vector4(1.5, 3, 1.25, 0.25),
+ "Vector4 multiplication with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 * 2) == Vector4(8, 10, 18, 4),
+ "Vector4 multiplication with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 / 2).is_equal_approx(Vector4(1.15, 2.45, 3.9, 1.6)),
+ "Vector4 division should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 / 2) == Vector4(0.375, 0.75, 0.3125, 0.0625),
+ "Vector4 division with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 / 2) == Vector4(2, 2.5, 4.5, 1),
+ "Vector4 division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ ((String)decimal1) == "(2.3, 4.9, 7.8, 3.2)",
+ "Vector4 cast to String should work as expected.");
+ CHECK_MESSAGE(
+ ((String)decimal2) == "(1.2, 3.4, 5.6, 1.7)",
+ "Vector4 cast to String should work as expected.");
+ CHECK_MESSAGE(
+ ((String)Vector4(9.7, 9.8, 9.9, -1.8)) == "(9.7, 9.8, 9.9, -1.8)",
+ "Vector4 cast to String should work as expected.");
+#ifdef REAL_T_IS_DOUBLE
+ CHECK_MESSAGE(
+ ((String)Vector4(Math_E, Math_SQRT2, Math_SQRT3, Math_SQRT3)) == "(2.71828182845905, 1.4142135623731, 1.73205080756888, 1.73205080756888)",
+ "Vector4 cast to String should print the correct amount of digits for real_t = double.");
+#else
+ CHECK_MESSAGE(
+ ((String)Vector4(Math_E, Math_SQRT2, Math_SQRT3, Math_SQRT3)) == "(2.718282, 1.414214, 1.732051, 1.732051)",
+ "Vector4 cast to String should print the correct amount of digits for real_t = float.");
+#endif // REAL_T_IS_DOUBLE
+}
+
+TEST_CASE("[Vector4] Other methods") {
+ const Vector4 vector = Vector4(1.2, 3.4, 5.6, 1.6);
+ CHECK_MESSAGE(
+ vector.direction_to(Vector4()).is_equal_approx(-vector.normalized()),
+ "Vector4 direction_to should work as expected.");
+ CHECK_MESSAGE(
+ Vector4(1, 1, 1, 1).direction_to(Vector4(2, 2, 2, 2)).is_equal_approx(Vector4(0.5, 0.5, 0.5, 0.5)),
+ "Vector4 direction_to should work as expected.");
+ CHECK_MESSAGE(
+ vector.inverse().is_equal_approx(Vector4(1 / 1.2, 1 / 3.4, 1 / 5.6, 1 / 1.6)),
+ "Vector4 inverse should work as expected.");
+ CHECK_MESSAGE(
+ vector.posmod(2).is_equal_approx(Vector4(1.2, 1.4, 1.6, 1.6)),
+ "Vector4 posmod should work as expected.");
+ CHECK_MESSAGE(
+ (-vector).posmod(2).is_equal_approx(Vector4(0.8, 0.6, 0.4, 0.4)),
+ "Vector4 posmod should work as expected.");
+ CHECK_MESSAGE(
+ vector.posmodv(Vector4(1, 2, 3, 4)).is_equal_approx(Vector4(0.2, 1.4, 2.6, 1.6)),
+ "Vector4 posmodv should work as expected.");
+ CHECK_MESSAGE(
+ (-vector).posmodv(Vector4(2, 3, 4, 5)).is_equal_approx(Vector4(0.8, 2.6, 2.4, 3.4)),
+ "Vector4 posmodv should work as expected.");
+ CHECK_MESSAGE(
+ vector.snapped(Vector4(1, 1, 1, 1)) == Vector4(1, 3, 6, 2),
+ "Vector4 snapped to integers should be the same as rounding.");
+ CHECK_MESSAGE(
+ vector.snapped(Vector4(0.25, 0.25, 0.25, 0.25)) == Vector4(1.25, 3.5, 5.5, 1.5),
+ "Vector4 snapped to 0.25 should give exact results.");
+}
+
+TEST_CASE("[Vector4] Rounding methods") {
+ const Vector4 vector1 = Vector4(1.2, 3.4, 5.6, 1.6);
+ const Vector4 vector2 = Vector4(1.2, -3.4, -5.6, -1.6);
+ CHECK_MESSAGE(
+ vector1.abs() == vector1,
+ "Vector4 abs should work as expected.");
+ CHECK_MESSAGE(
+ vector2.abs() == vector1,
+ "Vector4 abs should work as expected.");
+ CHECK_MESSAGE(
+ vector1.ceil() == Vector4(2, 4, 6, 2),
+ "Vector4 ceil should work as expected.");
+ CHECK_MESSAGE(
+ vector2.ceil() == Vector4(2, -3, -5, -1),
+ "Vector4 ceil should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.floor() == Vector4(1, 3, 5, 1),
+ "Vector4 floor should work as expected.");
+ CHECK_MESSAGE(
+ vector2.floor() == Vector4(1, -4, -6, -2),
+ "Vector4 floor should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.round() == Vector4(1, 3, 6, 2),
+ "Vector4 round should work as expected.");
+ CHECK_MESSAGE(
+ vector2.round() == Vector4(1, -3, -6, -2),
+ "Vector4 round should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.sign() == Vector4(1, 1, 1, 1),
+ "Vector4 sign should work as expected.");
+ CHECK_MESSAGE(
+ vector2.sign() == Vector4(1, -1, -1, -1),
+ "Vector4 sign should work as expected.");
+}
+
+TEST_CASE("[Vector4] Linear algebra methods") {
+ const Vector4 vector_x = Vector4(1, 0, 0, 0);
+ const Vector4 vector_y = Vector4(0, 1, 0, 0);
+ const Vector4 vector1 = Vector4(1.7, 2.3, 1, 9.1);
+ const Vector4 vector2 = Vector4(-8.2, -16, 3, 2.4);
+
+ CHECK_MESSAGE(
+ vector_x.dot(vector_y) == 0.0,
+ "Vector4 dot product of perpendicular vectors should be zero.");
+ CHECK_MESSAGE(
+ vector_x.dot(vector_x) == 1.0,
+ "Vector4 dot product of identical unit vectors should be one.");
+ CHECK_MESSAGE(
+ (vector_x * 10).dot(vector_x * 10) == 100.0,
+ "Vector4 dot product of same direction vectors should behave as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx((vector1 * 2).dot(vector2 * 4), (real_t)-25.9 * 8),
+ "Vector4 dot product should work as expected.");
+}
+} // namespace TestVector4
+
+#endif // TEST_VECTOR4_H
diff --git a/tests/core/math/test_vector4i.h b/tests/core/math/test_vector4i.h
new file mode 100644
index 0000000000..ac63001b24
--- /dev/null
+++ b/tests/core/math/test_vector4i.h
@@ -0,0 +1,148 @@
+/*************************************************************************/
+/* test_vector4i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VECTOR4I_H
+#define TEST_VECTOR4I_H
+
+#include "core/math/vector4i.h"
+#include "tests/test_macros.h"
+
+namespace TestVector4i {
+
+TEST_CASE("[Vector4i] Axis methods") {
+ Vector4i vector = Vector4i(1, 2, 3, 4);
+ CHECK_MESSAGE(
+ vector.max_axis_index() == Vector4i::Axis::AXIS_W,
+ "Vector4i max_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.min_axis_index() == Vector4i::Axis::AXIS_X,
+ "Vector4i min_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.get_axis(vector.max_axis_index()) == 4,
+ "Vector4i get_axis should work as expected.");
+ CHECK_MESSAGE(
+ vector[vector.min_axis_index()] == 1,
+ "Vector4i array operator should work as expected.");
+
+ vector.set_axis(Vector4i::Axis::AXIS_Y, 5);
+ CHECK_MESSAGE(
+ vector.get_axis(Vector4i::Axis::AXIS_Y) == 5,
+ "Vector4i set_axis should work as expected.");
+ vector[Vector4i::Axis::AXIS_Y] = 5;
+ CHECK_MESSAGE(
+ vector[Vector4i::Axis::AXIS_Y] == 5,
+ "Vector4i array operator setter should work as expected.");
+}
+
+TEST_CASE("[Vector4i] Clamp method") {
+ const Vector4i vector = Vector4i(10, 10, 10, 10);
+ CHECK_MESSAGE(
+ Vector4i(-5, 5, 15, INT_MAX).clamp(Vector4i(), vector) == Vector4i(0, 5, 10, 10),
+ "Vector4i clamp should work as expected.");
+ CHECK_MESSAGE(
+ vector.clamp(Vector4i(0, 10, 15, -10), Vector4i(5, 10, 20, -5)) == Vector4i(5, 10, 15, -5),
+ "Vector4i clamp should work as expected.");
+}
+
+TEST_CASE("[Vector4i] Length methods") {
+ const Vector4i vector1 = Vector4i(10, 10, 10, 10);
+ const Vector4i vector2 = Vector4i(20, 30, 40, 50);
+ CHECK_MESSAGE(
+ vector1.length_squared() == 400,
+ "Vector4i length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.length(), 20),
+ "Vector4i length should work as expected.");
+ CHECK_MESSAGE(
+ vector2.length_squared() == 5400,
+ "Vector4i length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector2.length(), 73.4846922835),
+ "Vector4i length should work as expected.");
+}
+
+TEST_CASE("[Vector4i] Operators") {
+ const Vector4i vector1 = Vector4i(4, 5, 9, 2);
+ const Vector4i vector2 = Vector4i(1, 2, 3, 4);
+
+ CHECK_MESSAGE(
+ -vector1 == Vector4i(-4, -5, -9, -2),
+ "Vector4i change of sign should work as expected.");
+ CHECK_MESSAGE(
+ (vector1 + vector2) == Vector4i(5, 7, 12, 6),
+ "Vector4i addition with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 - vector2) == Vector4i(3, 3, 6, -2),
+ "Vector4i subtraction with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 * vector2) == Vector4i(4, 10, 27, 8),
+ "Vector4i multiplication with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 / vector2) == Vector4i(4, 2, 3, 0),
+ "Vector4i division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (vector1 * 2) == Vector4i(8, 10, 18, 4),
+ "Vector4i multiplication with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 / 2) == Vector4i(2, 2, 4, 1),
+ "Vector4i division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ ((Vector4)vector1) == Vector4(4, 5, 9, 2),
+ "Vector4i cast to Vector4 should work as expected.");
+ CHECK_MESSAGE(
+ ((Vector4)vector2) == Vector4(1, 2, 3, 4),
+ "Vector4i cast to Vector4 should work as expected.");
+ CHECK_MESSAGE(
+ Vector4i(Vector4(1.1, 2.9, 3.9, 100.5)) == Vector4i(1, 2, 3, 100),
+ "Vector4i constructed from Vector4 should work as expected.");
+}
+
+TEST_CASE("[Vector4i] Abs and sign methods") {
+ const Vector4i vector1 = Vector4i(1, 3, 5, 7);
+ const Vector4i vector2 = Vector4i(1, -3, -5, 7);
+ CHECK_MESSAGE(
+ vector1.abs() == vector1,
+ "Vector4i abs should work as expected.");
+ CHECK_MESSAGE(
+ vector2.abs() == vector1,
+ "Vector4i abs should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.sign() == Vector4i(1, 1, 1, 1),
+ "Vector4i sign should work as expected.");
+ CHECK_MESSAGE(
+ vector2.sign() == Vector4i(1, -1, -1, 1),
+ "Vector4i sign should work as expected.");
+}
+} // namespace TestVector4i
+
+#endif // TEST_VECTOR4I_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 628b9cbc3c..3d186711cb 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -58,6 +58,8 @@
#include "tests/core/math/test_vector2i.h"
#include "tests/core/math/test_vector3.h"
#include "tests/core/math/test_vector3i.h"
+#include "tests/core/math/test_vector4.h"
+#include "tests/core/math/test_vector4i.h"
#include "tests/core/object/test_class_db.h"
#include "tests/core/object/test_method_bind.h"
#include "tests/core/object/test_object.h"