Parcourir la source

Merge pull request #85474 from fire/packedvector4array

Add `PackedVector4Array` Variant type
Rémi Verschelde il y a 1 an
Parent
commit
03e6fbb010
79 fichiers modifiés avec 1036 ajouts et 88 suppressions
  1. 1 0
      core/core_constants.cpp
  2. 2 0
      core/extension/extension_api_dump.cpp
  3. 22 0
      core/extension/gdextension_interface.cpp
  4. 53 26
      core/extension/gdextension_interface.h
  5. 94 0
      core/io/marshalls.cpp
  6. 1 0
      core/io/packed_data_container.cpp
  7. 2 1
      core/io/resource.cpp
  8. 45 16
      core/io/resource_format_binary.cpp
  9. 6 0
      core/math/vector4.cpp
  10. 9 21
      core/math/vector4.h
  11. 1 0
      core/object/script_language.cpp
  12. 1 0
      core/variant/method_ptrcall.h
  13. 1 0
      core/variant/type_info.h
  14. 2 0
      core/variant/typed_array.h
  15. 81 2
      core/variant/variant.cpp
  16. 5 0
      core/variant/variant.h
  17. 24 0
      core/variant/variant_call.cpp
  18. 5 0
      core/variant/variant_construct.cpp
  19. 1 0
      core/variant/variant_construct.h
  20. 1 0
      core/variant/variant_destruct.cpp
  21. 1 0
      core/variant/variant_destruct.h
  22. 35 0
      core/variant/variant_internal.h
  23. 12 1
      core/variant/variant_op.cpp
  24. 33 0
      core/variant/variant_parser.cpp
  25. 33 0
      core/variant/variant_setget.cpp
  26. 2 0
      core/variant/variant_utility.cpp
  27. 5 2
      doc/classes/@GlobalScope.xml
  28. 7 0
      doc/classes/Array.xml
  29. 225 0
      doc/classes/PackedVector4Array.xml
  30. 2 0
      doc/tools/make_rst.py
  31. 2 1
      editor/animation_track_editor.cpp
  32. 22 0
      editor/debugger/debug_adapter/debug_adapter_protocol.cpp
  33. 2 0
      editor/editor_help.cpp
  34. 5 0
      editor/editor_properties.cpp
  35. 1 0
      editor/icons/PackedVector4Array.svg
  36. 2 1
      editor/property_selector.cpp
  37. 11 5
      modules/gdscript/gdscript_analyzer.cpp
  38. 8 0
      modules/gdscript/gdscript_byte_codegen.cpp
  39. 2 0
      modules/gdscript/gdscript_disassembler.cpp
  40. 3 0
      modules/gdscript/gdscript_function.h
  41. 5 0
      modules/gdscript/gdscript_parser.cpp
  42. 4 0
      modules/gdscript/gdscript_utility_functions.cpp
  43. 8 0
      modules/gdscript/gdscript_vm.cpp
  44. 9 0
      modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd
  45. 6 0
      modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out
  46. 2 0
      modules/gdscript/tests/scripts/parser/features/export_arrays.gd
  47. 2 0
      modules/gdscript/tests/scripts/parser/features/export_arrays.out
  48. 4 0
      modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.gd
  49. 1 0
      modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.out
  50. 4 0
      modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.gd
  51. 1 0
      modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.out
  52. 4 0
      modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.gd
  53. 1 0
      modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.out
  54. 4 0
      modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.gd
  55. 1 0
      modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.out
  56. 1 0
      modules/gdscript/tests/scripts/runtime/features/stringify.gd
  57. 1 0
      modules/gdscript/tests/scripts/runtime/features/stringify.out
  58. 2 1
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
  59. 1 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
  60. 3 0
      modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
  61. 7 0
      modules/mono/editor/bindings_generator.cpp
  62. 2 1
      modules/mono/editor/bindings_generator.h
  63. 1 0
      modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
  64. 16 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
  65. 33 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
  66. 27 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
  67. 11 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
  68. 19 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
  69. 6 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
  70. 19 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
  71. 29 0
      modules/mono/glue/runtime_interop.cpp
  72. 2 1
      modules/multiplayer/editor/replication_editor.cpp
  73. 5 3
      platform/windows/godot.natvis
  74. 8 4
      scene/resources/resource_format_text.cpp
  75. 2 1
      servers/rendering/storage/variant_converters.h
  76. 3 1
      tests/core/object/test_class_db.h
  77. 4 0
      tests/core/variant/test_variant.h
  78. 1 0
      tests/test_macros.h
  79. 7 0
      tests/test_validate_testing.h

+ 1 - 0
core/core_constants.cpp

@@ -761,6 +761,7 @@ void register_global_constants() {
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_VECTOR2_ARRAY", Variant::PACKED_VECTOR2_ARRAY);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_VECTOR3_ARRAY", Variant::PACKED_VECTOR3_ARRAY);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_COLOR_ARRAY", Variant::PACKED_COLOR_ARRAY);
+	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_VECTOR4_ARRAY", Variant::PACKED_VECTOR4_ARRAY);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_MAX", Variant::VARIANT_MAX);
 
 	//comparison

+ 2 - 0
core/extension/extension_api_dump.cpp

@@ -189,6 +189,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
 			{ Variant::PACKED_VECTOR2_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
 			{ Variant::PACKED_VECTOR3_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
 			{ Variant::PACKED_COLOR_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
+			{ Variant::PACKED_VECTOR4_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
 			{ Variant::VARIANT_MAX, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(double) * 4, sizeof(uint64_t) + sizeof(double) * 4 },
 		};
 
@@ -230,6 +231,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
 		static_assert(type_size_array[Variant::PACKED_VECTOR2_ARRAY][sizeof(void *)] == sizeof(PackedVector2Array), "Size of PackedVector2Array mismatch");
 		static_assert(type_size_array[Variant::PACKED_VECTOR3_ARRAY][sizeof(void *)] == sizeof(PackedVector3Array), "Size of PackedVector3Array mismatch");
 		static_assert(type_size_array[Variant::PACKED_COLOR_ARRAY][sizeof(void *)] == sizeof(PackedColorArray), "Size of PackedColorArray mismatch");
+		static_assert(type_size_array[Variant::PACKED_VECTOR4_ARRAY][sizeof(void *)] == sizeof(PackedVector4Array), "Size of PackedVector4Array mismatch");
 		static_assert(type_size_array[Variant::VARIANT_MAX][sizeof(void *)] == sizeof(Variant), "Size of Variant mismatch");
 
 		Array core_type_sizes;

+ 22 - 0
core/extension/gdextension_interface.cpp

@@ -595,6 +595,8 @@ static GDExtensionVariantFromTypeConstructorFunc gdextension_get_variant_from_ty
 			return VariantTypeConstructor<PackedVector2Array>::variant_from_type;
 		case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY:
 			return VariantTypeConstructor<PackedVector3Array>::variant_from_type;
+		case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR4_ARRAY:
+			return VariantTypeConstructor<PackedVector4Array>::variant_from_type;
 		case GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY:
 			return VariantTypeConstructor<PackedColorArray>::variant_from_type;
 		case GDEXTENSION_VARIANT_TYPE_NIL:
@@ -678,6 +680,8 @@ static GDExtensionTypeFromVariantConstructorFunc gdextension_get_variant_to_type
 			return VariantTypeConstructor<PackedVector2Array>::type_from_variant;
 		case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY:
 			return VariantTypeConstructor<PackedVector3Array>::type_from_variant;
+		case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR4_ARRAY:
+			return VariantTypeConstructor<PackedVector4Array>::type_from_variant;
 		case GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY:
 			return VariantTypeConstructor<PackedColorArray>::type_from_variant;
 		case GDEXTENSION_VARIANT_TYPE_NIL:
@@ -1116,6 +1120,22 @@ static GDExtensionTypePtr gdextension_packed_vector3_array_operator_index_const(
 	return (GDExtensionTypePtr)&self->ptr()[p_index];
 }
 
+static GDExtensionTypePtr gdextension_packed_vector4_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
+	PackedVector4Array *self = (PackedVector4Array *)p_self;
+	if (unlikely(p_index < 0 || p_index >= self->size())) {
+		return nullptr;
+	}
+	return (GDExtensionTypePtr)&self->ptrw()[p_index];
+}
+
+static GDExtensionTypePtr gdextension_packed_vector4_array_operator_index_const(GDExtensionConstTypePtr p_self, GDExtensionInt p_index) {
+	const PackedVector4Array *self = (const PackedVector4Array *)p_self;
+	if (unlikely(p_index < 0 || p_index >= self->size())) {
+		return nullptr;
+	}
+	return (GDExtensionTypePtr)&self->ptr()[p_index];
+}
+
 static GDExtensionVariantPtr gdextension_array_operator_index(GDExtensionTypePtr p_self, GDExtensionInt p_index) {
 	Array *self = (Array *)p_self;
 	if (unlikely(p_index < 0 || p_index >= self->size())) {
@@ -1620,6 +1640,8 @@ void gdextension_setup_interface() {
 	REGISTER_INTERFACE_FUNC(packed_vector2_array_operator_index_const);
 	REGISTER_INTERFACE_FUNC(packed_vector3_array_operator_index);
 	REGISTER_INTERFACE_FUNC(packed_vector3_array_operator_index_const);
+	REGISTER_INTERFACE_FUNC(packed_vector4_array_operator_index);
+	REGISTER_INTERFACE_FUNC(packed_vector4_array_operator_index_const);
 	REGISTER_INTERFACE_FUNC(array_operator_index);
 	REGISTER_INTERFACE_FUNC(array_operator_index_const);
 	REGISTER_INTERFACE_FUNC(array_ref);

+ 53 - 26
core/extension/gdextension_interface.h

@@ -96,6 +96,7 @@ typedef enum {
 	GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR2_ARRAY,
 	GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY,
 	GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY,
+	GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR4_ARRAY,
 
 	GDEXTENSION_VARIANT_TYPE_VARIANT_MAX
 } GDExtensionVariantType;
@@ -1963,32 +1964,6 @@ typedef uint8_t *(*GDExtensionInterfacePackedByteArrayOperatorIndex)(GDExtension
  */
 typedef const uint8_t *(*GDExtensionInterfacePackedByteArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
 
-/**
- * @name packed_color_array_operator_index
- * @since 4.1
- *
- * Gets a pointer to a color in a PackedColorArray.
- *
- * @param p_self A pointer to a PackedColorArray object.
- * @param p_index The index of the Color to get.
- *
- * @return A pointer to the requested Color.
- */
-typedef GDExtensionTypePtr (*GDExtensionInterfacePackedColorArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
-
-/**
- * @name packed_color_array_operator_index_const
- * @since 4.1
- *
- * Gets a const pointer to a color in a PackedColorArray.
- *
- * @param p_self A const pointer to a const PackedColorArray object.
- * @param p_index The index of the Color to get.
- *
- * @return A const pointer to the requested Color.
- */
-typedef GDExtensionTypePtr (*GDExtensionInterfacePackedColorArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
-
 /**
  * @name packed_float32_array_operator_index
  * @since 4.1
@@ -2171,6 +2146,58 @@ typedef GDExtensionTypePtr (*GDExtensionInterfacePackedVector3ArrayOperatorIndex
  */
 typedef GDExtensionTypePtr (*GDExtensionInterfacePackedVector3ArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
 
+/**
+ * @name packed_vector4_array_operator_index
+ * @since 4.3
+ *
+ * Gets a pointer to a Vector4 in a PackedVector4Array.
+ *
+ * @param p_self A pointer to a PackedVector4Array object.
+ * @param p_index The index of the Vector4 to get.
+ *
+ * @return A pointer to the requested Vector4.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedVector4ArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_vector4_array_operator_index_const
+ * @since 4.3
+ *
+ * Gets a const pointer to a Vector4 in a PackedVector4Array.
+ *
+ * @param p_self A const pointer to a PackedVector4Array object.
+ * @param p_index The index of the Vector4 to get.
+ *
+ * @return A const pointer to the requested Vector4.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedVector4ArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_color_array_operator_index
+ * @since 4.1
+ *
+ * Gets a pointer to a color in a PackedColorArray.
+ *
+ * @param p_self A pointer to a PackedColorArray object.
+ * @param p_index The index of the Color to get.
+ *
+ * @return A pointer to the requested Color.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedColorArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);
+
+/**
+ * @name packed_color_array_operator_index_const
+ * @since 4.1
+ *
+ * Gets a const pointer to a color in a PackedColorArray.
+ *
+ * @param p_self A const pointer to a PackedColorArray object.
+ * @param p_index The index of the Color to get.
+ *
+ * @return A const pointer to the requested Color.
+ */
+typedef GDExtensionTypePtr (*GDExtensionInterfacePackedColorArrayOperatorIndexConst)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index);
+
 /**
  * @name array_operator_index
  * @since 4.1

+ 94 - 0
core/io/marshalls.cpp

@@ -1178,6 +1178,73 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 			r_variant = carray;
 
+		} break;
+
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+			int32_t count = decode_uint32(buf);
+			buf += 4;
+			len -= 4;
+
+			Vector<Vector4> varray;
+
+			if (header & HEADER_DATA_FLAG_64) {
+				ERR_FAIL_MUL_OF(count, sizeof(double) * 4, ERR_INVALID_DATA);
+				ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 4 > (size_t)len, ERR_INVALID_DATA);
+
+				if (r_len) {
+					(*r_len) += 4; // Size of count number.
+				}
+
+				if (count) {
+					varray.resize(count);
+					Vector4 *w = varray.ptrw();
+
+					for (int32_t i = 0; i < count; i++) {
+						w[i].x = decode_double(buf + i * sizeof(double) * 4 + sizeof(double) * 0);
+						w[i].y = decode_double(buf + i * sizeof(double) * 4 + sizeof(double) * 1);
+						w[i].z = decode_double(buf + i * sizeof(double) * 4 + sizeof(double) * 2);
+						w[i].w = decode_double(buf + i * sizeof(double) * 4 + sizeof(double) * 3);
+					}
+
+					int adv = sizeof(double) * 4 * count;
+
+					if (r_len) {
+						(*r_len) += adv;
+					}
+					len -= adv;
+					buf += adv;
+				}
+			} else {
+				ERR_FAIL_MUL_OF(count, sizeof(float) * 4, ERR_INVALID_DATA);
+				ERR_FAIL_COND_V(count < 0 || count * sizeof(float) * 4 > (size_t)len, ERR_INVALID_DATA);
+
+				if (r_len) {
+					(*r_len) += 4; // Size of count number.
+				}
+
+				if (count) {
+					varray.resize(count);
+					Vector4 *w = varray.ptrw();
+
+					for (int32_t i = 0; i < count; i++) {
+						w[i].x = decode_float(buf + i * sizeof(float) * 4 + sizeof(float) * 0);
+						w[i].y = decode_float(buf + i * sizeof(float) * 4 + sizeof(float) * 1);
+						w[i].z = decode_float(buf + i * sizeof(float) * 4 + sizeof(float) * 2);
+						w[i].w = decode_float(buf + i * sizeof(float) * 4 + sizeof(float) * 3);
+					}
+
+					int adv = sizeof(float) * 4 * count;
+
+					if (r_len) {
+						(*r_len) += adv;
+					}
+					len -= adv;
+					buf += adv;
+				}
+			}
+			r_variant = varray;
+
 		} break;
 		default: {
 			ERR_FAIL_V(ERR_BUG);
@@ -1263,6 +1330,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 		case Variant::VECTOR4:
 		case Variant::PACKED_VECTOR2_ARRAY:
 		case Variant::PACKED_VECTOR3_ARRAY:
+		case Variant::PACKED_VECTOR4_ARRAY:
 		case Variant::TRANSFORM2D:
 		case Variant::TRANSFORM3D:
 		case Variant::PROJECTION:
@@ -1946,6 +2014,32 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 
 			r_len += 4 * 4 * len;
 
+		} break;
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			Vector<Vector4> data = p_variant;
+			int len = data.size();
+
+			if (buf) {
+				encode_uint32(len, buf);
+				buf += 4;
+			}
+
+			r_len += 4;
+
+			if (buf) {
+				for (int i = 0; i < len; i++) {
+					Vector4 v = data.get(i);
+
+					encode_real(v.x, &buf[0]);
+					encode_real(v.y, &buf[sizeof(real_t)]);
+					encode_real(v.z, &buf[sizeof(real_t) * 2]);
+					encode_real(v.w, &buf[sizeof(real_t) * 3]);
+					buf += sizeof(real_t) * 4;
+				}
+			}
+
+			r_len += sizeof(real_t) * 4 * len;
+
 		} break;
 		default: {
 			ERR_FAIL_V(ERR_BUG);

+ 1 - 0
core/io/packed_data_container.cpp

@@ -244,6 +244,7 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd
 		case Variant::PACKED_VECTOR2_ARRAY:
 		case Variant::PACKED_VECTOR3_ARRAY:
 		case Variant::PACKED_COLOR_ARRAY:
+		case Variant::PACKED_VECTOR4_ARRAY:
 		case Variant::STRING_NAME:
 		case Variant::NODE_PATH: {
 			uint32_t pos = tmpdata.size();

+ 2 - 1
core/io/resource.cpp

@@ -383,7 +383,8 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const {
 			case Variant::Type::PACKED_FLOAT64_ARRAY:
 			case Variant::Type::PACKED_STRING_ARRAY:
 			case Variant::Type::PACKED_VECTOR2_ARRAY:
-			case Variant::Type::PACKED_VECTOR3_ARRAY: {
+			case Variant::Type::PACKED_VECTOR3_ARRAY:
+			case Variant::Type::PACKED_VECTOR4_ARRAY: {
 				r->set(E.name, p.duplicate(p_subresources));
 			} break;
 

+ 45 - 16
core/io/resource_format_binary.cpp

@@ -85,15 +85,17 @@ enum {
 	VARIANT_VECTOR4 = 50,
 	VARIANT_VECTOR4I = 51,
 	VARIANT_PROJECTION = 52,
+	VARIANT_PACKED_VECTOR4_ARRAY = 53,
 	OBJECT_EMPTY = 0,
 	OBJECT_EXTERNAL_RESOURCE = 1,
 	OBJECT_INTERNAL_RESOURCE = 2,
 	OBJECT_EXTERNAL_RESOURCE_INDEX = 3,
-	// Version 2: added 64 bits support for float and int.
-	// Version 3: changed nodepath encoding.
-	// Version 4: new string ID for ext/subresources, breaks forward compat.
+	// Version 2: Added 64-bit support for float and int.
+	// Version 3: Changed NodePath encoding.
+	// Version 4: New string ID for ext/subresources, breaks forward compat.
 	// Version 5: Ability to store script class in the header.
-	FORMAT_VERSION = 5,
+	// Version 6: Added PackedVector4Array Variant type.
+	FORMAT_VERSION = 6,
 	FORMAT_VERSION_CAN_RENAME_DEPS = 1,
 	FORMAT_VERSION_NO_NODEPATH_PROPERTY = 3,
 };
@@ -653,6 +655,19 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
 
 			r_v = array;
 		} break;
+		case VARIANT_PACKED_VECTOR4_ARRAY: {
+			uint32_t len = f->get_32();
+
+			Vector<Vector4> array;
+			array.resize(len);
+			Vector4 *w = array.ptrw();
+			static_assert(sizeof(Vector4) == 4 * sizeof(real_t));
+			const Error err = read_reals(reinterpret_cast<real_t *>(w), f, len * 4);
+			ERR_FAIL_COND_V(err != OK, err);
+
+			r_v = array;
+
+		} break;
 		default: {
 			ERR_FAIL_V(ERR_FILE_CORRUPT);
 		} break;
@@ -1912,33 +1927,33 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 			for (int i = 0; i < len; i++) {
 				save_unicode_string(f, r[i]);
 			}
-
 		} break;
-		case Variant::PACKED_VECTOR3_ARRAY: {
-			f->store_32(VARIANT_PACKED_VECTOR3_ARRAY);
-			Vector<Vector3> arr = p_property;
+
+		case Variant::PACKED_VECTOR2_ARRAY: {
+			f->store_32(VARIANT_PACKED_VECTOR2_ARRAY);
+			Vector<Vector2> arr = p_property;
 			int len = arr.size();
 			f->store_32(len);
-			const Vector3 *r = arr.ptr();
+			const Vector2 *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 				f->store_real(r[i].x);
 				f->store_real(r[i].y);
-				f->store_real(r[i].z);
 			}
-
 		} break;
-		case Variant::PACKED_VECTOR2_ARRAY: {
-			f->store_32(VARIANT_PACKED_VECTOR2_ARRAY);
-			Vector<Vector2> arr = p_property;
+
+		case Variant::PACKED_VECTOR3_ARRAY: {
+			f->store_32(VARIANT_PACKED_VECTOR3_ARRAY);
+			Vector<Vector3> arr = p_property;
 			int len = arr.size();
 			f->store_32(len);
-			const Vector2 *r = arr.ptr();
+			const Vector3 *r = arr.ptr();
 			for (int i = 0; i < len; i++) {
 				f->store_real(r[i].x);
 				f->store_real(r[i].y);
+				f->store_real(r[i].z);
 			}
-
 		} break;
+
 		case Variant::PACKED_COLOR_ARRAY: {
 			f->store_32(VARIANT_PACKED_COLOR_ARRAY);
 			Vector<Color> arr = p_property;
@@ -1952,6 +1967,20 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 				f->store_float(r[i].a);
 			}
 
+		} break;
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			f->store_32(VARIANT_PACKED_VECTOR4_ARRAY);
+			Vector<Vector4> arr = p_property;
+			int len = arr.size();
+			f->store_32(len);
+			const Vector4 *r = arr.ptr();
+			for (int i = 0; i < len; i++) {
+				f->store_real(r[i].x);
+				f->store_real(r[i].y);
+				f->store_real(r[i].z);
+				f->store_real(r[i].w);
+			}
+
 		} break;
 		default: {
 			ERR_FAIL_MSG("Invalid variant.");

+ 6 - 0
core/math/vector4.cpp

@@ -30,6 +30,8 @@
 
 #include "vector4.h"
 
+#include "core/math/math_funcs.h"
+#include "core/math/vector4i.h"
 #include "core/string/ustring.h"
 
 Vector4::Axis Vector4::min_axis_index() const {
@@ -215,3 +217,7 @@ Vector4::operator String() const {
 }
 
 static_assert(sizeof(Vector4) == 4 * sizeof(real_t));
+
+Vector4::operator Vector4i() const {
+	return Vector4i(x, y, z, w);
+}

+ 9 - 21
core/math/vector4.h

@@ -32,9 +32,11 @@
 #define VECTOR4_H
 
 #include "core/error/error_macros.h"
-#include "core/math/math_funcs.h"
+#include "core/math/math_defs.h"
+#include "core/typedefs.h"
 
 class String;
+struct Vector4i;
 
 struct _NO_DISCARD_ Vector4 {
 	static const int AXIS_COUNT = 4;
@@ -140,28 +142,14 @@ struct _NO_DISCARD_ Vector4 {
 	_FORCE_INLINE_ bool operator<=(const Vector4 &p_vec4) const;
 
 	operator String() const;
+	operator Vector4i() const;
 
 	_FORCE_INLINE_ Vector4() {}
-
-	_FORCE_INLINE_ Vector4(real_t p_x, real_t p_y, real_t p_z, real_t p_w) :
-			x(p_x),
-			y(p_y),
-			z(p_z),
-			w(p_w) {
-	}
-
-	Vector4(const Vector4 &p_vec4) :
-			x(p_vec4.x),
-			y(p_vec4.y),
-			z(p_vec4.z),
-			w(p_vec4.w) {
-	}
-
-	void operator=(const Vector4 &p_vec4) {
-		x = p_vec4.x;
-		y = p_vec4.y;
-		z = p_vec4.z;
-		w = p_vec4.w;
+	_FORCE_INLINE_ Vector4(real_t p_x, real_t p_y, real_t p_z, real_t p_w) {
+		x = p_x;
+		y = p_y;
+		z = p_z;
+		w = p_w;
 	}
 };
 

+ 1 - 0
core/object/script_language.cpp

@@ -537,6 +537,7 @@ void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const
 	p_core_type_words->push_back("PackedVector2Array");
 	p_core_type_words->push_back("PackedVector3Array");
 	p_core_type_words->push_back("PackedColorArray");
+	p_core_type_words->push_back("PackedVector4Array");
 }
 
 void ScriptLanguage::frame() {

+ 1 - 0
core/variant/method_ptrcall.h

@@ -152,6 +152,7 @@ MAKE_PTRARG(PackedStringArray);
 MAKE_PTRARG(PackedVector2Array);
 MAKE_PTRARG(PackedVector3Array);
 MAKE_PTRARG(PackedColorArray);
+MAKE_PTRARG(PackedVector4Array);
 MAKE_PTRARG_BY_REFERENCE(Variant);
 
 // This is for Object.

+ 1 - 0
core/variant/type_info.h

@@ -142,6 +142,7 @@ MAKE_TYPE_INFO(PackedStringArray, Variant::PACKED_STRING_ARRAY)
 MAKE_TYPE_INFO(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
 MAKE_TYPE_INFO(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
 MAKE_TYPE_INFO(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
+MAKE_TYPE_INFO(PackedVector4Array, Variant::PACKED_VECTOR4_ARRAY)
 
 MAKE_TYPE_INFO(IPAddress, Variant::STRING)
 

+ 2 - 0
core/variant/typed_array.h

@@ -134,6 +134,7 @@ MAKE_TYPED_ARRAY(PackedStringArray, Variant::PACKED_STRING_ARRAY)
 MAKE_TYPED_ARRAY(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
 MAKE_TYPED_ARRAY(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
 MAKE_TYPED_ARRAY(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
+MAKE_TYPED_ARRAY(PackedVector4Array, Variant::PACKED_VECTOR4_ARRAY)
 MAKE_TYPED_ARRAY(IPAddress, Variant::STRING)
 
 template <typename T>
@@ -235,6 +236,7 @@ MAKE_TYPED_ARRAY_INFO(PackedStringArray, Variant::PACKED_STRING_ARRAY)
 MAKE_TYPED_ARRAY_INFO(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
 MAKE_TYPED_ARRAY_INFO(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
 MAKE_TYPED_ARRAY_INFO(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedVector4Array, Variant::PACKED_VECTOR4_ARRAY)
 MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING)
 
 #undef MAKE_TYPED_ARRAY

+ 81 - 2
core/variant/variant.cpp

@@ -167,6 +167,9 @@ String Variant::get_type_name(Variant::Type p_type) {
 		case PACKED_COLOR_ARRAY: {
 			return "PackedColorArray";
 		}
+		case PACKED_VECTOR4_ARRAY: {
+			return "PackedVector4Array";
+		}
 		default: {
 		}
 	}
@@ -404,6 +407,7 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
 				PACKED_COLOR_ARRAY,
 				PACKED_VECTOR2_ARRAY,
 				PACKED_VECTOR3_ARRAY,
+				PACKED_VECTOR4_ARRAY,
 				NIL
 			};
 
@@ -479,6 +483,14 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
 
 			valid_types = valid;
 
+		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			static const Type valid[] = {
+				ARRAY,
+				NIL
+			};
+			valid_types = valid;
+
 		} break;
 		default: {
 		}
@@ -738,6 +750,7 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
 				PACKED_COLOR_ARRAY,
 				PACKED_VECTOR2_ARRAY,
 				PACKED_VECTOR3_ARRAY,
+				PACKED_VECTOR4_ARRAY,
 				NIL
 			};
 
@@ -813,6 +826,14 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
 
 			valid_types = valid;
 
+		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			static const Type valid[] = {
+				ARRAY,
+				NIL
+			};
+			valid_types = valid;
+
 		} break;
 		default: {
 		}
@@ -980,6 +1001,9 @@ bool Variant::is_zero() const {
 		case PACKED_COLOR_ARRAY: {
 			return PackedArrayRef<Color>::get_array(_data.packed_array).size() == 0;
 		}
+		case PACKED_VECTOR4_ARRAY: {
+			return PackedArrayRef<Vector4>::get_array(_data.packed_array).size() == 0;
+		}
 		default: {
 		}
 	}
@@ -1236,6 +1260,12 @@ void Variant::reference(const Variant &p_variant) {
 				_data.packed_array = PackedArrayRef<Color>::create();
 			}
 		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			_data.packed_array = static_cast<PackedArrayRef<Vector4> *>(p_variant._data.packed_array)->reference();
+			if (!_data.packed_array) {
+				_data.packed_array = PackedArrayRef<Vector4>::create();
+			}
+		} break;
 		default: {
 		}
 	}
@@ -1410,9 +1440,12 @@ void Variant::_clear_internal() {
 		case PACKED_COLOR_ARRAY: {
 			PackedArrayRefBase::destroy(_data.packed_array);
 		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			PackedArrayRefBase::destroy(_data.packed_array);
+		} break;
 		default: {
 			// Not needed, there is no point. The following do not allocate memory:
-			// VECTOR2, VECTOR3, RECT2, PLANE, QUATERNION, COLOR.
+			// VECTOR2, VECTOR3, VECTOR4, RECT2, PLANE, QUATERNION, COLOR.
 		}
 	}
 }
@@ -1759,6 +1792,9 @@ String Variant::stringify(int recursion_count) const {
 		case PACKED_COLOR_ARRAY: {
 			return stringify_vector(operator PackedColorArray(), recursion_count);
 		}
+		case PACKED_VECTOR4_ARRAY: {
+			return stringify_vector(operator PackedVector4Array(), recursion_count);
+		}
 		case PACKED_STRING_ARRAY: {
 			return stringify_vector(operator PackedStringArray(), recursion_count);
 		}
@@ -2191,6 +2227,9 @@ inline DA _convert_array_from_variant(const Variant &p_variant) {
 		case Variant::PACKED_COLOR_ARRAY: {
 			return _convert_array<DA, PackedColorArray>(p_variant.operator PackedColorArray());
 		}
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			return _convert_array<DA, PackedVector4Array>(p_variant.operator PackedVector4Array());
+		}
 		default: {
 			return DA();
 		}
@@ -2277,6 +2316,14 @@ Variant::operator PackedColorArray() const {
 	}
 }
 
+Variant::operator PackedVector4Array() const {
+	if (type == PACKED_VECTOR4_ARRAY) {
+		return static_cast<PackedArrayRef<Vector4> *>(_data.packed_array)->array;
+	} else {
+		return _convert_array_from_variant<PackedVector4Array>(*this);
+	}
+}
+
 /* helpers */
 
 Variant::operator Vector<::RID>() const {
@@ -2635,6 +2682,11 @@ Variant::Variant(const PackedColorArray &p_color_array) :
 	_data.packed_array = PackedArrayRef<Color>::create(p_color_array);
 }
 
+Variant::Variant(const PackedVector4Array &p_vector4_array) :
+		type(PACKED_VECTOR4_ARRAY) {
+	_data.packed_array = PackedArrayRef<Vector4>::create(p_vector4_array);
+}
+
 /* helpers */
 Variant::Variant(const Vector<::RID> &p_array) :
 		type(ARRAY) {
@@ -2853,6 +2905,9 @@ void Variant::operator=(const Variant &p_variant) {
 		case PACKED_COLOR_ARRAY: {
 			_data.packed_array = PackedArrayRef<Color>::reference_from(_data.packed_array, p_variant._data.packed_array);
 		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			_data.packed_array = PackedArrayRef<Vector4>::reference_from(_data.packed_array, p_variant._data.packed_array);
+		} break;
 		default: {
 		}
 	}
@@ -3175,6 +3230,25 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
 
 			return hash;
 		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			uint32_t hash = HASH_MURMUR3_SEED;
+			const PackedVector4Array &arr = PackedArrayRef<Vector4>::get_array(_data.packed_array);
+			int len = arr.size();
+
+			if (likely(len)) {
+				const Vector4 *r = arr.ptr();
+
+				for (int i = 0; i < len; i++) {
+					hash = hash_murmur3_one_real(r[i].x, hash);
+					hash = hash_murmur3_one_real(r[i].y, hash);
+					hash = hash_murmur3_one_real(r[i].z, hash);
+					hash = hash_murmur3_one_real(r[i].w, hash);
+				}
+				hash = hash_fmix32(hash);
+			}
+
+			return hash;
+		} break;
 		default: {
 		}
 	}
@@ -3430,6 +3504,10 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count, bool s
 			hash_compare_packed_array(_data.packed_array, p_variant._data.packed_array, Color, hash_compare_color);
 		} break;
 
+		case PACKED_VECTOR4_ARRAY: {
+			hash_compare_packed_array(_data.packed_array, p_variant._data.packed_array, Vector4, hash_compare_vector4);
+		} break;
+
 		default:
 			bool v;
 			Variant r;
@@ -3468,7 +3546,8 @@ bool Variant::identity_compare(const Variant &p_variant) const {
 		case PACKED_STRING_ARRAY:
 		case PACKED_VECTOR2_ARRAY:
 		case PACKED_VECTOR3_ARRAY:
-		case PACKED_COLOR_ARRAY: {
+		case PACKED_COLOR_ARRAY:
+		case PACKED_VECTOR4_ARRAY: {
 			return _data.packed_array == p_variant._data.packed_array;
 		} break;
 

+ 5 - 0
core/variant/variant.h

@@ -75,6 +75,7 @@ typedef Vector<String> PackedStringArray;
 typedef Vector<Vector2> PackedVector2Array;
 typedef Vector<Vector3> PackedVector3Array;
 typedef Vector<Color> PackedColorArray;
+typedef Vector<Vector4> PackedVector4Array;
 
 class Variant {
 public:
@@ -126,6 +127,7 @@ public:
 		PACKED_VECTOR2_ARRAY,
 		PACKED_VECTOR3_ARRAY,
 		PACKED_COLOR_ARRAY,
+		PACKED_VECTOR4_ARRAY,
 
 		VARIANT_MAX
 	};
@@ -297,6 +299,7 @@ private:
 			true, //PACKED_VECTOR2_ARRAY,
 			true, //PACKED_VECTOR3_ARRAY,
 			true, //PACKED_COLOR_ARRAY,
+			true, //PACKED_VECTOR4_ARRAY,
 		};
 
 		if (unlikely(needs_deinit[type])) { // Make it fast for types that don't need deinit.
@@ -409,6 +412,7 @@ public:
 	operator PackedVector3Array() const;
 	operator PackedVector2Array() const;
 	operator PackedColorArray() const;
+	operator PackedVector4Array() const;
 
 	operator Vector<::RID>() const;
 	operator Vector<Plane>() const;
@@ -474,6 +478,7 @@ public:
 	Variant(const PackedVector2Array &p_vector2_array);
 	Variant(const PackedVector3Array &p_vector3_array);
 	Variant(const PackedColorArray &p_color_array);
+	Variant(const PackedVector4Array &p_vector4_array);
 
 	Variant(const Vector<::RID> &p_array); // helper
 	Variant(const Vector<Plane> &p_array); // helper

+ 24 - 0
core/variant/variant_call.cpp

@@ -2568,6 +2568,30 @@ static void _register_variant_builtin_methods() {
 	bind_method(PackedColorArray, rfind, sarray("value", "from"), varray(-1));
 	bind_method(PackedColorArray, count, sarray("value"), varray());
 
+	/* Vector4 Array */
+
+	bind_method(PackedVector4Array, size, sarray(), varray());
+	bind_method(PackedVector4Array, is_empty, sarray(), varray());
+	bind_method(PackedVector4Array, set, sarray("index", "value"), varray());
+	bind_method(PackedVector4Array, push_back, sarray("value"), varray());
+	bind_method(PackedVector4Array, append, sarray("value"), varray());
+	bind_method(PackedVector4Array, append_array, sarray("array"), varray());
+	bind_method(PackedVector4Array, remove_at, sarray("index"), varray());
+	bind_method(PackedVector4Array, insert, sarray("at_index", "value"), varray());
+	bind_method(PackedVector4Array, fill, sarray("value"), varray());
+	bind_methodv(PackedVector4Array, resize, &PackedVector4Array::resize_zeroed, sarray("new_size"), varray());
+	bind_method(PackedVector4Array, clear, sarray(), varray());
+	bind_method(PackedVector4Array, has, sarray("value"), varray());
+	bind_method(PackedVector4Array, reverse, sarray(), varray());
+	bind_method(PackedVector4Array, slice, sarray("begin", "end"), varray(INT_MAX));
+	bind_method(PackedVector4Array, to_byte_array, sarray(), varray());
+	bind_method(PackedVector4Array, sort, sarray(), varray());
+	bind_method(PackedVector4Array, bsearch, sarray("value", "before"), varray(true));
+	bind_method(PackedVector4Array, duplicate, sarray(), varray());
+	bind_method(PackedVector4Array, find, sarray("value", "from"), varray(0));
+	bind_method(PackedVector4Array, rfind, sarray("value", "from"), varray(-1));
+	bind_method(PackedVector4Array, count, sarray("value"), varray());
+
 	/* Register constants */
 
 	int ncc = Color::get_named_color_count();

+ 5 - 0
core/variant/variant_construct.cpp

@@ -211,6 +211,7 @@ void Variant::_register_variant_constructors() {
 	add_constructor<VariantConstructorToArray<PackedVector2Array>>(sarray("from"));
 	add_constructor<VariantConstructorToArray<PackedVector3Array>>(sarray("from"));
 	add_constructor<VariantConstructorToArray<PackedColorArray>>(sarray("from"));
+	add_constructor<VariantConstructorToArray<PackedVector4Array>>(sarray("from"));
 
 	add_constructor<VariantConstructNoArgs<PackedByteArray>>(sarray());
 	add_constructor<VariantConstructor<PackedByteArray, PackedByteArray>>(sarray("from"));
@@ -247,6 +248,10 @@ void Variant::_register_variant_constructors() {
 	add_constructor<VariantConstructNoArgs<PackedColorArray>>(sarray());
 	add_constructor<VariantConstructor<PackedColorArray, PackedColorArray>>(sarray("from"));
 	add_constructor<VariantConstructorFromArray<PackedColorArray>>(sarray("from"));
+
+	add_constructor<VariantConstructNoArgs<PackedVector4Array>>(sarray());
+	add_constructor<VariantConstructor<PackedVector4Array, PackedVector4Array>>(sarray("from"));
+	add_constructor<VariantConstructorFromArray<PackedVector4Array>>(sarray("from"));
 }
 
 void Variant::_unregister_variant_constructors() {

+ 1 - 0
core/variant/variant_construct.h

@@ -97,6 +97,7 @@ MAKE_PTRCONSTRUCT(PackedStringArray);
 MAKE_PTRCONSTRUCT(PackedVector2Array);
 MAKE_PTRCONSTRUCT(PackedVector3Array);
 MAKE_PTRCONSTRUCT(PackedColorArray);
+MAKE_PTRCONSTRUCT(PackedVector4Array);
 MAKE_PTRCONSTRUCT(Variant);
 
 template <typename T, typename... P>

+ 1 - 0
core/variant/variant_destruct.cpp

@@ -56,6 +56,7 @@ void Variant::_register_variant_destructors() {
 	add_destructor<VariantDestruct<PackedVector2Array>>();
 	add_destructor<VariantDestruct<PackedVector3Array>>();
 	add_destructor<VariantDestruct<PackedColorArray>>();
+	add_destructor<VariantDestruct<PackedVector4Array>>();
 }
 
 void Variant::_unregister_variant_destructors() {

+ 1 - 0
core/variant/variant_destruct.h

@@ -65,6 +65,7 @@ MAKE_PTRDESTRUCT(PackedStringArray);
 MAKE_PTRDESTRUCT(PackedVector2Array);
 MAKE_PTRDESTRUCT(PackedVector3Array);
 MAKE_PTRDESTRUCT(PackedColorArray);
+MAKE_PTRDESTRUCT(PackedVector4Array);
 
 #undef MAKE_PTRDESTRUCT
 

+ 35 - 0
core/variant/variant_internal.h

@@ -114,6 +114,9 @@ public:
 			case Variant::PACKED_COLOR_ARRAY:
 				init_color_array(v);
 				break;
+			case Variant::PACKED_VECTOR4_ARRAY:
+				init_vector4_array(v);
+				break;
 			case Variant::OBJECT:
 				init_object(v);
 				break;
@@ -205,6 +208,8 @@ public:
 	_FORCE_INLINE_ static const PackedVector3Array *get_vector3_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Vector3> *>(v->_data.packed_array)->array; }
 	_FORCE_INLINE_ static PackedColorArray *get_color_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<Color> *>(v->_data.packed_array)->array; }
 	_FORCE_INLINE_ static const PackedColorArray *get_color_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Color> *>(v->_data.packed_array)->array; }
+	_FORCE_INLINE_ static PackedVector4Array *get_vector4_array(Variant *v) { return &static_cast<Variant::PackedArrayRef<Vector4> *>(v->_data.packed_array)->array; }
+	_FORCE_INLINE_ static const PackedVector4Array *get_vector4_array(const Variant *v) { return &static_cast<const Variant::PackedArrayRef<Vector4> *>(v->_data.packed_array)->array; }
 
 	_FORCE_INLINE_ static Object **get_object(Variant *v) { return (Object **)&v->_get_obj().obj; }
 	_FORCE_INLINE_ static const Object **get_object(const Variant *v) { return (const Object **)&v->_get_obj().obj; }
@@ -313,6 +318,10 @@ public:
 		v->_data.packed_array = Variant::PackedArrayRef<Color>::create(Vector<Color>());
 		v->type = Variant::PACKED_COLOR_ARRAY;
 	}
+	_FORCE_INLINE_ static void init_vector4_array(Variant *v) {
+		v->_data.packed_array = Variant::PackedArrayRef<Vector4>::create(Vector<Vector4>());
+		v->type = Variant::PACKED_VECTOR4_ARRAY;
+	}
 	_FORCE_INLINE_ static void init_object(Variant *v) {
 		object_assign_null(v);
 		v->type = Variant::OBJECT;
@@ -417,6 +426,8 @@ public:
 				return get_vector3_array(v);
 			case Variant::PACKED_COLOR_ARRAY:
 				return get_color_array(v);
+			case Variant::PACKED_VECTOR4_ARRAY:
+				return get_vector4_array(v);
 			case Variant::OBJECT:
 				return get_object(v);
 			case Variant::VARIANT_MAX:
@@ -501,6 +512,8 @@ public:
 				return get_vector3_array(v);
 			case Variant::PACKED_COLOR_ARRAY:
 				return get_color_array(v);
+			case Variant::PACKED_VECTOR4_ARRAY:
+				return get_vector4_array(v);
 			case Variant::OBJECT:
 				return get_object(v);
 			case Variant::VARIANT_MAX:
@@ -797,6 +810,12 @@ struct VariantGetInternalPtr<PackedColorArray> {
 	static const PackedColorArray *get_ptr(const Variant *v) { return VariantInternal::get_color_array(v); }
 };
 
+template <>
+struct VariantGetInternalPtr<PackedVector4Array> {
+	static PackedVector4Array *get_ptr(Variant *v) { return VariantInternal::get_vector4_array(v); }
+	static const PackedVector4Array *get_ptr(const Variant *v) { return VariantInternal::get_vector4_array(v); }
+};
+
 template <typename T>
 struct VariantInternalAccessor {
 };
@@ -1057,6 +1076,12 @@ struct VariantInternalAccessor<PackedColorArray> {
 	static _FORCE_INLINE_ void set(Variant *v, const PackedColorArray &p_value) { *VariantInternal::get_color_array(v) = p_value; }
 };
 
+template <>
+struct VariantInternalAccessor<PackedVector4Array> {
+	static _FORCE_INLINE_ const PackedVector4Array &get(const Variant *v) { return *VariantInternal::get_vector4_array(v); }
+	static _FORCE_INLINE_ void set(Variant *v, const PackedVector4Array &p_value) { *VariantInternal::get_vector4_array(v) = p_value; }
+};
+
 template <>
 struct VariantInternalAccessor<Object *> {
 	static _FORCE_INLINE_ Object *get(const Variant *v) { return const_cast<Object *>(*VariantInternal::get_object(v)); }
@@ -1296,6 +1321,11 @@ struct VariantInitializer<PackedColorArray> {
 	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_color_array(v); }
 };
 
+template <>
+struct VariantInitializer<PackedVector4Array> {
+	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_vector4_array(v); }
+};
+
 template <>
 struct VariantInitializer<Object *> {
 	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_object(v); }
@@ -1490,6 +1520,11 @@ struct VariantDefaultInitializer<PackedColorArray> {
 	static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); }
 };
 
+template <>
+struct VariantDefaultInitializer<PackedVector4Array> {
+	static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4_array(v) = PackedVector4Array(); }
+};
+
 template <typename T>
 struct VariantTypeChanger {
 	static _FORCE_INLINE_ void change(Variant *v) {

+ 12 - 1
core/variant/variant_op.cpp

@@ -274,6 +274,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAppendArray<Vector2>>(Variant::OP_ADD, Variant::PACKED_VECTOR2_ARRAY, Variant::PACKED_VECTOR2_ARRAY);
 	register_op<OperatorEvaluatorAppendArray<Vector3>>(Variant::OP_ADD, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY);
 	register_op<OperatorEvaluatorAppendArray<Color>>(Variant::OP_ADD, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY);
+	register_op<OperatorEvaluatorAppendArray<Vector4>>(Variant::OP_ADD, Variant::PACKED_VECTOR4_ARRAY, Variant::PACKED_VECTOR4_ARRAY);
 
 	register_op<OperatorEvaluatorSub<int64_t, int64_t, int64_t>>(Variant::OP_SUBTRACT, Variant::INT, Variant::INT);
 	register_op<OperatorEvaluatorSub<double, int64_t, double>>(Variant::OP_SUBTRACT, Variant::INT, Variant::FLOAT);
@@ -480,6 +481,7 @@ void Variant::_register_variant_operators() {
 	register_string_modulo_op(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY);
 	register_string_modulo_op(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY);
 	register_string_modulo_op(PackedColorArray, Variant::PACKED_COLOR_ARRAY);
+	register_string_modulo_op(PackedVector4Array, Variant::PACKED_VECTOR4_ARRAY);
 
 	register_op<OperatorEvaluatorPow<int64_t, int64_t, int64_t>>(Variant::OP_POWER, Variant::INT, Variant::INT);
 	register_op<OperatorEvaluatorPow<double, int64_t, double>>(Variant::OP_POWER, Variant::INT, Variant::FLOAT);
@@ -561,6 +563,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorEqual<PackedVector2Array, PackedVector2Array>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::PACKED_VECTOR2_ARRAY);
 	register_op<OperatorEvaluatorEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY);
 	register_op<OperatorEvaluatorEqual<PackedColorArray, PackedColorArray>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY);
+	register_op<OperatorEvaluatorEqual<PackedVector4Array, PackedVector4Array>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR4_ARRAY, Variant::PACKED_VECTOR4_ARRAY);
 
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::BOOL, Variant::NIL>>(Variant::OP_EQUAL, Variant::BOOL, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::INT, Variant::NIL>>(Variant::OP_EQUAL, Variant::INT, Variant::NIL);
@@ -598,6 +601,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PACKED_VECTOR4_ARRAY, Variant::NIL>>(Variant::OP_EQUAL, Variant::PACKED_VECTOR4_ARRAY, Variant::NIL);
 
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::BOOL>>(Variant::OP_EQUAL, Variant::NIL, Variant::BOOL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::INT>>(Variant::OP_EQUAL, Variant::NIL, Variant::INT);
@@ -635,6 +639,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY);
+	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR4_ARRAY>>(Variant::OP_EQUAL, Variant::NIL, Variant::PACKED_VECTOR4_ARRAY);
 
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::NIL);
 	register_op<OperatorEvaluatorNotEqual<bool, bool>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::BOOL);
@@ -680,6 +685,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorNotEqual<PackedVector2Array, PackedVector2Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::PACKED_VECTOR2_ARRAY);
 	register_op<OperatorEvaluatorNotEqual<PackedVector3Array, PackedVector3Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::PACKED_VECTOR3_ARRAY);
 	register_op<OperatorEvaluatorNotEqual<PackedColorArray, PackedColorArray>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::PACKED_COLOR_ARRAY);
+	register_op<OperatorEvaluatorNotEqual<PackedVector4Array, PackedVector4Array>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR4_ARRAY, Variant::PACKED_VECTOR4_ARRAY);
 
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::BOOL, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::INT, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::INT, Variant::NIL);
@@ -717,6 +723,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_COLOR_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR4_ARRAY, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PACKED_VECTOR4_ARRAY, Variant::NIL);
 
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BOOL>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::BOOL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::INT>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::INT);
@@ -754,6 +761,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR2_ARRAY);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR3_ARRAY);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_COLOR_ARRAY);
+	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR4_ARRAY>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PACKED_VECTOR4_ARRAY);
 
 	register_op<OperatorEvaluatorLess<bool, bool>>(Variant::OP_LESS, Variant::BOOL, Variant::BOOL);
 	register_op<OperatorEvaluatorLess<int64_t, int64_t>>(Variant::OP_LESS, Variant::INT, Variant::INT);
@@ -944,6 +952,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorNot<PackedVector2Array>>(Variant::OP_NOT, Variant::PACKED_VECTOR2_ARRAY, Variant::NIL);
 	register_op<OperatorEvaluatorNot<PackedVector3Array>>(Variant::OP_NOT, Variant::PACKED_VECTOR3_ARRAY, Variant::NIL);
 	register_op<OperatorEvaluatorNot<PackedColorArray>>(Variant::OP_NOT, Variant::PACKED_COLOR_ARRAY, Variant::NIL);
+	register_op<OperatorEvaluatorNot<PackedVector4Array>>(Variant::OP_NOT, Variant::PACKED_VECTOR4_ARRAY, Variant::NIL);
 
 	register_string_op(OperatorEvaluatorInStringFind, Variant::OP_IN);
 
@@ -986,6 +995,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorInDictionaryHas<PackedVector2Array>>(Variant::OP_IN, Variant::PACKED_VECTOR2_ARRAY, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<PackedVector3Array>>(Variant::OP_IN, Variant::PACKED_VECTOR3_ARRAY, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<PackedColorArray>>(Variant::OP_IN, Variant::PACKED_COLOR_ARRAY, Variant::DICTIONARY);
+	register_op<OperatorEvaluatorInDictionaryHas<PackedVector4Array>>(Variant::OP_IN, Variant::PACKED_VECTOR4_ARRAY, Variant::DICTIONARY);
 
 	register_op<OperatorEvaluatorInArrayFindNil>(Variant::OP_IN, Variant::NIL, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<bool, Array>>(Variant::OP_IN, Variant::BOOL, Variant::ARRAY);
@@ -1026,6 +1036,7 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorInArrayFind<PackedVector2Array, Array>>(Variant::OP_IN, Variant::PACKED_VECTOR2_ARRAY, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<PackedVector3Array, Array>>(Variant::OP_IN, Variant::PACKED_VECTOR3_ARRAY, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<PackedColorArray, Array>>(Variant::OP_IN, Variant::PACKED_COLOR_ARRAY, Variant::ARRAY);
+	register_op<OperatorEvaluatorInArrayFind<PackedVector4Array, Array>>(Variant::OP_IN, Variant::PACKED_VECTOR4_ARRAY, Variant::ARRAY);
 
 	register_op<OperatorEvaluatorInArrayFind<int, PackedByteArray>>(Variant::OP_IN, Variant::INT, Variant::PACKED_BYTE_ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<float, PackedByteArray>>(Variant::OP_IN, Variant::FLOAT, Variant::PACKED_BYTE_ARRAY);
@@ -1047,8 +1058,8 @@ void Variant::_register_variant_operators() {
 
 	register_op<OperatorEvaluatorInArrayFind<Vector2, PackedVector2Array>>(Variant::OP_IN, Variant::VECTOR2, Variant::PACKED_VECTOR2_ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<Vector3, PackedVector3Array>>(Variant::OP_IN, Variant::VECTOR3, Variant::PACKED_VECTOR3_ARRAY);
-
 	register_op<OperatorEvaluatorInArrayFind<Color, PackedColorArray>>(Variant::OP_IN, Variant::COLOR, Variant::PACKED_COLOR_ARRAY);
+	register_op<OperatorEvaluatorInArrayFind<Vector4, PackedVector4Array>>(Variant::OP_IN, Variant::VECTOR4, Variant::PACKED_VECTOR4_ARRAY);
 
 	register_op<OperatorEvaluatorObjectHasPropertyString>(Variant::OP_IN, Variant::STRING, Variant::OBJECT);
 	register_op<OperatorEvaluatorObjectHasPropertyStringName>(Variant::OP_IN, Variant::STRING_NAME, Variant::OBJECT);

+ 33 - 0
core/variant/variant_parser.cpp

@@ -1394,6 +1394,24 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
 				}
 			}
 
+			value = arr;
+		} else if (id == "PackedVector4Array" || id == "PoolVector4Array" || id == "Vector4Array") {
+			Vector<real_t> args;
+			Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str);
+			if (err) {
+				return err;
+			}
+
+			Vector<Vector4> arr;
+			{
+				int len = args.size() / 4;
+				arr.resize(len);
+				Vector4 *w = arr.ptrw();
+				for (int i = 0; i < len; i++) {
+					w[i] = Vector4(args[i * 4 + 0], args[i * 4 + 1], args[i * 4 + 2], args[i * 4 + 3]);
+				}
+			}
+
 			value = arr;
 		} else if (id == "PackedColorArray" || id == "PoolColorArray" || id == "ColorArray") {
 			Vector<float> args;
@@ -2248,6 +2266,21 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
 
 			p_store_string_func(p_store_string_ud, ")");
 		} break;
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			p_store_string_func(p_store_string_ud, "PackedVector4Array(");
+			Vector<Vector4> data = p_variant;
+			int len = data.size();
+			const Vector4 *ptr = data.ptr();
+
+			for (int i = 0; i < len; i++) {
+				if (i > 0) {
+					p_store_string_func(p_store_string_ud, ", ");
+				}
+				p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].x) + ", " + rtos_fix(ptr[i].y) + ", " + rtos_fix(ptr[i].z) + ", " + rtos_fix(ptr[i].w));
+			}
+
+			p_store_string_func(p_store_string_ud, ")");
+		} break;
 
 		default: {
 			ERR_PRINT("Unknown variant type");

+ 33 - 0
core/variant/variant_setget.cpp

@@ -856,6 +856,7 @@ INDEXED_SETGET_STRUCT_TYPED(PackedVector2Array, Vector2)
 INDEXED_SETGET_STRUCT_TYPED(PackedVector3Array, Vector3)
 INDEXED_SETGET_STRUCT_TYPED(PackedStringArray, String)
 INDEXED_SETGET_STRUCT_TYPED(PackedColorArray, Color)
+INDEXED_SETGET_STRUCT_TYPED(PackedVector4Array, Vector4)
 
 INDEXED_SETGET_STRUCT_DICT(Dictionary)
 
@@ -923,6 +924,7 @@ void register_indexed_setters_getters() {
 	REGISTER_INDEXED_MEMBER(PackedVector3Array);
 	REGISTER_INDEXED_MEMBER(PackedStringArray);
 	REGISTER_INDEXED_MEMBER(PackedColorArray);
+	REGISTER_INDEXED_MEMBER(PackedVector4Array);
 
 	REGISTER_INDEXED_MEMBER(Array);
 	REGISTER_INDEXED_MEMBER(Dictionary);
@@ -1498,6 +1500,14 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
 			return true;
 
 		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			const Vector<Vector4> *arr = &PackedArrayRef<Vector4>::get_array(_data.packed_array);
+			if (arr->size() == 0) {
+				return false;
+			}
+			r_iter = 0;
+			return true;
+		} break;
 		default: {
 		}
 	}
@@ -1747,6 +1757,16 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
 			r_iter = idx;
 			return true;
 		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			const Vector<Vector4> *arr = &PackedArrayRef<Vector4>::get_array(_data.packed_array);
+			int idx = r_iter;
+			idx++;
+			if (idx >= arr->size()) {
+				return false;
+			}
+			r_iter = idx;
+			return true;
+		} break;
 		default: {
 		}
 	}
@@ -1918,6 +1938,17 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
 				r_valid = false;
 				return Variant();
 			}
+#endif
+			return arr->get(idx);
+		} break;
+		case PACKED_VECTOR4_ARRAY: {
+			const Vector<Vector4> *arr = &PackedArrayRef<Vector4>::get_array(_data.packed_array);
+			int idx = r_iter;
+#ifdef DEBUG_ENABLED
+			if (idx < 0 || idx >= arr->size()) {
+				r_valid = false;
+				return Variant();
+			}
 #endif
 			return arr->get(idx);
 		} break;
@@ -1968,6 +1999,8 @@ Variant Variant::recursive_duplicate(bool p_deep, int recursion_count) const {
 			return operator Vector<Vector3>().duplicate();
 		case PACKED_COLOR_ARRAY:
 			return operator Vector<Color>().duplicate();
+		case PACKED_VECTOR4_ARRAY:
+			return operator Vector<Vector4>().duplicate();
 		default:
 			return *this;
 	}

+ 2 - 0
core/variant/variant_utility.cpp

@@ -917,6 +917,8 @@ Variant VariantUtilityFunctions::type_convert(const Variant &p_variant, const Va
 			return p_variant.operator PackedVector3Array();
 		case Variant::Type::PACKED_COLOR_ARRAY:
 			return p_variant.operator PackedColorArray();
+		case Variant::Type::PACKED_VECTOR4_ARRAY:
+			return p_variant.operator PackedVector4Array();
 		case Variant::Type::VARIANT_MAX:
 			ERR_PRINT("Invalid type argument to type_convert(), use the TYPE_* constants. Returning the unconverted Variant.");
 	}

+ 5 - 2
doc/classes/@GlobalScope.xml

@@ -604,7 +604,7 @@
 				is_same(arr_a, arr_b)  # false
 				[/codeblock]
 				These are [Variant] value types: [code]null[/code], [bool], [int], [float], [String], [StringName], [Vector2], [Vector2i], [Vector3], [Vector3i], [Vector4], [Vector4i], [Rect2], [Rect2i], [Transform2D], [Transform3D], [Plane], [Quaternion], [AABB], [Basis], [Projection], [Color], [NodePath], [RID], [Callable] and [Signal].
-				These are [Variant] reference types: [Object], [Dictionary], [Array], [PackedByteArray], [PackedInt32Array], [PackedInt64Array], [PackedFloat32Array], [PackedFloat64Array], [PackedStringArray], [PackedVector2Array], [PackedVector3Array] and [PackedColorArray].
+				These are [Variant] reference types: [Object], [Dictionary], [Array], [PackedByteArray], [PackedInt32Array], [PackedInt64Array], [PackedFloat32Array], [PackedFloat64Array], [PackedStringArray], [PackedVector2Array], [PackedVector3Array], [PackedVector4Array], and [PackedColorArray].
 			</description>
 		</method>
 		<method name="is_zero_approx">
@@ -3169,7 +3169,10 @@
 		<constant name="TYPE_PACKED_COLOR_ARRAY" value="37" enum="Variant.Type">
 			Variable is of type [PackedColorArray].
 		</constant>
-		<constant name="TYPE_MAX" value="38" enum="Variant.Type">
+		<constant name="TYPE_PACKED_VECTOR4_ARRAY" value="38" enum="Variant.Type">
+			Variable is of type [PackedVector4Array].
+		</constant>
+		<constant name="TYPE_MAX" value="39" enum="Variant.Type">
 			Represents the size of the [enum Variant.Type] enum.
 		</constant>
 		<constant name="OP_EQUAL" value="0" enum="Variant.Operator">

+ 7 - 0
doc/classes/Array.xml

@@ -154,6 +154,13 @@
 				Constructs an array from a [PackedVector3Array].
 			</description>
 		</constructor>
+		<constructor name="Array">
+			<return type="Array" />
+			<param index="0" name="from" type="PackedVector4Array" />
+			<description>
+				Constructs an array from a [PackedVector4Array].
+			</description>
+		</constructor>
 	</constructors>
 	<methods>
 		<method name="all" qualifiers="const">

+ 225 - 0
doc/classes/PackedVector4Array.xml

@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="PackedVector4Array" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		A packed array of [Vector4]s.
+	</brief_description>
+	<description>
+		An array specifically designed to hold [Vector4]. Packs data tightly, so it saves memory for large array sizes.
+		[b]Note:[/b] Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use [method duplicate]. This is [i]not[/i] the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will [i]not[/i] affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again.
+	</description>
+	<tutorials>
+	</tutorials>
+	<constructors>
+		<constructor name="PackedVector4Array">
+			<return type="PackedVector4Array" />
+			<description>
+				Constructs an empty [PackedVector4Array].
+			</description>
+		</constructor>
+		<constructor name="PackedVector4Array">
+			<return type="PackedVector4Array" />
+			<param index="0" name="from" type="PackedVector4Array" />
+			<description>
+				Constructs a [PackedVector4Array] as a copy of the given [PackedVector4Array].
+			</description>
+		</constructor>
+		<constructor name="PackedVector4Array">
+			<return type="PackedVector4Array" />
+			<param index="0" name="from" type="Array" />
+			<description>
+				Constructs a new [PackedVector4Array]. Optionally, you can pass in a generic [Array] that will be converted.
+				[b]Note:[/b] When initializing a [PackedVector4Array] with elements, it must be initialized with an [Array] of [Vector4] values:
+				[codeblock]
+				var array = PackedVector4Array([Vector4(12, 34, 56, 78), Vector4(90, 12, 34, 56)])
+				[/codeblock]
+			</description>
+		</constructor>
+	</constructors>
+	<methods>
+		<method name="append">
+			<return type="bool" />
+			<param index="0" name="value" type="Vector4" />
+			<description>
+				Appends an element at the end of the array (alias of [method push_back]).
+			</description>
+		</method>
+		<method name="append_array">
+			<return type="void" />
+			<param index="0" name="array" type="PackedVector4Array" />
+			<description>
+				Appends a [PackedVector4Array] at the end of this array.
+			</description>
+		</method>
+		<method name="bsearch">
+			<return type="int" />
+			<param index="0" name="value" type="Vector4" />
+			<param index="1" name="before" type="bool" default="true" />
+			<description>
+				Finds the index of an existing value (or the insertion index that maintains sorting order, if the value is not yet present in the array) using binary search. Optionally, a [param before] specifier can be passed. If [code]false[/code], the returned index comes after all existing entries of the value in the array.
+				[b]Note:[/b] Calling [method bsearch] on an unsorted array results in unexpected behavior.
+				[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
+			</description>
+		</method>
+		<method name="clear">
+			<return type="void" />
+			<description>
+				Clears the array. This is equivalent to using [method resize] with a size of [code]0[/code].
+			</description>
+		</method>
+		<method name="count" qualifiers="const">
+			<return type="int" />
+			<param index="0" name="value" type="Vector4" />
+			<description>
+				Returns the number of times an element is in the array.
+				[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
+			</description>
+		</method>
+		<method name="duplicate">
+			<return type="PackedVector4Array" />
+			<description>
+				Creates a copy of the array, and returns it.
+			</description>
+		</method>
+		<method name="fill">
+			<return type="void" />
+			<param index="0" name="value" type="Vector4" />
+			<description>
+				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" />
+			<param index="0" name="value" type="Vector4" />
+			<param 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.
+				[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
+			</description>
+		</method>
+		<method name="has" qualifiers="const">
+			<return type="bool" />
+			<param index="0" name="value" type="Vector4" />
+			<description>
+				Returns [code]true[/code] if the array contains [param value].
+				[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
+			</description>
+		</method>
+		<method name="insert">
+			<return type="int" />
+			<param index="0" name="at_index" type="int" />
+			<param index="1" name="value" type="Vector4" />
+			<description>
+				Inserts a new element at a given position in the array. The position must be valid, or at the end of the array ([code]idx == size()[/code]).
+			</description>
+		</method>
+		<method name="is_empty" qualifiers="const">
+			<return type="bool" />
+			<description>
+				Returns [code]true[/code] if the array is empty.
+			</description>
+		</method>
+		<method name="push_back">
+			<return type="bool" />
+			<param index="0" name="value" type="Vector4" />
+			<description>
+				Inserts a [Vector4] at the end.
+			</description>
+		</method>
+		<method name="remove_at">
+			<return type="void" />
+			<param index="0" name="index" type="int" />
+			<description>
+				Removes an element from the array by index.
+			</description>
+		</method>
+		<method name="resize">
+			<return type="int" />
+			<param index="0" name="new_size" type="int" />
+			<description>
+				Sets the size of the array. If the array is grown, reserves elements at the end of the array. If the array is shrunk, truncates the array to the new size.
+			</description>
+		</method>
+		<method name="reverse">
+			<return type="void" />
+			<description>
+				Reverses the order of the elements in the array.
+			</description>
+		</method>
+		<method name="rfind" qualifiers="const">
+			<return type="int" />
+			<param index="0" name="value" type="Vector4" />
+			<param 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.
+				[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
+			</description>
+		</method>
+		<method name="set">
+			<return type="void" />
+			<param index="0" name="index" type="int" />
+			<param index="1" name="value" type="Vector4" />
+			<description>
+				Changes the [Vector4] at the given index.
+			</description>
+		</method>
+		<method name="size" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the number of elements in the array.
+			</description>
+		</method>
+		<method name="slice" qualifiers="const">
+			<return type="PackedVector4Array" />
+			<param index="0" name="begin" type="int" />
+			<param index="1" name="end" type="int" default="2147483647" />
+			<description>
+				Returns the slice of the [PackedVector4Array], from [param begin] (inclusive) to [param end] (exclusive), as a new [PackedVector4Array].
+				The absolute value of [param begin] and [param end] will be clamped to the array size, so the default value for [param end] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+				If either [param begin] or [param end] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
+			</description>
+		</method>
+		<method name="sort">
+			<return type="void" />
+			<description>
+				Sorts the elements of the array in ascending order.
+				[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
+			</description>
+		</method>
+		<method name="to_byte_array" qualifiers="const">
+			<return type="PackedByteArray" />
+			<description>
+				Returns a [PackedByteArray] with each vector encoded as bytes.
+			</description>
+		</method>
+	</methods>
+	<operators>
+		<operator name="operator !=">
+			<return type="bool" />
+			<param index="0" name="right" type="PackedVector4Array" />
+			<description>
+				Returns [code]true[/code] if contents of the arrays differ.
+			</description>
+		</operator>
+		<operator name="operator +">
+			<return type="PackedVector4Array" />
+			<param index="0" name="right" type="PackedVector4Array" />
+			<description>
+				Returns a new [PackedVector4Array] with contents of [param right] added at the end of this array. For better performance, consider using [method append_array] instead.
+			</description>
+		</operator>
+		<operator name="operator ==">
+			<return type="bool" />
+			<param index="0" name="right" type="PackedVector4Array" />
+			<description>
+				Returns [code]true[/code] if contents of both arrays are the same, i.e. they have all equal [Vector4]s at the corresponding indices.
+			</description>
+		</operator>
+		<operator name="operator []">
+			<return type="Vector4" />
+			<param index="0" name="index" type="int" />
+			<description>
+				Returns the [Vector4] at index [param index]. Negative indices can be used to access the elements starting from the end. Using index out of array's bounds will result in an error.
+			</description>
+		</operator>
+	</operators>
+</class>

+ 2 - 0
doc/tools/make_rst.py

@@ -143,6 +143,7 @@ CLASSES_WITH_CSHARP_DIFFERENCES: List[str] = [
     "PackedStringArray",
     "PackedVector2Array",
     "PackedVector3Array",
+    "PackedVector4Array",
     "Variant",
 ]
 
@@ -156,6 +157,7 @@ PACKED_ARRAY_TYPES: List[str] = [
     "PackedStringArray",
     "PackedVector2Array",
     "PackedVector3Array",
+    "PackedVector4Array",
 ]
 
 

+ 2 - 1
editor/animation_track_editor.cpp

@@ -5075,7 +5075,8 @@ void AnimationTrackEditor::_fetch_value_track_options(const NodePath &p_path, An
 		case Variant::PACKED_FLOAT64_ARRAY:
 		case Variant::PACKED_VECTOR2_ARRAY:
 		case Variant::PACKED_VECTOR3_ARRAY:
-		case Variant::PACKED_COLOR_ARRAY: {
+		case Variant::PACKED_COLOR_ARRAY:
+		case Variant::PACKED_VECTOR4_ARRAY: {
 			*r_update_mode = Animation::UPDATE_CONTINUOUS;
 		} break;
 		default: {

+ 22 - 0
editor/debugger/debug_adapter/debug_adapter_protocol.cpp

@@ -649,6 +649,28 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
 			variable_list.insert(id, arr);
 			return id;
 		}
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			int id = variable_id++;
+			PackedVector4Array array = p_var;
+			DAP::Variable size;
+			size.name = "size";
+			size.type = Variant::get_type_name(Variant::INT);
+			size.value = itos(array.size());
+
+			Array arr;
+			arr.push_back(size.to_json());
+
+			for (int i = 0; i < array.size(); i++) {
+				DAP::Variable var;
+				var.name = itos(i);
+				var.type = Variant::get_type_name(Variant::VECTOR4);
+				var.value = array[i];
+				var.variablesReference = parse_variant(array[i]);
+				arr.push_back(var.to_json());
+			}
+			variable_list.insert(id, arr);
+			return id;
+		}
 		default:
 			// Simple atomic stuff, or too complex to be manipulated
 			return 0;

+ 2 - 0
editor/editor_help.cpp

@@ -93,6 +93,7 @@ const Vector<String> classes_with_csharp_differences = {
 	"PackedStringArray",
 	"PackedVector2Array",
 	"PackedVector3Array",
+	"PackedVector4Array",
 	"Variant",
 };
 #endif
@@ -107,6 +108,7 @@ const Vector<String> packed_array_types = {
 	"PackedStringArray",
 	"PackedVector2Array",
 	"PackedVector3Array",
+	"PackedVector4Array",
 };
 
 // TODO: this is sometimes used directly as doc->something, other times as EditorHelp::get_doc_data(), which is thread-safe.

+ 5 - 0
editor/editor_properties.cpp

@@ -3911,6 +3911,11 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 			editor->setup(Variant::PACKED_COLOR_ARRAY, p_hint_text);
 			return editor;
 		} break;
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			EditorPropertyArray *editor = memnew(EditorPropertyArray);
+			editor->setup(Variant::PACKED_VECTOR4_ARRAY, p_hint_text);
+			return editor;
+		} break;
 		default: {
 		}
 	}

+ 1 - 0
editor/icons/PackedVector4Array.svg

@@ -0,0 +1 @@
+<svg width="16" height="12" viewBox="0 0 16 12" xmlns="http://www.w3.org/2000/svg"><path d="m0 0v12h4v-2h-2v-8h2v-2zm12 0v2h2v8h-2v2h4v-12z" fill="#e0e0e0"/><path d="M8 3h2v2h1v-3h2v8h-2v-3h-3zM3 3v6h2a3 3 0 003-3v-3h-2v3a1 1 0 01-1 1v-4z" fill="#ac73f1"/><path d="M8 3h2v2h1v-3h2v8h-2v-3h-3z" fill="#fff" fill-opacity=".3922"/></svg>

+ 2 - 1
editor/property_selector.cpp

@@ -162,7 +162,8 @@ void PropertySelector::_update_search() {
 			search_options->get_editor_theme_icon(SNAME("PackedStringArray")),
 			search_options->get_editor_theme_icon(SNAME("PackedVector2Array")),
 			search_options->get_editor_theme_icon(SNAME("PackedVector3Array")),
-			search_options->get_editor_theme_icon(SNAME("PackedColorArray"))
+			search_options->get_editor_theme_icon(SNAME("PackedColorArray")),
+			search_options->get_editor_theme_icon(SNAME("PackedVector4Array")),
 		};
 		static_assert((sizeof(type_icons) / sizeof(type_icons[0])) == Variant::VARIANT_MAX, "Number of type icons doesn't match the number of Variant types.");
 

+ 11 - 5
modules/gdscript/gdscript_analyzer.cpp

@@ -3030,6 +3030,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
 				case Variant::PACKED_VECTOR2_ARRAY:
 				case Variant::PACKED_VECTOR3_ARRAY:
 				case Variant::PACKED_COLOR_ARRAY:
+				case Variant::PACKED_VECTOR4_ARRAY:
 					safe_to_fold = false;
 					break;
 				default:
@@ -4425,7 +4426,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 						switch (base_type.builtin_type) {
 							// Expect int or real as index.
 							case Variant::PACKED_BYTE_ARRAY:
-							case Variant::PACKED_COLOR_ARRAY:
 							case Variant::PACKED_FLOAT32_ARRAY:
 							case Variant::PACKED_FLOAT64_ARRAY:
 							case Variant::PACKED_INT32_ARRAY:
@@ -4433,6 +4433,8 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 							case Variant::PACKED_STRING_ARRAY:
 							case Variant::PACKED_VECTOR2_ARRAY:
 							case Variant::PACKED_VECTOR3_ARRAY:
+							case Variant::PACKED_COLOR_ARRAY:
+							case Variant::PACKED_VECTOR4_ARRAY:
 							case Variant::ARRAY:
 							case Variant::STRING:
 								error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT;
@@ -4531,10 +4533,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 					case Variant::QUATERNION:
 						result_type.builtin_type = Variant::FLOAT;
 						break;
-					// Return Color.
-					case Variant::PACKED_COLOR_ARRAY:
-						result_type.builtin_type = Variant::COLOR;
-						break;
 					// Return String.
 					case Variant::PACKED_STRING_ARRAY:
 					case Variant::STRING:
@@ -4556,6 +4554,14 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 					case Variant::BASIS:
 						result_type.builtin_type = Variant::VECTOR3;
 						break;
+					// Return Color.
+					case Variant::PACKED_COLOR_ARRAY:
+						result_type.builtin_type = Variant::COLOR;
+						break;
+					// Return Vector4.
+					case Variant::PACKED_VECTOR4_ARRAY:
+						result_type.builtin_type = Variant::VECTOR4;
+						break;
 					// Depends on the index.
 					case Variant::TRANSFORM3D:
 					case Variant::PROJECTION:

+ 8 - 0
modules/gdscript/gdscript_byte_codegen.cpp

@@ -109,6 +109,7 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type
 			case Variant::PACKED_VECTOR2_ARRAY:
 			case Variant::PACKED_VECTOR3_ARRAY:
 			case Variant::PACKED_COLOR_ARRAY:
+			case Variant::PACKED_VECTOR4_ARRAY:
 			case Variant::VARIANT_MAX:
 				// Arrays, dictionaries, and objects are reference counted, so we don't use the pool for them.
 				temp_type = Variant::NIL;
@@ -543,6 +544,9 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia
 		case Variant::PACKED_COLOR_ARRAY:
 			append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY);
 			break;
+		case Variant::PACKED_VECTOR4_ARRAY:
+			append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_VECTOR4_ARRAY);
+			break;
 		case Variant::NIL:
 		case Variant::VARIANT_MAX:
 			return;
@@ -1573,6 +1577,10 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_
 					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY;
 					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_COLOR_ARRAY;
 					break;
+				case Variant::PACKED_VECTOR4_ARRAY:
+					begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY;
+					iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR4_ARRAY;
+					break;
 				default:
 					break;
 			}

+ 2 - 0
modules/gdscript/gdscript_disassembler.cpp

@@ -1046,6 +1046,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 	m_macro(PACKED_VECTOR2_ARRAY);         \
 	m_macro(PACKED_VECTOR3_ARRAY);         \
 	m_macro(PACKED_COLOR_ARRAY);           \
+	m_macro(PACKED_VECTOR4_ARRAY);         \
 	m_macro(OBJECT)
 
 			case OPCODE_ITERATE_BEGIN: {
@@ -1150,6 +1151,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 				DISASSEMBLE_TYPE_ADJUST(PACKED_VECTOR2_ARRAY);
 				DISASSEMBLE_TYPE_ADJUST(PACKED_VECTOR3_ARRAY);
 				DISASSEMBLE_TYPE_ADJUST(PACKED_COLOR_ARRAY);
+				DISASSEMBLE_TYPE_ADJUST(PACKED_VECTOR4_ARRAY);
 
 			case OPCODE_ASSERT: {
 				text += "assert (";

+ 3 - 0
modules/gdscript/gdscript_function.h

@@ -301,6 +301,7 @@ public:
 		OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY,
 		OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY,
 		OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
+		OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY,
 		OPCODE_ITERATE_BEGIN_OBJECT,
 		OPCODE_ITERATE,
 		OPCODE_ITERATE_INT,
@@ -321,6 +322,7 @@ public:
 		OPCODE_ITERATE_PACKED_VECTOR2_ARRAY,
 		OPCODE_ITERATE_PACKED_VECTOR3_ARRAY,
 		OPCODE_ITERATE_PACKED_COLOR_ARRAY,
+		OPCODE_ITERATE_PACKED_VECTOR4_ARRAY,
 		OPCODE_ITERATE_OBJECT,
 		OPCODE_STORE_GLOBAL,
 		OPCODE_STORE_NAMED_GLOBAL,
@@ -361,6 +363,7 @@ public:
 		OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY,
 		OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY,
 		OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY,
+		OPCODE_TYPE_ADJUST_PACKED_VECTOR4_ARRAY,
 		OPCODE_ASSERT,
 		OPCODE_BREAKPOINT,
 		OPCODE_LINE,

+ 5 - 0
modules/gdscript/gdscript_parser.cpp

@@ -4129,6 +4129,9 @@ static String _get_annotation_error_string(const StringName &p_annotation_name,
 			case Variant::COLOR:
 				types.push_back("PackedColorArray");
 				break;
+			case Variant::VECTOR4:
+				types.push_back("PackedVector4Array");
+				break;
 			default:
 				break;
 		}
@@ -4827,6 +4830,8 @@ static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_t
 			return Variant::VECTOR3;
 		case Variant::PACKED_COLOR_ARRAY:
 			return Variant::COLOR;
+		case Variant::PACKED_VECTOR4_ARRAY:
+			return Variant::VECTOR4;
 		default:
 			return Variant::NIL;
 	}

+ 4 - 0
modules/gdscript/gdscript_utility_functions.cpp

@@ -519,6 +519,10 @@ struct GDScriptUtilityFunctionsDefinitions {
 				Vector<Color> d = *p_args[0];
 				*r_ret = d.size();
 			} break;
+			case Variant::PACKED_VECTOR4_ARRAY: {
+				Vector<Vector4> d = *p_args[0];
+				*r_ret = d.size();
+			} break;
 			default: {
 				r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
 				r_error.argument = 0;

+ 8 - 0
modules/gdscript/gdscript_vm.cpp

@@ -208,6 +208,7 @@ void (*type_init_function_table[])(Variant *) = {
 	&VariantInitializer<PackedVector2Array>::init, // PACKED_VECTOR2_ARRAY.
 	&VariantInitializer<PackedVector3Array>::init, // PACKED_VECTOR3_ARRAY.
 	&VariantInitializer<PackedColorArray>::init, // PACKED_COLOR_ARRAY.
+	&VariantInitializer<PackedVector4Array>::init, // PACKED_VECTOR4_ARRAY.
 };
 
 #if defined(__GNUC__)
@@ -298,6 +299,7 @@ void (*type_init_function_table[])(Variant *) = {
 		&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY,     \
 		&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY,     \
 		&&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,       \
+		&&OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY,     \
 		&&OPCODE_ITERATE_BEGIN_OBJECT,                   \
 		&&OPCODE_ITERATE,                                \
 		&&OPCODE_ITERATE_INT,                            \
@@ -318,6 +320,7 @@ void (*type_init_function_table[])(Variant *) = {
 		&&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY,           \
 		&&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY,           \
 		&&OPCODE_ITERATE_PACKED_COLOR_ARRAY,             \
+		&&OPCODE_ITERATE_PACKED_VECTOR4_ARRAY,           \
 		&&OPCODE_ITERATE_OBJECT,                         \
 		&&OPCODE_STORE_GLOBAL,                           \
 		&&OPCODE_STORE_NAMED_GLOBAL,                     \
@@ -358,6 +361,7 @@ void (*type_init_function_table[])(Variant *) = {
 		&&OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY,       \
 		&&OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY,       \
 		&&OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY,         \
+		&&OPCODE_TYPE_ADJUST_PACKED_VECTOR4_ARRAY,       \
 		&&OPCODE_ASSERT,                                 \
 		&&OPCODE_BREAKPOINT,                             \
 		&&OPCODE_LINE,                                   \
@@ -430,6 +434,7 @@ void (*type_init_function_table[])(Variant *) = {
 #define OP_GET_PACKED_VECTOR2_ARRAY get_vector2_array
 #define OP_GET_PACKED_VECTOR3_ARRAY get_vector3_array
 #define OP_GET_PACKED_COLOR_ARRAY get_color_array
+#define OP_GET_PACKED_VECTOR4_ARRAY get_vector4_array
 #define OP_GET_TRANSFORM3D get_transform
 #define OP_GET_TRANSFORM2D get_transform2d
 #define OP_GET_PROJECTION get_projection
@@ -3059,6 +3064,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, VECTOR2, Vector2, get_vector2);
 			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, VECTOR3, Vector3, get_vector3);
 			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(COLOR, Color, get_color_array, COLOR, Color, get_color);
+			OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR4, Vector4, get_vector4_array, VECTOR4, Vector4, get_vector4);
 
 			OPCODE(OPCODE_ITERATE_BEGIN_OBJECT) {
 				CHECK_SPACE(4);
@@ -3394,6 +3400,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			OPCODE_ITERATE_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, get_vector2);
 			OPCODE_ITERATE_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, get_vector3);
 			OPCODE_ITERATE_PACKED_ARRAY(COLOR, Color, get_color_array, get_color);
+			OPCODE_ITERATE_PACKED_ARRAY(VECTOR4, Vector4, get_vector4_array, get_vector4);
 
 			OPCODE(OPCODE_ITERATE_OBJECT) {
 				CHECK_SPACE(4);
@@ -3525,6 +3532,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			OPCODE_TYPE_ADJUST(PACKED_VECTOR2_ARRAY, PackedVector2Array);
 			OPCODE_TYPE_ADJUST(PACKED_VECTOR3_ARRAY, PackedVector3Array);
 			OPCODE_TYPE_ADJUST(PACKED_COLOR_ARRAY, PackedColorArray);
+			OPCODE_TYPE_ADJUST(PACKED_VECTOR4_ARRAY, PackedVector4Array);
 
 			OPCODE(OPCODE_ASSERT) {
 				CHECK_SPACE(3);

+ 9 - 0
modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.gd

@@ -345,3 +345,12 @@ func test():
 	prints(x and true)
 	prints(x or false)
 	prints(x or true)
+
+	# TYPE_PACKED_VECTOR4_ARRAY
+	x = PackedVector4Array([Vector4.ONE])
+	prints("TYPE_PACKED_VECTOR4_ARRAY")
+	prints(not x)
+	prints(x and false)
+	prints(x and true)
+	prints(x or false)
+	prints(x or true)

+ 6 - 0
modules/gdscript/tests/scripts/analyzer/features/boolean_operators_for_all_types.out

@@ -227,3 +227,9 @@ false
 true
 true
 true
+TYPE_PACKED_VECTOR4_ARRAY
+false
+false
+true
+true
+true

+ 2 - 0
modules/gdscript/tests/scripts/parser/features/export_arrays.gd

@@ -63,6 +63,7 @@ var temp_packed_float64_array: PackedFloat64Array
 var temp_packed_color_array: PackedColorArray
 var temp_packed_vector2_array: PackedVector2Array
 var temp_packed_vector3_array: PackedVector3Array
+var temp_packed_vector4_array: PackedVector4Array
 
 @export var test_weak_packed_byte_array = temp_packed_byte_array
 @export var test_weak_packed_int32_array = temp_packed_int32_array
@@ -72,6 +73,7 @@ var temp_packed_vector3_array: PackedVector3Array
 @export var test_weak_packed_color_array = temp_packed_color_array
 @export var test_weak_packed_vector2_array = temp_packed_vector2_array
 @export var test_weak_packed_vector3_array = temp_packed_vector3_array
+@export var test_weak_packed_vector4_array = temp_packed_vector4_array
 
 @export_range(1, 10) var test_range_weak_packed_byte_array = temp_packed_byte_array
 @export_range(1, 10) var test_range_weak_packed_int32_array = temp_packed_int32_array

+ 2 - 0
modules/gdscript/tests/scripts/parser/features/export_arrays.out

@@ -123,6 +123,8 @@ var test_weak_packed_vector2_array: PackedVector2Array
   hint=TYPE_STRING hint_string="Vector2:Vector2" usage=DEFAULT|SCRIPT_VARIABLE
 var test_weak_packed_vector3_array: PackedVector3Array
   hint=TYPE_STRING hint_string="Vector3:Vector3" usage=DEFAULT|SCRIPT_VARIABLE
+var test_weak_packed_vector4_array: PackedVector4Array
+  hint=TYPE_STRING hint_string="Vector4:Vector4" usage=DEFAULT|SCRIPT_VARIABLE
 var test_range_weak_packed_byte_array: PackedByteArray
   hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE
 var test_range_weak_packed_int32_array: PackedInt32Array

+ 4 - 0
modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.gd

@@ -140,3 +140,7 @@ func test():
 	# PackedColorArray
 	value = PackedColorArray()
 	print(value == null)
+
+	# PackedVector4Array
+	value = PackedVector4Array()
+	print(value == null)

+ 1 - 0
modules/gdscript/tests/scripts/runtime/features/compare_builtin_equals_null.out

@@ -34,3 +34,4 @@ false
 false
 false
 false
+false

+ 4 - 0
modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.gd

@@ -140,3 +140,7 @@ func test():
 	# PackedColorArray
 	value = PackedColorArray()
 	print(value != null)
+
+	# PackedVector4Array
+	value = PackedVector4Array()
+	print(value != null)

+ 1 - 0
modules/gdscript/tests/scripts/runtime/features/compare_builtin_not_equals_null.out

@@ -34,3 +34,4 @@ true
 true
 true
 true
+true

+ 4 - 0
modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.gd

@@ -136,3 +136,7 @@ func test():
 	# PackedColorArray
 	value = PackedColorArray()
 	print(null == value)
+
+	# PackedVector4Array
+	value = PackedVector4Array()
+	print(null == value)

+ 1 - 0
modules/gdscript/tests/scripts/runtime/features/compare_null_equals_builtin.out

@@ -33,3 +33,4 @@ false
 false
 false
 false
+false

+ 4 - 0
modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.gd

@@ -136,3 +136,7 @@ func test():
 	# PackedColorArray
 	value = PackedColorArray()
 	print(null != value)
+
+	# PackedVector4Array
+	value = PackedVector4Array()
+	print(null != value)

+ 1 - 0
modules/gdscript/tests/scripts/runtime/features/compare_null_not_equals_builtin.out

@@ -33,3 +33,4 @@ true
 true
 true
 true
+true

+ 1 - 0
modules/gdscript/tests/scripts/runtime/features/stringify.gd

@@ -40,3 +40,4 @@ func test():
 	print(PackedVector2Array([Vector2.ONE, Vector2.ZERO]))
 	print(PackedVector3Array([Vector3.ONE, Vector3.ZERO]))
 	print(PackedColorArray([Color.RED, Color.BLUE, Color.GREEN]))
+	print(PackedVector4Array([Vector4.ONE, Vector4.ZERO]))

+ 1 - 0
modules/gdscript/tests/scripts/runtime/features/stringify.out

@@ -32,3 +32,4 @@ Node::[signal]property_list_changed
 [(1, 1), (0, 0)]
 [(1, 1, 1), (0, 0, 0)]
 [(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)]
+[(1, 1, 1, 1), (0, 0, 0, 0)]

+ 2 - 1
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs

@@ -44,7 +44,8 @@ namespace Godot.SourceGenerators
         PackedVector2Array = 35,
         PackedVector3Array = 36,
         PackedColorArray = 37,
-        Max = 38
+        PackedVector4Array = 38,
+        Max = 39
     }
 
     internal enum PropertyHint

+ 1 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs

@@ -51,6 +51,7 @@ namespace Godot.SourceGenerators
         StringArray,
         Vector2Array,
         Vector3Array,
+        Vector4Array,
         ColorArray,
         GodotObjectOrDerivedArray,
         SystemArrayOfStringName,

+ 3 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs

@@ -66,6 +66,7 @@ namespace Godot.SourceGenerators
                 MarshalType.StringArray => VariantType.PackedStringArray,
                 MarshalType.Vector2Array => VariantType.PackedVector2Array,
                 MarshalType.Vector3Array => VariantType.PackedVector3Array,
+                MarshalType.Vector4Array => VariantType.PackedVector4Array,
                 MarshalType.ColorArray => VariantType.PackedColorArray,
                 MarshalType.GodotObjectOrDerivedArray => VariantType.Array,
                 MarshalType.SystemArrayOfStringName => VariantType.Array,
@@ -190,6 +191,8 @@ namespace Godot.SourceGenerators
                                     return MarshalType.Vector2Array;
                                 case { Name: "Vector3" }:
                                     return MarshalType.Vector3Array;
+                                case { Name: "Vector4" }:
+                                    return MarshalType.Vector4Array;
                                 case { Name: "Color" }:
                                     return MarshalType.ColorArray;
                                 case { Name: "StringName" }:

+ 7 - 0
modules/mono/editor/bindings_generator.cpp

@@ -330,6 +330,8 @@ String BindingsGenerator::bbcode_to_text(const String &p_bbcode, const TypeInter
 				output.append("'" BINDINGS_NAMESPACE ".Vector3[]'");
 			} else if (tag == "PackedColorArray") {
 				output.append("'" BINDINGS_NAMESPACE ".Color[]'");
+			} else if (tag == "PackedVector4Array") {
+				output.append("'" BINDINGS_NAMESPACE ".Vector4[]'");
 			} else {
 				const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
 
@@ -646,6 +648,8 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
 				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>[]");
 			} else if (tag == "PackedColorArray") {
 				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>[]");
+			} else if (tag == "PackedVector4Array") {
+				xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector4\"/>[]");
 			} else {
 				const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
 
@@ -3516,6 +3520,7 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
 		case Variant::PACKED_STRING_ARRAY:
 		case Variant::PACKED_VECTOR2_ARRAY:
 		case Variant::PACKED_VECTOR3_ARRAY:
+		case Variant::PACKED_VECTOR4_ARRAY:
 		case Variant::PACKED_COLOR_ARRAY:
 		case Variant::CALLABLE:
 		case Variant::SIGNAL:
@@ -4246,6 +4251,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
 		case Variant::PACKED_STRING_ARRAY:
 		case Variant::PACKED_VECTOR2_ARRAY:
 		case Variant::PACKED_VECTOR3_ARRAY:
+		case Variant::PACKED_VECTOR4_ARRAY:
 		case Variant::PACKED_COLOR_ARRAY:
 			r_iarg.default_argument = "Array.Empty<%s>()";
 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
@@ -4585,6 +4591,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
 	INSERT_ARRAY(PackedColorArray, godot_packed_color_array, Color);
 	INSERT_ARRAY(PackedVector2Array, godot_packed_vector2_array, Vector2);
 	INSERT_ARRAY(PackedVector3Array, godot_packed_vector3_array, Vector3);
+	INSERT_ARRAY(PackedVector4Array, godot_packed_vector4_array, Vector4);
 
 #undef INSERT_ARRAY
 

+ 2 - 1
modules/mono/editor/bindings_generator.h

@@ -705,7 +705,7 @@ class BindingsGenerator {
 		StringName type_Vector4i = StaticCString::create("Vector4i");
 
 		// Object not included as it must be checked for all derived classes
-		static constexpr int nullable_types_count = 18;
+		static constexpr int nullable_types_count = 19;
 		StringName nullable_types[nullable_types_count] = {
 			type_String,
 			type_StringName,
@@ -727,6 +727,7 @@ class BindingsGenerator {
 			StaticCString::create(_STR(PackedVector2Array)),
 			StaticCString::create(_STR(PackedVector3Array)),
 			StaticCString::create(_STR(PackedColorArray)),
+			StaticCString::create(_STR(PackedVector4Array)),
 		};
 
 		bool is_nullable_type(const StringName &p_type) const {

+ 1 - 0
modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs

@@ -468,6 +468,7 @@ using Godot.NativeInterop;
         "Godot.NativeInterop.godot_packed_string_array",
         "Godot.NativeInterop.godot_packed_vector2_array",
         "Godot.NativeInterop.godot_packed_vector3_array",
+        "Godot.NativeInterop.godot_packed_vector4_array",
         "Godot.NativeInterop.godot_packed_color_array",
     };
 }

+ 16 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs

@@ -295,6 +295,22 @@ public static class CustomUnsafe
     public static unsafe ref godot_packed_vector3_array AsRef(in godot_packed_vector3_array source)
         => ref *ReadOnlyRefAsPointer(in source);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static unsafe godot_packed_vector4_array* AsPointer(ref godot_packed_vector4_array value)
+        => value.GetUnsafeAddress();
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static unsafe godot_packed_vector4_array* ReadOnlyRefAsPointer(in godot_packed_vector4_array value)
+        => value.GetUnsafeAddress();
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static unsafe ref godot_packed_vector4_array AsRef(godot_packed_vector4_array* source)
+        => ref *source;
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static unsafe ref godot_packed_vector4_array AsRef(in godot_packed_vector4_array source)
+        => ref *ReadOnlyRefAsPointer(in source);
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static unsafe godot_packed_color_array* AsPointer(ref godot_packed_color_array value)
         => value.GetUnsafeAddress();

+ 33 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs

@@ -1134,6 +1134,39 @@ namespace Godot.NativeInterop
     }
 
     [StructLayout(LayoutKind.Sequential)]
+    // ReSharper disable once InconsistentNaming
+    public ref struct godot_packed_vector4_array
+    {
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal readonly unsafe godot_packed_vector4_array* GetUnsafeAddress()
+            => (godot_packed_vector4_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+        private IntPtr _writeProxy;
+        private unsafe Vector4* _ptr;
+
+        public unsafe void Dispose()
+        {
+            if (_ptr == null)
+                return;
+            NativeFuncs.godotsharp_packed_vector4_array_destroy(ref this);
+            _ptr = null;
+        }
+
+        public readonly unsafe Vector4* Buffer
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => _ptr;
+        }
+
+        public readonly unsafe int Size
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => _ptr != null ? (int)(*((ulong*)_ptr - 1)) : 0;
+        }
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    // ReSharper disable once InconsistentNaming
     public ref struct godot_packed_color_array
     {
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 27 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs

@@ -133,6 +133,9 @@ namespace Godot.NativeInterop
                         if (type == typeof(Vector3[]))
                             return Variant.Type.PackedVector3Array;
 
+                        if (type == typeof(Vector4[]))
+                            return Variant.Type.PackedVector4Array;
+
                         if (type == typeof(Color[]))
                             return Variant.Type.PackedColorArray;
 
@@ -574,6 +577,30 @@ namespace Godot.NativeInterop
                 return NativeFuncs.godotsharp_packed_vector3_array_new_mem_copy(src, p_array.Length);
         }
 
+        // PackedVector4Array
+
+        public static unsafe Vector4[] ConvertNativePackedVector4ArrayToSystemArray(godot_packed_vector4_array p_array)
+        {
+            Vector4* buffer = p_array.Buffer;
+            int size = p_array.Size;
+            if (size == 0)
+                return Array.Empty<Vector4>();
+            int sizeInBytes = size * sizeof(Vector4);
+            var array = new Vector4[size];
+            fixed (Vector4* dest = array)
+                Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+            return array;
+        }
+
+        public static unsafe godot_packed_vector4_array ConvertSystemArrayToNativePackedVector4Array(
+            Span<Vector4> p_array)
+        {
+            if (p_array.IsEmpty)
+                return new godot_packed_vector4_array();
+            fixed (Vector4* src = p_array)
+                return NativeFuncs.godotsharp_packed_vector4_array_new_mem_copy(src, p_array.Length);
+        }
+
         // PackedColorArray
 
         public static unsafe Color[] ConvertNativePackedColorArrayToSystemArray(godot_packed_color_array p_array)

+ 11 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs

@@ -144,6 +144,9 @@ namespace Godot.NativeInterop
         public static partial godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src,
             int p_length);
 
+        public static partial godot_packed_vector4_array godotsharp_packed_vector4_array_new_mem_copy(Vector4* p_src,
+            int p_length);
+
         public static partial godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src,
             int p_length);
 
@@ -224,6 +227,9 @@ namespace Godot.NativeInterop
         public static partial void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest,
             in godot_packed_vector3_array p_pv3a);
 
+        public static partial void godotsharp_variant_new_packed_vector4_array(out godot_variant r_dest,
+            in godot_packed_vector4_array p_pv4a);
+
         public static partial void godotsharp_variant_new_packed_color_array(out godot_variant r_dest,
             in godot_packed_color_array p_pca);
 
@@ -302,6 +308,9 @@ namespace Godot.NativeInterop
         public static partial godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(
             in godot_variant p_self);
 
+        public static partial godot_packed_vector4_array godotsharp_variant_as_packed_vector4_array(
+            in godot_variant p_self);
+
         public static partial godot_packed_color_array godotsharp_variant_as_packed_color_array(in godot_variant p_self);
 
         public static partial godot_bool godotsharp_variant_equals(in godot_variant p_a, in godot_variant p_b);
@@ -352,6 +361,8 @@ namespace Godot.NativeInterop
 
         public static partial void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self);
 
+        public static partial void godotsharp_packed_vector4_array_destroy(ref godot_packed_vector4_array p_self);
+
         public static partial void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self);
 
         public static partial void godotsharp_variant_destroy(ref godot_variant p_self);

+ 19 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs

@@ -165,6 +165,12 @@ namespace Godot.NativeInterop
             return ret;
         }
 
+        public static godot_variant CreateFromPackedVector4Array(in godot_packed_vector4_array from)
+        {
+            NativeFuncs.godotsharp_variant_new_packed_vector4_array(out godot_variant ret, from);
+            return ret;
+        }
+
         public static godot_variant CreateFromPackedColorArray(in godot_packed_color_array from)
         {
             NativeFuncs.godotsharp_variant_new_packed_color_array(out godot_variant ret, from);
@@ -227,6 +233,13 @@ namespace Godot.NativeInterop
             return CreateFromPackedVector3Array(nativePackedArray);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static godot_variant CreateFromPackedVector4Array(Span<Vector4> from)
+        {
+            using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedVector4Array(from);
+            return CreateFromPackedVector4Array(nativePackedArray);
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static godot_variant CreateFromPackedColorArray(Span<Color> from)
         {
@@ -605,6 +618,12 @@ namespace Godot.NativeInterop
             return Marshaling.ConvertNativePackedVector3ArrayToSystemArray(packedArray);
         }
 
+        public static Vector4[] ConvertAsPackedVector4ArrayToSystemArray(in godot_variant p_var)
+        {
+            using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector4_array(p_var);
+            return Marshaling.ConvertNativePackedVector4ArrayToSystemArray(packedArray);
+        }
+
         public static Color[] ConvertAsPackedColorArrayToSystemArray(in godot_variant p_var)
         {
             using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);

+ 6 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs

@@ -155,6 +155,9 @@ public partial class VariantUtils
         if (typeof(T) == typeof(Vector3[]))
             return CreateFromPackedVector3Array(UnsafeAs<Vector3[]>(from));
 
+        if (typeof(T) == typeof(Vector4[]))
+            return CreateFromPackedVector4Array(UnsafeAs<Vector4[]>(from));
+
         if (typeof(T) == typeof(Color[]))
             return CreateFromPackedColorArray(UnsafeAs<Color[]>(from));
 
@@ -343,6 +346,9 @@ public partial class VariantUtils
         if (typeof(T) == typeof(Vector3[]))
             return UnsafeAsT(ConvertAsPackedVector3ArrayToSystemArray(variant));
 
+        if (typeof(T) == typeof(Vector4[]))
+            return UnsafeAsT(ConvertAsPackedVector4ArrayToSystemArray(variant));
+
         if (typeof(T) == typeof(Color[]))
             return UnsafeAsT(ConvertAsPackedColorArrayToSystemArray(variant));
 

+ 19 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs

@@ -149,6 +149,7 @@ public partial struct Variant : IDisposable
             Type.PackedStringArray => AsStringArray(),
             Type.PackedVector2Array => AsVector2Array(),
             Type.PackedVector3Array => AsVector3Array(),
+            Type.PackedVector4Array => AsVector4Array(),
             Type.PackedColorArray => AsColorArray(),
             Type.Nil => null,
             Type.Max or _ =>
@@ -319,6 +320,10 @@ public partial struct Variant : IDisposable
     public Vector3[] AsVector3Array() =>
         VariantUtils.ConvertAsPackedVector3ArrayToSystemArray((godot_variant)NativeVar);
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public Vector4[] AsVector4Array() =>
+        VariantUtils.ConvertAsPackedVector4ArrayToSystemArray((godot_variant)NativeVar);
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public Color[] AsColorArray() =>
         VariantUtils.ConvertAsPackedColorArrayToSystemArray((godot_variant)NativeVar);
@@ -491,6 +496,9 @@ public partial struct Variant : IDisposable
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static explicit operator Vector3[](Variant from) => from.AsVector3Array();
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static explicit operator Vector4[](Variant from) => from.AsVector4Array();
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static explicit operator Color[](Variant from) => from.AsColorArray();
 
@@ -641,6 +649,9 @@ public partial struct Variant : IDisposable
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Variant CreateFrom(Span<Vector3> from) => from;
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Variant CreateFrom(Span<Vector4> from) => from;
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static Variant CreateFrom(Span<Color> from) => from;
 
@@ -840,6 +851,10 @@ public partial struct Variant : IDisposable
     public static implicit operator Variant(Vector3[] from) =>
         (Variant)from.AsSpan();
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Vector4[] from) =>
+        (Variant)from.AsSpan();
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Variant(Color[] from) =>
         (Variant)from.AsSpan();
@@ -892,6 +907,10 @@ public partial struct Variant : IDisposable
     public static implicit operator Variant(Span<Vector3> from) =>
         CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector3Array(from));
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static implicit operator Variant(Span<Vector4> from) =>
+        CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector4Array(from));
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static implicit operator Variant(Span<Color> from) =>
         CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from));

+ 29 - 0
modules/mono/glue/runtime_interop.cpp

@@ -461,6 +461,16 @@ godot_packed_array godotsharp_packed_vector3_array_new_mem_copy(const Vector3 *p
 	return ret;
 }
 
+godot_packed_array godotsharp_packed_vector4_array_new_mem_copy(const Vector4 *p_src, int32_t p_length) {
+	godot_packed_array ret;
+	memnew_placement(&ret, PackedVector4Array);
+	PackedVector4Array *array = reinterpret_cast<PackedVector4Array *>(&ret);
+	array->resize(p_length);
+	Vector4 *dst = array->ptrw();
+	memcpy(dst, p_src, p_length * sizeof(Vector4));
+	return ret;
+}
+
 godot_packed_array godotsharp_packed_color_array_new_mem_copy(const Color *p_src, int32_t p_length) {
 	godot_packed_array ret;
 	memnew_placement(&ret, PackedColorArray);
@@ -646,6 +656,10 @@ void godotsharp_variant_new_packed_vector3_array(godot_variant *r_dest, const Pa
 	memnew_placement(r_dest, Variant(*p_pv3a));
 }
 
+void godotsharp_variant_new_packed_vector4_array(godot_variant *r_dest, const PackedVector4Array *p_pv4a) {
+	memnew_placement(r_dest, Variant(*p_pv4a));
+}
+
 void godotsharp_variant_new_packed_color_array(godot_variant *r_dest, const PackedColorArray *p_pca) {
 	memnew_placement(r_dest, Variant(*p_pca));
 }
@@ -886,6 +900,13 @@ godot_packed_array godotsharp_variant_as_packed_vector3_array(const Variant *p_s
 	return raw_dest;
 }
 
+godot_packed_array godotsharp_variant_as_packed_vector4_array(const Variant *p_self) {
+	godot_packed_array raw_dest;
+	PackedVector4Array *dest = (PackedVector4Array *)&raw_dest;
+	memnew_placement(dest, PackedVector4Array(p_self->operator PackedVector4Array()));
+	return raw_dest;
+}
+
 godot_packed_array godotsharp_variant_as_packed_color_array(const Variant *p_self) {
 	godot_packed_array raw_dest;
 	PackedColorArray *dest = (PackedColorArray *)&raw_dest;
@@ -974,6 +995,10 @@ void godotsharp_packed_vector3_array_destroy(PackedVector3Array *p_self) {
 	p_self->~PackedVector3Array();
 }
 
+void godotsharp_packed_vector4_array_destroy(PackedVector4Array *p_self) {
+	p_self->~PackedVector4Array();
+}
+
 void godotsharp_packed_color_array_destroy(PackedColorArray *p_self) {
 	p_self->~PackedColorArray();
 }
@@ -1456,6 +1481,7 @@ static const void *unmanaged_callbacks[]{
 	(void *)godotsharp_packed_float64_array_new_mem_copy,
 	(void *)godotsharp_packed_vector2_array_new_mem_copy,
 	(void *)godotsharp_packed_vector3_array_new_mem_copy,
+	(void *)godotsharp_packed_vector4_array_new_mem_copy,
 	(void *)godotsharp_packed_color_array_new_mem_copy,
 	(void *)godotsharp_packed_string_array_add,
 	(void *)godotsharp_callable_new_with_delegate,
@@ -1484,6 +1510,7 @@ static const void *unmanaged_callbacks[]{
 	(void *)godotsharp_variant_new_packed_string_array,
 	(void *)godotsharp_variant_new_packed_vector2_array,
 	(void *)godotsharp_variant_new_packed_vector3_array,
+	(void *)godotsharp_variant_new_packed_vector4_array,
 	(void *)godotsharp_variant_new_packed_color_array,
 	(void *)godotsharp_variant_as_bool,
 	(void *)godotsharp_variant_as_int,
@@ -1520,6 +1547,7 @@ static const void *unmanaged_callbacks[]{
 	(void *)godotsharp_variant_as_packed_string_array,
 	(void *)godotsharp_variant_as_packed_vector2_array,
 	(void *)godotsharp_variant_as_packed_vector3_array,
+	(void *)godotsharp_variant_as_packed_vector4_array,
 	(void *)godotsharp_variant_as_packed_color_array,
 	(void *)godotsharp_variant_equals,
 	(void *)godotsharp_string_new_with_utf16_chars,
@@ -1538,6 +1566,7 @@ static const void *unmanaged_callbacks[]{
 	(void *)godotsharp_packed_string_array_destroy,
 	(void *)godotsharp_packed_vector2_array_destroy,
 	(void *)godotsharp_packed_vector3_array_destroy,
+	(void *)godotsharp_packed_vector4_array_destroy,
 	(void *)godotsharp_packed_color_array_destroy,
 	(void *)godotsharp_variant_destroy,
 	(void *)godotsharp_string_destroy,

+ 2 - 1
modules/multiplayer/editor/replication_editor.cpp

@@ -234,7 +234,8 @@ ReplicationEditor::ReplicationEditor() {
 		Variant::PACKED_STRING_ARRAY,
 		Variant::PACKED_VECTOR2_ARRAY,
 		Variant::PACKED_VECTOR3_ARRAY,
-		Variant::PACKED_COLOR_ARRAY
+		Variant::PACKED_COLOR_ARRAY,
+		Variant::PACKED_VECTOR4_ARRAY,
 	};
 	prop_selector->set_type_filter(types);
 	prop_selector->connect("selected", callable_mp(this, &ReplicationEditor::_pick_node_property_selected));

+ 5 - 3
platform/windows/godot.natvis

@@ -29,7 +29,7 @@
 			</ArrayItems>
 		</Expand>
 	</Type>
-  
+
 	<Type Name="Dictionary">
 		<Expand>
 			<Item Name="[size]">_p &amp;&amp; _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Item>
@@ -38,7 +38,7 @@
 				<HeadPointer>_p ? _p->variant_map.head_element : nullptr</HeadPointer>
 				<NextPointer>next</NextPointer>
 				<ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
-			</LinkedListItems> 
+			</LinkedListItems>
 		</Expand>
 	</Type>
 
@@ -186,6 +186,7 @@
 		<DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array}</DisplayString>
 		<DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array}</DisplayString>
 		<DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+		<DisplayString Condition="type == Variant::PACKED_VECTOR4_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector4&gt;*&gt;(_data.packed_array)->array}</DisplayString>
 		<DisplayString Condition="type &lt; 0 || type >= Variant::VARIANT_MAX">[INVALID]</DisplayString>
 
 		<StringView Condition="type == Variant::STRING &amp;&amp; ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</StringView>
@@ -214,12 +215,13 @@
 			<Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array</Item>
 			<Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;int&gt;*&gt;(_data.packed_array)->array</Item>
 			<Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*reinterpret_cast&lt;PackedInt64Array *&gt;(&amp;_data.packed_array[1])</Item>
-			<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array</Item>		
+			<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array</Item>
 			<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;double&gt;*&gt;(_data.packed_array)->array</Item>
 			<Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;String&gt;*&gt;(_data.packed_array)->array</Item>
 			<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array</Item>
 			<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array</Item>
 			<Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array</Item>
+			<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR4_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector4&gt;*&gt;(_data.packed_array)->array</Item>
 
 		</Expand>
 	</Type>

+ 8 - 4
scene/resources/resource_format_text.cpp

@@ -37,11 +37,12 @@
 #include "core/object/script_language.h"
 #include "core/version.h"
 
-// Version 2: changed names for Basis, AABB, Vectors, etc.
-// Version 3: new string ID for ext/subresources, breaks forward compat.
-// Version 4: PackedByteArray is now stored as base64 encoded.
-#define FORMAT_VERSION_COMPAT 3
+// Version 2: Changed names for Basis, AABB, Vectors, etc.
+// Version 3: New string ID for ext/subresources, breaks forward compat.
+// Version 4: PackedByteArray can be base64 encoded, and PackedVector4Array was added.
 #define FORMAT_VERSION 4
+// For compat, save as version 3 if not using PackedVector4Array or no big PackedByteArray.
+#define FORMAT_VERSION_COMPAT 3
 
 #define BINARY_FORMAT_VERSION 4
 
@@ -1979,6 +1980,9 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
 				use_compat = false;
 			}
 		} break;
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			use_compat = false;
+		} break;
 		default: {
 		}
 	}

+ 2 - 1
servers/rendering/storage/variant_converters.h

@@ -239,7 +239,8 @@ inline bool is_convertible_array(Variant::Type type) {
 	return type == Variant::ARRAY ||
 			type == Variant::PACKED_VECTOR2_ARRAY ||
 			type == Variant::PACKED_VECTOR3_ARRAY ||
-			type == Variant::PACKED_COLOR_ARRAY;
+			type == Variant::PACKED_COLOR_ARRAY ||
+			type == Variant::PACKED_VECTOR4_ARRAY;
 }
 
 template <typename, typename = void>

+ 3 - 1
tests/core/object/test_class_db.h

@@ -141,7 +141,7 @@ struct NamesCache {
 	StringName vector3_type = StaticCString::create("Vector3");
 
 	// Object not included as it must be checked for all derived classes
-	static constexpr int nullable_types_count = 17;
+	static constexpr int nullable_types_count = 18;
 	StringName nullable_types[nullable_types_count] = {
 		string_type,
 		string_name_type,
@@ -161,6 +161,7 @@ struct NamesCache {
 		StaticCString::create(_STR(PackedVector2Array)),
 		StaticCString::create(_STR(PackedVector3Array)),
 		StaticCString::create(_STR(PackedColorArray)),
+		StaticCString::create(_STR(PackedVector4Array)),
 	};
 
 	bool is_nullable_type(const StringName &p_type) const {
@@ -258,6 +259,7 @@ bool arg_default_value_is_assignable_to_type(const Context &p_context, const Var
 		case Variant::PACKED_VECTOR2_ARRAY:
 		case Variant::PACKED_VECTOR3_ARRAY:
 		case Variant::PACKED_COLOR_ARRAY:
+		case Variant::PACKED_VECTOR4_ARRAY:
 		case Variant::CALLABLE:
 		case Variant::SIGNAL:
 			return p_arg_type.name == Variant::get_type_name(p_val.get_type());

+ 4 - 0
tests/core/variant/test_variant.h

@@ -2034,6 +2034,10 @@ TEST_CASE("[Variant] Identity comparison") {
 	CHECK(packed_color_array.identity_compare(packed_color_array));
 	CHECK_FALSE(packed_color_array.identity_compare(PackedColorArray()));
 
+	Variant packed_vector4_array = PackedVector4Array();
+	CHECK(packed_vector4_array.identity_compare(packed_vector4_array));
+	CHECK_FALSE(packed_vector4_array.identity_compare(PackedVector4Array()));
+
 	Variant packed_float32_array = PackedFloat32Array();
 	CHECK(packed_float32_array.identity_compare(packed_float32_array));
 	CHECK_FALSE(packed_float32_array.identity_compare(PackedFloat32Array()));

+ 1 - 0
tests/test_macros.h

@@ -119,6 +119,7 @@ DOCTEST_STRINGIFY_VARIANT(PackedStringArray);
 DOCTEST_STRINGIFY_VARIANT(PackedVector2Array);
 DOCTEST_STRINGIFY_VARIANT(PackedVector3Array);
 DOCTEST_STRINGIFY_VARIANT(PackedColorArray);
+DOCTEST_STRINGIFY_VARIANT(PackedVector4Array);
 
 // Register test commands to be launched from the command-line.
 // For instance: REGISTER_TEST_COMMAND("gdscript-parser" &test_parser_func).

+ 7 - 0
tests/test_validate_testing.h

@@ -181,6 +181,13 @@ TEST_SUITE("Validate tests") {
 		color_arr.push_back(Color(2, 2, 2));
 		INFO(color_arr);
 
+		PackedVector4Array vec4_arr;
+		vec4_arr.push_back(Vector4(0, 0, 0, 0));
+		vec4_arr.push_back(Vector4(1, 1, 1, 1));
+		vec4_arr.push_back(Vector4(2, 2, 2, 2));
+		vec4_arr.push_back(Vector4(3, 3, 3, 3));
+		INFO(vec4_arr);
+
 		// doctest string concatenation.
 		CHECK_MESSAGE(true, var, " ", vec2, " ", rect2, " ", color);
 	}