summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/extension/native_extension.cpp2
-rw-r--r--core/templates/cowdata.h32
-rw-r--r--core/templates/vector.h2
-rw-r--r--core/variant/variant_call.cpp27
-rw-r--r--doc/classes/PackedByteArray.xml23
-rw-r--r--doc/classes/PackedColorArray.xml23
-rw-r--r--doc/classes/PackedFloat32Array.xml23
-rw-r--r--doc/classes/PackedFloat64Array.xml23
-rw-r--r--doc/classes/PackedInt32Array.xml23
-rw-r--r--doc/classes/PackedInt64Array.xml23
-rw-r--r--doc/classes/PackedStringArray.xml23
-rw-r--r--doc/classes/PackedVector2Array.xml23
-rw-r--r--doc/classes/PackedVector3Array.xml23
-rw-r--r--drivers/gles3/storage/light_storage.cpp241
-rw-r--r--drivers/gles3/storage/light_storage.h192
-rw-r--r--editor/editor_file_dialog.cpp2
-rw-r--r--editor/editor_file_dialog.h2
-rw-r--r--editor/editor_plugin_settings.cpp5
-rw-r--r--editor/editor_plugin_settings.h2
-rw-r--r--editor/filesystem_dock.cpp12
-rw-r--r--editor/filesystem_dock.h4
-rw-r--r--editor/plugin_config_dialog.cpp56
-rw-r--r--editor/plugin_config_dialog.h1
-rw-r--r--modules/text_server_adv/text_server_adv.cpp5
-rw-r--r--modules/text_server_fb/text_server_fb.cpp5
-rw-r--r--platform/windows/display_server_windows.cpp12
-rw-r--r--platform/windows/display_server_windows.h1
-rw-r--r--tests/core/math/test_geometry_2d.h301
28 files changed, 1020 insertions, 91 deletions
diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp
index db1cbd53b4..5738b42049 100644
--- a/core/extension/native_extension.cpp
+++ b/core/extension/native_extension.cpp
@@ -426,7 +426,7 @@ Ref<Resource> NativeExtensionResourceLoader::load(const String &p_path, const St
return Ref<Resource>();
}
- if (!library_path.is_resource_file()) {
+ if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
library_path = p_path.get_base_dir().plus_file(library_path);
}
diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h
index f1ac32928f..e760fc2176 100644
--- a/core/templates/cowdata.h
+++ b/core/templates/cowdata.h
@@ -183,6 +183,8 @@ public:
}
int find(const T &p_val, int p_from = 0) const;
+ int rfind(const T &p_val, int p_from = -1) const;
+ int count(const T &p_val) const;
_FORCE_INLINE_ CowData() {}
_FORCE_INLINE_ ~CowData();
@@ -350,6 +352,36 @@ int CowData<T>::find(const T &p_val, int p_from) const {
}
template <class T>
+int CowData<T>::rfind(const T &p_val, int p_from) const {
+ const int s = size();
+
+ if (p_from < 0) {
+ p_from = s + p_from;
+ }
+ if (p_from < 0 || p_from >= s) {
+ p_from = s - 1;
+ }
+
+ for (int i = p_from; i >= 0; i--) {
+ if (get(i) == p_val) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+template <class T>
+int CowData<T>::count(const T &p_val) const {
+ int amount = 0;
+ for (int i = 0; i < size(); i++) {
+ if (get(i) == p_val) {
+ amount++;
+ }
+ }
+ return amount;
+}
+
+template <class T>
void CowData<T>::_ref(const CowData *p_from) {
_ref(*p_from);
}
diff --git a/core/templates/vector.h b/core/templates/vector.h
index d87e76139b..2ac7c7630a 100644
--- a/core/templates/vector.h
+++ b/core/templates/vector.h
@@ -92,6 +92,8 @@ public:
_FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); }
Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); }
+ int rfind(const T &p_val, int p_from = -1) const { return _cowdata.rfind(p_val, p_from); }
+ int count(const T &p_val) const { return _cowdata.count(p_val); }
void append_array(Vector<T> p_other);
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index f4b2af5a94..882a89b8ba 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1873,6 +1873,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedByteArray, sort, sarray(), varray());
bind_method(PackedByteArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedByteArray, duplicate, sarray(), varray());
+ bind_method(PackedByteArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedByteArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedByteArray, count, sarray("value"), varray());
bind_function(PackedByteArray, get_string_from_ascii, _VariantCall::func_PackedByteArray_get_string_from_ascii, sarray(), varray());
bind_function(PackedByteArray, get_string_from_utf8, _VariantCall::func_PackedByteArray_get_string_from_utf8, sarray(), varray());
@@ -1935,6 +1938,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt32Array, sort, sarray(), varray());
bind_method(PackedInt32Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedInt32Array, duplicate, sarray(), varray());
+ bind_method(PackedInt32Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedInt32Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedInt32Array, count, sarray("value"), varray());
/* Int64 Array */
@@ -1955,6 +1961,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt64Array, sort, sarray(), varray());
bind_method(PackedInt64Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedInt64Array, duplicate, sarray(), varray());
+ bind_method(PackedInt64Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedInt64Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedInt64Array, count, sarray("value"), varray());
/* Float32 Array */
@@ -1975,6 +1984,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat32Array, sort, sarray(), varray());
bind_method(PackedFloat32Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedFloat32Array, duplicate, sarray(), varray());
+ bind_method(PackedFloat32Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedFloat32Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedFloat32Array, count, sarray("value"), varray());
/* Float64 Array */
@@ -1995,6 +2007,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat64Array, sort, sarray(), varray());
bind_method(PackedFloat64Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedFloat64Array, duplicate, sarray(), varray());
+ bind_method(PackedFloat64Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedFloat64Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedFloat64Array, count, sarray("value"), varray());
/* String Array */
@@ -2015,6 +2030,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedStringArray, sort, sarray(), varray());
bind_method(PackedStringArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedStringArray, duplicate, sarray(), varray());
+ bind_method(PackedStringArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedStringArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedStringArray, count, sarray("value"), varray());
/* Vector2 Array */
@@ -2035,6 +2053,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector2Array, sort, sarray(), varray());
bind_method(PackedVector2Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedVector2Array, duplicate, sarray(), varray());
+ bind_method(PackedVector2Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedVector2Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedVector2Array, count, sarray("value"), varray());
/* Vector3 Array */
@@ -2055,6 +2076,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector3Array, sort, sarray(), varray());
bind_method(PackedVector3Array, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedVector3Array, duplicate, sarray(), varray());
+ bind_method(PackedVector3Array, find, sarray("value", "from"), varray(0));
+ bind_method(PackedVector3Array, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedVector3Array, count, sarray("value"), varray());
/* Color Array */
@@ -2075,6 +2099,9 @@ static void _register_variant_builtin_methods() {
bind_method(PackedColorArray, sort, sarray(), varray());
bind_method(PackedColorArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedColorArray, duplicate, sarray(), varray());
+ bind_method(PackedColorArray, find, sarray("value", "from"), varray(0));
+ bind_method(PackedColorArray, rfind, sarray("value", "from"), varray(-1));
+ bind_method(PackedColorArray, count, sarray("value"), varray());
/* Register constants */
diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml
index 61dc1eef00..5d0861bcf3 100644
--- a/doc/classes/PackedByteArray.xml
+++ b/doc/classes/PackedByteArray.xml
@@ -61,6 +61,13 @@
Returns a new [PackedByteArray] with the data compressed. Set the compression mode using one of [enum File.CompressionMode]'s constants.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="decode_double" qualifiers="const">
<return type="float" />
<argument index="0" name="byte_offset" type="int" />
@@ -257,6 +264,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="get_string_from_ascii" qualifiers="const">
<return type="String" />
<description>
@@ -352,6 +367,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedColorArray.xml b/doc/classes/PackedColorArray.xml
index f880771c77..12a553af49 100644
--- a/doc/classes/PackedColorArray.xml
+++ b/doc/classes/PackedColorArray.xml
@@ -54,6 +54,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedColorArray" />
<description>
@@ -67,6 +74,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Color" />
@@ -115,6 +130,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Color" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedFloat32Array.xml b/doc/classes/PackedFloat32Array.xml
index e2b877ad5f..0a114e6c06 100644
--- a/doc/classes/PackedFloat32Array.xml
+++ b/doc/classes/PackedFloat32Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedFloat32Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="float" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedFloat64Array.xml b/doc/classes/PackedFloat64Array.xml
index be7a52b7f4..0327559f5b 100644
--- a/doc/classes/PackedFloat64Array.xml
+++ b/doc/classes/PackedFloat64Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedFloat64Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="float" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedInt32Array.xml b/doc/classes/PackedInt32Array.xml
index 108273d859..f8b606d266 100644
--- a/doc/classes/PackedInt32Array.xml
+++ b/doc/classes/PackedInt32Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedInt32Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="int" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedInt64Array.xml b/doc/classes/PackedInt64Array.xml
index c34f2fc75e..ea3e304d35 100644
--- a/doc/classes/PackedInt64Array.xml
+++ b/doc/classes/PackedInt64Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedInt64Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="int" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="int" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedStringArray.xml b/doc/classes/PackedStringArray.xml
index 536f5f02eb..d0f1d5f753 100644
--- a/doc/classes/PackedStringArray.xml
+++ b/doc/classes/PackedStringArray.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedStringArray" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="String" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="String" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedVector2Array.xml b/doc/classes/PackedVector2Array.xml
index 29423d1cde..8f3e5d173d 100644
--- a/doc/classes/PackedVector2Array.xml
+++ b/doc/classes/PackedVector2Array.xml
@@ -55,6 +55,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedVector2Array" />
<description>
@@ -68,6 +75,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Vector2" />
@@ -116,6 +131,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector2" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/PackedVector3Array.xml b/doc/classes/PackedVector3Array.xml
index 6d460cecab..1207293c32 100644
--- a/doc/classes/PackedVector3Array.xml
+++ b/doc/classes/PackedVector3Array.xml
@@ -54,6 +54,13 @@
[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
</description>
</method>
+ <method name="count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <description>
+ Returns the number of times an element is in the array.
+ </description>
+ </method>
<method name="duplicate">
<return type="PackedVector3Array" />
<description>
@@ -67,6 +74,14 @@
Assigns the given value to all elements in the array. This can typically be used together with [method resize] to create an array with a given size and initialized elements.
</description>
</method>
+ <method name="find" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <argument index="1" name="from" type="int" default="0" />
+ <description>
+ Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<argument index="0" name="value" type="Vector3" />
@@ -115,6 +130,14 @@
Reverses the order of the elements in the array.
</description>
</method>
+ <method name="rfind" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="value" type="Vector3" />
+ <argument index="1" name="from" type="int" default="-1" />
+ <description>
+ Searches the array in reverse order. Optionally, a start search index can be passed. If negative, the start index is considered relative to the end of the array.
+ </description>
+ </method>
<method name="set">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index 7395611d71..954aa11c0d 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -32,6 +32,7 @@
#include "light_storage.h"
#include "config.h"
+#include "texture_storage.h"
using namespace GLES3;
@@ -51,122 +52,284 @@ LightStorage::~LightStorage() {
/* Light API */
+void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) {
+ Light light;
+ light.type = p_type;
+
+ light.param[RS::LIGHT_PARAM_ENERGY] = 1.0;
+ light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0;
+ light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5;
+ light.param[RS::LIGHT_PARAM_RANGE] = 1.0;
+ light.param[RS::LIGHT_PARAM_SIZE] = 0.0;
+ light.param[RS::LIGHT_PARAM_ATTENUATION] = 1.0;
+ light.param[RS::LIGHT_PARAM_SPOT_ANGLE] = 45;
+ light.param[RS::LIGHT_PARAM_SPOT_ATTENUATION] = 1.0;
+ light.param[RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0;
+ light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1;
+ light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3;
+ 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_BIAS] = 0.02;
+ 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;
+ light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05;
+
+ light_owner.initialize_rid(p_light, light);
+}
+
RID LightStorage::directional_light_allocate() {
- return RID();
+ return light_owner.allocate_rid();
}
void LightStorage::directional_light_initialize(RID p_rid) {
+ _light_initialize(p_rid, RS::LIGHT_DIRECTIONAL);
}
RID LightStorage::omni_light_allocate() {
- return RID();
+ return light_owner.allocate_rid();
}
void LightStorage::omni_light_initialize(RID p_rid) {
+ _light_initialize(p_rid, RS::LIGHT_OMNI);
}
RID LightStorage::spot_light_allocate() {
- return RID();
+ return light_owner.allocate_rid();
}
void LightStorage::spot_light_initialize(RID p_rid) {
+ _light_initialize(p_rid, RS::LIGHT_SPOT);
}
void LightStorage::light_free(RID p_rid) {
+ light_set_projector(p_rid, RID()); //clear projector
+
+ // delete the texture
+ Light *light = light_owner.get_or_null(p_rid);
+ light->dependency.deleted_notify(p_rid);
+ light_owner.free(p_rid);
}
void LightStorage::light_set_color(RID p_light, const Color &p_color) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->color = p_color;
}
void LightStorage::light_set_param(RID p_light, RS::LightParam p_param, float p_value) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+ ERR_FAIL_INDEX(p_param, RS::LIGHT_PARAM_MAX);
+
+ if (light->param[p_param] == p_value) {
+ return;
+ }
+
+ switch (p_param) {
+ case RS::LIGHT_PARAM_RANGE:
+ case RS::LIGHT_PARAM_SPOT_ANGLE:
+ case RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE:
+ case RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET:
+ case RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET:
+ case RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET:
+ case RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS:
+ case RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE:
+ case RS::LIGHT_PARAM_SHADOW_BIAS: {
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
+ } break;
+ case RS::LIGHT_PARAM_SIZE: {
+ if ((light->param[p_param] > CMP_EPSILON) != (p_value > CMP_EPSILON)) {
+ //changing from no size to size and the opposite
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR);
+ }
+ } break;
+ default: {
+ }
+ }
+
+ light->param[p_param] = p_value;
}
void LightStorage::light_set_shadow(RID p_light, bool p_enabled) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+ light->shadow = p_enabled;
+
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_set_projector(RID p_light, RID p_texture) {
+ GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ if (light->projector == p_texture) {
+ return;
+ }
+
+ if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) {
+ texture_storage->texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
+ }
+
+ light->projector = p_texture;
+
+ if (light->type != RS::LIGHT_DIRECTIONAL) {
+ if (light->projector.is_valid()) {
+ texture_storage->texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
+ }
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR);
+ }
}
void LightStorage::light_set_negative(RID p_light, bool p_enable) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->negative = p_enable;
}
void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->cull_mask = p_mask;
+
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->distance_fade = p_enabled;
+ light->distance_fade_begin = p_begin;
+ light->distance_fade_shadow = p_shadow;
+ light->distance_fade_length = p_length;
}
void LightStorage::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->reverse_cull = p_enabled;
+
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) {
-}
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->bake_mode = p_bake_mode;
-void LightStorage::light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) {
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
void LightStorage::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) {
-}
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
-void LightStorage::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) {
-}
+ light->omni_shadow_mode = p_mode;
-void LightStorage::light_directional_set_blend_splits(RID p_light, bool p_enable) {
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
-bool LightStorage::light_directional_get_blend_splits(RID p_light) const {
- return false;
-}
+RS::LightOmniShadowMode LightStorage::light_omni_get_shadow_mode(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_OMNI_SHADOW_CUBE);
-void LightStorage::light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) {
+ return light->omni_shadow_mode;
}
-RS::LightDirectionalSkyMode LightStorage::light_directional_get_sky_mode(RID p_light) const {
- return RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY;
-}
+void LightStorage::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
-RS::LightDirectionalShadowMode LightStorage::light_directional_get_shadow_mode(RID p_light) {
- return RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
+ light->directional_shadow_mode = p_mode;
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
-RS::LightOmniShadowMode LightStorage::light_omni_get_shadow_mode(RID p_light) {
- return RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID;
-}
+void LightStorage::light_directional_set_blend_splits(RID p_light, bool p_enable) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
-bool LightStorage::light_has_shadow(RID p_light) const {
- return false;
+ light->directional_blend_splits = p_enable;
+ light->version++;
+ light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT);
}
-bool LightStorage::light_has_projector(RID p_light) const {
- return false;
-}
+bool LightStorage::light_directional_get_blend_splits(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, false);
-RS::LightType LightStorage::light_get_type(RID p_light) const {
- return RS::LIGHT_OMNI;
+ return light->directional_blend_splits;
}
-AABB LightStorage::light_get_aabb(RID p_light) const {
- return AABB();
+void LightStorage::light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND(!light);
+
+ light->directional_sky_mode = p_mode;
}
-float LightStorage::light_get_param(RID p_light, RS::LightParam p_param) {
- return 0.0;
+RS::LightDirectionalSkyMode LightStorage::light_directional_get_sky_mode(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY);
+
+ return light->directional_sky_mode;
}
-Color LightStorage::light_get_color(RID p_light) {
- return Color();
+RS::LightDirectionalShadowMode LightStorage::light_directional_get_shadow_mode(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL);
+
+ return light->directional_shadow_mode;
}
RS::LightBakeMode LightStorage::light_get_bake_mode(RID p_light) {
- return RS::LIGHT_BAKE_DISABLED;
-}
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_BAKE_DISABLED);
-uint32_t LightStorage::light_get_max_sdfgi_cascade(RID p_light) {
- return 0;
+ return light->bake_mode;
}
uint64_t LightStorage::light_get_version(RID p_light) const {
- return 0;
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0);
+
+ return light->version;
+}
+
+AABB LightStorage::light_get_aabb(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, AABB());
+
+ switch (light->type) {
+ case RS::LIGHT_SPOT: {
+ float len = light->param[RS::LIGHT_PARAM_RANGE];
+ float size = Math::tan(Math::deg2rad(light->param[RS::LIGHT_PARAM_SPOT_ANGLE])) * len;
+ return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len));
+ };
+ case RS::LIGHT_OMNI: {
+ float r = light->param[RS::LIGHT_PARAM_RANGE];
+ return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2);
+ };
+ case RS::LIGHT_DIRECTIONAL: {
+ return AABB();
+ };
+ }
+
+ ERR_FAIL_V(AABB());
}
/* PROBE API */
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index 6f24e467bc..5acaf45aa3 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -40,12 +40,100 @@
#include "servers/rendering/renderer_storage.h"
#include "servers/rendering/storage/light_storage.h"
+#include "platform_config.h"
+#ifndef OPENGL_INCLUDE_H
+#include <GLES3/gl3.h>
+#else
+#include OPENGL_INCLUDE_H
+#endif
+
namespace GLES3 {
+/* LIGHT */
+
+struct Light {
+ RS::LightType type;
+ float param[RS::LIGHT_PARAM_MAX];
+ Color color = Color(1, 1, 1, 1);
+ RID projector;
+ bool shadow = false;
+ bool negative = false;
+ bool reverse_cull = false;
+ RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
+ uint32_t max_sdfgi_cascade = 2;
+ uint32_t cull_mask = 0xFFFFFFFF;
+ bool distance_fade = false;
+ real_t distance_fade_begin = 40.0;
+ real_t distance_fade_shadow = 50.0;
+ real_t distance_fade_length = 10.0;
+ RS::LightOmniShadowMode omni_shadow_mode = RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID;
+ RS::LightDirectionalShadowMode directional_shadow_mode = RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
+ bool directional_blend_splits = false;
+ RS::LightDirectionalSkyMode directional_sky_mode = RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY;
+ uint64_t version = 0;
+
+ RendererStorage::Dependency dependency;
+};
+
+/* REFLECTION PROBE */
+
+struct ReflectionProbe {
+ RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE;
+ int resolution = 256;
+ float intensity = 1.0;
+ RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT;
+ Color ambient_color;
+ float ambient_color_energy = 1.0;
+ float max_distance = 0;
+ Vector3 extents = Vector3(1, 1, 1);
+ Vector3 origin_offset;
+ bool interior = false;
+ bool box_projection = false;
+ bool enable_shadows = false;
+ uint32_t cull_mask = (1 << 20) - 1;
+ float mesh_lod_threshold = 0.01;
+
+ RendererStorage::Dependency dependency;
+};
+
+/* LIGHTMAP */
+
+struct Lightmap {
+ RID light_texture;
+ bool uses_spherical_harmonics = false;
+ bool interior = false;
+ AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
+ int32_t array_index = -1; //unassigned
+ PackedVector3Array points;
+ PackedColorArray point_sh;
+ PackedInt32Array tetrahedra;
+ PackedInt32Array bsp_tree;
+
+ struct BSP {
+ static const int32_t EMPTY_LEAF = INT32_MIN;
+ float plane[4];
+ int32_t over = EMPTY_LEAF, under = EMPTY_LEAF;
+ };
+
+ RendererStorage::Dependency dependency;
+};
+
class LightStorage : public RendererLightStorage {
private:
static LightStorage *singleton;
+ /* LIGHT */
+ mutable RID_Owner<Light, true> light_owner;
+
+ /* REFLECTION PROBE */
+ mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner;
+
+ /* LIGHTMAP */
+
+ Vector<RID> lightmap_textures;
+
+ mutable RID_Owner<Lightmap, true> lightmap_owner;
+
public:
static LightStorage *get_singleton();
@@ -54,6 +142,11 @@ public:
/* Light API */
+ Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); };
+ bool owns_light(RID p_rid) { return light_owner.owns(p_rid); };
+
+ void _light_initialize(RID p_rid, RS::LightType p_type);
+
virtual RID directional_light_allocate() override;
virtual void directional_light_initialize(RID p_rid) override;
virtual RID omni_light_allocate() override;
@@ -72,7 +165,7 @@ public:
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override;
- virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override;
+ virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override;
@@ -84,16 +177,99 @@ public:
virtual RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override;
virtual RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override;
+ virtual RS::LightType light_get_type(RID p_light) const override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
- virtual bool light_has_shadow(RID p_light) const override;
- virtual bool light_has_projector(RID p_light) const override;
-
- virtual RS::LightType light_get_type(RID p_light) const override;
+ return light->type;
+ }
virtual AABB light_get_aabb(RID p_light) const override;
- virtual float light_get_param(RID p_light, RS::LightParam p_param) override;
- virtual Color light_get_color(RID p_light) override;
+
+ virtual float light_get_param(RID p_light, RS::LightParam p_param) override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0);
+
+ return light->param[p_param];
+ }
+
+ _FORCE_INLINE_ RID light_get_projector(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RID());
+
+ return light->projector;
+ }
+
+ virtual Color light_get_color(RID p_light) override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, Color());
+
+ return light->color;
+ }
+
+ _FORCE_INLINE_ uint32_t light_get_cull_mask(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0);
+
+ return light->cull_mask;
+ }
+
+ _FORCE_INLINE_ bool light_is_distance_fade_enabled(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ return light->distance_fade;
+ }
+
+ _FORCE_INLINE_ float light_get_distance_fade_begin(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ return light->distance_fade_begin;
+ }
+
+ _FORCE_INLINE_ float light_get_distance_fade_shadow(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ return light->distance_fade_shadow;
+ }
+
+ _FORCE_INLINE_ float light_get_distance_fade_length(RID p_light) {
+ const Light *light = light_owner.get_or_null(p_light);
+ return light->distance_fade_length;
+ }
+
+ virtual bool light_has_shadow(RID p_light) const override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
+
+ return light->shadow;
+ }
+
+ virtual bool light_has_projector(RID p_light) const override {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
+
+ return light_owner.owns(light->projector);
+ }
+
+ _FORCE_INLINE_ bool light_is_negative(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL);
+
+ return light->negative;
+ }
+
+ _FORCE_INLINE_ float light_get_transmittance_bias(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0.0);
+
+ return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS];
+ }
+
+ _FORCE_INLINE_ float light_get_shadow_volumetric_fog_fade(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0.0);
+
+ return light->param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE];
+ }
+
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override;
- virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override;
+ virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; }
virtual uint64_t light_get_version(RID p_light) const override;
/* PROBE API */
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 84d656fc20..13084e2aab 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -529,7 +529,7 @@ void EditorFileDialog::_multi_selected(int p_item, bool p_selected) {
get_ok_button()->set_disabled(_is_open_should_be_disabled());
}
-void EditorFileDialog::_items_clear_selection(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+void EditorFileDialog::_items_clear_selection(const Vector2 &p_pos, MouseButton p_mouse_button_index) {
if (p_mouse_button_index != MouseButton::LEFT) {
return;
}
diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h
index d9a58a5b7f..5f2e29b690 100644
--- a/editor/editor_file_dialog.h
+++ b/editor/editor_file_dialog.h
@@ -165,7 +165,7 @@ private:
void _item_selected(int p_item);
void _multi_selected(int p_item, bool p_selected);
- void _items_clear_selection(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
+ void _items_clear_selection(const Vector2 &p_pos, MouseButton p_mouse_button_index);
void _item_dc_selected(int p_item);
void _item_list_item_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp
index b728ce64c9..85a906ef51 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/editor_plugin_settings.cpp
@@ -200,12 +200,9 @@ EditorPluginSettings::EditorPluginSettings() {
l->set_theme_type_variation("HeaderSmall");
title_hb->add_child(l);
title_hb->add_spacer();
- create_plugin = memnew(Button(TTR("Create")));
+ Button *create_plugin = memnew(Button(TTR("Create New Plugin")));
create_plugin->connect("pressed", callable_mp(this, &EditorPluginSettings::_create_clicked));
title_hb->add_child(create_plugin);
- update_list = memnew(Button(TTR("Update")));
- update_list->connect("pressed", callable_mp(this, &EditorPluginSettings::update_plugins));
- title_hb->add_child(update_list);
add_child(title_hb);
plugin_list = memnew(Tree);
diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h
index 826bb8c00f..121534b613 100644
--- a/editor/editor_plugin_settings.h
+++ b/editor/editor_plugin_settings.h
@@ -45,8 +45,6 @@ class EditorPluginSettings : public VBoxContainer {
};
PluginConfigDialog *plugin_config_dialog = nullptr;
- Button *create_plugin = nullptr;
- Button *update_list = nullptr;
Tree *plugin_list = nullptr;
bool updating = false;
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 47c016803c..aae8cf25b6 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -2637,7 +2637,7 @@ void FileSystemDock::_tree_empty_selected() {
tree->deselect_all();
}
-void FileSystemDock::_file_list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+void FileSystemDock::_file_list_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) {
if (p_mouse_button_index != MouseButton::RIGHT) {
return;
}
@@ -2665,7 +2665,11 @@ void FileSystemDock::_file_list_rmb_clicked(int p_item, const Vector2 &p_pos, Mo
}
}
-void FileSystemDock::_file_list_rmb_pressed(const Vector2 &p_pos) {
+void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index) {
+ if (p_mouse_button_index != MouseButton::RIGHT) {
+ return;
+ }
+
// Right click on empty space for file list.
if (searched_string.length() > 0) {
return;
@@ -3132,10 +3136,10 @@ FileSystemDock::FileSystemDock() {
files->set_v_size_flags(SIZE_EXPAND_FILL);
files->set_select_mode(ItemList::SELECT_MULTI);
files->set_drag_forwarding(this);
- files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_rmb_clicked));
+ files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_item_clicked));
files->connect("gui_input", callable_mp(this, &FileSystemDock::_file_list_gui_input));
files->connect("multi_selected", callable_mp(this, &FileSystemDock::_file_multi_selected));
- files->connect("rmb_clicked", callable_mp(this, &FileSystemDock::_file_list_rmb_pressed));
+ files->connect("empty_clicked", callable_mp(this, &FileSystemDock::_file_list_empty_clicked));
files->set_custom_minimum_size(Size2(0, 15 * EDSCALE));
files->set_allow_rmb_select(true);
file_list_vb->add_child(files);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index f6c4e271bf..fc24b3e9fd 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -260,8 +260,8 @@ private:
void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options = true);
void _tree_rmb_select(const Vector2 &p_pos);
void _tree_rmb_empty(const Vector2 &p_pos);
- void _file_list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
- void _file_list_rmb_pressed(const Vector2 &p_pos);
+ void _file_list_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
+ void _file_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
void _tree_empty_selected();
struct FileInfo {
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index 755bf7ce07..77e4905341 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -47,7 +47,7 @@ void PluginConfigDialog::_clear_fields() {
}
void PluginConfigDialog::_on_confirmed() {
- String path = "res://addons/" + subfolder_edit->get_text();
+ String path = "res://addons/" + _get_subfolder();
if (!_edit_mode) {
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
@@ -56,32 +56,35 @@ void PluginConfigDialog::_on_confirmed() {
}
}
+ int lang_idx = script_option_edit->get_selected();
+ String ext = ScriptServer::get_language(lang_idx)->get_extension();
+ String script_name = script_edit->get_text().is_empty() ? _get_subfolder() : script_edit->get_text();
+ if (script_name.get_extension().is_empty()) {
+ script_name += "." + ext;
+ }
+ String script_path = path.plus_file(script_name);
+
Ref<ConfigFile> cf = memnew(ConfigFile);
cf->set_value("plugin", "name", name_edit->get_text());
cf->set_value("plugin", "description", desc_edit->get_text());
cf->set_value("plugin", "author", author_edit->get_text());
cf->set_value("plugin", "version", version_edit->get_text());
- cf->set_value("plugin", "script", script_edit->get_text());
+ cf->set_value("plugin", "script", script_name);
cf->save(path.plus_file("plugin.cfg"));
if (!_edit_mode) {
- int lang_idx = script_option_edit->get_selected();
- String lang_name = ScriptServer::get_language(lang_idx)->get_name();
-
- Ref<Script> script;
- String script_path = path.plus_file(script_edit->get_text());
- String class_name = script_path.get_file().get_basename();
+ String class_name = script_name.get_basename();
String template_content = "";
Vector<ScriptLanguage::ScriptTemplate> templates = ScriptServer::get_language(lang_idx)->get_built_in_templates("EditorPlugin");
- if (templates.size() > 0) {
- template_content = templates.get(0).content;
+ if (!templates.is_empty()) {
+ template_content = templates[0].content;
}
- script = ScriptServer::get_language(lang_idx)->make_template(template_content, class_name, "EditorPlugin");
+ Ref<Script> script = ScriptServer::get_language(lang_idx)->make_template(template_content, class_name, "EditorPlugin");
script->set_path(script_path);
ResourceSaver::save(script_path, script);
- emit_signal(SNAME("plugin_ready"), script.operator->(), active_edit->is_pressed() ? _to_absolute_plugin_path(subfolder_edit->get_text()) : "");
+ emit_signal(SNAME("plugin_ready"), script.ptr(), active_edit->is_pressed() ? _to_absolute_plugin_path(_get_subfolder()) : "");
} else {
EditorNode::get_singleton()->get_project_settings()->update_plugins();
}
@@ -119,27 +122,18 @@ void PluginConfigDialog::_on_required_text_changed(const String &) {
name_validation->set_texture(invalid_icon);
name_validation->set_tooltip(TTR("Plugin name cannot be blank."));
}
- if (script_edit->get_text().get_extension() != ext) {
+ if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
is_valid = false;
script_validation->set_texture(invalid_icon);
script_validation->set_tooltip(vformat(TTR("Script extension must match chosen language extension (.%s)."), ext));
}
- if (script_edit->get_text().get_basename().is_empty()) {
+ if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) {
is_valid = false;
- script_validation->set_texture(invalid_icon);
- script_validation->set_tooltip(TTR("Script name cannot be blank."));
- }
- if (subfolder_edit->get_text().is_empty()) {
- is_valid = false;
- subfolder_validation->set_texture(invalid_icon);
- subfolder_validation->set_tooltip(TTR("Subfolder cannot be blank."));
- } else if (!subfolder_edit->get_text().is_valid_filename()) {
subfolder_validation->set_texture(invalid_icon);
subfolder_validation->set_tooltip(TTR("Subfolder name is not a valid folder name."));
} else {
- Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- String path = "res://addons/" + subfolder_edit->get_text();
- if (dir->dir_exists(path) && !_edit_mode) { // Only show this error if in "create" mode.
+ String path = "res://addons/" + _get_subfolder();
+ if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode.
is_valid = false;
subfolder_validation->set_texture(invalid_icon);
subfolder_validation->set_tooltip(TTR("Subfolder cannot be one which already exists."));
@@ -149,6 +143,10 @@ void PluginConfigDialog::_on_required_text_changed(const String &) {
get_ok_button()->set_disabled(!is_valid);
}
+String PluginConfigDialog::_get_subfolder() {
+ return subfolder_edit->get_text().is_empty() ? name_edit->get_text().replace(" ", "_").to_lower() : subfolder_edit->get_text();
+}
+
String PluginConfigDialog::_to_absolute_plugin_path(const String &p_plugin_name) {
return "res://addons/" + p_plugin_name + "/plugin.cfg";
}
@@ -225,6 +223,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Plugin Name
Label *name_lb = memnew(Label);
name_lb->set_text(TTR("Plugin Name:"));
+ name_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(name_lb);
name_validation = memnew(TextureRect);
@@ -239,6 +238,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Subfolder
Label *subfolder_lb = memnew(Label);
subfolder_lb->set_text(TTR("Subfolder:"));
+ subfolder_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(subfolder_lb);
subfolder_validation = memnew(TextureRect);
@@ -253,6 +253,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Description
Label *desc_lb = memnew(Label);
desc_lb->set_text(TTR("Description:"));
+ desc_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(desc_lb);
Control *desc_spacer = memnew(Control);
@@ -266,6 +267,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Author
Label *author_lb = memnew(Label);
author_lb->set_text(TTR("Author:"));
+ author_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(author_lb);
Control *author_spacer = memnew(Control);
@@ -278,6 +280,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Version
Label *version_lb = memnew(Label);
version_lb->set_text(TTR("Version:"));
+ version_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(version_lb);
Control *version_spacer = memnew(Control);
@@ -290,6 +293,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Language dropdown
Label *script_option_lb = memnew(Label);
script_option_lb->set_text(TTR("Language:"));
+ script_option_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(script_option_lb);
Control *script_opt_spacer = memnew(Control);
@@ -311,6 +315,7 @@ PluginConfigDialog::PluginConfigDialog() {
// Plugin Script Name
Label *script_lb = memnew(Label);
script_lb->set_text(TTR("Script Name:"));
+ script_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(script_lb);
script_validation = memnew(TextureRect);
@@ -326,6 +331,7 @@ PluginConfigDialog::PluginConfigDialog() {
// TODO Make this option work better with languages like C#. Right now, it does not work because the C# project must be compiled first.
Label *active_lb = memnew(Label);
active_lb->set_text(TTR("Activate now?"));
+ active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
grid->add_child(active_lb);
Control *active_spacer = memnew(Control);
diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h
index 5c6043da12..6e0cbea378 100644
--- a/editor/plugin_config_dialog.h
+++ b/editor/plugin_config_dialog.h
@@ -61,6 +61,7 @@ class PluginConfigDialog : public ConfirmationDialog {
void _on_cancelled();
void _on_language_changed(const int p_language);
void _on_required_text_changed(const String &p_text);
+ String _get_subfolder();
static String _to_absolute_plugin_path(const String &p_plugin_name);
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index ba249aff7a..07421b7275 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -2172,6 +2172,11 @@ void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, d
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
+#endif
fd->cache[size]->scale = p_scale;
}
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 8ae56aa64d..257c569a25 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -1334,6 +1334,11 @@ void TextServerFallback::font_set_scale(const RID &p_font_rid, int64_t p_size, d
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
+#endif
fd->cache[size]->scale = p_scale;
}
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b548277f95..93cab85441 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -1106,6 +1106,10 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
+ if (icon.is_valid()) {
+ set_icon(icon);
+ }
+
SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ((wd.no_focus || wd.is_popup) ? SWP_NOACTIVATE : 0));
if (p_repaint) {
@@ -1895,9 +1899,11 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!p_icon.is_valid());
- Ref<Image> icon = p_icon->duplicate();
- if (icon->get_format() != Image::FORMAT_RGBA8) {
- icon->convert(Image::FORMAT_RGBA8);
+ if (icon != p_icon) {
+ icon = p_icon->duplicate();
+ if (icon->get_format() != Image::FORMAT_RGBA8) {
+ icon->convert(Image::FORMAT_RGBA8);
+ }
}
int w = icon->get_width();
int h = icon->get_height();
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index c039b29c54..febc8a2043 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -399,6 +399,7 @@ class DisplayServerWindows : public DisplayServer {
HHOOK mouse_monitor = nullptr;
List<WindowID> popup_list;
uint64_t time_since_popup = 0;
+ Ref<Image> icon;
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);
WindowID window_id_counter = MAIN_WINDOW_ID;
diff --git a/tests/core/math/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h
index 3487e4d7e8..db4e6e2177 100644
--- a/tests/core/math/test_geometry_2d.h
+++ b/tests/core/math/test_geometry_2d.h
@@ -135,7 +135,7 @@ TEST_CASE("[Geometry2D] Line intersection") {
"Parallel lines should not intersect.");
}
-TEST_CASE("[Geometry2D] Segment intersection.") {
+TEST_CASE("[Geometry2D] Segment intersection") {
Vector2 r;
CHECK(Geometry2D::segment_intersects_segment(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), &r));
@@ -148,6 +148,10 @@ TEST_CASE("[Geometry2D] Segment intersection.") {
Geometry2D::segment_intersects_segment(Vector2(-1, 1), Vector2(1, -1), Vector2(0, 1), Vector2(2, -1), &r),
"Parallel segments should not intersect.");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::segment_intersects_segment(Vector2(1, 2), Vector2(3, 2), Vector2(0, 2), Vector2(-2, 2), &r),
+ "Non-overlapping collinear segments should not intersect.");
+
CHECK_MESSAGE(
Geometry2D::segment_intersects_segment(Vector2(0, 0), Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), &r),
"Touching segments should intersect.");
@@ -159,11 +163,114 @@ TEST_CASE("[Geometry2D] Segment intersection.") {
CHECK(r.is_equal_approx(Vector2(0, 0)));
}
+TEST_CASE("[Geometry2D] Segment intersection with circle") {
+ real_t minus_one = -1.0;
+ real_t zero = 0.0;
+ real_t one_quarter = 0.25;
+ real_t three_quarters = 0.75;
+ real_t one = 1.0;
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(4, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment from inside to outside of circle should intersect it.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(4, 0), Vector2(0, 0), Vector2(0, 0), 1.0), three_quarters),
+ "Segment from outside to inside of circle should intersect it.");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-2, 0), Vector2(2, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment running through circle should intersect it.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(-2, 0), Vector2(0, 0), 1.0), one_quarter),
+ "Segment running through circle should intersect it.");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one),
+ "Segment starting inside the circle and ending on the circle should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(0, 0), Vector2(0, 0), 1.0), zero),
+ "Segment starting on the circle and going inwards should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(2, 0), Vector2(0, 0), 1.0), zero),
+ "Segment starting on the circle and going outwards should intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one),
+ "Segment starting outside the circle and ending on the circle intersect it");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-1, 0), Vector2(1, 0), Vector2(0, 0), 2.0), minus_one),
+ "Segment completely within the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(-1, 0), Vector2(0, 0), 2.0), minus_one),
+ "Segment completely within the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(3, 0), Vector2(0, 0), 1.0), minus_one),
+ "Segment completely outside the circle should not intersect it");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(3, 0), Vector2(2, 0), Vector2(0, 0), 1.0), minus_one),
+ "Segment completely outside the circle should not intersect it");
+}
+
+TEST_CASE("[Geometry2D] Segment intersection with polygon") {
+ Vector<Point2> a;
+
+ a.push_back(Point2(-2, 2));
+ a.push_back(Point2(3, 4));
+ a.push_back(Point2(1, 1));
+ a.push_back(Point2(2, -2));
+ a.push_back(Point2(-1, -1));
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(0, 2), Vector2(2, 2), a),
+ "Segment from inside to outside of polygon should intersect it.");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 2), Vector2(0, 2), a),
+ "Segment from outside to inside of polygon should intersect it.");
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 4), Vector2(3, 3), a),
+ "Segment running through polygon should intersect it.");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(3, 3), Vector2(2, 4), a),
+ "Segment running through polygon should intersect it.");
+
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(0, 0), Vector2(1, 1), a),
+ "Segment starting inside the polygon and ending on the polygon should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(1, 1), Vector2(0, 0), a),
+ "Segment starting on the polygon and going inwards should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-2, 2), Vector2(-2, -1), a),
+ "Segment starting on the polygon and going outwards should intersect it");
+ CHECK_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-2, 1), Vector2(-2, 2), a),
+ "Segment starting outside the polygon and ending on the polygon intersect it");
+
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(-1, 2), Vector2(1, -1), a),
+ "Segment completely within the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(1, -1), Vector2(-1, 2), a),
+ "Segment completely within the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, 2), Vector2(2, -1), a),
+ "Segment completely outside the polygon should not intersect it");
+ CHECK_FALSE_MESSAGE(
+ Geometry2D::is_segment_intersecting_polygon(Vector2(2, -1), Vector2(2, 2), a),
+ "Segment completely outside the polygon should not intersect it");
+}
+
TEST_CASE("[Geometry2D] Closest point to segment") {
Vector2 s[] = { Vector2(-4, -4), Vector2(4, 4) };
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(4.1, 4.1), s).is_equal_approx(Vector2(4, 4)));
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(-4.1, -4.1), s).is_equal_approx(Vector2(-4, -4)));
CHECK(Geometry2D::get_closest_point_to_segment(Vector2(-1, 1), s).is_equal_approx(Vector2(0, 0)));
+
+ Vector2 t[] = { Vector2(1, -2), Vector2(1, -2) };
+ CHECK_MESSAGE(
+ Geometry2D::get_closest_point_to_segment(Vector2(-3, 4), t).is_equal_approx(Vector2(1, -2)),
+ "Line segment is only a single point. This point should be the closest.");
}
TEST_CASE("[Geometry2D] Closest point to uncapped segment") {
@@ -186,6 +293,30 @@ TEST_CASE("[Geometry2D] Closest points between segments") {
Geometry2D::get_closest_points_between_segments(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), c1, c2);
CHECK(c1.is_equal_approx(Vector2(0, 0)));
CHECK(c2.is_equal_approx(Vector2(0, 0)));
+
+ Geometry2D::get_closest_points_between_segments(Vector2(-3, 4), Vector2(-3, 4), Vector2(-4, 3), Vector2(-2, 3), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(-3, 4)),
+ "1st line segment is only a point, this point should be the closest point to the 2nd line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-3, 3)),
+ "1st line segment is only a point, this should not matter when determining the closest point on the 2nd line segment.");
+
+ Geometry2D::get_closest_points_between_segments(Vector2(-4, 3), Vector2(-2, 3), Vector2(-3, 4), Vector2(-3, 4), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(-3, 3)),
+ "2nd line segment is only a point, this should not matter when determining the closest point on the 1st line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-3, 4)),
+ "2nd line segment is only a point, this point should be the closest point to the 1st line segment.");
+
+ Geometry2D::get_closest_points_between_segments(Vector2(5, -4), Vector2(5, -4), Vector2(-2, 1), Vector2(-2, 1), c1, c2);
+ CHECK_MESSAGE(
+ c1.is_equal_approx(Vector2(5, -4)),
+ "Both line segments are only a point. On the 1st line segment, that point should be the closest point to the 2nd line segment.");
+ CHECK_MESSAGE(
+ c2.is_equal_approx(Vector2(-2, 1)),
+ "Both line segments are only a point. On the 2nd line segment, that point should be the closest point to the 1st line segment.");
}
TEST_CASE("[Geometry2D] Make atlas") {
@@ -562,6 +693,174 @@ TEST_CASE("[Geometry2D] Clip polyline with polygon") {
CHECK(r[1][1].is_equal_approx(Vector2(55, 70)));
}
}
+
+TEST_CASE("[Geometry2D] Convex hull") {
+ Vector<Point2> a;
+ Vector<Point2> r;
+
+ a.push_back(Point2(-4, -8));
+ a.push_back(Point2(-10, -4));
+ a.push_back(Point2(8, 2));
+ a.push_back(Point2(-6, 10));
+ a.push_back(Point2(-12, 4));
+ a.push_back(Point2(10, -8));
+ a.push_back(Point2(4, 8));
+
+ SUBCASE("[Geometry2D] No points") {
+ r = Geometry2D::convex_hull(Vector<Vector2>());
+
+ CHECK_MESSAGE(r.is_empty(), "The convex hull should be empty if there are no input points.");
+ }
+
+ SUBCASE("[Geometry2D] Single point") {
+ Vector<Point2> b;
+ b.push_back(Point2(4, -3));
+
+ r = Geometry2D::convex_hull(b);
+ REQUIRE_MESSAGE(r.size() == 1, "Convex hull should contain 1 point.");
+ CHECK(r[0].is_equal_approx(b[0]));
+ }
+
+ SUBCASE("[Geometry2D] All points form the convex hull") {
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points inside original convex hull") {
+ a.push_back(Point2(-4, -8));
+ a.push_back(Point2(0, 0));
+ a.push_back(Point2(0, 8));
+ a.push_back(Point2(-10, -3));
+ a.push_back(Point2(9, -4));
+ a.push_back(Point2(6, 4));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points on border of original convex hull") {
+ a.push_back(Point2(9, -3));
+ a.push_back(Point2(-2, -8));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[2].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[3].is_equal_approx(Point2(10, -8)));
+ CHECK(r[4].is_equal_approx(Point2(8, 2)));
+ CHECK(r[5].is_equal_approx(Point2(4, 8)));
+ CHECK(r[6].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[7].is_equal_approx(Point2(-12, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Add extra points outside border of original convex hull") {
+ a.push_back(Point2(-11, -1));
+ a.push_back(Point2(7, 6));
+
+ r = Geometry2D::convex_hull(a);
+ REQUIRE_MESSAGE(r.size() == 10, "Convex hull should contain 10 points.");
+ CHECK(r[0].is_equal_approx(Point2(-12, 4)));
+ CHECK(r[1].is_equal_approx(Point2(-11, -1)));
+ CHECK(r[2].is_equal_approx(Point2(-10, -4)));
+ CHECK(r[3].is_equal_approx(Point2(-4, -8)));
+ CHECK(r[4].is_equal_approx(Point2(10, -8)));
+ CHECK(r[5].is_equal_approx(Point2(8, 2)));
+ CHECK(r[6].is_equal_approx(Point2(7, 6)));
+ CHECK(r[7].is_equal_approx(Point2(4, 8)));
+ CHECK(r[8].is_equal_approx(Point2(-6, 10)));
+ CHECK(r[9].is_equal_approx(Point2(-12, 4)));
+ }
+}
+
+TEST_CASE("[Geometry2D] Bresenham line") {
+ Vector<Vector2i> r;
+
+ SUBCASE("[Geometry2D] Single point") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(0, 0));
+
+ REQUIRE_MESSAGE(r.size() == 1, "The Bresenham line should contain exactly one point.");
+ CHECK(r[0] == Vector2i(0, 0));
+ }
+
+ SUBCASE("[Geometry2D] Line parallel to x-axis") {
+ r = Geometry2D::bresenham_line(Point2i(1, 2), Point2i(5, 2));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(1, 2));
+ CHECK(r[1] == Vector2i(2, 2));
+ CHECK(r[2] == Vector2i(3, 2));
+ CHECK(r[3] == Vector2i(4, 2));
+ CHECK(r[4] == Vector2i(5, 2));
+ }
+
+ SUBCASE("[Geometry2D] 45 degree line from the origin") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 4));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 1));
+ CHECK(r[2] == Vector2i(2, 2));
+ CHECK(r[3] == Vector2i(3, 3));
+ CHECK(r[4] == Vector2i(4, 4));
+ }
+
+ SUBCASE("[Geometry2D] Sloped line going up one unit") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 1));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 0));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 1));
+ }
+
+ SUBCASE("[Geometry2D] Sloped line going up two units") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 2));
+
+ REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 1));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 2));
+ }
+
+ SUBCASE("[Geometry2D] Long sloped line") {
+ r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(11, 5));
+
+ REQUIRE_MESSAGE(r.size() == 12, "The Bresenham line should contain exactly twelve points.");
+ CHECK(r[0] == Vector2i(0, 0));
+ CHECK(r[1] == Vector2i(1, 0));
+ CHECK(r[2] == Vector2i(2, 1));
+ CHECK(r[3] == Vector2i(3, 1));
+ CHECK(r[4] == Vector2i(4, 2));
+ CHECK(r[5] == Vector2i(5, 2));
+ CHECK(r[6] == Vector2i(6, 3));
+ CHECK(r[7] == Vector2i(7, 3));
+ CHECK(r[8] == Vector2i(8, 4));
+ CHECK(r[9] == Vector2i(9, 4));
+ CHECK(r[10] == Vector2i(10, 5));
+ CHECK(r[11] == Vector2i(11, 5));
+ }
+}
} // namespace TestGeometry2D
#endif // TEST_GEOMETRY_2D_H