Browse Source

Merge pull request #63219 from reduz/implement-vector4-projection

Rémi Verschelde 3 years ago
parent
commit
3084a48ace
100 changed files with 3783 additions and 311 deletions
  1. 33 6
      .github/workflows/linux_builds.yml
  2. 3 0
      core/core_constants.cpp
  3. 14 0
      core/extension/extension_api_dump.cpp
  4. 12 0
      core/extension/gdnative_interface.cpp
  5. 3 0
      core/extension/gdnative_interface.h
  6. 104 0
      core/io/marshalls.cpp
  7. 78 0
      core/io/resource_format_binary.cpp
  8. 2 2
      core/math/delaunay_3d.h
  9. 30 0
      core/math/math_fieldwise.cpp
  10. 211 44
      core/math/projection.cpp
  11. 42 11
      core/math/projection.h
  12. 102 0
      core/math/vector4.cpp
  13. 275 0
      core/math/vector4.h
  14. 91 0
      core/math/vector4i.cpp
  15. 338 0
      core/math/vector4i.h
  16. 3 0
      core/object/script_language.cpp
  17. 16 0
      core/templates/hashfuncs.h
  18. 3 0
      core/variant/binder_common.h
  19. 3 0
      core/variant/method_ptrcall.h
  20. 3 0
      core/variant/type_info.h
  21. 269 1
      core/variant/variant.cpp
  22. 16 0
      core/variant/variant.h
  23. 104 0
      core/variant/variant_call.cpp
  24. 15 0
      core/variant/variant_construct.cpp
  25. 3 0
      core/variant/variant_construct.h
  26. 89 0
      core/variant/variant_internal.h
  27. 131 0
      core/variant/variant_op.cpp
  28. 48 0
      core/variant/variant_op.h
  29. 61 0
      core/variant/variant_parser.cpp
  30. 17 0
      core/variant/variant_setget.cpp
  31. 15 0
      core/variant/variant_setget.h
  32. 12 0
      core/variant/variant_utility.cpp
  33. 30 24
      doc/classes/@GlobalScope.xml
  34. 4 4
      doc/classes/Camera3D.xml
  35. 0 1
      doc/classes/Label.xml
  36. 0 10
      doc/classes/LabelSettings.xml
  37. 273 0
      doc/classes/Projection.xml
  38. 6 0
      doc/classes/Transform3D.xml
  39. 231 0
      doc/classes/Vector4.xml
  40. 208 0
      doc/classes/Vector4i.xml
  41. 12 0
      doc/classes/float.xml
  42. 12 0
      doc/classes/int.xml
  43. 1 1
      drivers/gles3/rasterizer_canvas_gles3.cpp
  44. 1 1
      drivers/gles3/rasterizer_canvas_gles3.h
  45. 12 12
      drivers/gles3/rasterizer_scene_gles3.cpp
  46. 8 8
      drivers/gles3/rasterizer_scene_gles3.h
  47. 1 1
      drivers/gles3/shader_gles3.h
  48. 167 107
      drivers/gles3/storage/material_storage.cpp
  49. 1 1
      drivers/gles3/storage/material_storage.h
  50. 306 0
      editor/editor_properties.cpp
  51. 52 0
      editor/editor_properties.h
  52. 18 0
      editor/editor_properties_array_dict.cpp
  53. 3 3
      editor/plugins/node_3d_editor_plugin.cpp
  54. 1 1
      editor/plugins/node_3d_editor_plugin.h
  55. 4 4
      editor/shader_globals_editor.cpp
  56. 1 1
      gles3_builders.py
  57. 6 0
      modules/gdscript/gdscript_analyzer.cpp
  58. 12 0
      modules/gdscript/gdscript_byte_codegen.cpp
  59. 6 0
      modules/gdscript/gdscript_disassembler.cpp
  60. 6 0
      modules/gdscript/gdscript_function.h
  61. 3 0
      modules/gdscript/gdscript_parser.cpp
  62. 18 0
      modules/gdscript/gdscript_vm.cpp
  63. 1 1
      modules/gltf/gltf_document.cpp
  64. 2 2
      modules/mobile_vr/mobile_vr_interface.cpp
  65. 1 1
      modules/mobile_vr/mobile_vr_interface.h
  66. 3 0
      modules/mono/csharp_script.cpp
  67. 16 0
      modules/mono/editor/bindings_generator.cpp
  68. 3 0
      modules/mono/editor/bindings_generator.h
  69. 6 0
      modules/mono/mono_gd/gd_mono_cache.cpp
  70. 3 0
      modules/mono/mono_gd/gd_mono_cache.h
  71. 30 0
      modules/mono/mono_gd/gd_mono_field.cpp
  72. 22 1
      modules/mono/mono_gd/gd_mono_marshal.cpp
  73. 62 3
      modules/mono/mono_gd/gd_mono_marshal.h
  74. 2 2
      modules/openxr/extensions/openxr_extension_wrapper.h
  75. 1 1
      modules/openxr/extensions/openxr_vulkan_extension.cpp
  76. 1 1
      modules/openxr/extensions/openxr_vulkan_extension.h
  77. 1 1
      modules/openxr/openxr_api.cpp
  78. 2 2
      modules/openxr/openxr_api.h
  79. 2 2
      modules/openxr/openxr_interface.cpp
  80. 1 1
      modules/openxr/openxr_interface.h
  81. 3 3
      modules/raycast/raycast_occlusion_cull.cpp
  82. 3 3
      modules/raycast/raycast_occlusion_cull.h
  83. 9 0
      modules/visual_script/editor/visual_script_editor.cpp
  84. 3 0
      modules/visual_script/visual_script_nodes.cpp
  85. 2 2
      modules/webxr/webxr_interface_js.cpp
  86. 1 1
      modules/webxr/webxr_interface_js.h
  87. 2 0
      platform/windows/godot.natvis
  88. 8 8
      scene/3d/camera_3d.cpp
  89. 5 5
      scene/3d/camera_3d.h
  90. 4 4
      scene/3d/xr_nodes.cpp
  91. 11 0
      scene/animation/tween.cpp
  92. 4 4
      scene/main/shader_globals_override.cpp
  93. 2 2
      servers/rendering/dummy/rasterizer_scene_dummy.h
  94. 1 1
      servers/rendering/renderer_canvas_render.h
  95. 2 2
      servers/rendering/renderer_rd/cluster_builder_rd.cpp
  96. 3 3
      servers/rendering/renderer_rd/cluster_builder_rd.h
  97. 5 5
      servers/rendering/renderer_rd/effects/ss_effects.cpp
  98. 4 4
      servers/rendering/renderer_rd/effects/ss_effects.h
  99. 1 1
      servers/rendering/renderer_rd/effects_rd.cpp
  100. 2 2
      servers/rendering/renderer_rd/effects_rd.h

+ 33 - 6
.github/workflows/linux_builds.yml

@@ -19,15 +19,30 @@ jobs:
       fail-fast: false
       matrix:
         include:
-          - name: Editor w/ Mono (target=release_debug, tools=yes, tests=yes)
+# Temporarily disabled until Mono is fixed
+#
+#          - name: Editor w Mono (target=release_debug, tools=yes, tests=yes)
+#            cache-name: linux-editor-mono
+#            target: release_debug
+#            tools: true
+#            tests: false # Disabled due freeze caused by mix Mono build and CI
+#            sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no
+#            doc-test: true
+#            bin: "./bin/godot.linuxbsd.opt.tools.64.mono"
+#            build-mono: true
+#            proj-conv: true
+#            artifact: true
+
+# Temporary replacement:
+
+          - name: Editor w/o Mono (target=release_debug, tools=yes, tests=yes)
             cache-name: linux-editor-mono
             target: release_debug
             tools: true
             tests: false # Disabled due freeze caused by mix Mono build and CI
-            sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no
             doc-test: true
-            bin: "./bin/godot.linuxbsd.opt.tools.64.mono"
-            build-mono: true
+            bin: "./bin/godot.linuxbsd.opt.tools.64"
+            build-mono: false
             proj-conv: true
             artifact: true
 
@@ -57,12 +72,24 @@ jobs:
             # Skip 2GiB artifact speeding up action.
             artifact: false
 
-          - name: Template w/ Mono (target=release, tools=no)
+# Temporarily disabled:
+#
+#          - name: Template w/ Mono (target=release, tools=no)
+#            cache-name: linux-template-mono
+#            target: release
+#            tools: false
+#            tests: false
+#            sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no debug_symbols=no
+#            build-mono: false
+#            artifact: true
+
+# Temporary replacement:
+
+          - name: Template w/o Mono (target=release, tools=no)
             cache-name: linux-template-mono
             target: release
             tools: false
             tests: false
-            sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no debug_symbols=no
             build-mono: false
             artifact: true
 

+ 3 - 0
core/core_constants.cpp

@@ -681,11 +681,14 @@ void register_global_constants() {
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3", Variant::VECTOR3);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3I", Variant::VECTOR3I);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM2D", Variant::TRANSFORM2D);
+	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR4", Variant::VECTOR4);
+	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR4I", Variant::VECTOR4I);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PLANE", Variant::PLANE);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_QUATERNION", Variant::QUATERNION);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_AABB", Variant::AABB);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BASIS", Variant::BASIS);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM3D", Variant::TRANSFORM3D);
+	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PROJECTION", Variant::PROJECTION);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_COLOR", Variant::COLOR);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_STRING_NAME", Variant::STRING_NAME);
 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NODE_PATH", Variant::NODE_PATH);

+ 14 - 0
core/extension/extension_api_dump.cpp

@@ -131,11 +131,14 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
 			{ Variant::VECTOR3, vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) },
 			{ Variant::VECTOR3I, 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t) },
 			{ Variant::TRANSFORM2D, 6 * sizeof(float), 6 * sizeof(float), 6 * sizeof(double), 6 * sizeof(double) },
+			{ Variant::VECTOR4, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
+			{ Variant::VECTOR4I, 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t) },
 			{ Variant::PLANE, (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(double), (vec3_elems + 1) * sizeof(double) },
 			{ Variant::QUATERNION, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
 			{ Variant::AABB, (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(double), (vec3_elems * 2) * sizeof(double) },
 			{ Variant::BASIS, (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) },
 			{ Variant::TRANSFORM3D, (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(double), (vec3_elems * 4) * sizeof(double) },
+			{ Variant::PROJECTION, 4 * 4 * sizeof(float), 4 * 4 * sizeof(float), 4 * 4 * sizeof(double), 4 * 4 * sizeof(double) },
 			{ Variant::COLOR, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float) },
 			{ Variant::STRING_NAME, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
 			{ Variant::NODE_PATH, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
@@ -169,11 +172,14 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
 		static_assert(type_size_array[Variant::VECTOR3][sizeof(void *)] == sizeof(Vector3), "Size of Vector3 mismatch");
 		static_assert(type_size_array[Variant::VECTOR3I][sizeof(void *)] == sizeof(Vector3i), "Size of Vector3i mismatch");
 		static_assert(type_size_array[Variant::TRANSFORM2D][sizeof(void *)] == sizeof(Transform2D), "Size of Transform2D mismatch");
+		static_assert(type_size_array[Variant::VECTOR4][sizeof(void *)] == sizeof(Vector4), "Size of Vector4 mismatch");
+		static_assert(type_size_array[Variant::VECTOR4I][sizeof(void *)] == sizeof(Vector4i), "Size of Vector4i mismatch");
 		static_assert(type_size_array[Variant::PLANE][sizeof(void *)] == sizeof(Plane), "Size of Plane mismatch");
 		static_assert(type_size_array[Variant::QUATERNION][sizeof(void *)] == sizeof(Quaternion), "Size of Quaternion mismatch");
 		static_assert(type_size_array[Variant::AABB][sizeof(void *)] == sizeof(AABB), "Size of AABB mismatch");
 		static_assert(type_size_array[Variant::BASIS][sizeof(void *)] == sizeof(Basis), "Size of Basis mismatch");
 		static_assert(type_size_array[Variant::TRANSFORM3D][sizeof(void *)] == sizeof(Transform3D), "Size of Transform3D mismatch");
+		static_assert(type_size_array[Variant::PROJECTION][sizeof(void *)] == sizeof(Projection), "Size of Projection mismatch");
 		static_assert(type_size_array[Variant::COLOR][sizeof(void *)] == sizeof(Color), "Size of Color mismatch");
 		static_assert(type_size_array[Variant::STRING_NAME][sizeof(void *)] == sizeof(StringName), "Size of StringName mismatch");
 		static_assert(type_size_array[Variant::NODE_PATH][sizeof(void *)] == sizeof(NodePath), "Size of NodePath mismatch");
@@ -256,6 +262,14 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
 			{ Variant::TRANSFORM2D, "x", 0, 0, 0, 0 },
 			{ Variant::TRANSFORM2D, "y", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
 			{ Variant::TRANSFORM2D, "origin", 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
+			{ Variant::VECTOR4, "x", 0, 0, 0, 0 },
+			{ Variant::VECTOR4, "y", sizeof(float), sizeof(float), sizeof(double), sizeof(double) },
+			{ Variant::VECTOR4, "z", 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
+			{ Variant::VECTOR4, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(double), 3 * sizeof(double) },
+			{ Variant::VECTOR4I, "x", 0, 0, 0, 0 },
+			{ Variant::VECTOR4I, "y", sizeof(int32_t), sizeof(int32_t), sizeof(int32_t), sizeof(int32_t) },
+			{ Variant::VECTOR4I, "z", 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) },
+			{ Variant::VECTOR4I, "w", 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t) },
 			{ Variant::PLANE, "normal", 0, 0, 0, 0 },
 			{ Variant::PLANE, "d", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) },
 			{ Variant::QUATERNION, "x", 0, 0, 0, 0 },

+ 12 - 0
core/extension/gdnative_interface.cpp

@@ -344,6 +344,10 @@ static GDNativeVariantFromTypeConstructorFunc gdnative_get_variant_from_type_con
 			return VariantTypeConstructor<Vector3i>::variant_from_type;
 		case GDNATIVE_VARIANT_TYPE_TRANSFORM2D:
 			return VariantTypeConstructor<Transform2D>::variant_from_type;
+		case GDNATIVE_VARIANT_TYPE_VECTOR4:
+			return VariantTypeConstructor<Vector4>::variant_from_type;
+		case GDNATIVE_VARIANT_TYPE_VECTOR4I:
+			return VariantTypeConstructor<Vector4i>::variant_from_type;
 		case GDNATIVE_VARIANT_TYPE_PLANE:
 			return VariantTypeConstructor<Plane>::variant_from_type;
 		case GDNATIVE_VARIANT_TYPE_QUATERNION:
@@ -354,6 +358,8 @@ static GDNativeVariantFromTypeConstructorFunc gdnative_get_variant_from_type_con
 			return VariantTypeConstructor<Basis>::variant_from_type;
 		case GDNATIVE_VARIANT_TYPE_TRANSFORM3D:
 			return VariantTypeConstructor<Transform3D>::variant_from_type;
+		case GDNATIVE_VARIANT_TYPE_PROJECTION:
+			return VariantTypeConstructor<Projection>::variant_from_type;
 		case GDNATIVE_VARIANT_TYPE_COLOR:
 			return VariantTypeConstructor<Color>::variant_from_type;
 		case GDNATIVE_VARIANT_TYPE_STRING_NAME:
@@ -421,6 +427,10 @@ static GDNativeTypeFromVariantConstructorFunc gdnative_get_type_from_variant_con
 			return VariantTypeConstructor<Vector3i>::type_from_variant;
 		case GDNATIVE_VARIANT_TYPE_TRANSFORM2D:
 			return VariantTypeConstructor<Transform2D>::type_from_variant;
+		case GDNATIVE_VARIANT_TYPE_VECTOR4:
+			return VariantTypeConstructor<Vector4>::type_from_variant;
+		case GDNATIVE_VARIANT_TYPE_VECTOR4I:
+			return VariantTypeConstructor<Vector4i>::type_from_variant;
 		case GDNATIVE_VARIANT_TYPE_PLANE:
 			return VariantTypeConstructor<Plane>::type_from_variant;
 		case GDNATIVE_VARIANT_TYPE_QUATERNION:
@@ -431,6 +441,8 @@ static GDNativeTypeFromVariantConstructorFunc gdnative_get_type_from_variant_con
 			return VariantTypeConstructor<Basis>::type_from_variant;
 		case GDNATIVE_VARIANT_TYPE_TRANSFORM3D:
 			return VariantTypeConstructor<Transform3D>::type_from_variant;
+		case GDNATIVE_VARIANT_TYPE_PROJECTION:
+			return VariantTypeConstructor<Projection>::type_from_variant;
 		case GDNATIVE_VARIANT_TYPE_COLOR:
 			return VariantTypeConstructor<Color>::type_from_variant;
 		case GDNATIVE_VARIANT_TYPE_STRING_NAME:

+ 3 - 0
core/extension/gdnative_interface.h

@@ -67,11 +67,14 @@ typedef enum {
 	GDNATIVE_VARIANT_TYPE_VECTOR3,
 	GDNATIVE_VARIANT_TYPE_VECTOR3I,
 	GDNATIVE_VARIANT_TYPE_TRANSFORM2D,
+	GDNATIVE_VARIANT_TYPE_VECTOR4,
+	GDNATIVE_VARIANT_TYPE_VECTOR4I,
 	GDNATIVE_VARIANT_TYPE_PLANE,
 	GDNATIVE_VARIANT_TYPE_QUATERNION,
 	GDNATIVE_VARIANT_TYPE_AABB,
 	GDNATIVE_VARIANT_TYPE_BASIS,
 	GDNATIVE_VARIANT_TYPE_TRANSFORM3D,
+	GDNATIVE_VARIANT_TYPE_PROJECTION,
 
 	/* misc types */
 	GDNATIVE_VARIANT_TYPE_COLOR,

+ 104 - 0
core/io/marshalls.cpp

@@ -284,6 +284,46 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 				(*r_len) += 4 * 3;
 			}
 
+		} break;
+		case Variant::VECTOR4: {
+			Vector4 val;
+			if (type & ENCODE_FLAG_64) {
+				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
+				val.x = decode_double(&buf[0]);
+				val.y = decode_double(&buf[sizeof(double)]);
+				val.z = decode_double(&buf[sizeof(double) * 2]);
+				val.w = decode_double(&buf[sizeof(double) * 3]);
+
+				if (r_len) {
+					(*r_len) += sizeof(double) * 4;
+				}
+			} else {
+				ERR_FAIL_COND_V((size_t)len < sizeof(float) * 4, ERR_INVALID_DATA);
+				val.x = decode_float(&buf[0]);
+				val.y = decode_float(&buf[sizeof(float)]);
+				val.z = decode_float(&buf[sizeof(float) * 2]);
+				val.w = decode_float(&buf[sizeof(float) * 3]);
+
+				if (r_len) {
+					(*r_len) += sizeof(float) * 4;
+				}
+			}
+			r_variant = val;
+
+		} break;
+		case Variant::VECTOR4I: {
+			ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
+			Vector4i val;
+			val.x = decode_uint32(&buf[0]);
+			val.y = decode_uint32(&buf[4]);
+			val.z = decode_uint32(&buf[8]);
+			val.w = decode_uint32(&buf[12]);
+			r_variant = val;
+
+			if (r_len) {
+				(*r_len) += 4 * 4;
+			}
+
 		} break;
 		case Variant::TRANSFORM2D: {
 			Transform2D val;
@@ -456,6 +496,33 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 			}
 			r_variant = val;
 
+		} break;
+		case Variant::PROJECTION: {
+			Projection val;
+			if (type & ENCODE_FLAG_64) {
+				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 16, ERR_INVALID_DATA);
+				for (int i = 0; i < 4; i++) {
+					for (int j = 0; j < 4; j++) {
+						val.matrix[i][j] = decode_double(&buf[(i * 4 + j) * sizeof(double)]);
+					}
+				}
+				if (r_len) {
+					(*r_len) += sizeof(double) * 16;
+				}
+			} else {
+				ERR_FAIL_COND_V((size_t)len < sizeof(float) * 62, ERR_INVALID_DATA);
+				for (int i = 0; i < 4; i++) {
+					for (int j = 0; j < 4; j++) {
+						val.matrix[i][j] = decode_float(&buf[(i * 4 + j) * sizeof(float)]);
+					}
+				}
+
+				if (r_len) {
+					(*r_len) += sizeof(float) * 16;
+				}
+			}
+			r_variant = val;
+
 		} break;
 		// misc types
 		case Variant::COLOR: {
@@ -1285,6 +1352,30 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 
 			r_len += 6 * sizeof(real_t);
 
+		} break;
+		case Variant::VECTOR4: {
+			if (buf) {
+				Vector4 v4 = p_variant;
+				encode_real(v4.x, &buf[0]);
+				encode_real(v4.y, &buf[sizeof(real_t)]);
+				encode_real(v4.z, &buf[sizeof(real_t) * 2]);
+				encode_real(v4.w, &buf[sizeof(real_t) * 3]);
+			}
+
+			r_len += 4 * sizeof(real_t);
+
+		} break;
+		case Variant::VECTOR4I: {
+			if (buf) {
+				Vector4i v4 = p_variant;
+				encode_uint32(v4.x, &buf[0]);
+				encode_uint32(v4.y, &buf[4]);
+				encode_uint32(v4.z, &buf[8]);
+				encode_uint32(v4.w, &buf[12]);
+			}
+
+			r_len += 4 * 4;
+
 		} break;
 		case Variant::PLANE: {
 			if (buf) {
@@ -1353,6 +1444,19 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 
 			r_len += 12 * sizeof(real_t);
 
+		} break;
+		case Variant::PROJECTION: {
+			if (buf) {
+				Projection val = p_variant;
+				for (int i = 0; i < 4; i++) {
+					for (int j = 0; j < 4; j++) {
+						memcpy(&buf[(i * 4 + j) * sizeof(real_t)], &val.matrix[i][j], sizeof(real_t));
+					}
+				}
+			}
+
+			r_len += 16 * sizeof(real_t);
+
 		} break;
 
 		// misc types

+ 78 - 0
core/io/resource_format_binary.cpp

@@ -81,6 +81,9 @@ enum {
 	VARIANT_VECTOR3I = 47,
 	VARIANT_PACKED_INT64_ARRAY = 48,
 	VARIANT_PACKED_FLOAT64_ARRAY = 49,
+	VARIANT_VECTOR4 = 50,
+	VARIANT_VECTOR4I = 51,
+	VARIANT_PROJECTION = 52,
 	OBJECT_EMPTY = 0,
 	OBJECT_EXTERNAL_RESOURCE = 1,
 	OBJECT_INTERNAL_RESOURCE = 2,
@@ -237,6 +240,22 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
 			v.z = f->get_32();
 			r_v = v;
 		} break;
+		case VARIANT_VECTOR4: {
+			Vector4 v;
+			v.x = f->get_real();
+			v.y = f->get_real();
+			v.z = f->get_real();
+			v.w = f->get_real();
+			r_v = v;
+		} break;
+		case VARIANT_VECTOR4I: {
+			Vector4i v;
+			v.x = f->get_32();
+			v.y = f->get_32();
+			v.z = f->get_32();
+			v.w = f->get_32();
+			r_v = v;
+		} break;
 		case VARIANT_PLANE: {
 			Plane v;
 			v.normal.x = f->get_real();
@@ -306,6 +325,26 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
 			v.origin.z = f->get_real();
 			r_v = v;
 		} break;
+		case VARIANT_PROJECTION: {
+			Projection v;
+			v.matrix[0].x = f->get_real();
+			v.matrix[0].y = f->get_real();
+			v.matrix[0].z = f->get_real();
+			v.matrix[0].w = f->get_real();
+			v.matrix[1].x = f->get_real();
+			v.matrix[1].y = f->get_real();
+			v.matrix[1].z = f->get_real();
+			v.matrix[1].w = f->get_real();
+			v.matrix[2].x = f->get_real();
+			v.matrix[2].y = f->get_real();
+			v.matrix[2].z = f->get_real();
+			v.matrix[2].w = f->get_real();
+			v.matrix[3].x = f->get_real();
+			v.matrix[3].y = f->get_real();
+			v.matrix[3].z = f->get_real();
+			v.matrix[3].w = f->get_real();
+			r_v = v;
+		} break;
 		case VARIANT_COLOR: {
 			Color v; // Colors should always be in single-precision.
 			v.r = f->get_float();
@@ -1498,6 +1537,24 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 			f->store_32(val.y);
 			f->store_32(val.z);
 
+		} break;
+		case Variant::VECTOR4: {
+			f->store_32(VARIANT_VECTOR4);
+			Vector4 val = p_property;
+			f->store_real(val.x);
+			f->store_real(val.y);
+			f->store_real(val.z);
+			f->store_real(val.w);
+
+		} break;
+		case Variant::VECTOR4I: {
+			f->store_32(VARIANT_VECTOR4I);
+			Vector4i val = p_property;
+			f->store_32(val.x);
+			f->store_32(val.y);
+			f->store_32(val.z);
+			f->store_32(val.w);
+
 		} break;
 		case Variant::PLANE: {
 			f->store_32(VARIANT_PLANE);
@@ -1569,6 +1626,27 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 			f->store_real(val.origin.y);
 			f->store_real(val.origin.z);
 
+		} break;
+		case Variant::PROJECTION: {
+			f->store_32(VARIANT_PROJECTION);
+			Projection val = p_property;
+			f->store_real(val.matrix[0].x);
+			f->store_real(val.matrix[0].y);
+			f->store_real(val.matrix[0].z);
+			f->store_real(val.matrix[0].w);
+			f->store_real(val.matrix[1].x);
+			f->store_real(val.matrix[1].y);
+			f->store_real(val.matrix[1].z);
+			f->store_real(val.matrix[1].w);
+			f->store_real(val.matrix[2].x);
+			f->store_real(val.matrix[2].y);
+			f->store_real(val.matrix[2].z);
+			f->store_real(val.matrix[2].w);
+			f->store_real(val.matrix[3].x);
+			f->store_real(val.matrix[3].y);
+			f->store_real(val.matrix[3].z);
+			f->store_real(val.matrix[3].w);
+
 		} break;
 		case Variant::COLOR: {
 			f->store_32(VARIANT_COLOR);

+ 2 - 2
core/math/delaunay_3d.h

@@ -33,7 +33,7 @@
 
 #include "core/io/file_access.h"
 #include "core/math/aabb.h"
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
 #include "core/math/vector3.h"
 #include "core/string/print_string.h"
 #include "core/templates/local_vector.h"
@@ -184,7 +184,7 @@ class Delaunay3D {
 			return true;
 		}
 
-		CameraMatrix cm;
+		Projection cm;
 
 		cm.matrix[0][0] = p_points[p_simplex.points[0]].x;
 		cm.matrix[0][1] = p_points[p_simplex.points[1]].x;

+ 30 - 0
core/math/math_fieldwise.cpp

@@ -76,6 +76,36 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const
 
 			return target;
 		}
+		case Variant::VECTOR3I: {
+			SETUP_TYPE(Vector3i)
+
+			/**/ TRY_TRANSFER_FIELD("x", x)
+			else TRY_TRANSFER_FIELD("y", y)
+			else TRY_TRANSFER_FIELD("z", z)
+
+			return target;
+		}
+		case Variant::VECTOR4: {
+			SETUP_TYPE(Vector4)
+
+			/**/ TRY_TRANSFER_FIELD("x", x)
+			else TRY_TRANSFER_FIELD("y", y)
+			else TRY_TRANSFER_FIELD("z", z)
+			else TRY_TRANSFER_FIELD("w", w)
+
+			return target;
+		}
+		case Variant::VECTOR4I: {
+			SETUP_TYPE(Vector4i)
+
+			/**/ TRY_TRANSFER_FIELD("x", x)
+			else TRY_TRANSFER_FIELD("y", y)
+			else TRY_TRANSFER_FIELD("z", z)
+			else TRY_TRANSFER_FIELD("w", w)
+
+			return target;
+		}
+
 
 		case Variant::PLANE: {
 			SETUP_TYPE(Plane)

+ 211 - 44
core/math/camera_matrix.cpp → core/math/projection.cpp

@@ -1,5 +1,5 @@
 /*************************************************************************/
-/*  camera_matrix.cpp                                                    */
+/*  projection.cpp                                                       */
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
@@ -28,7 +28,7 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 
-#include "camera_matrix.h"
+#include "projection.h"
 
 #include "core/math/aabb.h"
 #include "core/math/math_funcs.h"
@@ -37,7 +37,7 @@
 #include "core/math/transform_3d.h"
 #include "core/string/print_string.h"
 
-float CameraMatrix::determinant() const {
+float Projection::determinant() const {
 	return matrix[0][3] * matrix[1][2] * matrix[2][1] * matrix[3][0] - matrix[0][2] * matrix[1][3] * matrix[2][1] * matrix[3][0] -
 			matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0] + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0] +
 			matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0] - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0] -
@@ -52,7 +52,7 @@ float CameraMatrix::determinant() const {
 			matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3] + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3];
 }
 
-void CameraMatrix::set_identity() {
+void Projection::set_identity() {
 	for (int i = 0; i < 4; i++) {
 		for (int j = 0; j < 4; j++) {
 			matrix[i][j] = (i == j) ? 1 : 0;
@@ -60,7 +60,7 @@ void CameraMatrix::set_identity() {
 	}
 }
 
-void CameraMatrix::set_zero() {
+void Projection::set_zero() {
 	for (int i = 0; i < 4; i++) {
 		for (int j = 0; j < 4; j++) {
 			matrix[i][j] = 0;
@@ -68,7 +68,7 @@ void CameraMatrix::set_zero() {
 	}
 }
 
-Plane CameraMatrix::xform4(const Plane &p_vec4) const {
+Plane Projection::xform4(const Plane &p_vec4) const {
 	Plane ret;
 
 	ret.normal.x = matrix[0][0] * p_vec4.normal.x + matrix[1][0] * p_vec4.normal.y + matrix[2][0] * p_vec4.normal.z + matrix[3][0] * p_vec4.d;
@@ -78,7 +78,22 @@ Plane CameraMatrix::xform4(const Plane &p_vec4) const {
 	return ret;
 }
 
-void CameraMatrix::adjust_perspective_znear(real_t p_new_znear) {
+Vector4 Projection::xform(const Vector4 &p_vec4) const {
+	return Vector4(
+			matrix[0][0] * p_vec4.x + matrix[1][0] * p_vec4.y + matrix[2][0] * p_vec4.z + matrix[3][0] * p_vec4.w,
+			matrix[0][1] * p_vec4.x + matrix[1][1] * p_vec4.y + matrix[2][1] * p_vec4.z + matrix[3][1] * p_vec4.w,
+			matrix[0][2] * p_vec4.x + matrix[1][2] * p_vec4.y + matrix[2][2] * p_vec4.z + matrix[3][2] * p_vec4.w,
+			matrix[0][3] * p_vec4.x + matrix[1][3] * p_vec4.y + matrix[2][3] * p_vec4.z + matrix[3][3] * p_vec4.w);
+}
+Vector4 Projection::xform_inv(const Vector4 &p_vec4) const {
+	return Vector4(
+			matrix[0][0] * p_vec4.x + matrix[0][1] * p_vec4.y + matrix[0][2] * p_vec4.z + matrix[0][3] * p_vec4.w,
+			matrix[1][0] * p_vec4.x + matrix[1][1] * p_vec4.y + matrix[1][2] * p_vec4.z + matrix[1][3] * p_vec4.w,
+			matrix[2][0] * p_vec4.x + matrix[2][1] * p_vec4.y + matrix[2][2] * p_vec4.z + matrix[2][3] * p_vec4.w,
+			matrix[3][0] * p_vec4.x + matrix[3][1] * p_vec4.y + matrix[3][2] * p_vec4.z + matrix[3][3] * p_vec4.w);
+}
+
+void Projection::adjust_perspective_znear(real_t p_new_znear) {
 	real_t zfar = get_z_far();
 	real_t znear = p_new_znear;
 
@@ -87,7 +102,154 @@ void CameraMatrix::adjust_perspective_znear(real_t p_new_znear) {
 	matrix[3][2] = -2 * znear * zfar / deltaZ;
 }
 
-void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) {
+Projection Projection::create_depth_correction(bool p_flip_y) {
+	Projection proj;
+	proj.set_depth_correction(p_flip_y);
+	return proj;
+}
+
+Projection Projection::create_light_atlas_rect(const Rect2 &p_rect) {
+	Projection proj;
+	proj.set_light_atlas_rect(p_rect);
+	return proj;
+}
+
+Projection Projection::create_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) {
+	Projection proj;
+	proj.set_perspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov);
+	return proj;
+}
+
+Projection Projection::create_perspective_hmd(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) {
+	Projection proj;
+	proj.set_perspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov, p_eye, p_intraocular_dist, p_convergence_dist);
+	return proj;
+}
+
+Projection Projection::create_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) {
+	Projection proj;
+	proj.set_for_hmd(p_eye, p_aspect, p_intraocular_dist, p_display_width, p_display_to_lens, p_oversample, p_z_near, p_z_far);
+	return proj;
+}
+
+Projection Projection::create_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) {
+	Projection proj;
+	proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_zfar, p_zfar);
+	return proj;
+}
+
+Projection Projection::create_orthogonal_aspect(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) {
+	Projection proj;
+	proj.set_orthogonal(p_size, p_aspect, p_znear, p_zfar, p_flip_fov);
+	return proj;
+}
+
+Projection Projection::create_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) {
+	Projection proj;
+	proj.set_frustum(p_left, p_right, p_bottom, p_top, p_near, p_far);
+	return proj;
+}
+
+Projection Projection::create_frustum_aspect(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) {
+	Projection proj;
+	proj.set_frustum(p_size, p_aspect, p_offset, p_near, p_far, p_flip_fov);
+	return proj;
+}
+
+Projection Projection::create_fit_aabb(const AABB &p_aabb) {
+	Projection proj;
+	proj.scale_translate_to_fit(p_aabb);
+	return proj;
+}
+
+Projection Projection::perspective_znear_adjusted(real_t p_new_znear) const {
+	Projection proj = *this;
+	proj.adjust_perspective_znear(p_new_znear);
+	return proj;
+}
+
+Plane Projection::get_projection_plane(Planes p_plane) const {
+	const real_t *matrix = (const real_t *)this->matrix;
+
+	switch (p_plane) {
+		case PLANE_NEAR: {
+			Plane new_plane = Plane(matrix[3] + matrix[2],
+					matrix[7] + matrix[6],
+					matrix[11] + matrix[10],
+					matrix[15] + matrix[14]);
+
+			new_plane.normal = -new_plane.normal;
+			new_plane.normalize();
+			return new_plane;
+		} break;
+		case PLANE_FAR: {
+			Plane new_plane = Plane(matrix[3] - matrix[2],
+					matrix[7] - matrix[6],
+					matrix[11] - matrix[10],
+					matrix[15] - matrix[14]);
+
+			new_plane.normal = -new_plane.normal;
+			new_plane.normalize();
+			return new_plane;
+		} break;
+		case PLANE_LEFT: {
+			Plane new_plane = Plane(matrix[3] + matrix[0],
+					matrix[7] + matrix[4],
+					matrix[11] + matrix[8],
+					matrix[15] + matrix[12]);
+
+			new_plane.normal = -new_plane.normal;
+			new_plane.normalize();
+			return new_plane;
+		} break;
+		case PLANE_TOP: {
+			Plane new_plane = Plane(matrix[3] - matrix[1],
+					matrix[7] - matrix[5],
+					matrix[11] - matrix[9],
+					matrix[15] - matrix[13]);
+
+			new_plane.normal = -new_plane.normal;
+			new_plane.normalize();
+			return new_plane;
+		} break;
+		case PLANE_RIGHT: {
+			Plane new_plane = Plane(matrix[3] - matrix[0],
+					matrix[7] - matrix[4],
+					matrix[11] - matrix[8],
+					matrix[15] - matrix[12]);
+
+			new_plane.normal = -new_plane.normal;
+			new_plane.normalize();
+			return new_plane;
+		} break;
+		case PLANE_BOTTOM: {
+			Plane new_plane = Plane(matrix[3] + matrix[1],
+					matrix[7] + matrix[5],
+					matrix[11] + matrix[9],
+					matrix[15] + matrix[13]);
+
+			new_plane.normal = -new_plane.normal;
+			new_plane.normalize();
+			return new_plane;
+		} break;
+	}
+
+	return Plane();
+}
+
+Projection Projection::flipped_y() const {
+	Projection proj = *this;
+	proj.flip_y();
+	return proj;
+}
+
+Projection Projection ::jitter_offseted(const Vector2 &p_offset) const {
+	Projection proj = *this;
+	proj.add_jitter_offset(p_offset);
+	return proj;
+}
+
+void Projection::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) {
 	if (p_flip_fov) {
 		p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect);
 	}
@@ -113,7 +275,7 @@ void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_
 	matrix[3][3] = 0;
 }
 
-void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) {
+void Projection::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) {
 	if (p_flip_fov) {
 		p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect);
 	}
@@ -145,13 +307,13 @@ void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_
 	set_frustum(left, right, -ymax, ymax, p_z_near, p_z_far);
 
 	// translate matrix by (modeltranslation, 0.0, 0.0)
-	CameraMatrix cm;
+	Projection cm;
 	cm.set_identity();
 	cm.matrix[3][0] = modeltranslation;
 	*this = *this * cm;
 }
 
-void CameraMatrix::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) {
+void Projection::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) {
 	// we first calculate our base frustum on our values without taking our lens magnification into account.
 	real_t f1 = (p_intraocular_dist * 0.5) / p_display_to_lens;
 	real_t f2 = ((p_display_width - p_intraocular_dist) * 0.5) / p_display_to_lens;
@@ -179,7 +341,7 @@ void CameraMatrix::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_
 	}
 }
 
-void CameraMatrix::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) {
+void Projection::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) {
 	set_identity();
 
 	matrix[0][0] = 2.0 / (p_right - p_left);
@@ -191,7 +353,7 @@ void CameraMatrix::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom
 	matrix[3][3] = 1.0;
 }
 
-void CameraMatrix::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) {
+void Projection::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) {
 	if (!p_flip_fov) {
 		p_size *= p_aspect;
 	}
@@ -199,7 +361,7 @@ void CameraMatrix::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear
 	set_orthogonal(-p_size / 2, +p_size / 2, -p_size / p_aspect / 2, +p_size / p_aspect / 2, p_znear, p_zfar);
 }
 
-void CameraMatrix::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) {
+void Projection::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) {
 	ERR_FAIL_COND(p_right <= p_left);
 	ERR_FAIL_COND(p_top <= p_bottom);
 	ERR_FAIL_COND(p_far <= p_near);
@@ -231,7 +393,7 @@ void CameraMatrix::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, r
 	te[15] = 0;
 }
 
-void CameraMatrix::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) {
+void Projection::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) {
 	if (!p_flip_fov) {
 		p_size *= p_aspect;
 	}
@@ -239,7 +401,7 @@ void CameraMatrix::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset,
 	set_frustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset.y, p_near, p_far);
 }
 
-real_t CameraMatrix::get_z_far() const {
+real_t Projection::get_z_far() const {
 	const real_t *matrix = (const real_t *)this->matrix;
 	Plane new_plane = Plane(matrix[3] - matrix[2],
 			matrix[7] - matrix[6],
@@ -252,7 +414,7 @@ real_t CameraMatrix::get_z_far() const {
 	return new_plane.d;
 }
 
-real_t CameraMatrix::get_z_near() const {
+real_t Projection::get_z_near() const {
 	const real_t *matrix = (const real_t *)this->matrix;
 	Plane new_plane = Plane(matrix[3] + matrix[2],
 			matrix[7] + matrix[6],
@@ -263,7 +425,7 @@ real_t CameraMatrix::get_z_near() const {
 	return new_plane.d;
 }
 
-Vector2 CameraMatrix::get_viewport_half_extents() const {
+Vector2 Projection::get_viewport_half_extents() const {
 	const real_t *matrix = (const real_t *)this->matrix;
 	///////--- Near Plane ---///////
 	Plane near_plane = Plane(matrix[3] + matrix[2],
@@ -291,7 +453,7 @@ Vector2 CameraMatrix::get_viewport_half_extents() const {
 	return Vector2(res.x, res.y);
 }
 
-Vector2 CameraMatrix::get_far_plane_half_extents() const {
+Vector2 Projection::get_far_plane_half_extents() const {
 	const real_t *matrix = (const real_t *)this->matrix;
 	///////--- Far Plane ---///////
 	Plane far_plane = Plane(matrix[3] - matrix[2],
@@ -319,7 +481,7 @@ Vector2 CameraMatrix::get_far_plane_half_extents() const {
 	return Vector2(res.x, res.y);
 }
 
-bool CameraMatrix::get_endpoints(const Transform3D &p_transform, Vector3 *p_8points) const {
+bool Projection::get_endpoints(const Transform3D &p_transform, Vector3 *p_8points) const {
 	Vector<Plane> planes = get_projection_planes(Transform3D());
 	const Planes intersections[8][3] = {
 		{ PLANE_FAR, PLANE_LEFT, PLANE_TOP },
@@ -342,7 +504,7 @@ bool CameraMatrix::get_endpoints(const Transform3D &p_transform, Vector3 *p_8poi
 	return true;
 }
 
-Vector<Plane> CameraMatrix::get_projection_planes(const Transform3D &p_transform) const {
+Vector<Plane> Projection::get_projection_planes(const Transform3D &p_transform) const {
 	/** Fast Plane Extraction from combined modelview/projection matrices.
 	 * References:
 	 * https://web.archive.org/web/20011221205252/https://www.markmorley.com/opengl/frustumculling.html
@@ -425,13 +587,13 @@ Vector<Plane> CameraMatrix::get_projection_planes(const Transform3D &p_transform
 	return planes;
 }
 
-CameraMatrix CameraMatrix::inverse() const {
-	CameraMatrix cm = *this;
+Projection Projection::inverse() const {
+	Projection cm = *this;
 	cm.invert();
 	return cm;
 }
 
-void CameraMatrix::invert() {
+void Projection::invert() {
 	int i, j, k;
 	int pvt_i[4], pvt_j[4]; /* Locations of pivot matrix */
 	real_t pvt_val; /* Value of current pivot element */
@@ -529,18 +691,18 @@ void CameraMatrix::invert() {
 	}
 }
 
-void CameraMatrix::flip_y() {
+void Projection::flip_y() {
 	for (int i = 0; i < 4; i++) {
 		matrix[1][i] = -matrix[1][i];
 	}
 }
 
-CameraMatrix::CameraMatrix() {
+Projection::Projection() {
 	set_identity();
 }
 
-CameraMatrix CameraMatrix::operator*(const CameraMatrix &p_matrix) const {
-	CameraMatrix new_matrix;
+Projection Projection::operator*(const Projection &p_matrix) const {
+	Projection new_matrix;
 
 	for (int j = 0; j < 4; j++) {
 		for (int i = 0; i < 4; i++) {
@@ -555,7 +717,7 @@ CameraMatrix CameraMatrix::operator*(const CameraMatrix &p_matrix) const {
 	return new_matrix;
 }
 
-void CameraMatrix::set_depth_correction(bool p_flip_y) {
+void Projection::set_depth_correction(bool p_flip_y) {
 	real_t *m = &matrix[0][0];
 
 	m[0] = 1;
@@ -576,7 +738,7 @@ void CameraMatrix::set_depth_correction(bool p_flip_y) {
 	m[15] = 1.0;
 }
 
-void CameraMatrix::set_light_bias() {
+void Projection::set_light_bias() {
 	real_t *m = &matrix[0][0];
 
 	m[0] = 0.5;
@@ -597,7 +759,7 @@ void CameraMatrix::set_light_bias() {
 	m[15] = 1.0;
 }
 
-void CameraMatrix::set_light_atlas_rect(const Rect2 &p_rect) {
+void Projection::set_light_atlas_rect(const Rect2 &p_rect) {
 	real_t *m = &matrix[0][0];
 
 	m[0] = p_rect.size.width;
@@ -618,7 +780,7 @@ void CameraMatrix::set_light_atlas_rect(const Rect2 &p_rect) {
 	m[15] = 1.0;
 }
 
-CameraMatrix::operator String() const {
+Projection::operator String() const {
 	String str;
 	for (int i = 0; i < 4; i++) {
 		for (int j = 0; j < 4; j++) {
@@ -629,22 +791,22 @@ CameraMatrix::operator String() const {
 	return str;
 }
 
-real_t CameraMatrix::get_aspect() const {
+real_t Projection::get_aspect() const {
 	Vector2 vp_he = get_viewport_half_extents();
 	return vp_he.x / vp_he.y;
 }
 
-int CameraMatrix::get_pixels_per_meter(int p_for_pixel_width) const {
+int Projection::get_pixels_per_meter(int p_for_pixel_width) const {
 	Vector3 result = xform(Vector3(1, 0, -1));
 
 	return int((result.x * 0.5 + 0.5) * p_for_pixel_width);
 }
 
-bool CameraMatrix::is_orthogonal() const {
+bool Projection::is_orthogonal() const {
 	return matrix[3][3] == 1.0;
 }
 
-real_t CameraMatrix::get_fov() const {
+real_t Projection::get_fov() const {
 	const real_t *matrix = (const real_t *)this->matrix;
 
 	Plane right_plane = Plane(matrix[3] - matrix[0],
@@ -667,7 +829,7 @@ real_t CameraMatrix::get_fov() const {
 	}
 }
 
-float CameraMatrix::get_lod_multiplier() const {
+float Projection::get_lod_multiplier() const {
 	if (is_orthogonal()) {
 		return get_viewport_half_extents().x;
 	} else {
@@ -678,14 +840,14 @@ float CameraMatrix::get_lod_multiplier() const {
 
 	//usage is lod_size / (lod_distance * multiplier) < threshold
 }
-void CameraMatrix::make_scale(const Vector3 &p_scale) {
+void Projection::make_scale(const Vector3 &p_scale) {
 	set_identity();
 	matrix[0][0] = p_scale.x;
 	matrix[1][1] = p_scale.y;
 	matrix[2][2] = p_scale.z;
 }
 
-void CameraMatrix::scale_translate_to_fit(const AABB &p_aabb) {
+void Projection::scale_translate_to_fit(const AABB &p_aabb) {
 	Vector3 min = p_aabb.position;
 	Vector3 max = p_aabb.position + p_aabb.size;
 
@@ -710,12 +872,12 @@ void CameraMatrix::scale_translate_to_fit(const AABB &p_aabb) {
 	matrix[3][3] = 1;
 }
 
-void CameraMatrix::add_jitter_offset(const Vector2 &p_offset) {
+void Projection::add_jitter_offset(const Vector2 &p_offset) {
 	matrix[3][0] += p_offset.x;
 	matrix[3][1] += p_offset.y;
 }
 
-CameraMatrix::operator Transform3D() const {
+Projection::operator Transform3D() const {
 	Transform3D tr;
 	const real_t *m = &matrix[0][0];
 
@@ -737,8 +899,13 @@ CameraMatrix::operator Transform3D() const {
 
 	return tr;
 }
-
-CameraMatrix::CameraMatrix(const Transform3D &p_transform) {
+Projection::Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_z, const Vector4 &p_w) {
+	matrix[0] = p_x;
+	matrix[1] = p_y;
+	matrix[2] = p_z;
+	matrix[3] = p_w;
+}
+Projection::Projection(const Transform3D &p_transform) {
 	const Transform3D &tr = p_transform;
 	real_t *m = &matrix[0][0];
 
@@ -760,5 +927,5 @@ CameraMatrix::CameraMatrix(const Transform3D &p_transform) {
 	m[15] = 1.0;
 }
 
-CameraMatrix::~CameraMatrix() {
+Projection::~Projection() {
 }

+ 42 - 11
core/math/camera_matrix.h → core/math/projection.h

@@ -1,5 +1,5 @@
 /*************************************************************************/
-/*  camera_matrix.h                                                      */
+/*  projection.h                                                         */
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
@@ -33,6 +33,7 @@
 
 #include "core/math/math_defs.h"
 #include "core/math/vector3.h"
+#include "core/math/vector4.h"
 #include "core/templates/vector.h"
 
 struct AABB;
@@ -41,7 +42,7 @@ struct Rect2;
 struct Transform3D;
 struct Vector2;
 
-struct CameraMatrix {
+struct Projection {
 	enum Planes {
 		PLANE_NEAR,
 		PLANE_FAR,
@@ -51,13 +52,24 @@ struct CameraMatrix {
 		PLANE_BOTTOM
 	};
 
-	real_t matrix[4][4];
+	Vector4 matrix[4];
+
+	_FORCE_INLINE_ const Vector4 &operator[](const int p_axis) const {
+		DEV_ASSERT((unsigned int)p_axis < 4);
+		return matrix[p_axis];
+	}
+
+	_FORCE_INLINE_ Vector4 &operator[](const int p_axis) {
+		DEV_ASSERT((unsigned int)p_axis < 4);
+		return matrix[p_axis];
+	}
 
 	float determinant() const;
 	void set_identity();
 	void set_zero();
 	void set_light_bias();
 	void set_depth_correction(bool p_flip_y = true);
+
 	void set_light_atlas_rect(const Rect2 &p_rect);
 	void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false);
 	void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist);
@@ -68,6 +80,21 @@ struct CameraMatrix {
 	void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false);
 	void adjust_perspective_znear(real_t p_new_znear);
 
+	static Projection create_depth_correction(bool p_flip_y);
+	static Projection create_light_atlas_rect(const Rect2 &p_rect);
+	static Projection create_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false);
+	static Projection create_perspective_hmd(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist);
+	static Projection create_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far);
+	static Projection create_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar);
+	static Projection create_orthogonal_aspect(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false);
+	static Projection create_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far);
+	static Projection create_frustum_aspect(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false);
+	static Projection create_fit_aabb(const AABB &p_aabb);
+	Projection perspective_znear_adjusted(real_t p_new_znear) const;
+	Plane get_projection_plane(Planes p_plane) const;
+	Projection flipped_y() const;
+	Projection jitter_offseted(const Vector2 &p_offset) const;
+
 	static real_t get_fovy(real_t p_fovx, real_t p_aspect) {
 		return Math::rad2deg(Math::atan(p_aspect * Math::tan(Math::deg2rad(p_fovx) * 0.5)) * 2.0);
 	}
@@ -85,13 +112,16 @@ struct CameraMatrix {
 	Vector2 get_far_plane_half_extents() const;
 
 	void invert();
-	CameraMatrix inverse() const;
+	Projection inverse() const;
 
-	CameraMatrix operator*(const CameraMatrix &p_matrix) const;
+	Projection operator*(const Projection &p_matrix) const;
 
 	Plane xform4(const Plane &p_vec4) const;
 	_FORCE_INLINE_ Vector3 xform(const Vector3 &p_vec3) const;
 
+	Vector4 xform(const Vector4 &p_vec4) const;
+	Vector4 xform_inv(const Vector4 &p_vec4) const;
+
 	operator String() const;
 
 	void scale_translate_to_fit(const AABB &p_aabb);
@@ -102,7 +132,7 @@ struct CameraMatrix {
 
 	void flip_y();
 
-	bool operator==(const CameraMatrix &p_cam) const {
+	bool operator==(const Projection &p_cam) const {
 		for (uint32_t i = 0; i < 4; i++) {
 			for (uint32_t j = 0; j < 4; j++) {
 				if (matrix[i][j] != p_cam.matrix[i][j]) {
@@ -113,18 +143,19 @@ struct CameraMatrix {
 		return true;
 	}
 
-	bool operator!=(const CameraMatrix &p_cam) const {
+	bool operator!=(const Projection &p_cam) const {
 		return !(*this == p_cam);
 	}
 
 	float get_lod_multiplier() const;
 
-	CameraMatrix();
-	CameraMatrix(const Transform3D &p_transform);
-	~CameraMatrix();
+	Projection();
+	Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_z, const Vector4 &p_w);
+	Projection(const Transform3D &p_transform);
+	~Projection();
 };
 
-Vector3 CameraMatrix::xform(const Vector3 &p_vec3) const {
+Vector3 Projection::xform(const Vector3 &p_vec3) const {
 	Vector3 ret;
 	ret.x = matrix[0][0] * p_vec3.x + matrix[1][0] * p_vec3.y + matrix[2][0] * p_vec3.z + matrix[3][0];
 	ret.y = matrix[0][1] * p_vec3.x + matrix[1][1] * p_vec3.y + matrix[2][1] * p_vec3.z + matrix[3][1];

+ 102 - 0
core/math/vector4.cpp

@@ -0,0 +1,102 @@
+/*************************************************************************/
+/*  vector4.cpp                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "vector4.h"
+
+#include "core/math/basis.h"
+#include "core/string/print_string.h"
+
+bool Vector4::is_equal_approx(const Vector4 &p_vec4) const {
+	return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w);
+}
+
+real_t Vector4::length() const {
+	return Math::sqrt(length_squared());
+}
+
+void Vector4::normalize() {
+	*this /= length();
+}
+
+Vector4 Vector4::normalized() const {
+	return *this / length();
+}
+
+bool Vector4::is_normalized() const {
+	return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); //use less epsilon
+}
+
+Vector4 Vector4::abs() const {
+	return Vector4(Math::abs(x), Math::abs(y), Math::abs(z), Math::abs(w));
+}
+
+Vector4 Vector4::sign() const {
+	return Vector4(SIGN(x), SIGN(y), SIGN(z), SIGN(w));
+}
+
+Vector4 Vector4::inverse() const {
+	return Vector4(1.0f / x, 1.0f / y, 1.0f / z, 1.0f / w);
+}
+
+Vector4::Axis Vector4::min_axis_index() const {
+	uint32_t min_index = 0;
+	real_t min_value = x;
+	for (uint32_t i = 1; i < 4; i++) {
+		if (operator[](i) < min_value) {
+			min_index = i;
+			min_value = operator[](i);
+		}
+	}
+	return Vector4::Axis(min_index);
+}
+
+Vector4::Axis Vector4::max_axis_index() const {
+	uint32_t max_index = 0;
+	real_t max_value = x;
+	for (uint32_t i = 1; i < 4; i++) {
+		if (operator[](i) > max_value) {
+			max_index = i;
+			max_value = operator[](i);
+		}
+	}
+	return Vector4::Axis(max_index);
+}
+
+Vector4 Vector4::clamp(const Vector4 &p_min, const Vector4 &p_max) const {
+	return Vector4(
+			CLAMP(x, p_min.x, p_max.x),
+			CLAMP(y, p_min.y, p_max.y),
+			CLAMP(z, p_min.z, p_max.z),
+			CLAMP(w, p_min.w, p_max.w));
+}
+
+Vector4::operator String() const {
+	return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ", " + String::num_real(w, false) + ")";
+}

+ 275 - 0
core/math/vector4.h

@@ -0,0 +1,275 @@
+/*************************************************************************/
+/*  vector4.h                                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef VECTOR4_H
+#define VECTOR4_H
+
+#include "core/math/math_defs.h"
+#include "core/math/math_funcs.h"
+#include "core/math/vector3.h"
+#include "core/string/ustring.h"
+
+struct _NO_DISCARD_ Vector4 {
+	enum Axis {
+		AXIS_X,
+		AXIS_Y,
+		AXIS_Z,
+		AXIS_W,
+	};
+
+	union {
+		struct {
+			real_t x;
+			real_t y;
+			real_t z;
+			real_t w;
+		};
+		real_t components[4] = { 0, 0, 0, 0 };
+	};
+
+	_FORCE_INLINE_ real_t &operator[](int idx) {
+		return components[idx];
+	}
+	_FORCE_INLINE_ const real_t &operator[](int idx) const {
+		return components[idx];
+	}
+	_FORCE_INLINE_ real_t length_squared() const;
+	bool is_equal_approx(const Vector4 &p_vec4) const;
+	real_t length() const;
+	void normalize();
+	Vector4 normalized() const;
+	bool is_normalized() const;
+	Vector4 abs() const;
+	Vector4 sign() const;
+
+	Vector4::Axis min_axis_index() const;
+	Vector4::Axis max_axis_index() const;
+	Vector4 clamp(const Vector4 &p_min, const Vector4 &p_max) const;
+
+	Vector4 inverse() const;
+	_FORCE_INLINE_ real_t dot(const Vector4 &p_vec4) const;
+
+	_FORCE_INLINE_ void operator+=(const Vector4 &p_vec4);
+	_FORCE_INLINE_ void operator-=(const Vector4 &p_vec4);
+	_FORCE_INLINE_ void operator*=(const Vector4 &p_vec4);
+	_FORCE_INLINE_ void operator/=(const Vector4 &p_vec4);
+	_FORCE_INLINE_ void operator*=(const real_t &s);
+	_FORCE_INLINE_ void operator/=(const real_t &s);
+	_FORCE_INLINE_ Vector4 operator+(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ Vector4 operator-(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ Vector4 operator*(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ Vector4 operator/(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ Vector4 operator-() const;
+	_FORCE_INLINE_ Vector4 operator*(const real_t &s) const;
+	_FORCE_INLINE_ Vector4 operator/(const real_t &s) const;
+
+	_FORCE_INLINE_ bool operator==(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ bool operator!=(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ bool operator>(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ bool operator<(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ bool operator>=(const Vector4 &p_vec4) const;
+	_FORCE_INLINE_ bool operator<=(const Vector4 &p_vec4) const;
+
+	operator String() 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;
+	}
+};
+
+real_t Vector4::dot(const Vector4 &p_vec4) const {
+	return x * p_vec4.x + y * p_vec4.y + z * p_vec4.z + w * p_vec4.w;
+}
+
+real_t Vector4::length_squared() const {
+	return dot(*this);
+}
+
+void Vector4::operator+=(const Vector4 &p_vec4) {
+	x += p_vec4.x;
+	y += p_vec4.y;
+	z += p_vec4.z;
+	w += p_vec4.w;
+}
+
+void Vector4::operator-=(const Vector4 &p_vec4) {
+	x -= p_vec4.x;
+	y -= p_vec4.y;
+	z -= p_vec4.z;
+	w -= p_vec4.w;
+}
+
+void Vector4::operator*=(const Vector4 &p_vec4) {
+	x *= p_vec4.x;
+	y *= p_vec4.y;
+	z *= p_vec4.z;
+	w *= p_vec4.w;
+}
+
+void Vector4::operator/=(const Vector4 &p_vec4) {
+	x /= p_vec4.x;
+	y /= p_vec4.y;
+	z /= p_vec4.z;
+	w /= p_vec4.w;
+}
+void Vector4::operator*=(const real_t &s) {
+	x *= s;
+	y *= s;
+	z *= s;
+	w *= s;
+}
+
+void Vector4::operator/=(const real_t &s) {
+	*this *= 1.0f / s;
+}
+
+Vector4 Vector4::operator+(const Vector4 &p_vec4) const {
+	return Vector4(x + p_vec4.x, y + p_vec4.y, z + p_vec4.z, w + p_vec4.w);
+}
+
+Vector4 Vector4::operator-(const Vector4 &p_vec4) const {
+	return Vector4(x - p_vec4.x, y - p_vec4.y, z - p_vec4.z, w - p_vec4.w);
+}
+
+Vector4 Vector4::operator*(const Vector4 &p_vec4) const {
+	return Vector4(x * p_vec4.x, y * p_vec4.y, z * p_vec4.z, w * p_vec4.w);
+}
+
+Vector4 Vector4::operator/(const Vector4 &p_vec4) const {
+	return Vector4(x / p_vec4.x, y / p_vec4.y, z / p_vec4.z, w / p_vec4.w);
+}
+
+Vector4 Vector4::operator-() const {
+	return Vector4(x, y, z, w);
+}
+
+Vector4 Vector4::operator*(const real_t &s) const {
+	return Vector4(x * s, y * s, z * s, w * s);
+}
+
+Vector4 Vector4::operator/(const real_t &s) const {
+	return *this * (1.0f / s);
+}
+
+bool Vector4::operator==(const Vector4 &p_vec4) const {
+	return x == p_vec4.x && y == p_vec4.y && z == p_vec4.z && w == p_vec4.w;
+}
+
+bool Vector4::operator!=(const Vector4 &p_vec4) const {
+	return x != p_vec4.x || y != p_vec4.y || z != p_vec4.z || w != p_vec4.w;
+}
+
+bool Vector4::operator<(const Vector4 &p_v) const {
+	if (x == p_v.x) {
+		if (y == p_v.y) {
+			if (z == p_v.z) {
+				return w < p_v.w;
+			}
+			return z < p_v.z;
+		}
+		return y < p_v.y;
+	}
+	return x < p_v.x;
+}
+
+bool Vector4::operator>(const Vector4 &p_v) const {
+	if (x == p_v.x) {
+		if (y == p_v.y) {
+			if (z == p_v.z) {
+				return w > p_v.w;
+			}
+			return z > p_v.z;
+		}
+		return y > p_v.y;
+	}
+	return x > p_v.x;
+}
+
+bool Vector4::operator<=(const Vector4 &p_v) const {
+	if (x == p_v.x) {
+		if (y == p_v.y) {
+			if (z == p_v.z) {
+				return w <= p_v.w;
+			}
+			return z < p_v.z;
+		}
+		return y < p_v.y;
+	}
+	return x < p_v.x;
+}
+
+bool Vector4::operator>=(const Vector4 &p_v) const {
+	if (x == p_v.x) {
+		if (y == p_v.y) {
+			if (z == p_v.z) {
+				return w >= p_v.w;
+			}
+			return z > p_v.z;
+		}
+		return y > p_v.y;
+	}
+	return x > p_v.x;
+}
+
+_FORCE_INLINE_ Vector4 operator*(const float p_scalar, const Vector4 &p_vec) {
+	return p_vec * p_scalar;
+}
+
+_FORCE_INLINE_ Vector4 operator*(const double p_scalar, const Vector4 &p_vec) {
+	return p_vec * p_scalar;
+}
+
+_FORCE_INLINE_ Vector4 operator*(const int32_t p_scalar, const Vector4 &p_vec) {
+	return p_vec * p_scalar;
+}
+
+_FORCE_INLINE_ Vector4 operator*(const int64_t p_scalar, const Vector4 &p_vec) {
+	return p_vec * p_scalar;
+}
+#endif // VECTOR4_H

+ 91 - 0
core/math/vector4i.cpp

@@ -0,0 +1,91 @@
+/*************************************************************************/
+/*  vector4i.cpp                                                         */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "vector4i.h"
+
+#include "core/math/vector4.h"
+#include "core/string/ustring.h"
+
+void Vector4i::set_axis(const int p_axis, const int32_t p_value) {
+	ERR_FAIL_INDEX(p_axis, 4);
+	coord[p_axis] = p_value;
+}
+
+int32_t Vector4i::get_axis(const int p_axis) const {
+	ERR_FAIL_INDEX_V(p_axis, 4, 0);
+	return operator[](p_axis);
+}
+
+Vector4i::Axis Vector4i::min_axis_index() const {
+	uint32_t min_index = 0;
+	int32_t min_value = x;
+	for (uint32_t i = 1; i < 4; i++) {
+		if (operator[](i) < min_value) {
+			min_index = i;
+			min_value = operator[](i);
+		}
+	}
+	return Vector4i::Axis(min_index);
+}
+
+Vector4i::Axis Vector4i::max_axis_index() const {
+	uint32_t max_index = 0;
+	int32_t max_value = x;
+	for (uint32_t i = 1; i < 4; i++) {
+		if (operator[](i) > max_value) {
+			max_index = i;
+			max_value = operator[](i);
+		}
+	}
+	return Vector4i::Axis(max_index);
+}
+
+Vector4i Vector4i::clamp(const Vector4i &p_min, const Vector4i &p_max) const {
+	return Vector4i(
+			CLAMP(x, p_min.x, p_max.x),
+			CLAMP(y, p_min.y, p_max.y),
+			CLAMP(z, p_min.z, p_max.z),
+			CLAMP(w, p_min.w, p_max.w));
+}
+
+Vector4i::operator String() const {
+	return "(" + itos(x) + ", " + itos(y) + ", " + itos(z) + ", " + itos(w) + ")";
+}
+
+Vector4i::operator Vector4() const {
+	return Vector4(x, y, z, w);
+}
+
+Vector4i::Vector4i(const Vector4 &p_vec4) {
+	x = p_vec4.x;
+	y = p_vec4.y;
+	z = p_vec4.z;
+	w = p_vec4.w;
+}

+ 338 - 0
core/math/vector4i.h

@@ -0,0 +1,338 @@
+/*************************************************************************/
+/*  vector4i.h                                                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef VECTOR4I_H
+#define VECTOR4I_H
+
+#include "core/error/error_macros.h"
+#include "core/math/math_funcs.h"
+
+class String;
+struct Vector4;
+
+struct _NO_DISCARD_ Vector4i {
+	enum Axis {
+		AXIS_X,
+		AXIS_Y,
+		AXIS_Z,
+		AXIS_W,
+	};
+
+	union {
+		struct {
+			int32_t x;
+			int32_t y;
+			int32_t z;
+			int32_t w;
+		};
+
+		int32_t coord[4] = { 0 };
+	};
+
+	_FORCE_INLINE_ const int32_t &operator[](const int p_axis) const {
+		DEV_ASSERT((unsigned int)p_axis < 4);
+		return coord[p_axis];
+	}
+
+	_FORCE_INLINE_ int32_t &operator[](const int p_axis) {
+		DEV_ASSERT((unsigned int)p_axis < 4);
+		return coord[p_axis];
+	}
+
+	void set_axis(const int p_axis, const int32_t p_value);
+	int32_t get_axis(const int p_axis) const;
+
+	Vector4i::Axis min_axis_index() const;
+	Vector4i::Axis max_axis_index() const;
+
+	_FORCE_INLINE_ int64_t length_squared() const;
+	_FORCE_INLINE_ double length() const;
+
+	_FORCE_INLINE_ void zero();
+
+	_FORCE_INLINE_ Vector4i abs() const;
+	_FORCE_INLINE_ Vector4i sign() const;
+	Vector4i clamp(const Vector4i &p_min, const Vector4i &p_max) const;
+
+	/* Operators */
+
+	_FORCE_INLINE_ Vector4i &operator+=(const Vector4i &p_v);
+	_FORCE_INLINE_ Vector4i operator+(const Vector4i &p_v) const;
+	_FORCE_INLINE_ Vector4i &operator-=(const Vector4i &p_v);
+	_FORCE_INLINE_ Vector4i operator-(const Vector4i &p_v) const;
+	_FORCE_INLINE_ Vector4i &operator*=(const Vector4i &p_v);
+	_FORCE_INLINE_ Vector4i operator*(const Vector4i &p_v) const;
+	_FORCE_INLINE_ Vector4i &operator/=(const Vector4i &p_v);
+	_FORCE_INLINE_ Vector4i operator/(const Vector4i &p_v) const;
+	_FORCE_INLINE_ Vector4i &operator%=(const Vector4i &p_v);
+	_FORCE_INLINE_ Vector4i operator%(const Vector4i &p_v) const;
+
+	_FORCE_INLINE_ Vector4i &operator*=(const int32_t p_scalar);
+	_FORCE_INLINE_ Vector4i operator*(const int32_t p_scalar) const;
+	_FORCE_INLINE_ Vector4i &operator/=(const int32_t p_scalar);
+	_FORCE_INLINE_ Vector4i operator/(const int32_t p_scalar) const;
+	_FORCE_INLINE_ Vector4i &operator%=(const int32_t p_scalar);
+	_FORCE_INLINE_ Vector4i operator%(const int32_t p_scalar) const;
+
+	_FORCE_INLINE_ Vector4i operator-() const;
+
+	_FORCE_INLINE_ bool operator==(const Vector4i &p_v) const;
+	_FORCE_INLINE_ bool operator!=(const Vector4i &p_v) const;
+	_FORCE_INLINE_ bool operator<(const Vector4i &p_v) const;
+	_FORCE_INLINE_ bool operator<=(const Vector4i &p_v) const;
+	_FORCE_INLINE_ bool operator>(const Vector4i &p_v) const;
+	_FORCE_INLINE_ bool operator>=(const Vector4i &p_v) const;
+
+	operator String() const;
+	operator Vector4() const;
+
+	_FORCE_INLINE_ Vector4i() {}
+	Vector4i(const Vector4 &p_vec4);
+	_FORCE_INLINE_ Vector4i(const int32_t p_x, const int32_t p_y, const int32_t p_z, const int32_t p_w) {
+		x = p_x;
+		y = p_y;
+		z = p_z;
+		w = p_w;
+	}
+};
+
+int64_t Vector4i::length_squared() const {
+	return x * (int64_t)x + y * (int64_t)y + z * (int64_t)z + w * (int64_t)w;
+}
+
+double Vector4i::length() const {
+	return Math::sqrt((double)length_squared());
+}
+
+Vector4i Vector4i::abs() const {
+	return Vector4i(ABS(x), ABS(y), ABS(z), ABS(w));
+}
+
+Vector4i Vector4i::sign() const {
+	return Vector4i(SIGN(x), SIGN(y), SIGN(z), SIGN(w));
+}
+
+/* Operators */
+
+Vector4i &Vector4i::operator+=(const Vector4i &p_v) {
+	x += p_v.x;
+	y += p_v.y;
+	z += p_v.z;
+	w += p_v.w;
+	return *this;
+}
+
+Vector4i Vector4i::operator+(const Vector4i &p_v) const {
+	return Vector4i(x + p_v.x, y + p_v.y, z + p_v.z, w + p_v.w);
+}
+
+Vector4i &Vector4i::operator-=(const Vector4i &p_v) {
+	x -= p_v.x;
+	y -= p_v.y;
+	z -= p_v.z;
+	w -= p_v.w;
+	return *this;
+}
+
+Vector4i Vector4i::operator-(const Vector4i &p_v) const {
+	return Vector4i(x - p_v.x, y - p_v.y, z - p_v.z, w - p_v.w);
+}
+
+Vector4i &Vector4i::operator*=(const Vector4i &p_v) {
+	x *= p_v.x;
+	y *= p_v.y;
+	z *= p_v.z;
+	w *= p_v.w;
+	return *this;
+}
+
+Vector4i Vector4i::operator*(const Vector4i &p_v) const {
+	return Vector4i(x * p_v.x, y * p_v.y, z * p_v.z, w * p_v.w);
+}
+
+Vector4i &Vector4i::operator/=(const Vector4i &p_v) {
+	x /= p_v.x;
+	y /= p_v.y;
+	z /= p_v.z;
+	w /= p_v.w;
+	return *this;
+}
+
+Vector4i Vector4i::operator/(const Vector4i &p_v) const {
+	return Vector4i(x / p_v.x, y / p_v.y, z / p_v.z, w / p_v.w);
+}
+
+Vector4i &Vector4i::operator%=(const Vector4i &p_v) {
+	x %= p_v.x;
+	y %= p_v.y;
+	z %= p_v.z;
+	w %= p_v.w;
+	return *this;
+}
+
+Vector4i Vector4i::operator%(const Vector4i &p_v) const {
+	return Vector4i(x % p_v.x, y % p_v.y, z % p_v.z, w % p_v.w);
+}
+
+Vector4i &Vector4i::operator*=(const int32_t p_scalar) {
+	x *= p_scalar;
+	y *= p_scalar;
+	z *= p_scalar;
+	w *= p_scalar;
+	return *this;
+}
+
+Vector4i Vector4i::operator*(const int32_t p_scalar) const {
+	return Vector4i(x * p_scalar, y * p_scalar, z * p_scalar, w * p_scalar);
+}
+
+// Multiplication operators required to workaround issues with LLVM using implicit conversion.
+
+_FORCE_INLINE_ Vector4i operator*(const int32_t p_scalar, const Vector4i &p_vector) {
+	return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector4i operator*(const int64_t p_scalar, const Vector4i &p_vector) {
+	return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector4i operator*(const float p_scalar, const Vector4i &p_vector) {
+	return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector4i operator*(const double p_scalar, const Vector4i &p_vector) {
+	return p_vector * p_scalar;
+}
+
+Vector4i &Vector4i::operator/=(const int32_t p_scalar) {
+	x /= p_scalar;
+	y /= p_scalar;
+	z /= p_scalar;
+	w /= p_scalar;
+	return *this;
+}
+
+Vector4i Vector4i::operator/(const int32_t p_scalar) const {
+	return Vector4i(x / p_scalar, y / p_scalar, z / p_scalar, w / p_scalar);
+}
+
+Vector4i &Vector4i::operator%=(const int32_t p_scalar) {
+	x %= p_scalar;
+	y %= p_scalar;
+	z %= p_scalar;
+	w %= p_scalar;
+	return *this;
+}
+
+Vector4i Vector4i::operator%(const int32_t p_scalar) const {
+	return Vector4i(x % p_scalar, y % p_scalar, z % p_scalar, w % p_scalar);
+}
+
+Vector4i Vector4i::operator-() const {
+	return Vector4i(-x, -y, -z, -w);
+}
+
+bool Vector4i::operator==(const Vector4i &p_v) const {
+	return (x == p_v.x && y == p_v.y && z == p_v.z && w == p_v.w);
+}
+
+bool Vector4i::operator!=(const Vector4i &p_v) const {
+	return (x != p_v.x || y != p_v.y || z != p_v.z || w != p_v.w);
+}
+
+bool Vector4i::operator<(const Vector4i &p_v) const {
+	if (x == p_v.x) {
+		if (y == p_v.y) {
+			if (z == p_v.z) {
+				return w < p_v.w;
+			} else {
+				return z < p_v.z;
+			}
+		} else {
+			return y < p_v.y;
+		}
+	} else {
+		return x < p_v.x;
+	}
+}
+
+bool Vector4i::operator>(const Vector4i &p_v) const {
+	if (x == p_v.x) {
+		if (y == p_v.y) {
+			if (z == p_v.z) {
+				return w > p_v.w;
+			} else {
+				return z > p_v.z;
+			}
+		} else {
+			return y > p_v.y;
+		}
+	} else {
+		return x > p_v.x;
+	}
+}
+
+bool Vector4i::operator<=(const Vector4i &p_v) const {
+	if (x == p_v.x) {
+		if (y == p_v.y) {
+			if (z == p_v.z) {
+				return w <= p_v.w;
+			} else {
+				return z < p_v.z;
+			}
+		} else {
+			return y < p_v.y;
+		}
+	} else {
+		return x < p_v.x;
+	}
+}
+
+bool Vector4i::operator>=(const Vector4i &p_v) const {
+	if (x == p_v.x) {
+		if (y == p_v.y) {
+			if (z == p_v.z) {
+				return w >= p_v.w;
+			} else {
+				return z > p_v.z;
+			}
+		} else {
+			return y > p_v.y;
+		}
+	} else {
+		return x > p_v.x;
+	}
+}
+
+void Vector4i::zero() {
+	x = y = z = w = 0;
+}
+
+#endif // VECTOR4I_H

+ 3 - 0
core/object/script_language.cpp

@@ -344,11 +344,14 @@ void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const
 	p_core_type_words->push_back("Vector3");
 	p_core_type_words->push_back("Vector3i");
 	p_core_type_words->push_back("Transform2D");
+	p_core_type_words->push_back("Vector4");
+	p_core_type_words->push_back("Vector4i");
 	p_core_type_words->push_back("Plane");
 	p_core_type_words->push_back("Quaternion");
 	p_core_type_words->push_back("AABB");
 	p_core_type_words->push_back("Basis");
 	p_core_type_words->push_back("Transform3D");
+	p_core_type_words->push_back("Projection");
 	p_core_type_words->push_back("Color");
 	p_core_type_words->push_back("StringName");
 	p_core_type_words->push_back("NodePath");

+ 16 - 0
core/templates/hashfuncs.h

@@ -40,6 +40,8 @@
 #include "core/math/vector2i.h"
 #include "core/math/vector3.h"
 #include "core/math/vector3i.h"
+#include "core/math/vector4.h"
+#include "core/math/vector4i.h"
 #include "core/object/object_id.h"
 #include "core/string/node_path.h"
 #include "core/string/string_name.h"
@@ -332,6 +334,13 @@ struct HashMapHasherDefault {
 		h = hash_murmur3_one_32(p_vec.z, h);
 		return hash_fmix32(h);
 	}
+	static _FORCE_INLINE_ uint32_t hash(const Vector4i &p_vec) {
+		uint32_t h = hash_murmur3_one_32(p_vec.x);
+		h = hash_murmur3_one_32(p_vec.y, h);
+		h = hash_murmur3_one_32(p_vec.z, h);
+		h = hash_murmur3_one_32(p_vec.w, h);
+		return hash_fmix32(h);
+	}
 	static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
 		uint32_t h = hash_murmur3_one_real(p_vec.x);
 		h = hash_murmur3_one_real(p_vec.y, h);
@@ -343,6 +352,13 @@ struct HashMapHasherDefault {
 		h = hash_murmur3_one_real(p_vec.z, h);
 		return hash_fmix32(h);
 	}
+	static _FORCE_INLINE_ uint32_t hash(const Vector4 &p_vec) {
+		uint32_t h = hash_murmur3_one_real(p_vec.x);
+		h = hash_murmur3_one_real(p_vec.y, h);
+		h = hash_murmur3_one_real(p_vec.z, h);
+		h = hash_murmur3_one_real(p_vec.w, h);
+		return hash_fmix32(h);
+	}
 	static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
 		uint32_t h = hash_murmur3_one_32(p_rect.position.x);
 		h = hash_murmur3_one_32(p_rect.position.y, h);

+ 3 - 0
core/variant/binder_common.h

@@ -136,7 +136,10 @@ VARIANT_ENUM_CAST(Vector2::Axis);
 VARIANT_ENUM_CAST(Vector2i::Axis);
 VARIANT_ENUM_CAST(Vector3::Axis);
 VARIANT_ENUM_CAST(Vector3i::Axis);
+VARIANT_ENUM_CAST(Vector4::Axis);
+VARIANT_ENUM_CAST(Vector4i::Axis);
 VARIANT_ENUM_CAST(Basis::EulerOrder);
+VARIANT_ENUM_CAST(Projection::Planes);
 
 VARIANT_ENUM_CAST(Error);
 VARIANT_ENUM_CAST(Side);

+ 3 - 0
core/variant/method_ptrcall.h

@@ -125,7 +125,10 @@ MAKE_PTRARG(Rect2);
 MAKE_PTRARG(Rect2i);
 MAKE_PTRARG_BY_REFERENCE(Vector3);
 MAKE_PTRARG_BY_REFERENCE(Vector3i);
+MAKE_PTRARG_BY_REFERENCE(Vector4);
+MAKE_PTRARG_BY_REFERENCE(Vector4i);
 MAKE_PTRARG(Transform2D);
+MAKE_PTRARG(Projection);
 MAKE_PTRARG_BY_REFERENCE(Plane);
 MAKE_PTRARG(Quaternion);
 MAKE_PTRARG_BY_REFERENCE(AABB);

+ 3 - 0
core/variant/type_info.h

@@ -144,12 +144,15 @@ MAKE_TYPE_INFO(Vector3, Variant::VECTOR3)
 MAKE_TYPE_INFO(Vector2i, Variant::VECTOR2I)
 MAKE_TYPE_INFO(Rect2i, Variant::RECT2I)
 MAKE_TYPE_INFO(Vector3i, Variant::VECTOR3I)
+MAKE_TYPE_INFO(Vector4, Variant::VECTOR4)
+MAKE_TYPE_INFO(Vector4i, Variant::VECTOR4I)
 MAKE_TYPE_INFO(Transform2D, Variant::TRANSFORM2D)
 MAKE_TYPE_INFO(Plane, Variant::PLANE)
 MAKE_TYPE_INFO(Quaternion, Variant::QUATERNION)
 MAKE_TYPE_INFO(AABB, Variant::AABB)
 MAKE_TYPE_INFO(Basis, Variant::BASIS)
 MAKE_TYPE_INFO(Transform3D, Variant::TRANSFORM3D)
+MAKE_TYPE_INFO(Projection, Variant::PROJECTION)
 MAKE_TYPE_INFO(Color, Variant::COLOR)
 MAKE_TYPE_INFO(StringName, Variant::STRING_NAME)
 MAKE_TYPE_INFO(NodePath, Variant::NODE_PATH)

+ 269 - 1
core/variant/variant.cpp

@@ -83,6 +83,12 @@ String Variant::get_type_name(Variant::Type p_type) {
 		case VECTOR3I: {
 			return "Vector3i";
 		} break;
+		case VECTOR4: {
+			return "Vector4";
+		} break;
+		case VECTOR4I: {
+			return "Vector4i";
+		} break;
 		case PLANE: {
 			return "Plane";
 
@@ -101,6 +107,10 @@ String Variant::get_type_name(Variant::Type p_type) {
 		case TRANSFORM3D: {
 			return "Transform3D";
 
+		} break;
+		case PROJECTION: {
+			return "Projection";
+
 		} break;
 
 		// misc types
@@ -297,6 +307,24 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
 
 			valid_types = valid;
 
+		} break;
+		case VECTOR4: {
+			static const Type valid[] = {
+				VECTOR4I,
+				NIL,
+			};
+
+			valid_types = valid;
+
+		} break;
+		case VECTOR4I: {
+			static const Type valid[] = {
+				VECTOR4,
+				NIL,
+			};
+
+			valid_types = valid;
+
 		} break;
 
 		case QUATERNION: {
@@ -322,6 +350,16 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
 				TRANSFORM2D,
 				QUATERNION,
 				BASIS,
+				PROJECTION,
+				NIL
+			};
+
+			valid_types = valid;
+
+		} break;
+		case PROJECTION: {
+			static const Type valid[] = {
+				TRANSFORM3D,
 				NIL
 			};
 
@@ -603,6 +641,24 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
 
 			valid_types = valid;
 
+		} break;
+		case VECTOR4: {
+			static const Type valid[] = {
+				VECTOR4I,
+				NIL,
+			};
+
+			valid_types = valid;
+
+		} break;
+		case VECTOR4I: {
+			static const Type valid[] = {
+				VECTOR4,
+				NIL,
+			};
+
+			valid_types = valid;
+
 		} break;
 
 		case QUATERNION: {
@@ -628,6 +684,16 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
 				TRANSFORM2D,
 				QUATERNION,
 				BASIS,
+				PROJECTION,
+				NIL
+			};
+
+			valid_types = valid;
+
+		} break;
+		case PROJECTION: {
+			static const Type valid[] = {
+				TRANSFORM3D,
 				NIL
 			};
 
@@ -857,6 +923,14 @@ bool Variant::is_zero() const {
 		case VECTOR3I: {
 			return *reinterpret_cast<const Vector3i *>(_data._mem) == Vector3i();
 
+		} break;
+		case VECTOR4: {
+			return *reinterpret_cast<const Vector4 *>(_data._mem) == Vector4();
+
+		} break;
+		case VECTOR4I: {
+			return *reinterpret_cast<const Vector4i *>(_data._mem) == Vector4i();
+
 		} break;
 		case PLANE: {
 			return *reinterpret_cast<const Plane *>(_data._mem) == Plane();
@@ -876,6 +950,10 @@ bool Variant::is_zero() const {
 		case TRANSFORM3D: {
 			return *_data._transform3d == Transform3D();
 
+		} break;
+		case PROJECTION: {
+			return *_data._projection == Projection();
+
 		} break;
 
 		// misc types
@@ -997,6 +1075,14 @@ bool Variant::is_one() const {
 		case VECTOR3I: {
 			return *reinterpret_cast<const Vector3i *>(_data._mem) == Vector3i(1, 1, 1);
 
+		} break;
+		case VECTOR4: {
+			return *reinterpret_cast<const Vector4 *>(_data._mem) == Vector4(1, 1, 1, 1);
+
+		} break;
+		case VECTOR4I: {
+			return *reinterpret_cast<const Vector4i *>(_data._mem) == Vector4i(1, 1, 1, 1);
+
 		} break;
 		case PLANE: {
 			return *reinterpret_cast<const Plane *>(_data._mem) == Plane(1, 1, 1, 1);
@@ -1084,6 +1170,12 @@ void Variant::reference(const Variant &p_variant) {
 		case VECTOR3I: {
 			memnew_placement(_data._mem, Vector3i(*reinterpret_cast<const Vector3i *>(p_variant._data._mem)));
 		} break;
+		case VECTOR4: {
+			memnew_placement(_data._mem, Vector4(*reinterpret_cast<const Vector4 *>(p_variant._data._mem)));
+		} break;
+		case VECTOR4I: {
+			memnew_placement(_data._mem, Vector4i(*reinterpret_cast<const Vector4i *>(p_variant._data._mem)));
+		} break;
 		case PLANE: {
 			memnew_placement(_data._mem, Plane(*reinterpret_cast<const Plane *>(p_variant._data._mem)));
 		} break;
@@ -1102,6 +1194,9 @@ void Variant::reference(const Variant &p_variant) {
 		case TRANSFORM3D: {
 			_data._transform3d = memnew(Transform3D(*p_variant._data._transform3d));
 		} break;
+		case PROJECTION: {
+			_data._projection = memnew(Projection(*p_variant._data._projection));
+		} break;
 
 		// misc types
 		case COLOR: {
@@ -1250,6 +1345,12 @@ void Variant::zero() {
 		case VECTOR3I:
 			*reinterpret_cast<Vector3i *>(this->_data._mem) = Vector3i();
 			break;
+		case VECTOR4:
+			*reinterpret_cast<Vector4 *>(this->_data._mem) = Vector4();
+			break;
+		case VECTOR4I:
+			*reinterpret_cast<Vector4i *>(this->_data._mem) = Vector4i();
+			break;
 		case PLANE:
 			*reinterpret_cast<Plane *>(this->_data._mem) = Plane();
 			break;
@@ -1291,7 +1392,9 @@ void Variant::_clear_internal() {
 		case TRANSFORM3D: {
 			memdelete(_data._transform3d);
 		} break;
-
+		case PROJECTION: {
+			memdelete(_data._projection);
+		} break;
 			// misc types
 		case STRING_NAME: {
 			reinterpret_cast<StringName *>(_data._mem)->~StringName();
@@ -1681,6 +1784,10 @@ String Variant::stringify(int recursion_count) const {
 			return operator Vector3();
 		case VECTOR3I:
 			return operator Vector3i();
+		case VECTOR4:
+			return operator Vector4();
+		case VECTOR4I:
+			return operator Vector4i();
 		case PLANE:
 			return operator Plane();
 		case AABB:
@@ -1691,6 +1798,8 @@ String Variant::stringify(int recursion_count) const {
 			return operator Basis();
 		case TRANSFORM3D:
 			return operator Transform3D();
+		case PROJECTION:
+			return operator Projection();
 		case STRING_NAME:
 			return operator StringName();
 		case NODE_PATH:
@@ -1812,6 +1921,10 @@ Variant::operator Vector2() const {
 		return Vector2(reinterpret_cast<const Vector3 *>(_data._mem)->x, reinterpret_cast<const Vector3 *>(_data._mem)->y);
 	} else if (type == VECTOR3I) {
 		return Vector2(reinterpret_cast<const Vector3i *>(_data._mem)->x, reinterpret_cast<const Vector3i *>(_data._mem)->y);
+	} else if (type == VECTOR4) {
+		return Vector2(reinterpret_cast<const Vector4 *>(_data._mem)->x, reinterpret_cast<const Vector4 *>(_data._mem)->y);
+	} else if (type == VECTOR4I) {
+		return Vector2(reinterpret_cast<const Vector4i *>(_data._mem)->x, reinterpret_cast<const Vector4i *>(_data._mem)->y);
 	} else {
 		return Vector2();
 	}
@@ -1826,6 +1939,10 @@ Variant::operator Vector2i() const {
 		return Vector2(reinterpret_cast<const Vector3 *>(_data._mem)->x, reinterpret_cast<const Vector3 *>(_data._mem)->y);
 	} else if (type == VECTOR3I) {
 		return Vector2(reinterpret_cast<const Vector3i *>(_data._mem)->x, reinterpret_cast<const Vector3i *>(_data._mem)->y);
+	} else if (type == VECTOR4) {
+		return Vector2(reinterpret_cast<const Vector4 *>(_data._mem)->x, reinterpret_cast<const Vector4 *>(_data._mem)->y);
+	} else if (type == VECTOR4I) {
+		return Vector2(reinterpret_cast<const Vector4i *>(_data._mem)->x, reinterpret_cast<const Vector4i *>(_data._mem)->y);
 	} else {
 		return Vector2i();
 	}
@@ -1860,6 +1977,10 @@ Variant::operator Vector3() const {
 		return Vector3(reinterpret_cast<const Vector2 *>(_data._mem)->x, reinterpret_cast<const Vector2 *>(_data._mem)->y, 0.0);
 	} else if (type == VECTOR2I) {
 		return Vector3(reinterpret_cast<const Vector2i *>(_data._mem)->x, reinterpret_cast<const Vector2i *>(_data._mem)->y, 0.0);
+	} else if (type == VECTOR4) {
+		return Vector3(reinterpret_cast<const Vector4 *>(_data._mem)->x, reinterpret_cast<const Vector4 *>(_data._mem)->y, reinterpret_cast<const Vector4 *>(_data._mem)->z);
+	} else if (type == VECTOR4I) {
+		return Vector3(reinterpret_cast<const Vector4i *>(_data._mem)->x, reinterpret_cast<const Vector4i *>(_data._mem)->y, reinterpret_cast<const Vector4i *>(_data._mem)->z);
 	} else {
 		return Vector3();
 	}
@@ -1874,11 +1995,52 @@ Variant::operator Vector3i() const {
 		return Vector3i(reinterpret_cast<const Vector2 *>(_data._mem)->x, reinterpret_cast<const Vector2 *>(_data._mem)->y, 0.0);
 	} else if (type == VECTOR2I) {
 		return Vector3i(reinterpret_cast<const Vector2i *>(_data._mem)->x, reinterpret_cast<const Vector2i *>(_data._mem)->y, 0.0);
+	} else if (type == VECTOR4) {
+		return Vector3i(reinterpret_cast<const Vector4 *>(_data._mem)->x, reinterpret_cast<const Vector4 *>(_data._mem)->y, reinterpret_cast<const Vector4 *>(_data._mem)->z);
+	} else if (type == VECTOR4I) {
+		return Vector3i(reinterpret_cast<const Vector4i *>(_data._mem)->x, reinterpret_cast<const Vector4i *>(_data._mem)->y, reinterpret_cast<const Vector4i *>(_data._mem)->z);
 	} else {
 		return Vector3i();
 	}
 }
 
+Variant::operator Vector4() const {
+	if (type == VECTOR4) {
+		return *reinterpret_cast<const Vector4 *>(_data._mem);
+	} else if (type == VECTOR4I) {
+		return *reinterpret_cast<const Vector4i *>(_data._mem);
+	} else if (type == VECTOR2) {
+		return Vector4(reinterpret_cast<const Vector2 *>(_data._mem)->x, reinterpret_cast<const Vector2 *>(_data._mem)->y, 0.0, 0.0);
+	} else if (type == VECTOR2I) {
+		return Vector4(reinterpret_cast<const Vector2i *>(_data._mem)->x, reinterpret_cast<const Vector2i *>(_data._mem)->y, 0.0, 0.0);
+	} else if (type == VECTOR3) {
+		return Vector4(reinterpret_cast<const Vector3 *>(_data._mem)->x, reinterpret_cast<const Vector3 *>(_data._mem)->y, reinterpret_cast<const Vector3 *>(_data._mem)->z, 0.0);
+	} else if (type == VECTOR3I) {
+		return Vector4(reinterpret_cast<const Vector3i *>(_data._mem)->x, reinterpret_cast<const Vector3i *>(_data._mem)->y, reinterpret_cast<const Vector3i *>(_data._mem)->z, 0.0);
+	} else {
+		return Vector4();
+	}
+}
+
+Variant::operator Vector4i() const {
+	if (type == VECTOR4I) {
+		return *reinterpret_cast<const Vector4i *>(_data._mem);
+	} else if (type == VECTOR4) {
+		const Vector4 &v4 = *reinterpret_cast<const Vector4 *>(_data._mem);
+		return Vector4i(v4.x, v4.y, v4.z, v4.w);
+	} else if (type == VECTOR2) {
+		return Vector4i(reinterpret_cast<const Vector2 *>(_data._mem)->x, reinterpret_cast<const Vector2 *>(_data._mem)->y, 0.0, 0.0);
+	} else if (type == VECTOR2I) {
+		return Vector4i(reinterpret_cast<const Vector2i *>(_data._mem)->x, reinterpret_cast<const Vector2i *>(_data._mem)->y, 0.0, 0.0);
+	} else if (type == VECTOR3) {
+		return Vector4i(reinterpret_cast<const Vector3 *>(_data._mem)->x, reinterpret_cast<const Vector3 *>(_data._mem)->y, reinterpret_cast<const Vector3 *>(_data._mem)->z, 0.0);
+	} else if (type == VECTOR3I) {
+		return Vector4i(reinterpret_cast<const Vector3i *>(_data._mem)->x, reinterpret_cast<const Vector3i *>(_data._mem)->y, reinterpret_cast<const Vector3i *>(_data._mem)->z, 0.0);
+	} else {
+		return Vector4i();
+	}
+}
+
 Variant::operator Plane() const {
 	if (type == PLANE) {
 		return *reinterpret_cast<const Plane *>(_data._mem);
@@ -1936,11 +2098,37 @@ Variant::operator Transform3D() const {
 		m.origin[0] = t.columns[2][0];
 		m.origin[1] = t.columns[2][1];
 		return m;
+	} else if (type == PROJECTION) {
+		return *_data._projection;
 	} else {
 		return Transform3D();
 	}
 }
 
+Variant::operator Projection() const {
+	if (type == TRANSFORM3D) {
+		return *_data._transform3d;
+	} else if (type == BASIS) {
+		return Transform3D(*_data._basis, Vector3());
+	} else if (type == QUATERNION) {
+		return Transform3D(Basis(*reinterpret_cast<const Quaternion *>(_data._mem)), Vector3());
+	} else if (type == TRANSFORM2D) {
+		const Transform2D &t = *_data._transform2d;
+		Transform3D m;
+		m.basis.rows[0][0] = t.columns[0][0];
+		m.basis.rows[1][0] = t.columns[0][1];
+		m.basis.rows[0][1] = t.columns[1][0];
+		m.basis.rows[1][1] = t.columns[1][1];
+		m.origin[0] = t.columns[2][0];
+		m.origin[1] = t.columns[2][1];
+		return m;
+	} else if (type == PROJECTION) {
+		return *_data._projection;
+	} else {
+		return Projection();
+	}
+}
+
 Variant::operator Transform2D() const {
 	if (type == TRANSFORM2D) {
 		return *_data._transform2d;
@@ -2384,6 +2572,16 @@ Variant::Variant(const Vector3i &p_vector3i) {
 	memnew_placement(_data._mem, Vector3i(p_vector3i));
 }
 
+Variant::Variant(const Vector4 &p_vector4) {
+	type = VECTOR4;
+	memnew_placement(_data._mem, Vector4(p_vector4));
+}
+
+Variant::Variant(const Vector4i &p_vector4i) {
+	type = VECTOR4I;
+	memnew_placement(_data._mem, Vector4i(p_vector4i));
+}
+
 Variant::Variant(const Vector2 &p_vector2) {
 	type = VECTOR2;
 	memnew_placement(_data._mem, Vector2(p_vector2));
@@ -2429,6 +2627,11 @@ Variant::Variant(const Transform3D &p_transform) {
 	_data._transform3d = memnew(Transform3D(p_transform));
 }
 
+Variant::Variant(const Projection &pp_projection) {
+	type = PROJECTION;
+	_data._projection = memnew(Projection(pp_projection));
+}
+
 Variant::Variant(const Transform2D &p_transform) {
 	type = TRANSFORM2D;
 	_data._transform2d = memnew(Transform2D(p_transform));
@@ -2656,6 +2859,12 @@ void Variant::operator=(const Variant &p_variant) {
 		case VECTOR3I: {
 			*reinterpret_cast<Vector3i *>(_data._mem) = *reinterpret_cast<const Vector3i *>(p_variant._data._mem);
 		} break;
+		case VECTOR4: {
+			*reinterpret_cast<Vector4 *>(_data._mem) = *reinterpret_cast<const Vector4 *>(p_variant._data._mem);
+		} break;
+		case VECTOR4I: {
+			*reinterpret_cast<Vector4i *>(_data._mem) = *reinterpret_cast<const Vector4i *>(p_variant._data._mem);
+		} break;
 		case PLANE: {
 			*reinterpret_cast<Plane *>(_data._mem) = *reinterpret_cast<const Plane *>(p_variant._data._mem);
 		} break;
@@ -2672,6 +2881,9 @@ void Variant::operator=(const Variant &p_variant) {
 		case TRANSFORM3D: {
 			*_data._transform3d = *(p_variant._data._transform3d);
 		} break;
+		case PROJECTION: {
+			*_data._projection = *(p_variant._data._projection);
+		} break;
 
 		// misc types
 		case COLOR: {
@@ -2817,6 +3029,12 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
 		case VECTOR3I: {
 			return HashMapHasherDefault::hash(*reinterpret_cast<const Vector3i *>(_data._mem));
 		} break;
+		case VECTOR4: {
+			return HashMapHasherDefault::hash(*reinterpret_cast<const Vector4 *>(_data._mem));
+		} break;
+		case VECTOR4I: {
+			return HashMapHasherDefault::hash(*reinterpret_cast<const Vector4i *>(_data._mem));
+		} break;
 		case PLANE: {
 			uint32_t h = HASH_MURMUR3_SEED;
 			const Plane &p = *reinterpret_cast<const Plane *>(_data._mem);
@@ -2869,6 +3087,27 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
 			h = hash_murmur3_one_real(t.origin.z, h);
 			return hash_fmix32(h);
 		} break;
+		case PROJECTION: {
+			uint32_t h = HASH_MURMUR3_SEED;
+			const Projection &t = *_data._projection;
+			h = hash_murmur3_one_real(t.matrix[0].x, h);
+			h = hash_murmur3_one_real(t.matrix[0].y, h);
+			h = hash_murmur3_one_real(t.matrix[0].z, h);
+			h = hash_murmur3_one_real(t.matrix[0].w, h);
+			h = hash_murmur3_one_real(t.matrix[1].x, h);
+			h = hash_murmur3_one_real(t.matrix[1].y, h);
+			h = hash_murmur3_one_real(t.matrix[1].z, h);
+			h = hash_murmur3_one_real(t.matrix[1].w, h);
+			h = hash_murmur3_one_real(t.matrix[2].x, h);
+			h = hash_murmur3_one_real(t.matrix[2].y, h);
+			h = hash_murmur3_one_real(t.matrix[2].z, h);
+			h = hash_murmur3_one_real(t.matrix[2].w, h);
+			h = hash_murmur3_one_real(t.matrix[3].x, h);
+			h = hash_murmur3_one_real(t.matrix[3].y, h);
+			h = hash_murmur3_one_real(t.matrix[3].z, h);
+			h = hash_murmur3_one_real(t.matrix[3].w, h);
+			return hash_fmix32(h);
+		} break;
 		// misc types
 		case COLOR: {
 			uint32_t h = HASH_MURMUR3_SEED;
@@ -3062,6 +3301,11 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
 	(hash_compare_scalar((p_lhs).x, (p_rhs).x)) &&         \
 			(hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \
 			(hash_compare_scalar((p_lhs).z, (p_rhs).z))
+#define hash_compare_vector4(p_lhs, p_rhs)                 \
+	(hash_compare_scalar((p_lhs).x, (p_rhs).x)) &&         \
+			(hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \
+			(hash_compare_scalar((p_lhs).z, (p_rhs).z)) && \
+			(hash_compare_scalar((p_lhs).w, (p_rhs).w))
 
 #define hash_compare_quaternion(p_lhs, p_rhs)              \
 	(hash_compare_scalar((p_lhs).x, (p_rhs).x)) &&         \
@@ -3165,6 +3409,18 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
 
 			return *l == *r;
 		} break;
+		case VECTOR4: {
+			const Vector4 *l = reinterpret_cast<const Vector4 *>(_data._mem);
+			const Vector4 *r = reinterpret_cast<const Vector4 *>(p_variant._data._mem);
+
+			return hash_compare_vector4(*l, *r);
+		} break;
+		case VECTOR4I: {
+			const Vector4i *l = reinterpret_cast<const Vector4i *>(_data._mem);
+			const Vector4i *r = reinterpret_cast<const Vector4i *>(p_variant._data._mem);
+
+			return *l == *r;
+		} break;
 
 		case PLANE: {
 			const Plane *l = reinterpret_cast<const Plane *>(_data._mem);
@@ -3215,6 +3471,18 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
 
 			return hash_compare_vector3(l->origin, r->origin);
 		} break;
+		case PROJECTION: {
+			const Projection *l = _data._projection;
+			const Projection *r = p_variant._data._projection;
+
+			for (int i = 0; i < 4; i++) {
+				if (!(hash_compare_vector4(l->matrix[i], r->matrix[i]))) {
+					return false;
+				}
+			}
+
+			return true;
+		} break;
 
 		case COLOR: {
 			const Color *l = reinterpret_cast<const Color *>(_data._mem);

+ 16 - 0
core/variant/variant.h

@@ -38,6 +38,7 @@
 #include "core/math/color.h"
 #include "core/math/face3.h"
 #include "core/math/plane.h"
+#include "core/math/projection.h"
 #include "core/math/quaternion.h"
 #include "core/math/rect2.h"
 #include "core/math/rect2i.h"
@@ -47,6 +48,8 @@
 #include "core/math/vector2i.h"
 #include "core/math/vector3.h"
 #include "core/math/vector3i.h"
+#include "core/math/vector4.h"
+#include "core/math/vector4i.h"
 #include "core/object/object_id.h"
 #include "core/os/keyboard.h"
 #include "core/string/node_path.h"
@@ -91,11 +94,14 @@ public:
 		VECTOR3,
 		VECTOR3I,
 		TRANSFORM2D,
+		VECTOR4,
+		VECTOR4I,
 		PLANE,
 		QUATERNION,
 		AABB,
 		BASIS,
 		TRANSFORM3D,
+		PROJECTION,
 
 		// misc types
 		COLOR,
@@ -210,6 +216,7 @@ private:
 		::AABB *_aabb;
 		Basis *_basis;
 		Transform3D *_transform3d;
+		Projection *_projection;
 		PackedArrayRefBase *packed_array;
 		void *_ptr; //generic pointer
 		uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]{ 0 };
@@ -234,11 +241,14 @@ private:
 			false, //VECTOR3,
 			false, //VECTOR3I,
 			true, //TRANSFORM2D,
+			false, //VECTOR4,
+			false, //VECTOR4I,
 			false, //PLANE,
 			false, //QUATERNION,
 			true, //AABB,
 			true, //BASIS,
 			true, //TRANSFORM,
+			true, //PROJECTION,
 
 			// misc types
 			false, //COLOR,
@@ -339,12 +349,15 @@ public:
 	operator Rect2i() const;
 	operator Vector3() const;
 	operator Vector3i() const;
+	operator Vector4() const;
+	operator Vector4i() const;
 	operator Plane() const;
 	operator ::AABB() const;
 	operator Quaternion() const;
 	operator Basis() const;
 	operator Transform2D() const;
 	operator Transform3D() const;
+	operator Projection() const;
 
 	operator Color() const;
 	operator NodePath() const;
@@ -409,12 +422,15 @@ public:
 	Variant(const Rect2i &p_rect2i);
 	Variant(const Vector3 &p_vector3);
 	Variant(const Vector3i &p_vector3i);
+	Variant(const Vector4 &p_vector4);
+	Variant(const Vector4i &p_vector4i);
 	Variant(const Plane &p_plane);
 	Variant(const ::AABB &p_aabb);
 	Variant(const Quaternion &p_quat);
 	Variant(const Basis &p_matrix);
 	Variant(const Transform2D &p_transform);
 	Variant(const Transform3D &p_transform);
+	Variant(const Projection &p_projection);
 	Variant(const Color &p_color);
 	Variant(const NodePath &p_node_path);
 	Variant(const ::RID &p_rid);

+ 104 - 0
core/variant/variant_call.cpp

@@ -1725,6 +1725,31 @@ static void _register_variant_builtin_methods() {
 	bind_method(Vector3i, abs, sarray(), varray());
 	bind_method(Vector3i, clamp, sarray("min", "max"), varray());
 
+	/* Vector4 */
+
+	bind_method(Vector4, min_axis_index, sarray(), varray());
+	bind_method(Vector4, max_axis_index, sarray(), varray());
+	bind_method(Vector4, length, sarray(), varray());
+	bind_method(Vector4, length_squared, sarray(), varray());
+	bind_method(Vector4, sign, sarray(), varray());
+	bind_method(Vector4, abs, sarray(), varray());
+	bind_method(Vector4, clamp, sarray("min", "max"), varray());
+	bind_method(Vector4, normalized, sarray(), varray());
+	bind_method(Vector4, is_normalized, sarray(), varray());
+	bind_method(Vector4, dot, sarray("with"), varray());
+	bind_method(Vector4, inverse, sarray(), varray());
+	bind_method(Vector4, is_equal_approx, sarray("with"), varray());
+
+	/* Vector4i */
+
+	bind_method(Vector4i, min_axis_index, sarray(), varray());
+	bind_method(Vector4i, max_axis_index, sarray(), varray());
+	bind_method(Vector4i, length, sarray(), varray());
+	bind_method(Vector4i, length_squared, sarray(), varray());
+	bind_method(Vector4i, sign, sarray(), varray());
+	bind_method(Vector4i, abs, sarray(), varray());
+	bind_method(Vector4i, clamp, sarray("min", "max"), varray());
+
 	/* Plane */
 
 	bind_method(Plane, normalized, sarray(), varray());
@@ -1925,6 +1950,40 @@ static void _register_variant_builtin_methods() {
 	bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray());
 	bind_method(Transform3D, is_equal_approx, sarray("xform"), varray());
 
+	/* Projection */
+
+	bind_static_method(Projection, create_depth_correction, sarray("flip_y"), varray());
+	bind_static_method(Projection, create_light_atlas_rect, sarray("rect"), varray());
+	bind_static_method(Projection, create_perspective, sarray("fovy", "aspect", "z_near", "z_far", "flip_fov"), varray(false));
+	bind_static_method(Projection, create_perspective_hmd, sarray("fovy", "aspect", "z_near", "z_far", "flip_fov", "eye", "intraocular_dist", " convergence_dist"), varray());
+	bind_static_method(Projection, create_for_hmd, sarray("eye", "aspect", "intraocular_dist", "display_width", "display_to_lens", "oversample", "z_near", "z_far"), varray());
+	bind_static_method(Projection, create_orthogonal, sarray("left", "right", "bottom", "top", "z_near", "z_far"), varray());
+	bind_static_method(Projection, create_orthogonal_aspect, sarray("size", "aspect", "z_near", "z_far", "flip_fov"), varray(false));
+	bind_static_method(Projection, create_frustum, sarray("left", "right", "bottom", "top", "z_near", "z_far"), varray());
+	bind_static_method(Projection, create_frustum_aspect, sarray("size", "aspect", "offset", "z_near", "z_far", "flip_fov"), varray(false));
+	bind_static_method(Projection, create_fit_aabb, sarray("aabb"), varray());
+
+	bind_method(Projection, determinant, sarray(), varray());
+	bind_method(Projection, perspective_znear_adjusted, sarray("new_znear"), varray());
+	bind_method(Projection, get_projection_plane, sarray("plane"), varray());
+	bind_method(Projection, flipped_y, sarray(), varray());
+	bind_method(Projection, jitter_offseted, sarray("offset"), varray());
+
+	bind_static_method(Projection, get_fovy, sarray("fovx", "aspect"), varray());
+
+	bind_method(Projection, get_z_far, sarray(), varray());
+	bind_method(Projection, get_z_near, sarray(), varray());
+	bind_method(Projection, get_aspect, sarray(), varray());
+	bind_method(Projection, get_fov, sarray(), varray());
+	bind_method(Projection, is_orthogonal, sarray(), varray());
+
+	bind_method(Projection, get_viewport_half_extents, sarray(), varray());
+	bind_method(Projection, get_far_plane_half_extents, sarray(), varray());
+
+	bind_method(Projection, inverse, sarray(), varray());
+	bind_method(Projection, get_pixels_per_meter, sarray("for_pixel_width"), varray());
+	bind_method(Projection, get_lod_multiplier, sarray(), varray());
+
 	/* Dictionary */
 
 	bind_method(Dictionary, size, sarray(), varray());
@@ -2253,6 +2312,19 @@ static void _register_variant_builtin_methods() {
 	_VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1));
 	_VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1));
 
+	_VariantCall::add_constant(Variant::VECTOR4, "AXIS_X", Vector4::AXIS_X);
+	_VariantCall::add_constant(Variant::VECTOR4, "AXIS_Y", Vector4::AXIS_Y);
+	_VariantCall::add_constant(Variant::VECTOR4, "AXIS_Z", Vector4::AXIS_Z);
+	_VariantCall::add_constant(Variant::VECTOR4, "AXIS_W", Vector4::AXIS_W);
+
+	_VariantCall::add_enum_constant(Variant::VECTOR4, "Axis", "AXIS_X", Vector4::AXIS_X);
+	_VariantCall::add_enum_constant(Variant::VECTOR4, "Axis", "AXIS_Y", Vector4::AXIS_Y);
+	_VariantCall::add_enum_constant(Variant::VECTOR4, "Axis", "AXIS_Z", Vector4::AXIS_Z);
+	_VariantCall::add_enum_constant(Variant::VECTOR4, "Axis", "AXIS_W", Vector4::AXIS_W);
+	_VariantCall::add_variant_constant(Variant::VECTOR4, "ZERO", Vector4(0, 0, 0, 0));
+	_VariantCall::add_variant_constant(Variant::VECTOR4, "ONE", Vector4(1, 1, 1, 1));
+	_VariantCall::add_variant_constant(Variant::VECTOR4, "INF", Vector4(INFINITY, INFINITY, INFINITY, INFINITY));
+
 	_VariantCall::add_constant(Variant::VECTOR3I, "AXIS_X", Vector3i::AXIS_X);
 	_VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3i::AXIS_Y);
 	_VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3i::AXIS_Z);
@@ -2261,6 +2333,19 @@ static void _register_variant_builtin_methods() {
 	_VariantCall::add_enum_constant(Variant::VECTOR3I, "Axis", "AXIS_Y", Vector3i::AXIS_Y);
 	_VariantCall::add_enum_constant(Variant::VECTOR3I, "Axis", "AXIS_Z", Vector3i::AXIS_Z);
 
+	_VariantCall::add_constant(Variant::VECTOR4I, "AXIS_X", Vector4i::AXIS_X);
+	_VariantCall::add_constant(Variant::VECTOR4I, "AXIS_Y", Vector4i::AXIS_Y);
+	_VariantCall::add_constant(Variant::VECTOR4I, "AXIS_Z", Vector4i::AXIS_Z);
+	_VariantCall::add_constant(Variant::VECTOR4I, "AXIS_W", Vector4i::AXIS_W);
+
+	_VariantCall::add_enum_constant(Variant::VECTOR4I, "Axis", "AXIS_X", Vector4i::AXIS_X);
+	_VariantCall::add_enum_constant(Variant::VECTOR4I, "Axis", "AXIS_Y", Vector4i::AXIS_Y);
+	_VariantCall::add_enum_constant(Variant::VECTOR4I, "Axis", "AXIS_Z", Vector4i::AXIS_Z);
+	_VariantCall::add_enum_constant(Variant::VECTOR4I, "Axis", "AXIS_W", Vector4i::AXIS_W);
+
+	_VariantCall::add_variant_constant(Variant::VECTOR4I, "ZERO", Vector4i(0, 0, 0, 0));
+	_VariantCall::add_variant_constant(Variant::VECTOR4I, "ONE", Vector4i(1, 1, 1, 1));
+
 	_VariantCall::add_variant_constant(Variant::VECTOR3I, "ZERO", Vector3i(0, 0, 0));
 	_VariantCall::add_variant_constant(Variant::VECTOR3I, "ONE", Vector3i(1, 1, 1));
 	_VariantCall::add_variant_constant(Variant::VECTOR3I, "LEFT", Vector3i(-1, 0, 0));
@@ -2338,6 +2423,25 @@ static void _register_variant_builtin_methods() {
 	_VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XY", Plane(Vector3(0, 0, 1), 0));
 
 	_VariantCall::add_variant_constant(Variant::QUATERNION, "IDENTITY", Quaternion(0, 0, 0, 1));
+
+	_VariantCall::add_constant(Variant::PROJECTION, "PLANE_NEAR", Projection::PLANE_NEAR);
+	_VariantCall::add_constant(Variant::PROJECTION, "PLANE_FAR", Projection::PLANE_FAR);
+	_VariantCall::add_constant(Variant::PROJECTION, "PLANE_LEFT", Projection::PLANE_LEFT);
+	_VariantCall::add_constant(Variant::PROJECTION, "PLANE_TOP", Projection::PLANE_TOP);
+	_VariantCall::add_constant(Variant::PROJECTION, "PLANE_RIGHT", Projection::PLANE_RIGHT);
+	_VariantCall::add_constant(Variant::PROJECTION, "PLANE_BOTTOM", Projection::PLANE_BOTTOM);
+
+	_VariantCall::add_enum_constant(Variant::PROJECTION, "Planes", "PLANE_NEAR", Projection::PLANE_NEAR);
+	_VariantCall::add_enum_constant(Variant::PROJECTION, "Planes", "PLANE_FAR", Projection::PLANE_FAR);
+	_VariantCall::add_enum_constant(Variant::PROJECTION, "Planes", "PLANE_LEFT", Projection::PLANE_LEFT);
+	_VariantCall::add_enum_constant(Variant::PROJECTION, "Planes", "PLANE_TOP", Projection::PLANE_TOP);
+	_VariantCall::add_enum_constant(Variant::PROJECTION, "Planes", "PLANE_RIGHT", Projection::PLANE_RIGHT);
+	_VariantCall::add_enum_constant(Variant::PROJECTION, "Planes", "PLANE_BOTTOM", Projection::PLANE_BOTTOM);
+
+	Projection p;
+	_VariantCall::add_variant_constant(Variant::PROJECTION, "IDENTITY", p);
+	p.set_zero();
+	_VariantCall::add_variant_constant(Variant::PROJECTION, "ZERO", p);
 }
 
 void Variant::_register_variant_methods() {

+ 15 - 0
core/variant/variant_construct.cpp

@@ -111,6 +111,16 @@ void Variant::_register_variant_constructors() {
 	add_constructor<VariantConstructor<Vector3i, Vector3>>(sarray("from"));
 	add_constructor<VariantConstructor<Vector3i, int64_t, int64_t, int64_t>>(sarray("x", "y", "z"));
 
+	add_constructor<VariantConstructNoArgs<Vector4>>(sarray());
+	add_constructor<VariantConstructor<Vector4, Vector4>>(sarray("from"));
+	add_constructor<VariantConstructor<Vector4, Vector4i>>(sarray("from"));
+	add_constructor<VariantConstructor<Vector4, double, double, double, double>>(sarray("x", "y", "z", "w"));
+
+	add_constructor<VariantConstructNoArgs<Vector4i>>(sarray());
+	add_constructor<VariantConstructor<Vector4i, Vector4i>>(sarray("from"));
+	add_constructor<VariantConstructor<Vector4i, Vector4>>(sarray("from"));
+	add_constructor<VariantConstructor<Vector4i, int64_t, int64_t, int64_t, int64_t>>(sarray("x", "y", "z", "w"));
+
 	add_constructor<VariantConstructNoArgs<Transform2D>>(sarray());
 	add_constructor<VariantConstructor<Transform2D, Transform2D>>(sarray("from"));
 	add_constructor<VariantConstructor<Transform2D, float, Vector2>>(sarray("rotation", "position"));
@@ -147,6 +157,11 @@ void Variant::_register_variant_constructors() {
 	add_constructor<VariantConstructor<Transform3D, Transform3D>>(sarray("from"));
 	add_constructor<VariantConstructor<Transform3D, Basis, Vector3>>(sarray("basis", "origin"));
 	add_constructor<VariantConstructor<Transform3D, Vector3, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis", "origin"));
+	add_constructor<VariantConstructor<Transform3D, Projection>>(sarray("from"));
+
+	add_constructor<VariantConstructNoArgs<Projection>>(sarray());
+	add_constructor<VariantConstructor<Projection, Projection>>(sarray("from"));
+	add_constructor<VariantConstructor<Projection, Transform3D>>(sarray("from"));
 
 	add_constructor<VariantConstructNoArgs<Color>>(sarray());
 	add_constructor<VariantConstructor<Color, Color>>(sarray("from"));

+ 3 - 0
core/variant/variant_construct.h

@@ -63,12 +63,15 @@ MAKE_PTRCONSTRUCT(Rect2);
 MAKE_PTRCONSTRUCT(Rect2i);
 MAKE_PTRCONSTRUCT(Vector3);
 MAKE_PTRCONSTRUCT(Vector3i);
+MAKE_PTRCONSTRUCT(Vector4);
+MAKE_PTRCONSTRUCT(Vector4i);
 MAKE_PTRCONSTRUCT(Transform2D);
 MAKE_PTRCONSTRUCT(Plane);
 MAKE_PTRCONSTRUCT(Quaternion);
 MAKE_PTRCONSTRUCT(AABB);
 MAKE_PTRCONSTRUCT(Basis);
 MAKE_PTRCONSTRUCT(Transform3D);
+MAKE_PTRCONSTRUCT(Projection);
 MAKE_PTRCONSTRUCT(Color);
 MAKE_PTRCONSTRUCT(StringName);
 MAKE_PTRCONSTRUCT(NodePath);

+ 89 - 0
core/variant/variant_internal.h

@@ -138,6 +138,10 @@ public:
 	_FORCE_INLINE_ static const Vector3 *get_vector3(const Variant *v) { return reinterpret_cast<const Vector3 *>(v->_data._mem); }
 	_FORCE_INLINE_ static Vector3i *get_vector3i(Variant *v) { return reinterpret_cast<Vector3i *>(v->_data._mem); }
 	_FORCE_INLINE_ static const Vector3i *get_vector3i(const Variant *v) { return reinterpret_cast<const Vector3i *>(v->_data._mem); }
+	_FORCE_INLINE_ static Vector4 *get_vector4(Variant *v) { return reinterpret_cast<Vector4 *>(v->_data._mem); }
+	_FORCE_INLINE_ static const Vector4 *get_vector4(const Variant *v) { return reinterpret_cast<const Vector4 *>(v->_data._mem); }
+	_FORCE_INLINE_ static Vector4i *get_vector4i(Variant *v) { return reinterpret_cast<Vector4i *>(v->_data._mem); }
+	_FORCE_INLINE_ static const Vector4i *get_vector4i(const Variant *v) { return reinterpret_cast<const Vector4i *>(v->_data._mem); }
 	_FORCE_INLINE_ static Transform2D *get_transform2d(Variant *v) { return v->_data._transform2d; }
 	_FORCE_INLINE_ static const Transform2D *get_transform2d(const Variant *v) { return v->_data._transform2d; }
 	_FORCE_INLINE_ static Plane *get_plane(Variant *v) { return reinterpret_cast<Plane *>(v->_data._mem); }
@@ -150,6 +154,8 @@ public:
 	_FORCE_INLINE_ static const Basis *get_basis(const Variant *v) { return v->_data._basis; }
 	_FORCE_INLINE_ static Transform3D *get_transform(Variant *v) { return v->_data._transform3d; }
 	_FORCE_INLINE_ static const Transform3D *get_transform(const Variant *v) { return v->_data._transform3d; }
+	_FORCE_INLINE_ static Projection *get_projection(Variant *v) { return v->_data._projection; }
+	_FORCE_INLINE_ static const Projection *get_projection(const Variant *v) { return v->_data._projection; }
 
 	// Misc types.
 	_FORCE_INLINE_ static Color *get_color(Variant *v) { return reinterpret_cast<Color *>(v->_data._mem); }
@@ -224,6 +230,10 @@ public:
 		v->_data._transform3d = memnew(Transform3D);
 		v->type = Variant::TRANSFORM3D;
 	}
+	_FORCE_INLINE_ static void init_projection(Variant *v) {
+		v->_data._projection = memnew(Projection);
+		v->type = Variant::PROJECTION;
+	}
 	_FORCE_INLINE_ static void init_string_name(Variant *v) {
 		memnew_placement(v->_data._mem, StringName);
 		v->type = Variant::STRING_NAME;
@@ -331,12 +341,18 @@ public:
 				return get_vector3(v);
 			case Variant::VECTOR3I:
 				return get_vector3i(v);
+			case Variant::VECTOR4:
+				return get_vector4(v);
+			case Variant::VECTOR4I:
+				return get_vector4i(v);
 			case Variant::RECT2:
 				return get_rect2(v);
 			case Variant::RECT2I:
 				return get_rect2i(v);
 			case Variant::TRANSFORM3D:
 				return get_transform(v);
+			case Variant::PROJECTION:
+				return get_projection(v);
 			case Variant::TRANSFORM2D:
 				return get_transform2d(v);
 			case Variant::QUATERNION:
@@ -409,12 +425,18 @@ public:
 				return get_vector3(v);
 			case Variant::VECTOR3I:
 				return get_vector3i(v);
+			case Variant::VECTOR4:
+				return get_vector4(v);
+			case Variant::VECTOR4I:
+				return get_vector4i(v);
 			case Variant::RECT2:
 				return get_rect2(v);
 			case Variant::RECT2I:
 				return get_rect2i(v);
 			case Variant::TRANSFORM3D:
 				return get_transform(v);
+			case Variant::PROJECTION:
+				return get_projection(v);
 			case Variant::TRANSFORM2D:
 				return get_transform2d(v);
 			case Variant::QUATERNION:
@@ -598,6 +620,17 @@ struct VariantGetInternalPtr<Vector3i> {
 	static const Vector3i *get_ptr(const Variant *v) { return VariantInternal::get_vector3i(v); }
 };
 
+template <>
+struct VariantGetInternalPtr<Vector4> {
+	static Vector4 *get_ptr(Variant *v) { return VariantInternal::get_vector4(v); }
+	static const Vector4 *get_ptr(const Variant *v) { return VariantInternal::get_vector4(v); }
+};
+
+template <>
+struct VariantGetInternalPtr<Vector4i> {
+	static Vector4i *get_ptr(Variant *v) { return VariantInternal::get_vector4i(v); }
+	static const Vector4i *get_ptr(const Variant *v) { return VariantInternal::get_vector4i(v); }
+};
 template <>
 struct VariantGetInternalPtr<Transform2D> {
 	static Transform2D *get_ptr(Variant *v) { return VariantInternal::get_transform2d(v); }
@@ -610,6 +643,12 @@ struct VariantGetInternalPtr<Transform3D> {
 	static const Transform3D *get_ptr(const Variant *v) { return VariantInternal::get_transform(v); }
 };
 
+template <>
+struct VariantGetInternalPtr<Projection> {
+	static Projection *get_ptr(Variant *v) { return VariantInternal::get_projection(v); }
+	static const Projection *get_ptr(const Variant *v) { return VariantInternal::get_projection(v); }
+};
+
 template <>
 struct VariantGetInternalPtr<Plane> {
 	static Plane *get_ptr(Variant *v) { return VariantInternal::get_plane(v); }
@@ -772,6 +811,10 @@ VARIANT_ACCESSOR_NUMBER(Vector2::Axis)
 VARIANT_ACCESSOR_NUMBER(Vector2i::Axis)
 VARIANT_ACCESSOR_NUMBER(Vector3::Axis)
 VARIANT_ACCESSOR_NUMBER(Vector3i::Axis)
+VARIANT_ACCESSOR_NUMBER(Vector4::Axis)
+VARIANT_ACCESSOR_NUMBER(Vector4i::Axis)
+
+VARIANT_ACCESSOR_NUMBER(Projection::Planes)
 
 template <>
 struct VariantInternalAccessor<Basis::EulerOrder> {
@@ -839,6 +882,17 @@ struct VariantInternalAccessor<Vector3i> {
 	static _FORCE_INLINE_ void set(Variant *v, const Vector3i &p_value) { *VariantInternal::get_vector3i(v) = p_value; }
 };
 
+template <>
+struct VariantInternalAccessor<Vector4> {
+	static _FORCE_INLINE_ const Vector4 &get(const Variant *v) { return *VariantInternal::get_vector4(v); }
+	static _FORCE_INLINE_ void set(Variant *v, const Vector4 &p_value) { *VariantInternal::get_vector4(v) = p_value; }
+};
+
+template <>
+struct VariantInternalAccessor<Vector4i> {
+	static _FORCE_INLINE_ const Vector4i &get(const Variant *v) { return *VariantInternal::get_vector4i(v); }
+	static _FORCE_INLINE_ void set(Variant *v, const Vector4i &p_value) { *VariantInternal::get_vector4i(v) = p_value; }
+};
 template <>
 struct VariantInternalAccessor<Transform2D> {
 	static _FORCE_INLINE_ const Transform2D &get(const Variant *v) { return *VariantInternal::get_transform2d(v); }
@@ -851,6 +905,12 @@ struct VariantInternalAccessor<Transform3D> {
 	static _FORCE_INLINE_ void set(Variant *v, const Transform3D &p_value) { *VariantInternal::get_transform(v) = p_value; }
 };
 
+template <>
+struct VariantInternalAccessor<Projection> {
+	static _FORCE_INLINE_ const Projection &get(const Variant *v) { return *VariantInternal::get_projection(v); }
+	static _FORCE_INLINE_ void set(Variant *v, const Projection &p_value) { *VariantInternal::get_projection(v) = p_value; }
+};
+
 template <>
 struct VariantInternalAccessor<Plane> {
 	static _FORCE_INLINE_ const Plane &get(const Variant *v) { return *VariantInternal::get_plane(v); }
@@ -1041,6 +1101,8 @@ INITIALIZER_INT(Vector2::Axis)
 INITIALIZER_INT(Vector2i::Axis)
 INITIALIZER_INT(Vector3::Axis)
 INITIALIZER_INT(Vector3i::Axis)
+INITIALIZER_INT(Vector4::Axis)
+INITIALIZER_INT(Vector4i::Axis)
 
 template <>
 struct VariantInitializer<double> {
@@ -1086,7 +1148,15 @@ template <>
 struct VariantInitializer<Vector3i> {
 	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Vector3i>(v); }
 };
+template <>
+struct VariantInitializer<Vector4> {
+	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Vector4>(v); }
+};
 
+template <>
+struct VariantInitializer<Vector4i> {
+	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_generic<Vector4i>(v); }
+};
 template <>
 struct VariantInitializer<Transform2D> {
 	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform2d(v); }
@@ -1116,6 +1186,10 @@ template <>
 struct VariantInitializer<Transform3D> {
 	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform(v); }
 };
+template <>
+struct VariantInitializer<Projection> {
+	static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_projection(v); }
+};
 
 template <>
 struct VariantInitializer<Color> {
@@ -1266,6 +1340,16 @@ struct VariantZeroAssigner<Vector3i> {
 	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); }
 };
 
+template <>
+struct VariantZeroAssigner<Vector4> {
+	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); }
+};
+
+template <>
+struct VariantZeroAssigner<Vector4i> {
+	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); }
+};
+
 template <>
 struct VariantZeroAssigner<Transform2D> {
 	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); }
@@ -1296,6 +1380,11 @@ struct VariantZeroAssigner<Transform3D> {
 	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); }
 };
 
+template <>
+struct VariantZeroAssigner<Projection> {
+	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_projection(v) = Projection(); }
+};
+
 template <>
 struct VariantZeroAssigner<Color> {
 	static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color(v) = Color(); }

+ 131 - 0
core/variant/variant_op.cpp

@@ -165,6 +165,70 @@ public:
 	static Variant::Type get_return_type() { return GetTypeInfo<Vector3>::VARIANT_TYPE; }
 };
 
+//
+
+template <>
+class OperatorEvaluatorMul<Vector4, Vector4i, double> {
+public:
+	static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+		const Vector4i &a = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_left);
+		const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right);
+		*r_ret = Vector4(a.x, a.y, a.z, a.w) * b;
+		r_valid = true;
+	}
+	static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+		*VariantGetInternalPtr<Vector4>::get_ptr(r_ret) = Vector4(VariantGetInternalPtr<Vector4i>::get_ptr(left)->x, VariantGetInternalPtr<Vector4i>::get_ptr(left)->y, VariantGetInternalPtr<Vector4i>::get_ptr(left)->z, VariantGetInternalPtr<Vector4i>::get_ptr(left)->w) * *VariantGetInternalPtr<double>::get_ptr(right);
+	}
+	static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+		PtrToArg<Vector4>::encode(Vector4(PtrToArg<Vector4i>::convert(left).x, PtrToArg<Vector4i>::convert(left).y, PtrToArg<Vector4i>::convert(left).z, PtrToArg<Vector4i>::convert(left).w) * PtrToArg<double>::convert(right), r_ret);
+	}
+	static Variant::Type get_return_type() { return GetTypeInfo<Vector4>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorMul<Vector4, double, Vector4i> {
+public:
+	static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+		const Vector4i &a = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_right);
+		const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_left);
+		*r_ret = Vector4(a.x, a.y, a.z, a.w) * b;
+		r_valid = true;
+	}
+	static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+		*VariantGetInternalPtr<Vector4>::get_ptr(r_ret) = Vector4(VariantGetInternalPtr<Vector4i>::get_ptr(right)->x, VariantGetInternalPtr<Vector4i>::get_ptr(right)->y, VariantGetInternalPtr<Vector4i>::get_ptr(right)->z, VariantGetInternalPtr<Vector4i>::get_ptr(right)->w) * *VariantGetInternalPtr<double>::get_ptr(left);
+	}
+	static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+		PtrToArg<Vector4>::encode(Vector4(PtrToArg<Vector4i>::convert(right).x, PtrToArg<Vector4i>::convert(right).y, PtrToArg<Vector4i>::convert(right).z, PtrToArg<Vector4i>::convert(right).w) * PtrToArg<double>::convert(left), r_ret);
+	}
+	static Variant::Type get_return_type() { return GetTypeInfo<Vector4>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorDivNZ<Vector4, Vector4i, double> {
+public:
+	static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+		const Vector4i &a = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_left);
+		const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right);
+		if (unlikely(b == 0)) {
+			r_valid = false;
+			*r_ret = "Division by zero error";
+			return;
+		}
+		*r_ret = Vector4(a.x, a.y, a.z, a.w) / b;
+		r_valid = true;
+	}
+
+	static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+		*VariantGetInternalPtr<Vector4>::get_ptr(r_ret) = Vector4(VariantGetInternalPtr<Vector4i>::get_ptr(left)->x, VariantGetInternalPtr<Vector4i>::get_ptr(left)->y, VariantGetInternalPtr<Vector4i>::get_ptr(left)->z, VariantGetInternalPtr<Vector4i>::get_ptr(left)->w) / *VariantGetInternalPtr<double>::get_ptr(right);
+	}
+
+	static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+		PtrToArg<Vector4>::encode(Vector4(PtrToArg<Vector4i>::convert(left).x, PtrToArg<Vector4i>::convert(left).y, PtrToArg<Vector4i>::convert(left).z, PtrToArg<Vector4i>::convert(left).w) / PtrToArg<double>::convert(right), r_ret);
+	}
+
+	static Variant::Type get_return_type() { return GetTypeInfo<Vector4>::VARIANT_TYPE; }
+};
+
 void Variant::_register_variant_operators() {
 	memset(operator_return_type_table, 0, sizeof(operator_return_type_table));
 	memset(operator_evaluator_table, 0, sizeof(operator_evaluator_table));
@@ -182,6 +246,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAdd<Vector2i, Vector2i, Vector2i>>(Variant::OP_ADD, Variant::VECTOR2I, Variant::VECTOR2I);
 	register_op<OperatorEvaluatorAdd<Vector3, Vector3, Vector3>>(Variant::OP_ADD, Variant::VECTOR3, Variant::VECTOR3);
 	register_op<OperatorEvaluatorAdd<Vector3i, Vector3i, Vector3i>>(Variant::OP_ADD, Variant::VECTOR3I, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorAdd<Vector4, Vector4, Vector4>>(Variant::OP_ADD, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorAdd<Vector4i, Vector4i, Vector4i>>(Variant::OP_ADD, Variant::VECTOR4I, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorAdd<Quaternion, Quaternion, Quaternion>>(Variant::OP_ADD, Variant::QUATERNION, Variant::QUATERNION);
 	register_op<OperatorEvaluatorAdd<Color, Color, Color>>(Variant::OP_ADD, Variant::COLOR, Variant::COLOR);
 	register_op<OperatorEvaluatorAddArray>(Variant::OP_ADD, Variant::ARRAY, Variant::ARRAY);
@@ -203,6 +269,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorSub<Vector2i, Vector2i, Vector2i>>(Variant::OP_SUBTRACT, Variant::VECTOR2I, Variant::VECTOR2I);
 	register_op<OperatorEvaluatorSub<Vector3, Vector3, Vector3>>(Variant::OP_SUBTRACT, Variant::VECTOR3, Variant::VECTOR3);
 	register_op<OperatorEvaluatorSub<Vector3i, Vector3i, Vector3i>>(Variant::OP_SUBTRACT, Variant::VECTOR3I, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorSub<Vector4, Vector4, Vector4>>(Variant::OP_SUBTRACT, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorSub<Vector4i, Vector4i, Vector4i>>(Variant::OP_SUBTRACT, Variant::VECTOR4I, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorSub<Quaternion, Quaternion, Quaternion>>(Variant::OP_SUBTRACT, Variant::QUATERNION, Variant::QUATERNION);
 	register_op<OperatorEvaluatorSub<Color, Color, Color>>(Variant::OP_SUBTRACT, Variant::COLOR, Variant::COLOR);
 
@@ -212,6 +280,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorMul<Vector2i, int64_t, Vector2i>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR2I);
 	register_op<OperatorEvaluatorMul<Vector3, int64_t, Vector3>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR3);
 	register_op<OperatorEvaluatorMul<Vector3i, int64_t, Vector3i>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorMul<Vector4, int64_t, Vector4>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR4);
+	register_op<OperatorEvaluatorMul<Vector4i, int64_t, Vector4i>>(Variant::OP_MULTIPLY, Variant::INT, Variant::VECTOR4I);
 
 	register_op<OperatorEvaluatorMul<double, double, double>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::FLOAT);
 	register_op<OperatorEvaluatorMul<double, double, int64_t>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::INT);
@@ -219,6 +289,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorMul<Vector2, double, Vector2i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2I);
 	register_op<OperatorEvaluatorMul<Vector3, double, Vector3>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3);
 	register_op<OperatorEvaluatorMul<Vector3, double, Vector3i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorMul<Vector4, double, Vector4>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR4);
+	register_op<OperatorEvaluatorMul<Vector4, double, Vector4i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR4I);
 
 	register_op<OperatorEvaluatorMul<Vector2, Vector2, Vector2>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::VECTOR2);
 	register_op<OperatorEvaluatorMul<Vector2, Vector2, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::INT);
@@ -236,6 +308,14 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorMul<Vector3i, Vector3i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::INT);
 	register_op<OperatorEvaluatorMul<Vector3, Vector3i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::FLOAT);
 
+	register_op<OperatorEvaluatorMul<Vector4, Vector4, Vector4>>(Variant::OP_MULTIPLY, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorMul<Vector4, Vector4, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR4, Variant::INT);
+	register_op<OperatorEvaluatorMul<Vector4, Vector4, double>>(Variant::OP_MULTIPLY, Variant::VECTOR4, Variant::FLOAT);
+
+	register_op<OperatorEvaluatorMul<Vector4i, Vector4i, Vector4i>>(Variant::OP_MULTIPLY, Variant::VECTOR4I, Variant::VECTOR4I);
+	register_op<OperatorEvaluatorMul<Vector4i, Vector4i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR4I, Variant::INT);
+	register_op<OperatorEvaluatorMul<Vector4, Vector4i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR4I, Variant::FLOAT);
+
 	register_op<OperatorEvaluatorMul<Quaternion, Quaternion, Quaternion>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::QUATERNION);
 	register_op<OperatorEvaluatorMul<Quaternion, Quaternion, int64_t>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::INT);
 	register_op<OperatorEvaluatorMul<Quaternion, Quaternion, double>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::FLOAT);
@@ -264,6 +344,11 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorXForm<Vector<Vector3>, Transform3D, Vector<Vector3>>>(Variant::OP_MULTIPLY, Variant::TRANSFORM3D, Variant::PACKED_VECTOR3_ARRAY);
 	register_op<OperatorEvaluatorXFormInv<Vector<Vector3>, Vector<Vector3>, Transform3D>>(Variant::OP_MULTIPLY, Variant::PACKED_VECTOR3_ARRAY, Variant::TRANSFORM3D);
 
+	register_op<OperatorEvaluatorXForm<Vector4, Projection, Vector4>>(Variant::OP_MULTIPLY, Variant::PROJECTION, Variant::VECTOR4);
+	register_op<OperatorEvaluatorXFormInv<Vector4, Vector4, Projection>>(Variant::OP_MULTIPLY, Variant::VECTOR4, Variant::PROJECTION);
+
+	register_op<OperatorEvaluatorMul<Projection, Projection, Projection>>(Variant::OP_MULTIPLY, Variant::PROJECTION, Variant::PROJECTION);
+
 	register_op<OperatorEvaluatorMul<Basis, Basis, Basis>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::BASIS);
 	register_op<OperatorEvaluatorMul<Basis, Basis, int64_t>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::INT);
 	register_op<OperatorEvaluatorMul<Basis, Basis, double>>(Variant::OP_MULTIPLY, Variant::BASIS, Variant::FLOAT);
@@ -309,6 +394,14 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorDivNZ<Vector3, Vector3i, double>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::FLOAT);
 	register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::INT);
 
+	register_op<OperatorEvaluatorDiv<Vector4, Vector4, Vector4>>(Variant::OP_DIVIDE, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorDiv<Vector4, Vector4, double>>(Variant::OP_DIVIDE, Variant::VECTOR4, Variant::FLOAT);
+	register_op<OperatorEvaluatorDiv<Vector4, Vector4, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR4, Variant::INT);
+
+	register_op<OperatorEvaluatorDivNZ<Vector4i, Vector4i, Vector4i>>(Variant::OP_DIVIDE, Variant::VECTOR4I, Variant::VECTOR4I);
+	register_op<OperatorEvaluatorDivNZ<Vector4, Vector4i, double>>(Variant::OP_DIVIDE, Variant::VECTOR4I, Variant::FLOAT);
+	register_op<OperatorEvaluatorDivNZ<Vector4i, Vector4i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR4I, Variant::INT);
+
 	register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, double>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::FLOAT);
 	register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, int64_t>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::INT);
 
@@ -323,6 +416,9 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorModNZ<Vector3i, Vector3i, Vector3i>>(Variant::OP_MODULE, Variant::VECTOR3I, Variant::VECTOR3I);
 	register_op<OperatorEvaluatorModNZ<Vector3i, Vector3i, int64_t>>(Variant::OP_MODULE, Variant::VECTOR3I, Variant::INT);
 
+	register_op<OperatorEvaluatorModNZ<Vector4i, Vector4i, Vector4i>>(Variant::OP_MODULE, Variant::VECTOR4I, Variant::VECTOR4I);
+	register_op<OperatorEvaluatorModNZ<Vector4i, Vector4i, int64_t>>(Variant::OP_MODULE, Variant::VECTOR4I, Variant::INT);
+
 	register_op<OperatorEvaluatorStringModNil>(Variant::OP_MODULE, Variant::STRING, Variant::NIL);
 
 	register_op<OperatorEvaluatorStringModT<bool>>(Variant::OP_MODULE, Variant::STRING, Variant::BOOL);
@@ -335,12 +431,15 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorStringModT<Rect2i>>(Variant::OP_MODULE, Variant::STRING, Variant::RECT2I);
 	register_op<OperatorEvaluatorStringModT<Vector3>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR3);
 	register_op<OperatorEvaluatorStringModT<Vector3i>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorStringModT<Vector4>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR4);
+	register_op<OperatorEvaluatorStringModT<Vector4i>>(Variant::OP_MODULE, Variant::STRING, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorStringModT<Transform2D>>(Variant::OP_MODULE, Variant::STRING, Variant::TRANSFORM2D);
 	register_op<OperatorEvaluatorStringModT<Plane>>(Variant::OP_MODULE, Variant::STRING, Variant::PLANE);
 	register_op<OperatorEvaluatorStringModT<Quaternion>>(Variant::OP_MODULE, Variant::STRING, Variant::QUATERNION);
 	register_op<OperatorEvaluatorStringModT<::AABB>>(Variant::OP_MODULE, Variant::STRING, Variant::AABB);
 	register_op<OperatorEvaluatorStringModT<Basis>>(Variant::OP_MODULE, Variant::STRING, Variant::BASIS);
 	register_op<OperatorEvaluatorStringModT<Transform3D>>(Variant::OP_MODULE, Variant::STRING, Variant::TRANSFORM3D);
+	register_op<OperatorEvaluatorStringModT<Projection>>(Variant::OP_MODULE, Variant::STRING, Variant::PROJECTION);
 
 	register_op<OperatorEvaluatorStringModT<Color>>(Variant::OP_MODULE, Variant::STRING, Variant::COLOR);
 	register_op<OperatorEvaluatorStringModT<StringName>>(Variant::OP_MODULE, Variant::STRING, Variant::STRING_NAME);
@@ -372,6 +471,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorNeg<Vector2i, Vector2i>>(Variant::OP_NEGATE, Variant::VECTOR2I, Variant::NIL);
 	register_op<OperatorEvaluatorNeg<Vector3, Vector3>>(Variant::OP_NEGATE, Variant::VECTOR3, Variant::NIL);
 	register_op<OperatorEvaluatorNeg<Vector3i, Vector3i>>(Variant::OP_NEGATE, Variant::VECTOR3I, Variant::NIL);
+	register_op<OperatorEvaluatorNeg<Vector4, Vector4>>(Variant::OP_NEGATE, Variant::VECTOR4, Variant::NIL);
+	register_op<OperatorEvaluatorNeg<Vector4i, Vector4i>>(Variant::OP_NEGATE, Variant::VECTOR4I, Variant::NIL);
 	register_op<OperatorEvaluatorNeg<Quaternion, Quaternion>>(Variant::OP_NEGATE, Variant::QUATERNION, Variant::NIL);
 	register_op<OperatorEvaluatorNeg<Plane, Plane>>(Variant::OP_NEGATE, Variant::PLANE, Variant::NIL);
 	register_op<OperatorEvaluatorNeg<Color, Color>>(Variant::OP_NEGATE, Variant::COLOR, Variant::NIL);
@@ -382,6 +483,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorPos<Vector2i, Vector2i>>(Variant::OP_POSITIVE, Variant::VECTOR2I, Variant::NIL);
 	register_op<OperatorEvaluatorPos<Vector3, Vector3>>(Variant::OP_POSITIVE, Variant::VECTOR3, Variant::NIL);
 	register_op<OperatorEvaluatorPos<Vector3i, Vector3i>>(Variant::OP_POSITIVE, Variant::VECTOR3I, Variant::NIL);
+	register_op<OperatorEvaluatorPos<Vector4, Vector4>>(Variant::OP_POSITIVE, Variant::VECTOR4, Variant::NIL);
+	register_op<OperatorEvaluatorPos<Vector4i, Vector4i>>(Variant::OP_POSITIVE, Variant::VECTOR4I, Variant::NIL);
 	register_op<OperatorEvaluatorPos<Quaternion, Quaternion>>(Variant::OP_POSITIVE, Variant::QUATERNION, Variant::NIL);
 	register_op<OperatorEvaluatorPos<Plane, Plane>>(Variant::OP_POSITIVE, Variant::PLANE, Variant::NIL);
 	register_op<OperatorEvaluatorPos<Color, Color>>(Variant::OP_POSITIVE, Variant::COLOR, Variant::NIL);
@@ -409,11 +512,14 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorEqual<Vector3, Vector3>>(Variant::OP_EQUAL, Variant::VECTOR3, Variant::VECTOR3);
 	register_op<OperatorEvaluatorEqual<Vector3i, Vector3i>>(Variant::OP_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I);
 	register_op<OperatorEvaluatorEqual<Transform2D, Transform2D>>(Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::TRANSFORM2D);
+	register_op<OperatorEvaluatorEqual<Vector4, Vector4>>(Variant::OP_EQUAL, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorEqual<Vector4i, Vector4i>>(Variant::OP_EQUAL, Variant::VECTOR4I, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorEqual<Plane, Plane>>(Variant::OP_EQUAL, Variant::PLANE, Variant::PLANE);
 	register_op<OperatorEvaluatorEqual<Quaternion, Quaternion>>(Variant::OP_EQUAL, Variant::QUATERNION, Variant::QUATERNION);
 	register_op<OperatorEvaluatorEqual<::AABB, ::AABB>>(Variant::OP_EQUAL, Variant::AABB, Variant::AABB);
 	register_op<OperatorEvaluatorEqual<Basis, Basis>>(Variant::OP_EQUAL, Variant::BASIS, Variant::BASIS);
 	register_op<OperatorEvaluatorEqual<Transform3D, Transform3D>>(Variant::OP_EQUAL, Variant::TRANSFORM3D, Variant::TRANSFORM3D);
+	register_op<OperatorEvaluatorEqual<Projection, Projection>>(Variant::OP_EQUAL, Variant::PROJECTION, Variant::PROJECTION);
 	register_op<OperatorEvaluatorEqual<Color, Color>>(Variant::OP_EQUAL, Variant::COLOR, Variant::COLOR);
 
 	register_op<OperatorEvaluatorEqual<StringName, String>>(Variant::OP_EQUAL, Variant::STRING_NAME, Variant::STRING);
@@ -451,6 +557,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::RECT2I, Variant::NIL>>(Variant::OP_EQUAL, Variant::RECT2I, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR3, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR3, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR3I, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR3I, Variant::NIL);
+	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR4, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR4, Variant::NIL);
+	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::VECTOR4I, Variant::NIL>>(Variant::OP_EQUAL, Variant::VECTOR4I, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::NIL>>(Variant::OP_EQUAL, Variant::TRANSFORM2D, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::PLANE, Variant::NIL>>(Variant::OP_EQUAL, Variant::PLANE, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::QUATERNION, Variant::NIL>>(Variant::OP_EQUAL, Variant::QUATERNION, Variant::NIL);
@@ -485,6 +593,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::RECT2I>>(Variant::OP_EQUAL, Variant::NIL, Variant::RECT2I);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3I>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR4>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR4);
+	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR4I>>(Variant::OP_EQUAL, Variant::NIL, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM2D>>(Variant::OP_EQUAL, Variant::NIL, Variant::TRANSFORM2D);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::PLANE>>(Variant::OP_EQUAL, Variant::NIL, Variant::PLANE);
 	register_op<OperatorEvaluatorAlwaysFalse<Variant::OP_EQUAL, Variant::NIL, Variant::QUATERNION>>(Variant::OP_EQUAL, Variant::NIL, Variant::QUATERNION);
@@ -522,12 +632,15 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorNotEqual<Rect2i, Rect2i>>(Variant::OP_NOT_EQUAL, Variant::RECT2I, Variant::RECT2I);
 	register_op<OperatorEvaluatorNotEqual<Vector3, Vector3>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::VECTOR3);
 	register_op<OperatorEvaluatorNotEqual<Vector3i, Vector3i>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorNotEqual<Vector4, Vector4>>(Variant::OP_NOT_EQUAL, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorNotEqual<Vector4i, Vector4i>>(Variant::OP_NOT_EQUAL, Variant::VECTOR4I, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorNotEqual<Transform2D, Transform2D>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::TRANSFORM2D);
 	register_op<OperatorEvaluatorNotEqual<Plane, Plane>>(Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::PLANE);
 	register_op<OperatorEvaluatorNotEqual<Quaternion, Quaternion>>(Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::QUATERNION);
 	register_op<OperatorEvaluatorNotEqual<::AABB, ::AABB>>(Variant::OP_NOT_EQUAL, Variant::AABB, Variant::AABB);
 	register_op<OperatorEvaluatorNotEqual<Basis, Basis>>(Variant::OP_NOT_EQUAL, Variant::BASIS, Variant::BASIS);
 	register_op<OperatorEvaluatorNotEqual<Transform3D, Transform3D>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM3D, Variant::TRANSFORM3D);
+	register_op<OperatorEvaluatorNotEqual<Projection, Projection>>(Variant::OP_NOT_EQUAL, Variant::PROJECTION, Variant::PROJECTION);
 	register_op<OperatorEvaluatorNotEqual<Color, Color>>(Variant::OP_NOT_EQUAL, Variant::COLOR, Variant::COLOR);
 
 	register_op<OperatorEvaluatorNotEqual<StringName, String>>(Variant::OP_NOT_EQUAL, Variant::STRING_NAME, Variant::STRING);
@@ -566,6 +679,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR3I, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::TRANSFORM2D, Variant::NIL);
+	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR4, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR4, Variant::NIL);
+	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::VECTOR4I, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::VECTOR4I, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::PLANE, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::QUATERNION, Variant::NIL);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::AABB, Variant::NIL>>(Variant::OP_NOT_EQUAL, Variant::AABB, Variant::NIL);
@@ -599,6 +714,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::RECT2I);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR4>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR4);
+	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR4I>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM2D>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::TRANSFORM2D);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PLANE>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::PLANE);
 	register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_NOT_EQUAL, Variant::NIL, Variant::QUATERNION>>(Variant::OP_NOT_EQUAL, Variant::NIL, Variant::QUATERNION);
@@ -634,6 +751,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorLess<Vector2i, Vector2i>>(Variant::OP_LESS, Variant::VECTOR2I, Variant::VECTOR2I);
 	register_op<OperatorEvaluatorLess<Vector3, Vector3>>(Variant::OP_LESS, Variant::VECTOR3, Variant::VECTOR3);
 	register_op<OperatorEvaluatorLess<Vector3i, Vector3i>>(Variant::OP_LESS, Variant::VECTOR3I, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorLess<Vector4, Vector4>>(Variant::OP_LESS, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorLess<Vector4i, Vector4i>>(Variant::OP_LESS, Variant::VECTOR4I, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorLess<::RID, ::RID>>(Variant::OP_LESS, Variant::RID, Variant::RID);
 	register_op<OperatorEvaluatorLess<Array, Array>>(Variant::OP_LESS, Variant::ARRAY, Variant::ARRAY);
 
@@ -647,6 +766,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorLessEqual<Vector2i, Vector2i>>(Variant::OP_LESS_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I);
 	register_op<OperatorEvaluatorLessEqual<Vector3, Vector3>>(Variant::OP_LESS_EQUAL, Variant::VECTOR3, Variant::VECTOR3);
 	register_op<OperatorEvaluatorLessEqual<Vector3i, Vector3i>>(Variant::OP_LESS_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorLessEqual<Vector4, Vector4>>(Variant::OP_LESS_EQUAL, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorLessEqual<Vector4i, Vector4i>>(Variant::OP_LESS_EQUAL, Variant::VECTOR4I, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorLessEqual<::RID, ::RID>>(Variant::OP_LESS_EQUAL, Variant::RID, Variant::RID);
 	register_op<OperatorEvaluatorLessEqual<Array, Array>>(Variant::OP_LESS_EQUAL, Variant::ARRAY, Variant::ARRAY);
 
@@ -661,6 +782,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorGreater<Vector2i, Vector2i>>(Variant::OP_GREATER, Variant::VECTOR2I, Variant::VECTOR2I);
 	register_op<OperatorEvaluatorGreater<Vector3, Vector3>>(Variant::OP_GREATER, Variant::VECTOR3, Variant::VECTOR3);
 	register_op<OperatorEvaluatorGreater<Vector3i, Vector3i>>(Variant::OP_GREATER, Variant::VECTOR3I, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorGreater<Vector4, Vector4>>(Variant::OP_GREATER, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorGreater<Vector4i, Vector4i>>(Variant::OP_GREATER, Variant::VECTOR4I, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorGreater<::RID, ::RID>>(Variant::OP_GREATER, Variant::RID, Variant::RID);
 	register_op<OperatorEvaluatorGreater<Array, Array>>(Variant::OP_GREATER, Variant::ARRAY, Variant::ARRAY);
 
@@ -674,6 +797,8 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorGreaterEqual<Vector2i, Vector2i>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I);
 	register_op<OperatorEvaluatorGreaterEqual<Vector3, Vector3>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR3, Variant::VECTOR3);
 	register_op<OperatorEvaluatorGreaterEqual<Vector3i, Vector3i>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR3I, Variant::VECTOR3I);
+	register_op<OperatorEvaluatorGreaterEqual<Vector4, Vector4>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR4, Variant::VECTOR4);
+	register_op<OperatorEvaluatorGreaterEqual<Vector4i, Vector4i>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR4I, Variant::VECTOR4I);
 	register_op<OperatorEvaluatorGreaterEqual<::RID, ::RID>>(Variant::OP_GREATER_EQUAL, Variant::RID, Variant::RID);
 	register_op<OperatorEvaluatorGreaterEqual<Array, Array>>(Variant::OP_GREATER_EQUAL, Variant::ARRAY, Variant::ARRAY);
 
@@ -788,12 +913,15 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorInDictionaryHas<Rect2i>>(Variant::OP_IN, Variant::RECT2I, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<Vector3>>(Variant::OP_IN, Variant::VECTOR3, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<Vector3i>>(Variant::OP_IN, Variant::VECTOR3I, Variant::DICTIONARY);
+	register_op<OperatorEvaluatorInDictionaryHas<Vector4>>(Variant::OP_IN, Variant::VECTOR4, Variant::DICTIONARY);
+	register_op<OperatorEvaluatorInDictionaryHas<Vector4i>>(Variant::OP_IN, Variant::VECTOR4I, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<Transform2D>>(Variant::OP_IN, Variant::TRANSFORM2D, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<Plane>>(Variant::OP_IN, Variant::PLANE, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<Quaternion>>(Variant::OP_IN, Variant::QUATERNION, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<::AABB>>(Variant::OP_IN, Variant::AABB, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<Basis>>(Variant::OP_IN, Variant::BASIS, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<Transform3D>>(Variant::OP_IN, Variant::TRANSFORM3D, Variant::DICTIONARY);
+	register_op<OperatorEvaluatorInDictionaryHas<Projection>>(Variant::OP_IN, Variant::PROJECTION, Variant::DICTIONARY);
 
 	register_op<OperatorEvaluatorInDictionaryHas<Color>>(Variant::OP_IN, Variant::COLOR, Variant::DICTIONARY);
 	register_op<OperatorEvaluatorInDictionaryHas<StringName>>(Variant::OP_IN, Variant::STRING_NAME, Variant::DICTIONARY);
@@ -825,12 +953,15 @@ void Variant::_register_variant_operators() {
 	register_op<OperatorEvaluatorInArrayFind<Rect2i, Array>>(Variant::OP_IN, Variant::RECT2I, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<Vector3, Array>>(Variant::OP_IN, Variant::VECTOR3, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<Vector3i, Array>>(Variant::OP_IN, Variant::VECTOR3I, Variant::ARRAY);
+	register_op<OperatorEvaluatorInArrayFind<Vector4, Array>>(Variant::OP_IN, Variant::VECTOR4, Variant::ARRAY);
+	register_op<OperatorEvaluatorInArrayFind<Vector4i, Array>>(Variant::OP_IN, Variant::VECTOR4I, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<Transform2D, Array>>(Variant::OP_IN, Variant::TRANSFORM2D, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<Plane, Array>>(Variant::OP_IN, Variant::PLANE, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<Quaternion, Array>>(Variant::OP_IN, Variant::QUATERNION, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<::AABB, Array>>(Variant::OP_IN, Variant::AABB, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<Basis, Array>>(Variant::OP_IN, Variant::BASIS, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<Transform3D, Array>>(Variant::OP_IN, Variant::TRANSFORM3D, Variant::ARRAY);
+	register_op<OperatorEvaluatorInArrayFind<Projection, Array>>(Variant::OP_IN, Variant::PROJECTION, Variant::ARRAY);
 
 	register_op<OperatorEvaluatorInArrayFind<Color, Array>>(Variant::OP_IN, Variant::COLOR, Variant::ARRAY);
 	register_op<OperatorEvaluatorInArrayFind<StringName, Array>>(Variant::OP_IN, Variant::STRING_NAME, Variant::ARRAY);

+ 48 - 0
core/variant/variant_op.h

@@ -234,6 +234,30 @@ public:
 	static Variant::Type get_return_type() { return GetTypeInfo<Vector3i>::VARIANT_TYPE; }
 };
 
+template <>
+class OperatorEvaluatorDivNZ<Vector4i, Vector4i, Vector4i> {
+public:
+	static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+		const Vector4i &a = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_left);
+		const Vector4i &b = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_right);
+		if (unlikely(b.x == 0 || b.y == 0 || b.z == 0 || b.w == 0)) {
+			r_valid = false;
+			*r_ret = "Division by zero error";
+			return;
+		}
+		*r_ret = a / b;
+		r_valid = true;
+	}
+	static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+		VariantTypeChanger<Vector4i>::change(r_ret);
+		*VariantGetInternalPtr<Vector4i>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector4i>::get_ptr(left) / *VariantGetInternalPtr<Vector4i>::get_ptr(right);
+	}
+	static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+		PtrToArg<Vector4i>::encode(PtrToArg<Vector4i>::convert(left) / PtrToArg<Vector4i>::convert(right), r_ret);
+	}
+	static Variant::Type get_return_type() { return GetTypeInfo<Vector4i>::VARIANT_TYPE; }
+};
+
 template <class R, class A, class B>
 class OperatorEvaluatorMod {
 public:
@@ -323,6 +347,30 @@ public:
 	static Variant::Type get_return_type() { return GetTypeInfo<Vector3i>::VARIANT_TYPE; }
 };
 
+template <>
+class OperatorEvaluatorModNZ<Vector4i, Vector4i, Vector4i> {
+public:
+	static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+		const Vector4i &a = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_left);
+		const Vector4i &b = *VariantGetInternalPtr<Vector4i>::get_ptr(&p_right);
+		if (unlikely(b.x == 0 || b.y == 0 || b.z == 0 || b.w == 0)) {
+			r_valid = false;
+			*r_ret = "Module by zero error";
+			return;
+		}
+		*r_ret = a % b;
+		r_valid = true;
+	}
+	static void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+		VariantTypeChanger<Vector4i>::change(r_ret);
+		*VariantGetInternalPtr<Vector4i>::get_ptr(r_ret) = *VariantGetInternalPtr<Vector4i>::get_ptr(left) % *VariantGetInternalPtr<Vector4i>::get_ptr(right);
+	}
+	static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+		PtrToArg<Vector4i>::encode(PtrToArg<Vector4i>::convert(left) % PtrToArg<Vector4i>::convert(right), r_ret);
+	}
+	static Variant::Type get_return_type() { return GetTypeInfo<Vector4i>::VARIANT_TYPE; }
+};
+
 template <class R, class A>
 class OperatorEvaluatorNeg {
 public:

+ 61 - 0
core/variant/variant_parser.cpp

@@ -649,6 +649,32 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
 			}
 
 			value = Vector3i(args[0], args[1], args[2]);
+		} else if (id == "Vector4") {
+			Vector<real_t> args;
+			Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str);
+			if (err) {
+				return err;
+			}
+
+			if (args.size() != 4) {
+				r_err_str = "Expected 4 arguments for constructor";
+				return ERR_PARSE_ERROR;
+			}
+
+			value = Vector4(args[0], args[1], args[2], args[3]);
+		} else if (id == "Vector4i") {
+			Vector<int32_t> args;
+			Error err = _parse_construct<int32_t>(p_stream, args, line, r_err_str);
+			if (err) {
+				return err;
+			}
+
+			if (args.size() != 4) {
+				r_err_str = "Expected 4 arguments for constructor";
+				return ERR_PARSE_ERROR;
+			}
+
+			value = Vector4i(args[0], args[1], args[2], args[3]);
 		} else if (id == "Transform2D" || id == "Matrix32") { //compatibility
 			Vector<real_t> args;
 			Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str);
@@ -731,6 +757,19 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
 			}
 
 			value = Transform3D(Basis(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]), Vector3(args[9], args[10], args[11]));
+		} else if (id == "Projection") { // "Transform" kept for compatibility with Godot <4.
+			Vector<real_t> args;
+			Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str);
+			if (err) {
+				return err;
+			}
+
+			if (args.size() != 16) {
+				r_err_str = "Expected 16 arguments for constructor";
+				return ERR_PARSE_ERROR;
+			}
+
+			value = Projection(Vector4(args[0], args[1], args[2], args[3]), Vector4(args[4], args[5], args[6], args[7]), Vector4(args[8], args[9], args[10], args[11]), Vector4(args[12], args[13], args[14], args[15]));
 		} else if (id == "Color") {
 			Vector<float> args;
 			Error err = _parse_construct<float>(p_stream, args, line, r_err_str);
@@ -1534,6 +1573,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
 			Vector3i v = p_variant;
 			p_store_string_func(p_store_string_ud, "Vector3i(" + itos(v.x) + ", " + itos(v.y) + ", " + itos(v.z) + ")");
 		} break;
+		case Variant::VECTOR4: {
+			Vector4 v = p_variant;
+			p_store_string_func(p_store_string_ud, "Vector4(" + rtos_fix(v.x) + ", " + rtos_fix(v.y) + ", " + rtos_fix(v.z) + ")");
+		} break;
+		case Variant::VECTOR4I: {
+			Vector4i v = p_variant;
+			p_store_string_func(p_store_string_ud, "Vector4i(" + itos(v.x) + ", " + itos(v.y) + ", " + itos(v.z) + ")");
+		} break;
 		case Variant::PLANE: {
 			Plane p = p_variant;
 			p_store_string_func(p_store_string_ud, "Plane(" + rtos_fix(p.normal.x) + ", " + rtos_fix(p.normal.y) + ", " + rtos_fix(p.normal.z) + ", " + rtos_fix(p.d) + ")");
@@ -1596,6 +1643,20 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
 
 			p_store_string_func(p_store_string_ud, s + ")");
 		} break;
+		case Variant::PROJECTION: {
+			String s = "Projection(";
+			Projection t = p_variant;
+			for (int i = 0; i < 4; i++) {
+				for (int j = 0; j < 4; j++) {
+					if (i != 0 || j != 0) {
+						s += ", ";
+					}
+					s += rtos_fix(t.matrix[i][j]);
+				}
+			}
+
+			p_store_string_func(p_store_string_ud, s + ")");
+		} break;
 
 		// misc types
 		case Variant::COLOR: {

+ 17 - 0
core/variant/variant_setget.cpp

@@ -77,6 +77,16 @@ void register_named_setters_getters() {
 	REGISTER_MEMBER(Vector3i, y);
 	REGISTER_MEMBER(Vector3i, z);
 
+	REGISTER_MEMBER(Vector4, x);
+	REGISTER_MEMBER(Vector4, y);
+	REGISTER_MEMBER(Vector4, z);
+	REGISTER_MEMBER(Vector4, w);
+
+	REGISTER_MEMBER(Vector4i, x);
+	REGISTER_MEMBER(Vector4i, y);
+	REGISTER_MEMBER(Vector4i, z);
+	REGISTER_MEMBER(Vector4i, w);
+
 	REGISTER_MEMBER(Rect2, position);
 	REGISTER_MEMBER(Rect2, size);
 	REGISTER_MEMBER(Rect2, end);
@@ -111,6 +121,11 @@ void register_named_setters_getters() {
 	REGISTER_MEMBER(Transform3D, basis);
 	REGISTER_MEMBER(Transform3D, origin);
 
+	REGISTER_MEMBER(Projection, x);
+	REGISTER_MEMBER(Projection, y);
+	REGISTER_MEMBER(Projection, z);
+	REGISTER_MEMBER(Projection, w);
+
 	REGISTER_MEMBER(Color, r);
 	REGISTER_MEMBER(Color, g);
 	REGISTER_MEMBER(Color, b);
@@ -809,6 +824,7 @@ INDEXED_SETGET_STRUCT_BULTIN_NUMERIC(Color, double, float, 4)
 
 INDEXED_SETGET_STRUCT_BULTIN_ACCESSOR(Transform2D, Vector2, .columns, 3)
 INDEXED_SETGET_STRUCT_BULTIN_FUNC(Basis, Vector3, set_column, get_column, 3)
+INDEXED_SETGET_STRUCT_BULTIN_ACCESSOR(Projection, Vector4, .matrix, 4)
 
 INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedByteArray, int64_t, uint8_t)
 INDEXED_SETGET_STRUCT_TYPED_NUMERIC(PackedInt32Array, int64_t, int32_t)
@@ -871,6 +887,7 @@ void register_indexed_setters_getters() {
 	REGISTER_INDEXED_MEMBER(Color);
 	REGISTER_INDEXED_MEMBER(Transform2D);
 	REGISTER_INDEXED_MEMBER(Basis);
+	REGISTER_INDEXED_MEMBER(Projection);
 
 	REGISTER_INDEXED_MEMBER(PackedByteArray);
 	REGISTER_INDEXED_MEMBER(PackedInt32Array);

+ 15 - 0
core/variant/variant_setget.h

@@ -281,6 +281,16 @@ SETGET_NUMBER_STRUCT(Vector3i, int64_t, x)
 SETGET_NUMBER_STRUCT(Vector3i, int64_t, y)
 SETGET_NUMBER_STRUCT(Vector3i, int64_t, z)
 
+SETGET_NUMBER_STRUCT(Vector4, double, x)
+SETGET_NUMBER_STRUCT(Vector4, double, y)
+SETGET_NUMBER_STRUCT(Vector4, double, z)
+SETGET_NUMBER_STRUCT(Vector4, double, w)
+
+SETGET_NUMBER_STRUCT(Vector4i, int64_t, x)
+SETGET_NUMBER_STRUCT(Vector4i, int64_t, y)
+SETGET_NUMBER_STRUCT(Vector4i, int64_t, z)
+SETGET_NUMBER_STRUCT(Vector4i, int64_t, w)
+
 SETGET_STRUCT(Rect2, Vector2, position)
 SETGET_STRUCT(Rect2, Vector2, size)
 SETGET_STRUCT_FUNC(Rect2, Vector2, end, set_end, get_end)
@@ -315,6 +325,11 @@ SETGET_STRUCT_FUNC_INDEX(Basis, Vector3, z, set_column, get_column, 2)
 SETGET_STRUCT(Transform3D, Basis, basis)
 SETGET_STRUCT(Transform3D, Vector3, origin)
 
+SETGET_STRUCT_CUSTOM(Projection, Vector4, x, matrix[0])
+SETGET_STRUCT_CUSTOM(Projection, Vector4, y, matrix[1])
+SETGET_STRUCT_CUSTOM(Projection, Vector4, z, matrix[2])
+SETGET_STRUCT_CUSTOM(Projection, Vector4, w, matrix[3])
+
 SETGET_NUMBER_STRUCT(Color, double, r)
 SETGET_NUMBER_STRUCT(Color, double, g)
 SETGET_NUMBER_STRUCT(Color, double, b)

+ 12 - 0
core/variant/variant_utility.cpp

@@ -132,6 +132,12 @@ struct VariantUtilityFunctions {
 			case Variant::VECTOR3I: {
 				return VariantInternalAccessor<Vector3i>::get(&x).abs();
 			} break;
+			case Variant::VECTOR4: {
+				return VariantInternalAccessor<Vector4>::get(&x).abs();
+			} break;
+			case Variant::VECTOR4I: {
+				return VariantInternalAccessor<Vector4i>::get(&x).abs();
+			} break;
 			default: {
 				r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
 				return Variant();
@@ -168,6 +174,12 @@ struct VariantUtilityFunctions {
 			case Variant::VECTOR3I: {
 				return VariantInternalAccessor<Vector3i>::get(&x).sign();
 			} break;
+			case Variant::VECTOR4: {
+				return VariantInternalAccessor<Vector4>::get(&x).sign();
+			} break;
+			case Variant::VECTOR4I: {
+				return VariantInternalAccessor<Vector4i>::get(&x).sign();
+			} break;
 			default: {
 				r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
 				return Variant();

+ 30 - 24
doc/classes/@GlobalScope.xml

@@ -2782,76 +2782,82 @@
 		<constant name="TYPE_TRANSFORM2D" value="11" enum="Variant.Type">
 			Variable is of type [Transform2D].
 		</constant>
-		<constant name="TYPE_PLANE" value="12" enum="Variant.Type">
+		<constant name="TYPE_VECTOR4" value="12" enum="Variant.Type">
+		</constant>
+		<constant name="TYPE_VECTOR4I" value="13" enum="Variant.Type">
+		</constant>
+		<constant name="TYPE_PLANE" value="14" enum="Variant.Type">
 			Variable is of type [Plane].
 		</constant>
-		<constant name="TYPE_QUATERNION" value="13" enum="Variant.Type">
+		<constant name="TYPE_QUATERNION" value="15" enum="Variant.Type">
 			Variable is of type [Quaternion].
 		</constant>
-		<constant name="TYPE_AABB" value="14" enum="Variant.Type">
+		<constant name="TYPE_AABB" value="16" enum="Variant.Type">
 			Variable is of type [AABB].
 		</constant>
-		<constant name="TYPE_BASIS" value="15" enum="Variant.Type">
+		<constant name="TYPE_BASIS" value="17" enum="Variant.Type">
 			Variable is of type [Basis].
 		</constant>
-		<constant name="TYPE_TRANSFORM3D" value="16" enum="Variant.Type">
+		<constant name="TYPE_TRANSFORM3D" value="18" enum="Variant.Type">
 			Variable is of type [Transform3D].
 		</constant>
-		<constant name="TYPE_COLOR" value="17" enum="Variant.Type">
+		<constant name="TYPE_PROJECTION" value="19" enum="Variant.Type">
+		</constant>
+		<constant name="TYPE_COLOR" value="20" enum="Variant.Type">
 			Variable is of type [Color].
 		</constant>
-		<constant name="TYPE_STRING_NAME" value="18" enum="Variant.Type">
+		<constant name="TYPE_STRING_NAME" value="21" enum="Variant.Type">
 			Variable is of type [StringName].
 		</constant>
-		<constant name="TYPE_NODE_PATH" value="19" enum="Variant.Type">
+		<constant name="TYPE_NODE_PATH" value="22" enum="Variant.Type">
 			Variable is of type [NodePath].
 		</constant>
-		<constant name="TYPE_RID" value="20" enum="Variant.Type">
+		<constant name="TYPE_RID" value="23" enum="Variant.Type">
 			Variable is of type [RID].
 		</constant>
-		<constant name="TYPE_OBJECT" value="21" enum="Variant.Type">
+		<constant name="TYPE_OBJECT" value="24" enum="Variant.Type">
 			Variable is of type [Object].
 		</constant>
-		<constant name="TYPE_CALLABLE" value="22" enum="Variant.Type">
+		<constant name="TYPE_CALLABLE" value="25" enum="Variant.Type">
 			Variable is of type [Callable].
 		</constant>
-		<constant name="TYPE_SIGNAL" value="23" enum="Variant.Type">
+		<constant name="TYPE_SIGNAL" value="26" enum="Variant.Type">
 			Variable is of type [Signal].
 		</constant>
-		<constant name="TYPE_DICTIONARY" value="24" enum="Variant.Type">
+		<constant name="TYPE_DICTIONARY" value="27" enum="Variant.Type">
 			Variable is of type [Dictionary].
 		</constant>
-		<constant name="TYPE_ARRAY" value="25" enum="Variant.Type">
+		<constant name="TYPE_ARRAY" value="28" enum="Variant.Type">
 			Variable is of type [Array].
 		</constant>
-		<constant name="TYPE_PACKED_BYTE_ARRAY" value="26" enum="Variant.Type">
+		<constant name="TYPE_PACKED_BYTE_ARRAY" value="29" enum="Variant.Type">
 			Variable is of type [PackedByteArray].
 		</constant>
-		<constant name="TYPE_PACKED_INT32_ARRAY" value="27" enum="Variant.Type">
+		<constant name="TYPE_PACKED_INT32_ARRAY" value="30" enum="Variant.Type">
 			Variable is of type [PackedInt32Array].
 		</constant>
-		<constant name="TYPE_PACKED_INT64_ARRAY" value="28" enum="Variant.Type">
+		<constant name="TYPE_PACKED_INT64_ARRAY" value="31" enum="Variant.Type">
 			Variable is of type [PackedInt64Array].
 		</constant>
-		<constant name="TYPE_PACKED_FLOAT32_ARRAY" value="29" enum="Variant.Type">
+		<constant name="TYPE_PACKED_FLOAT32_ARRAY" value="32" enum="Variant.Type">
 			Variable is of type [PackedFloat32Array].
 		</constant>
-		<constant name="TYPE_PACKED_FLOAT64_ARRAY" value="30" enum="Variant.Type">
+		<constant name="TYPE_PACKED_FLOAT64_ARRAY" value="33" enum="Variant.Type">
 			Variable is of type [PackedFloat64Array].
 		</constant>
-		<constant name="TYPE_PACKED_STRING_ARRAY" value="31" enum="Variant.Type">
+		<constant name="TYPE_PACKED_STRING_ARRAY" value="34" enum="Variant.Type">
 			Variable is of type [PackedStringArray].
 		</constant>
-		<constant name="TYPE_PACKED_VECTOR2_ARRAY" value="32" enum="Variant.Type">
+		<constant name="TYPE_PACKED_VECTOR2_ARRAY" value="35" enum="Variant.Type">
 			Variable is of type [PackedVector2Array].
 		</constant>
-		<constant name="TYPE_PACKED_VECTOR3_ARRAY" value="33" enum="Variant.Type">
+		<constant name="TYPE_PACKED_VECTOR3_ARRAY" value="36" enum="Variant.Type">
 			Variable is of type [PackedVector3Array].
 		</constant>
-		<constant name="TYPE_PACKED_COLOR_ARRAY" value="34" enum="Variant.Type">
+		<constant name="TYPE_PACKED_COLOR_ARRAY" value="37" enum="Variant.Type">
 			Variable is of type [PackedColorArray].
 		</constant>
-		<constant name="TYPE_MAX" value="35" enum="Variant.Type">
+		<constant name="TYPE_MAX" value="38" enum="Variant.Type">
 			Represents the size of the [enum Variant.Type] enum.
 		</constant>
 		<constant name="OP_EQUAL" value="0" enum="Variant.Operator">

+ 4 - 4
doc/classes/Camera3D.xml

@@ -189,7 +189,7 @@
 		<member name="near" type="float" setter="set_near" getter="get_near" default="0.05">
 			The distance to the near culling boundary for this camera relative to its local Z axis.
 		</member>
-		<member name="projection" type="int" setter="set_projection" getter="get_projection" enum="Camera3D.Projection" default="0">
+		<member name="projection" type="int" setter="set_projection" getter="get_projection" enum="Camera3D.ProjectionType" default="0">
 			The camera's projection mode. In [constant PROJECTION_PERSPECTIVE] mode, objects' Z distance from the camera's local space scales their perceived size.
 		</member>
 		<member name="size" type="float" setter="set_size" getter="get_size" default="1.0">
@@ -200,13 +200,13 @@
 		</member>
 	</members>
 	<constants>
-		<constant name="PROJECTION_PERSPECTIVE" value="0" enum="Projection">
+		<constant name="PROJECTION_PERSPECTIVE" value="0" enum="ProjectionType">
 			Perspective projection. Objects on the screen becomes smaller when they are far away.
 		</constant>
-		<constant name="PROJECTION_ORTHOGONAL" value="1" enum="Projection">
+		<constant name="PROJECTION_ORTHOGONAL" value="1" enum="ProjectionType">
 			Orthogonal projection, also known as orthographic projection. Objects remain the same size on the screen no matter how far away they are.
 		</constant>
-		<constant name="PROJECTION_FRUSTUM" value="2" enum="Projection">
+		<constant name="PROJECTION_FRUSTUM" value="2" enum="ProjectionType">
 			Frustum projection. This mode allows adjusting [member frustum_offset] to create "tilted frustum" effects.
 		</constant>
 		<constant name="KEEP_WIDTH" value="0" enum="KeepAspect">

+ 0 - 1
doc/classes/Label.xml

@@ -50,7 +50,6 @@
 			Controls the text's horizontal alignment. Supports left, center, right, and fill, or justify. Set it to one of the [enum HorizontalAlignment] constants.
 		</member>
 		<member name="label_settings" type="LabelSettings" setter="set_label_settings" getter="get_label_settings">
-			Resource to override [Theme] font, outline and shadow properties.
 		</member>
 		<member name="language" type="String" setter="set_language" getter="get_language" default="&quot;&quot;">
 			Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.

+ 0 - 10
doc/classes/LabelSettings.xml

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <class name="LabelSettings" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
 	<brief_description>
-		Resource to override [Theme] font, outline and shadow properties of the [Label].
 	</brief_description>
 	<description>
 	</description>
@@ -9,31 +8,22 @@
 	</tutorials>
 	<members>
 		<member name="font" type="Font" setter="set_font" getter="get_font">
-			[Font] of the [Label]'s text.
 		</member>
 		<member name="font_color" type="Color" setter="set_font_color" getter="get_font_color" default="Color(0.875, 0.875, 0.875, 1)">
-			Default text [Color] of the [Label].
 		</member>
 		<member name="font_size" type="int" setter="set_font_size" getter="get_font_size" default="16">
-			Font size of the [Label]'s text.
 		</member>
 		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="0.0">
-			Vertical space between lines in multiline text.
 		</member>
 		<member name="outline_color" type="Color" setter="set_outline_color" getter="get_outline_color" default="Color(1, 1, 1, 1)">
-			The tint of text outline.
 		</member>
 		<member name="outline_size" type="int" setter="set_outline_size" getter="get_outline_size" default="0">
-			Text outline size.
 		</member>
 		<member name="shadow_color" type="Color" setter="set_shadow_color" getter="get_shadow_color" default="Color(1, 1, 1, 1)">
-			The tint of text shadow.
 		</member>
 		<member name="shadow_offset" type="Vector2" setter="set_shadow_offset" getter="get_shadow_offset" default="Vector2(1, 1)">
-			The offset of the text's shadow.
 		</member>
 		<member name="shadow_size" type="int" setter="set_shadow_size" getter="get_shadow_size" default="0">
-			The size of the text's shadow.
 		</member>
 	</members>
 </class>

+ 273 - 0
doc/classes/Projection.xml

@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Projection" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+	</brief_description>
+	<description>
+	</description>
+	<tutorials>
+	</tutorials>
+	<constructors>
+		<constructor name="Projection">
+			<return type="Projection" />
+			<description>
+			</description>
+		</constructor>
+		<constructor name="Projection">
+			<return type="Projection" />
+			<argument index="0" name="from" type="Projection" />
+			<description>
+			</description>
+		</constructor>
+		<constructor name="Projection">
+			<return type="Projection" />
+			<argument index="0" name="from" type="Transform3D" />
+			<description>
+			</description>
+		</constructor>
+	</constructors>
+	<methods>
+		<method name="create_depth_correction" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="flip_y" type="bool" />
+			<description>
+			</description>
+		</method>
+		<method name="create_fit_aabb" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="aabb" type="AABB" />
+			<description>
+			</description>
+		</method>
+		<method name="create_for_hmd" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="eye" type="int" />
+			<argument index="1" name="aspect" type="float" />
+			<argument index="2" name="intraocular_dist" type="float" />
+			<argument index="3" name="display_width" type="float" />
+			<argument index="4" name="display_to_lens" type="float" />
+			<argument index="5" name="oversample" type="float" />
+			<argument index="6" name="z_near" type="float" />
+			<argument index="7" name="z_far" type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="create_frustum" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="left" type="float" />
+			<argument index="1" name="right" type="float" />
+			<argument index="2" name="bottom" type="float" />
+			<argument index="3" name="top" type="float" />
+			<argument index="4" name="z_near" type="float" />
+			<argument index="5" name="z_far" type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="create_frustum_aspect" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="size" type="float" />
+			<argument index="1" name="aspect" type="float" />
+			<argument index="2" name="offset" type="Vector2" />
+			<argument index="3" name="z_near" type="float" />
+			<argument index="4" name="z_far" type="float" />
+			<argument index="5" name="flip_fov" type="bool" default="false" />
+			<description>
+			</description>
+		</method>
+		<method name="create_light_atlas_rect" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="rect" type="Rect2" />
+			<description>
+			</description>
+		</method>
+		<method name="create_orthogonal" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="left" type="float" />
+			<argument index="1" name="right" type="float" />
+			<argument index="2" name="bottom" type="float" />
+			<argument index="3" name="top" type="float" />
+			<argument index="4" name="z_near" type="float" />
+			<argument index="5" name="z_far" type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="create_orthogonal_aspect" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="size" type="float" />
+			<argument index="1" name="aspect" type="float" />
+			<argument index="2" name="z_near" type="float" />
+			<argument index="3" name="z_far" type="float" />
+			<argument index="4" name="flip_fov" type="bool" default="false" />
+			<description>
+			</description>
+		</method>
+		<method name="create_perspective" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="fovy" type="float" />
+			<argument index="1" name="aspect" type="float" />
+			<argument index="2" name="z_near" type="float" />
+			<argument index="3" name="z_far" type="float" />
+			<argument index="4" name="flip_fov" type="bool" default="false" />
+			<description>
+			</description>
+		</method>
+		<method name="create_perspective_hmd" qualifiers="static">
+			<return type="Projection" />
+			<argument index="0" name="fovy" type="float" />
+			<argument index="1" name="aspect" type="float" />
+			<argument index="2" name="z_near" type="float" />
+			<argument index="3" name="z_far" type="float" />
+			<argument index="4" name="flip_fov" type="bool" />
+			<argument index="5" name="eye" type="int" />
+			<argument index="6" name="intraocular_dist" type="float" />
+			<argument index="7" name=" convergence_dist" type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="determinant" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="flipped_y" qualifiers="const">
+			<return type="Projection" />
+			<description>
+			</description>
+		</method>
+		<method name="get_aspect" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="get_far_plane_half_extents" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+			</description>
+		</method>
+		<method name="get_fov" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="get_fovy" qualifiers="static">
+			<return type="float" />
+			<argument index="0" name="fovx" type="float" />
+			<argument index="1" name="aspect" type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="get_lod_multiplier" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="get_pixels_per_meter" qualifiers="const">
+			<return type="int" />
+			<argument index="0" name="for_pixel_width" type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="get_projection_plane" qualifiers="const">
+			<return type="Plane" />
+			<argument index="0" name="plane" type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="get_viewport_half_extents" qualifiers="const">
+			<return type="Vector2" />
+			<description>
+			</description>
+		</method>
+		<method name="get_z_far" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="get_z_near" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="inverse" qualifiers="const">
+			<return type="Projection" />
+			<description>
+			</description>
+		</method>
+		<method name="is_orthogonal" qualifiers="const">
+			<return type="bool" />
+			<description>
+			</description>
+		</method>
+		<method name="jitter_offseted" qualifiers="const">
+			<return type="Projection" />
+			<argument index="0" name="offset" type="Vector2" />
+			<description>
+			</description>
+		</method>
+		<method name="perspective_znear_adjusted" qualifiers="const">
+			<return type="Projection" />
+			<argument index="0" name="new_znear" type="float" />
+			<description>
+			</description>
+		</method>
+	</methods>
+	<members>
+		<member name="w" type="Vector4" setter="" getter="" default="Vector4(0, 0, 0)">
+		</member>
+		<member name="x" type="Vector4" setter="" getter="" default="Vector4(1, 0, 0)">
+		</member>
+		<member name="y" type="Vector4" setter="" getter="" default="Vector4(0, 1, 0)">
+		</member>
+		<member name="z" type="Vector4" setter="" getter="" default="Vector4(0, 0, 1)">
+		</member>
+	</members>
+	<constants>
+		<constant name="PLANE_NEAR" value="0">
+		</constant>
+		<constant name="PLANE_FAR" value="1">
+		</constant>
+		<constant name="PLANE_LEFT" value="2">
+		</constant>
+		<constant name="PLANE_TOP" value="3">
+		</constant>
+		<constant name="PLANE_RIGHT" value="4">
+		</constant>
+		<constant name="PLANE_BOTTOM" value="5">
+		</constant>
+		<constant name="IDENTITY" value="Projection(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)">
+		</constant>
+		<constant name="ZERO" value="Projection(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)">
+		</constant>
+	</constants>
+	<operators>
+		<operator name="operator !=">
+			<return type="bool" />
+			<argument index="0" name="right" type="Projection" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Projection" />
+			<argument index="0" name="right" type="Projection" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator ==">
+			<return type="bool" />
+			<argument index="0" name="right" type="Projection" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator []">
+			<return type="Vector4" />
+			<argument index="0" name="index" type="int" />
+			<description>
+			</description>
+		</operator>
+	</operators>
+</class>

+ 6 - 0
doc/classes/Transform3D.xml

@@ -37,6 +37,12 @@
 				Constructs a Transform3D from a [Basis] and [Vector3].
 			</description>
 		</constructor>
+		<constructor name="Transform3D">
+			<return type="Transform3D" />
+			<argument index="0" name="from" type="Projection" />
+			<description>
+			</description>
+		</constructor>
 		<constructor name="Transform3D">
 			<return type="Transform3D" />
 			<argument index="0" name="x_axis" type="Vector3" />

+ 231 - 0
doc/classes/Vector4.xml

@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Vector4" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+	</brief_description>
+	<description>
+	</description>
+	<tutorials>
+	</tutorials>
+	<constructors>
+		<constructor name="Vector4">
+			<return type="Vector4" />
+			<description>
+			</description>
+		</constructor>
+		<constructor name="Vector4">
+			<return type="Vector4" />
+			<argument index="0" name="from" type="Vector4" />
+			<description>
+			</description>
+		</constructor>
+		<constructor name="Vector4">
+			<return type="Vector4" />
+			<argument index="0" name="from" type="Vector4i" />
+			<description>
+			</description>
+		</constructor>
+		<constructor name="Vector4">
+			<return type="Vector4" />
+			<argument index="0" name="x" type="float" />
+			<argument index="1" name="y" type="float" />
+			<argument index="2" name="z" type="float" />
+			<argument index="3" name="w" type="float" />
+			<description>
+			</description>
+		</constructor>
+	</constructors>
+	<methods>
+		<method name="abs" qualifiers="const">
+			<return type="Vector4" />
+			<description>
+			</description>
+		</method>
+		<method name="clamp" qualifiers="const">
+			<return type="Vector4" />
+			<argument index="0" name="min" type="Vector4" />
+			<argument index="1" name="max" type="Vector4" />
+			<description>
+			</description>
+		</method>
+		<method name="dot" qualifiers="const">
+			<return type="float" />
+			<argument index="0" name="with" type="Vector4" />
+			<description>
+			</description>
+		</method>
+		<method name="inverse" qualifiers="const">
+			<return type="Vector4" />
+			<description>
+			</description>
+		</method>
+		<method name="is_equal_approx" qualifiers="const">
+			<return type="bool" />
+			<argument index="0" name="with" type="Vector4" />
+			<description>
+			</description>
+		</method>
+		<method name="is_normalized" qualifiers="const">
+			<return type="bool" />
+			<description>
+			</description>
+		</method>
+		<method name="length" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="length_squared" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="max_axis_index" qualifiers="const">
+			<return type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="min_axis_index" qualifiers="const">
+			<return type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="normalized" qualifiers="const">
+			<return type="Vector4" />
+			<description>
+			</description>
+		</method>
+		<method name="sign" qualifiers="const">
+			<return type="Vector4" />
+			<description>
+			</description>
+		</method>
+	</methods>
+	<members>
+		<member name="w" type="float" setter="" getter="" default="0.0">
+		</member>
+		<member name="x" type="float" setter="" getter="" default="0.0">
+		</member>
+		<member name="y" type="float" setter="" getter="" default="0.0">
+		</member>
+		<member name="z" type="float" setter="" getter="" default="0.0">
+		</member>
+	</members>
+	<constants>
+		<constant name="AXIS_X" value="0">
+		</constant>
+		<constant name="AXIS_Y" value="1">
+		</constant>
+		<constant name="AXIS_Z" value="2">
+		</constant>
+		<constant name="AXIS_W" value="3">
+		</constant>
+		<constant name="ZERO" value="Vector4(0, 0, 0)">
+		</constant>
+		<constant name="ONE" value="Vector4(1, 1, 1)">
+		</constant>
+		<constant name="INF" value="Vector4(inf, inf, inf)">
+		</constant>
+	</constants>
+	<operators>
+		<operator name="operator !=">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Projection" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="float" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="int" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator +">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator -">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator /">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator /">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="float" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator /">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="int" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator &lt;">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator &lt;=">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator ==">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator &gt;">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator &gt;=">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator unary+">
+			<return type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator unary-">
+			<return type="Vector4" />
+			<description>
+			</description>
+		</operator>
+	</operators>
+</class>

+ 208 - 0
doc/classes/Vector4i.xml

@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Vector4i" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+	</brief_description>
+	<description>
+	</description>
+	<tutorials>
+	</tutorials>
+	<constructors>
+		<constructor name="Vector4i">
+			<return type="Vector4i" />
+			<description>
+			</description>
+		</constructor>
+		<constructor name="Vector4i">
+			<return type="Vector4i" />
+			<argument index="0" name="from" type="Vector4i" />
+			<description>
+			</description>
+		</constructor>
+		<constructor name="Vector4i">
+			<return type="Vector4i" />
+			<argument index="0" name="from" type="Vector4" />
+			<description>
+			</description>
+		</constructor>
+		<constructor name="Vector4i">
+			<return type="Vector4i" />
+			<argument index="0" name="x" type="int" />
+			<argument index="1" name="y" type="int" />
+			<argument index="2" name="z" type="int" />
+			<argument index="3" name="w" type="int" />
+			<description>
+			</description>
+		</constructor>
+	</constructors>
+	<methods>
+		<method name="abs" qualifiers="const">
+			<return type="Vector4i" />
+			<description>
+			</description>
+		</method>
+		<method name="clamp" qualifiers="const">
+			<return type="Vector4i" />
+			<argument index="0" name="min" type="Vector4i" />
+			<argument index="1" name="max" type="Vector4i" />
+			<description>
+			</description>
+		</method>
+		<method name="length" qualifiers="const">
+			<return type="float" />
+			<description>
+			</description>
+		</method>
+		<method name="length_squared" qualifiers="const">
+			<return type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="max_axis_index" qualifiers="const">
+			<return type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="min_axis_index" qualifiers="const">
+			<return type="int" />
+			<description>
+			</description>
+		</method>
+		<method name="sign" qualifiers="const">
+			<return type="Vector4i" />
+			<description>
+			</description>
+		</method>
+	</methods>
+	<members>
+		<member name="w" type="int" setter="" getter="" default="0">
+		</member>
+		<member name="x" type="int" setter="" getter="" default="0">
+		</member>
+		<member name="y" type="int" setter="" getter="" default="0">
+		</member>
+		<member name="z" type="int" setter="" getter="" default="0">
+		</member>
+	</members>
+	<constants>
+		<constant name="AXIS_X" value="0">
+		</constant>
+		<constant name="AXIS_Y" value="1">
+		</constant>
+		<constant name="AXIS_Z" value="2">
+		</constant>
+		<constant name="AXIS_W" value="3">
+		</constant>
+		<constant name="ZERO" value="Vector4i(0, 0, 0)">
+		</constant>
+		<constant name="ONE" value="Vector4i(1, 1, 1)">
+		</constant>
+	</constants>
+	<operators>
+		<operator name="operator !=">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator %">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator %">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="int" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="float" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="int" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator +">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator -">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator /">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator /">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="float" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator /">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="int" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator &lt;">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator &lt;=">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator ==">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator &gt;">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator &gt;=">
+			<return type="bool" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator unary+">
+			<return type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator unary-">
+			<return type="Vector4i" />
+			<description>
+			</description>
+		</operator>
+	</operators>
+</class>

+ 12 - 0
doc/classes/float.xml

@@ -110,6 +110,18 @@
 				[/codeblock]
 			</description>
 		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
 		<operator name="operator *">
 			<return type="float" />
 			<argument index="0" name="right" type="float" />

+ 12 - 0
doc/classes/int.xml

@@ -157,6 +157,18 @@
 				Multiplies each component of the [Vector3i] by the given [int].
 			</description>
 		</operator>
+		<operator name="operator *">
+			<return type="Vector4" />
+			<argument index="0" name="right" type="Vector4" />
+			<description>
+			</description>
+		</operator>
+		<operator name="operator *">
+			<return type="Vector4i" />
+			<argument index="0" name="right" type="Vector4i" />
+			<description>
+			</description>
+		</operator>
 		<operator name="operator *">
 			<return type="float" />
 			<argument index="0" name="right" type="float" />

+ 1 - 1
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -1217,7 +1217,7 @@ void RasterizerCanvasGLES3::reset_canvas() {
 void RasterizerCanvasGLES3::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {
 }
 
-void RasterizerCanvasGLES3::canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) {
+void RasterizerCanvasGLES3::canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, Projection *p_xform_cache) {
 }
 
 void RasterizerCanvasGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) {

+ 1 - 1
drivers/gles3/rasterizer_canvas_gles3.h

@@ -209,7 +209,7 @@ public:
 	void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
 
 	void reset_canvas();
-	void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache);
+	void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, Projection *p_xform_cache);
 
 	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override;
 

+ 12 - 12
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -715,7 +715,7 @@ void RasterizerSceneGLES3::_update_dirty_skys() {
 	dirty_sky_list = nullptr;
 }
 
-void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) {
+void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) {
 	GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton();
 	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
 	ERR_FAIL_COND(!p_env);
@@ -861,7 +861,7 @@ void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers,
 	}
 }
 
-void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform) {
+void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const Projection &p_projection, const Transform3D &p_transform) {
 	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
 	ERR_FAIL_COND(!p_env);
 
@@ -901,7 +901,7 @@ void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_p
 	ERR_FAIL_COND(!shader_data);
 
 	// Camera
-	CameraMatrix camera;
+	Projection camera;
 
 	if (p_env->sky_custom_fov) {
 		float near_plane = p_projection.get_z_near();
@@ -926,7 +926,7 @@ void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_p
 	glDrawArrays(GL_TRIANGLES, 0, 3);
 }
 
-void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform) {
+void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const Projection &p_projection, const Transform3D &p_transform) {
 	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
 	ERR_FAIL_COND(!p_env);
 
@@ -1008,9 +1008,9 @@ void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const Camera
 			Vector3(0, -1, 0)
 		};
 
-		CameraMatrix cm;
+		Projection cm;
 		cm.set_perspective(90, 1, 0.01, 10.0);
-		CameraMatrix correction;
+		Projection correction;
 		correction.set_depth_correction(true);
 		cm = correction * cm;
 
@@ -1345,7 +1345,7 @@ void RasterizerSceneGLES3::light_instance_set_aabb(RID p_light_instance, const A
 	light_instance->aabb = p_aabb;
 }
 
-void RasterizerSceneGLES3::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) {
+void RasterizerSceneGLES3::light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) {
 }
 
 void RasterizerSceneGLES3::light_instance_mark_visible(RID p_light_instance) {
@@ -1608,9 +1608,9 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
 
 // Needs to be called after _setup_lights so that directional_light_count is accurate.
 void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows) {
-	CameraMatrix correction;
+	Projection correction;
 	correction.set_depth_correction(p_flip_y);
-	CameraMatrix projection = correction * p_render_data->cam_projection;
+	Projection projection = correction * p_render_data->cam_projection;
 	//store camera into ubo
 	GLES3::MaterialStorage::store_camera(projection, scene_state.ubo.projection_matrix);
 	GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix);
@@ -2057,9 +2057,9 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *
 		// setup sky if used for ambient, reflections, or background
 		if (draw_sky || draw_sky_fog_only || env->reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || env->ambient_source == RS::ENV_AMBIENT_SOURCE_SKY) {
 			RENDER_TIMESTAMP("Setup Sky");
-			CameraMatrix projection = render_data.cam_projection;
+			Projection projection = render_data.cam_projection;
 			if (render_data.reflection_probe.is_valid()) {
-				CameraMatrix correction;
+				Projection correction;
 				correction.set_depth_correction(true);
 				projection = correction * render_data.cam_projection;
 			}
@@ -2490,7 +2490,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 	}
 }
 
-void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) {
+void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) {
 }
 
 void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) {

+ 8 - 8
drivers/gles3/rasterizer_scene_gles3.h

@@ -33,7 +33,7 @@
 
 #ifdef GLES3_ENABLED
 
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
 #include "core/templates/paged_allocator.h"
 #include "core/templates/rid_owner.h"
 #include "core/templates/self_list.h"
@@ -96,13 +96,13 @@ struct RenderDataGLES3 {
 
 	Transform3D cam_transform = Transform3D();
 	Transform3D inv_cam_transform = Transform3D();
-	CameraMatrix cam_projection = CameraMatrix();
+	Projection cam_projection = Projection();
 	bool cam_orthogonal = false;
 
 	// For stereo rendering
 	uint32_t view_count = 1;
 	Vector3 view_eye_offset[RendererSceneRender::MAX_RENDER_VIEWS];
-	CameraMatrix view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
+	Projection view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
 
 	float z_near = 0.0;
 	float z_far = 0.0;
@@ -729,12 +729,12 @@ protected:
 	Sky *dirty_sky_list = nullptr;
 	mutable RID_Owner<Sky, true> sky_owner;
 
-	void _setup_sky(Environment *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size);
+	void _setup_sky(Environment *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size);
 	void _invalidate_sky(Sky *p_sky);
 	void _update_dirty_skys();
-	void _update_sky_radiance(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform);
+	void _update_sky_radiance(Environment *p_env, const Projection &p_projection, const Transform3D &p_transform);
 	void _filter_sky_radiance(Sky *p_sky, int p_base_layer);
-	void _draw_sky(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform);
+	void _draw_sky(Environment *p_env, const Projection &p_projection, const Transform3D &p_transform);
 	void _free_sky_data(Sky *p_sky);
 
 public:
@@ -860,7 +860,7 @@ public:
 	RID light_instance_create(RID p_light) override;
 	void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override;
 	void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override;
-	void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override;
+	void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override;
 	void light_instance_mark_visible(RID p_light_instance) override;
 
 	_FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) {
@@ -904,7 +904,7 @@ public:
 	void voxel_gi_set_quality(RS::VoxelGIQuality) override;
 
 	void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override;
-	void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
+	void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
 	void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) override;
 
 	void set_scene_pass(uint64_t p_pass) override {

+ 1 - 1
drivers/gles3/shader_gles3.h

@@ -31,7 +31,7 @@
 #ifndef SHADER_OPENGL_H
 #define SHADER_OPENGL_H
 
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
 #include "core/os/mutex.h"
 #include "core/string/string_builder.h"
 #include "core/templates/hash_map.h"

+ 167 - 107
drivers/gles3/storage/material_storage.cpp

@@ -176,75 +176,98 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
 			}
 		} break;
 		case ShaderLanguage::TYPE_IVEC2: {
-			Vector<int> iv = value;
-			int s = iv.size();
 			int32_t *gui = (int32_t *)data;
+			if (p_array_size > 0) {
+				Vector<int> iv = value;
+				int s = iv.size();
 
-			if (p_array_size <= 0) {
-				p_array_size = 1;
-			}
-			int count = 2 * p_array_size;
+				if (p_array_size <= 0) {
+					p_array_size = 1;
+				}
+				int count = 2 * p_array_size;
 
-			const int *r = iv.ptr();
-			for (int i = 0, j = 0; i < count; i += 2, j += 4) {
-				if (i < s) {
-					gui[j] = r[i];
-					gui[j + 1] = r[i + 1];
-				} else {
-					gui[j] = 0;
-					gui[j + 1] = 0;
+				const int *r = iv.ptr();
+				for (int i = 0, j = 0; i < count; i += 2, j += 4) {
+					if (i < s) {
+						gui[j] = r[i];
+						gui[j + 1] = r[i + 1];
+					} else {
+						gui[j] = 0;
+						gui[j + 1] = 0;
+					}
+					gui[j + 2] = 0; // ignored
+					gui[j + 3] = 0; // ignored
 				}
-				gui[j + 2] = 0; // ignored
-				gui[j + 3] = 0; // ignored
+			} else {
+				Vector2i v = value;
+				gui[0] = v.x;
+				gui[1] = v.y;
 			}
 		} break;
 		case ShaderLanguage::TYPE_IVEC3: {
-			Vector<int> iv = value;
-			int s = iv.size();
 			int32_t *gui = (int32_t *)data;
 
-			if (p_array_size <= 0) {
-				p_array_size = 1;
-			}
-			int count = 3 * p_array_size;
-
-			const int *r = iv.ptr();
-			for (int i = 0, j = 0; i < count; i += 3, j += 4) {
-				if (i < s) {
-					gui[j] = r[i];
-					gui[j + 1] = r[i + 1];
-					gui[j + 2] = r[i + 2];
-				} else {
-					gui[j] = 0;
-					gui[j + 1] = 0;
-					gui[j + 2] = 0;
+			if (p_array_size > 0) {
+				Vector<int> iv = value;
+				int s = iv.size();
+
+				if (p_array_size <= 0) {
+					p_array_size = 1;
 				}
-				gui[j + 3] = 0; // ignored
+				int count = 3 * p_array_size;
+
+				const int *r = iv.ptr();
+				for (int i = 0, j = 0; i < count; i += 3, j += 4) {
+					if (i < s) {
+						gui[j] = r[i];
+						gui[j + 1] = r[i + 1];
+						gui[j + 2] = r[i + 2];
+					} else {
+						gui[j] = 0;
+						gui[j + 1] = 0;
+						gui[j + 2] = 0;
+					}
+					gui[j + 3] = 0; // ignored
+				}
+			} else {
+				Vector3i v = value;
+				gui[0] = v.x;
+				gui[1] = v.y;
+				gui[2] = v.z;
 			}
 		} break;
 		case ShaderLanguage::TYPE_IVEC4: {
-			Vector<int> iv = value;
-			int s = iv.size();
 			int32_t *gui = (int32_t *)data;
 
-			if (p_array_size <= 0) {
-				p_array_size = 1;
-			}
-			int count = 4 * p_array_size;
-
-			const int *r = iv.ptr();
-			for (int i = 0; i < count; i += 4) {
-				if (i < s) {
-					gui[i] = r[i];
-					gui[i + 1] = r[i + 1];
-					gui[i + 2] = r[i + 2];
-					gui[i + 3] = r[i + 3];
-				} else {
-					gui[i] = 0;
-					gui[i + 1] = 0;
-					gui[i + 2] = 0;
-					gui[i + 3] = 0;
+			if (p_array_size > 0) {
+				Vector<int> iv = value;
+				int s = iv.size();
+
+				if (p_array_size <= 0) {
+					p_array_size = 1;
 				}
+				int count = 4 * p_array_size;
+
+				const int *r = iv.ptr();
+				for (int i = 0; i < count; i += 4) {
+					if (i < s) {
+						gui[i] = r[i];
+						gui[i + 1] = r[i + 1];
+						gui[i + 2] = r[i + 2];
+						gui[i + 3] = r[i + 3];
+					} else {
+						gui[i] = 0;
+						gui[i + 1] = 0;
+						gui[i + 2] = 0;
+						gui[i + 3] = 0;
+					}
+				}
+			} else {
+				Vector4i v = value;
+				gui[0] = v.x;
+				gui[1] = v.y;
+				gui[2] = v.z;
+				gui[3] = v.w;
 			}
 		} break;
 		case ShaderLanguage::TYPE_UINT: {
@@ -271,75 +294,98 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
 			}
 		} break;
 		case ShaderLanguage::TYPE_UVEC2: {
-			Vector<int> iv = value;
-			int s = iv.size();
 			uint32_t *gui = (uint32_t *)data;
+			if (p_array_size > 0) {
+				Vector<int> iv = value;
+				int s = iv.size();
 
-			if (p_array_size <= 0) {
-				p_array_size = 1;
-			}
-			int count = 2 * p_array_size;
+				if (p_array_size <= 0) {
+					p_array_size = 1;
+				}
+				int count = 2 * p_array_size;
 
-			const int *r = iv.ptr();
-			for (int i = 0, j = 0; i < count; i += 2, j += 4) {
-				if (i < s) {
-					gui[j] = r[i];
-					gui[j + 1] = r[i + 1];
-				} else {
-					gui[j] = 0;
-					gui[j + 1] = 0;
+				const int *r = iv.ptr();
+				for (int i = 0, j = 0; i < count; i += 2, j += 4) {
+					if (i < s) {
+						gui[j] = r[i];
+						gui[j + 1] = r[i + 1];
+					} else {
+						gui[j] = 0;
+						gui[j + 1] = 0;
+					}
+					gui[j + 2] = 0; // ignored
+					gui[j + 3] = 0; // ignored
 				}
-				gui[j + 2] = 0; // ignored
-				gui[j + 3] = 0; // ignored
+			} else {
+				Vector2i v = value;
+				gui[0] = v.x;
+				gui[1] = v.y;
 			}
 		} break;
 		case ShaderLanguage::TYPE_UVEC3: {
-			Vector<int> iv = value;
-			int s = iv.size();
 			uint32_t *gui = (uint32_t *)data;
 
-			if (p_array_size <= 0) {
-				p_array_size = 1;
-			}
-			int count = 3 * p_array_size;
-
-			const int *r = iv.ptr();
-			for (int i = 0, j = 0; i < count; i += 3, j += 4) {
-				if (i < s) {
-					gui[j] = r[i];
-					gui[j + 1] = r[i + 1];
-					gui[j + 2] = r[i + 2];
-				} else {
-					gui[j] = 0;
-					gui[j + 1] = 0;
-					gui[j + 2] = 0;
+			if (p_array_size > 0) {
+				Vector<int> iv = value;
+				int s = iv.size();
+
+				if (p_array_size <= 0) {
+					p_array_size = 1;
 				}
-				gui[j + 3] = 0; // ignored
+				int count = 3 * p_array_size;
+
+				const int *r = iv.ptr();
+				for (int i = 0, j = 0; i < count; i += 3, j += 4) {
+					if (i < s) {
+						gui[j] = r[i];
+						gui[j + 1] = r[i + 1];
+						gui[j + 2] = r[i + 2];
+					} else {
+						gui[j] = 0;
+						gui[j + 1] = 0;
+						gui[j + 2] = 0;
+					}
+					gui[j + 3] = 0; // ignored
+				}
+			} else {
+				Vector3i v = value;
+				gui[0] = v.x;
+				gui[1] = v.y;
+				gui[2] = v.z;
 			}
 		} break;
 		case ShaderLanguage::TYPE_UVEC4: {
-			Vector<int> iv = value;
-			int s = iv.size();
 			uint32_t *gui = (uint32_t *)data;
 
-			if (p_array_size <= 0) {
-				p_array_size = 1;
-			}
-			int count = 4 * p_array_size;
-
-			const int *r = iv.ptr();
-			for (int i = 0; i < count; i++) {
-				if (i < s) {
-					gui[i] = r[i];
-					gui[i + 1] = r[i + 1];
-					gui[i + 2] = r[i + 2];
-					gui[i + 3] = r[i + 3];
-				} else {
-					gui[i] = 0;
-					gui[i + 1] = 0;
-					gui[i + 2] = 0;
-					gui[i + 3] = 0;
+			if (p_array_size > 0) {
+				Vector<int> iv = value;
+				int s = iv.size();
+
+				if (p_array_size <= 0) {
+					p_array_size = 1;
 				}
+				int count = 4 * p_array_size;
+
+				const int *r = iv.ptr();
+				for (int i = 0; i < count; i++) {
+					if (i < s) {
+						gui[i] = r[i];
+						gui[i + 1] = r[i + 1];
+						gui[i + 2] = r[i + 2];
+						gui[i + 3] = r[i + 3];
+					} else {
+						gui[i] = 0;
+						gui[i + 1] = 0;
+						gui[i + 2] = 0;
+						gui[i + 3] = 0;
+					}
+				}
+			} else {
+				Vector4i v = value;
+				gui[0] = v.x;
+				gui[1] = v.y;
+				gui[2] = v.z;
+				gui[3] = v.w;
 			}
 		} break;
 		case ShaderLanguage::TYPE_FLOAT: {
@@ -500,6 +546,13 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
 				} else if (value.get_type() == Variant::QUATERNION) {
 					Quaternion v = value;
 
+					gui[0] = v.x;
+					gui[1] = v.y;
+					gui[2] = v.z;
+					gui[3] = v.w;
+				} else if (value.get_type() == Variant::VECTOR4) {
+					Vector4 v = value;
+
 					gui[0] = v.x;
 					gui[1] = v.y;
 					gui[2] = v.z;
@@ -660,7 +713,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
 						gui[i + 15] = 1;
 					}
 				}
-			} else {
+			} else if (value.get_type() == Variant::TRANSFORM3D) {
 				Transform3D v = value;
 				gui[0] = v.basis.rows[0][0];
 				gui[1] = v.basis.rows[1][0];
@@ -681,6 +734,13 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
 				gui[13] = v.origin.y;
 				gui[14] = v.origin.z;
 				gui[15] = 1;
+			} else {
+				Projection v = value;
+				for (int i = 0; i < 4; i++) {
+					for (int j = 0; j < 4; j++) {
+						gui[i * 4 + j] = v.matrix[i][j];
+					}
+				}
 			}
 		} break;
 		default: {

+ 1 - 1
drivers/gles3/storage/material_storage.h

@@ -488,7 +488,7 @@ public:
 		p_array[11] = 0;
 	}
 
-	static _FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array) {
+	static _FORCE_INLINE_ void store_camera(const Projection &p_mtx, float *p_array) {
 		for (int i = 0; i < 4; i++) {
 			for (int j = 0; j < 4; j++) {
 				p_array[i * 4 + j] = p_mtx.matrix[i][j];

+ 306 - 0
editor/editor_properties.cpp

@@ -2623,6 +2623,186 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() {
 		set_label_reference(spin[0]); //show text and buttons around this
 	}
 }
+///////////////////// VECTOR4 /////////////////////////
+
+void EditorPropertyVector4::_set_read_only(bool p_read_only) {
+	for (int i = 0; i < 4; i++) {
+		spin[i]->set_read_only(p_read_only);
+	}
+};
+
+void EditorPropertyVector4::_value_changed(double val, const String &p_name) {
+	if (setting) {
+		return;
+	}
+
+	Vector4 p;
+	p.x = spin[0]->get_value();
+	p.y = spin[1]->get_value();
+	p.z = spin[2]->get_value();
+	p.w = spin[3]->get_value();
+	emit_changed(get_edited_property(), p, p_name);
+}
+
+void EditorPropertyVector4::update_property() {
+	Vector4 val = get_edited_object()->get(get_edited_property());
+	setting = true;
+	spin[0]->set_value(val.x);
+	spin[1]->set_value(val.y);
+	spin[2]->set_value(val.z);
+	spin[3]->set_value(val.w);
+	setting = false;
+}
+
+void EditorPropertyVector4::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_ENTER_TREE:
+		case NOTIFICATION_THEME_CHANGED: {
+			const Color *colors = _get_property_colors();
+			for (int i = 0; i < 4; i++) {
+				spin[i]->add_theme_color_override("label_color", colors[i]);
+			}
+		} break;
+	}
+}
+
+void EditorPropertyVector4::_bind_methods() {
+}
+
+void EditorPropertyVector4::setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix) {
+	for (int i = 0; i < 4; i++) {
+		spin[i]->set_min(p_min);
+		spin[i]->set_max(p_max);
+		spin[i]->set_step(p_step);
+		spin[i]->set_hide_slider(p_no_slider);
+		spin[i]->set_allow_greater(true);
+		spin[i]->set_allow_lesser(true);
+		// Vector4 is inherently unitless, however someone may want to use it as
+		// a generic way to store 4 values, so we'll still respect the suffix.
+		spin[i]->set_suffix(p_suffix);
+	}
+}
+
+EditorPropertyVector4::EditorPropertyVector4() {
+	bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing");
+
+	BoxContainer *bc;
+
+	if (horizontal) {
+		bc = memnew(HBoxContainer);
+		add_child(bc);
+		set_bottom_editor(bc);
+	} else {
+		bc = memnew(VBoxContainer);
+		add_child(bc);
+	}
+
+	static const char *desc[4] = { "x", "y", "z", "w" };
+	for (int i = 0; i < 4; i++) {
+		spin[i] = memnew(EditorSpinSlider);
+		spin[i]->set_flat(true);
+		spin[i]->set_label(desc[i]);
+		bc->add_child(spin[i]);
+		add_focusable(spin[i]);
+		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector4::_value_changed), varray(desc[i]));
+		if (horizontal) {
+			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+		}
+	}
+
+	if (!horizontal) {
+		set_label_reference(spin[0]); //show text and buttons around this
+	}
+}
+
+///////////////////// VECTOR4I /////////////////////////
+
+void EditorPropertyVector4i::_set_read_only(bool p_read_only) {
+	for (int i = 0; i < 4; i++) {
+		spin[i]->set_read_only(p_read_only);
+	}
+};
+
+void EditorPropertyVector4i::_value_changed(double val, const String &p_name) {
+	if (setting) {
+		return;
+	}
+
+	Vector4i p;
+	p.x = spin[0]->get_value();
+	p.y = spin[1]->get_value();
+	p.z = spin[2]->get_value();
+	p.w = spin[3]->get_value();
+	emit_changed(get_edited_property(), p, p_name);
+}
+
+void EditorPropertyVector4i::update_property() {
+	Vector4i val = get_edited_object()->get(get_edited_property());
+	setting = true;
+	spin[0]->set_value(val.x);
+	spin[1]->set_value(val.y);
+	spin[2]->set_value(val.z);
+	spin[3]->set_value(val.w);
+	setting = false;
+}
+
+void EditorPropertyVector4i::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_ENTER_TREE:
+		case NOTIFICATION_THEME_CHANGED: {
+			const Color *colors = _get_property_colors();
+			for (int i = 0; i < 4; i++) {
+				spin[i]->add_theme_color_override("label_color", colors[i]);
+			}
+		} break;
+	}
+}
+
+void EditorPropertyVector4i::_bind_methods() {
+}
+
+void EditorPropertyVector4i::setup(double p_min, double p_max, bool p_no_slider, const String &p_suffix) {
+	for (int i = 0; i < 4; i++) {
+		spin[i]->set_min(p_min);
+		spin[i]->set_max(p_max);
+		spin[i]->set_hide_slider(p_no_slider);
+		spin[i]->set_allow_greater(true);
+		spin[i]->set_allow_lesser(true);
+		spin[i]->set_suffix(p_suffix);
+	}
+}
+
+EditorPropertyVector4i::EditorPropertyVector4i() {
+	bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing");
+
+	BoxContainer *bc;
+
+	if (horizontal) {
+		bc = memnew(HBoxContainer);
+		add_child(bc);
+		set_bottom_editor(bc);
+	} else {
+		bc = memnew(VBoxContainer);
+		add_child(bc);
+	}
+
+	static const char *desc[4] = { "x", "y", "z", "w" };
+	for (int i = 0; i < 4; i++) {
+		spin[i] = memnew(EditorSpinSlider);
+		spin[i]->set_flat(true);
+		spin[i]->set_label(desc[i]);
+		bc->add_child(spin[i]);
+		add_focusable(spin[i]);
+		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector4i::_value_changed), varray(desc[i]));
+		if (horizontal) {
+			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+		}
+	}
+
+	if (!horizontal) {
+		set_label_reference(spin[0]); //show text and buttons around this
+	}
+}
 
 ///////////////////// AABB /////////////////////////
 
@@ -2986,6 +3166,111 @@ EditorPropertyTransform3D::EditorPropertyTransform3D() {
 	set_bottom_editor(g);
 }
 
+///////////////////// PROJECTION /////////////////////////
+
+void EditorPropertyProjection::_set_read_only(bool p_read_only) {
+	for (int i = 0; i < 12; i++) {
+		spin[i]->set_read_only(p_read_only);
+	}
+};
+
+void EditorPropertyProjection::_value_changed(double val, const String &p_name) {
+	if (setting) {
+		return;
+	}
+
+	Projection p;
+	p.matrix[0][0] = spin[0]->get_value();
+	p.matrix[0][1] = spin[1]->get_value();
+	p.matrix[0][2] = spin[2]->get_value();
+	p.matrix[0][3] = spin[3]->get_value();
+	p.matrix[1][0] = spin[4]->get_value();
+	p.matrix[1][1] = spin[5]->get_value();
+	p.matrix[1][2] = spin[6]->get_value();
+	p.matrix[1][3] = spin[7]->get_value();
+	p.matrix[2][0] = spin[8]->get_value();
+	p.matrix[2][1] = spin[9]->get_value();
+	p.matrix[2][2] = spin[10]->get_value();
+	p.matrix[2][3] = spin[11]->get_value();
+	p.matrix[3][0] = spin[12]->get_value();
+	p.matrix[3][1] = spin[13]->get_value();
+	p.matrix[3][2] = spin[14]->get_value();
+	p.matrix[3][3] = spin[15]->get_value();
+
+	emit_changed(get_edited_property(), p, p_name);
+}
+
+void EditorPropertyProjection::update_property() {
+	update_using_transform(get_edited_object()->get(get_edited_property()));
+}
+
+void EditorPropertyProjection::update_using_transform(Projection p_transform) {
+	setting = true;
+	spin[0]->set_value(p_transform.matrix[0][0]);
+	spin[1]->set_value(p_transform.matrix[0][1]);
+	spin[2]->set_value(p_transform.matrix[0][2]);
+	spin[3]->set_value(p_transform.matrix[0][3]);
+	spin[4]->set_value(p_transform.matrix[1][0]);
+	spin[5]->set_value(p_transform.matrix[1][1]);
+	spin[6]->set_value(p_transform.matrix[1][2]);
+	spin[7]->set_value(p_transform.matrix[1][3]);
+	spin[8]->set_value(p_transform.matrix[2][0]);
+	spin[9]->set_value(p_transform.matrix[2][1]);
+	spin[10]->set_value(p_transform.matrix[2][2]);
+	spin[11]->set_value(p_transform.matrix[2][3]);
+	spin[12]->set_value(p_transform.matrix[3][0]);
+	spin[13]->set_value(p_transform.matrix[3][1]);
+	spin[14]->set_value(p_transform.matrix[3][2]);
+	spin[15]->set_value(p_transform.matrix[3][3]);
+	setting = false;
+}
+
+void EditorPropertyProjection::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_ENTER_TREE:
+		case NOTIFICATION_THEME_CHANGED: {
+			const Color *colors = _get_property_colors();
+			for (int i = 0; i < 16; i++) {
+				spin[i]->add_theme_color_override("label_color", colors[i % 4]);
+			}
+		} break;
+	}
+}
+
+void EditorPropertyProjection::_bind_methods() {
+}
+
+void EditorPropertyProjection::setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix) {
+	for (int i = 0; i < 16; i++) {
+		spin[i]->set_min(p_min);
+		spin[i]->set_max(p_max);
+		spin[i]->set_step(p_step);
+		spin[i]->set_hide_slider(p_no_slider);
+		spin[i]->set_allow_greater(true);
+		spin[i]->set_allow_lesser(true);
+		if (i % 4 == 3) {
+			spin[i]->set_suffix(p_suffix);
+		}
+	}
+}
+
+EditorPropertyProjection::EditorPropertyProjection() {
+	GridContainer *g = memnew(GridContainer);
+	g->set_columns(4);
+	add_child(g);
+
+	static const char *desc[16] = { "xx", "xy", "xz", "xw", "yx", "yy", "yz", "yw", "zx", "zy", "zz", "zw", "wx", "wy", "wz", "ww" };
+	for (int i = 0; i < 16; i++) {
+		spin[i] = memnew(EditorSpinSlider);
+		spin[i]->set_label(desc[i]);
+		spin[i]->set_flat(true);
+		g->add_child(spin[i]);
+		spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+		add_focusable(spin[i]);
+		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyProjection::_value_changed), varray(desc[i]));
+	}
+	set_bottom_editor(g);
+}
 ////////////// COLOR PICKER //////////////////////
 
 void EditorPropertyColor::_set_read_only(bool p_read_only) {
@@ -3953,6 +4238,20 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 			editor->setup(hint.min, hint.max, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
 			return editor;
 
+		} break;
+		case Variant::VECTOR4: {
+			EditorPropertyVector4 *editor = memnew(EditorPropertyVector4);
+			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
+			editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
+			return editor;
+
+		} break;
+		case Variant::VECTOR4I: {
+			EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
+			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
+			editor->setup(hint.min, hint.max, hint.hide_slider, hint.suffix);
+			return editor;
+
 		} break;
 		case Variant::TRANSFORM2D: {
 			EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);
@@ -3990,6 +4289,13 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 			editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
 			return editor;
 
+		} break;
+		case Variant::PROJECTION: {
+			EditorPropertyProjection *editor = memnew(EditorPropertyProjection);
+			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
+			editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
+			return editor;
+
 		} break;
 
 		// misc types

+ 52 - 0
editor/editor_properties.h

@@ -636,6 +636,40 @@ public:
 	EditorPropertyQuaternion();
 };
 
+class EditorPropertyVector4 : public EditorProperty {
+	GDCLASS(EditorPropertyVector4, EditorProperty);
+	EditorSpinSlider *spin[4];
+	bool setting = false;
+	void _value_changed(double p_val, const String &p_name);
+
+protected:
+	virtual void _set_read_only(bool p_read_only) override;
+	void _notification(int p_what);
+	static void _bind_methods();
+
+public:
+	virtual void update_property() override;
+	void setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix = String());
+	EditorPropertyVector4();
+};
+
+class EditorPropertyVector4i : public EditorProperty {
+	GDCLASS(EditorPropertyVector4i, EditorProperty);
+	EditorSpinSlider *spin[4];
+	bool setting = false;
+	void _value_changed(double p_val, const String &p_name);
+
+protected:
+	virtual void _set_read_only(bool p_read_only) override;
+	void _notification(int p_what);
+	static void _bind_methods();
+
+public:
+	virtual void update_property() override;
+	void setup(double p_min, double p_max, bool p_no_slider, const String &p_suffix = String());
+	EditorPropertyVector4i();
+};
+
 class EditorPropertyAABB : public EditorProperty {
 	GDCLASS(EditorPropertyAABB, EditorProperty);
 	EditorSpinSlider *spin[6];
@@ -705,6 +739,24 @@ public:
 	EditorPropertyTransform3D();
 };
 
+class EditorPropertyProjection : public EditorProperty {
+	GDCLASS(EditorPropertyProjection, EditorProperty);
+	EditorSpinSlider *spin[16];
+	bool setting = false;
+	void _value_changed(double p_val, const String &p_name);
+
+protected:
+	virtual void _set_read_only(bool p_read_only) override;
+	void _notification(int p_what);
+	static void _bind_methods();
+
+public:
+	virtual void update_property() override;
+	virtual void update_using_transform(Projection p_transform);
+	void setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix = String());
+	EditorPropertyProjection();
+};
+
 class EditorPropertyColor : public EditorProperty {
 	GDCLASS(EditorPropertyColor, EditorProperty);
 	ColorPickerButton *picker = nullptr;

+ 18 - 0
editor/editor_properties_array_dict.cpp

@@ -948,6 +948,18 @@ void EditorPropertyDictionary::update_property() {
 					editor->setup(-100000, 100000, true);
 					prop = editor;
 
+				} break;
+				case Variant::VECTOR4: {
+					EditorPropertyVector4 *editor = memnew(EditorPropertyVector4);
+					editor->setup(-100000, 100000, default_float_step, true);
+					prop = editor;
+
+				} break;
+				case Variant::VECTOR4I: {
+					EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
+					editor->setup(-100000, 100000, true);
+					prop = editor;
+
 				} break;
 				case Variant::TRANSFORM2D: {
 					EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);
@@ -984,6 +996,12 @@ void EditorPropertyDictionary::update_property() {
 					editor->setup(-100000, 100000, default_float_step, true);
 					prop = editor;
 
+				} break;
+				case Variant::PROJECTION: {
+					EditorPropertyProjection *editor = memnew(EditorPropertyProjection);
+					editor->setup(-100000, 100000, default_float_step, true);
+					prop = editor;
+
 				} break;
 
 				// Miscellaneous types.

+ 3 - 3
editor/plugins/node_3d_editor_plugin.cpp

@@ -33,8 +33,8 @@
 #include "core/config/project_settings.h"
 #include "core/input/input.h"
 #include "core/input/input_map.h"
-#include "core/math/camera_matrix.h"
 #include "core/math/math_funcs.h"
+#include "core/math/projection.h"
 #include "core/os/keyboard.h"
 #include "core/templates/sort_array.h"
 #include "editor/debugger/editor_debugger_node.h"
@@ -642,7 +642,7 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe
 }
 
 Vector3 Node3DEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) {
-	CameraMatrix cm;
+	Projection cm;
 	if (orthogonal) {
 		cm.set_orthogonal(camera->get_size(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
 	} else {
@@ -6639,7 +6639,7 @@ void Node3DEditor::_finish_grid() {
 }
 
 void Node3DEditor::update_grid() {
-	const Camera3D::Projection current_projection = viewports[0]->camera->get_projection();
+	const Camera3D::ProjectionType current_projection = viewports[0]->camera->get_projection();
 
 	if (current_projection != grid_camera_last_update_perspective) {
 		grid_init_draw = false; // redraw

+ 1 - 1
editor/plugins/node_3d_editor_plugin.h

@@ -560,7 +560,7 @@ private:
 	bool grid_enable[3]; //should be always visible if true
 	bool grid_enabled = false;
 	bool grid_init_draw = false;
-	Camera3D::Projection grid_camera_last_update_perspective = Camera3D::PROJECTION_PERSPECTIVE;
+	Camera3D::ProjectionType grid_camera_last_update_perspective = Camera3D::PROJECTION_PERSPECTIVE;
 	Vector3 grid_camera_last_update_position = Vector3();
 
 	Ref<ArrayMesh> move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[4], scale_gizmo[3], scale_plane_gizmo[3], axis_gizmo[3];

+ 4 - 4
editor/shader_globals_editor.cpp

@@ -156,7 +156,7 @@ protected:
 					pinfo.type = Variant::VECTOR3I;
 				} break;
 				case RS::GLOBAL_VAR_TYPE_IVEC4: {
-					pinfo.type = Variant::PACKED_INT32_ARRAY;
+					pinfo.type = Variant::VECTOR4I;
 				} break;
 				case RS::GLOBAL_VAR_TYPE_RECT2I: {
 					pinfo.type = Variant::RECT2I;
@@ -171,7 +171,7 @@ protected:
 					pinfo.type = Variant::VECTOR3I;
 				} break;
 				case RS::GLOBAL_VAR_TYPE_UVEC4: {
-					pinfo.type = Variant::PACKED_INT32_ARRAY;
+					pinfo.type = Variant::VECTOR4I;
 				} break;
 				case RS::GLOBAL_VAR_TYPE_FLOAT: {
 					pinfo.type = Variant::FLOAT;
@@ -183,7 +183,7 @@ protected:
 					pinfo.type = Variant::VECTOR3;
 				} break;
 				case RS::GLOBAL_VAR_TYPE_VEC4: {
-					pinfo.type = Variant::QUATERNION;
+					pinfo.type = Variant::VECTOR4;
 				} break;
 				case RS::GLOBAL_VAR_TYPE_RECT2: {
 					pinfo.type = Variant::RECT2;
@@ -204,7 +204,7 @@ protected:
 					pinfo.type = Variant::TRANSFORM3D;
 				} break;
 				case RS::GLOBAL_VAR_TYPE_MAT4: {
-					pinfo.type = Variant::PACKED_INT32_ARRAY;
+					pinfo.type = Variant::PROJECTION;
 				} break;
 				case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
 					pinfo.type = Variant::OBJECT;

+ 1 - 1
gles3_builders.py

@@ -436,7 +436,7 @@ def build_gles3_header(filename, include, class_suffix, output_attribs):
         )
 
         fd.write(
-            """_FORCE_INLINE_ void version_set_uniform(Uniforms p_uniform, const CameraMatrix& p_matrix,RID p_version,ShaderVariant p_variant"""
+            """_FORCE_INLINE_ void version_set_uniform(Uniforms p_uniform, const Projection& p_matrix,RID p_version,ShaderVariant p_variant"""
             + defvariant
             + """,uint64_t p_specialization="""
             + str(defspec)

+ 6 - 0
modules/gdscript/gdscript_analyzer.cpp

@@ -3329,8 +3329,11 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 							case Variant::VECTOR2I:
 							case Variant::VECTOR3:
 							case Variant::VECTOR3I:
+							case Variant::VECTOR4:
+							case Variant::VECTOR4I:
 							case Variant::TRANSFORM2D:
 							case Variant::TRANSFORM3D:
+							case Variant::PROJECTION:
 								error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT &&
 										index_type.builtin_type != Variant::STRING;
 								break;
@@ -3393,6 +3396,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 					case Variant::PACKED_INT64_ARRAY:
 					case Variant::VECTOR2I:
 					case Variant::VECTOR3I:
+					case Variant::VECTOR4I:
 						result_type.builtin_type = Variant::INT;
 						break;
 					// Return float.
@@ -3400,6 +3404,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 					case Variant::PACKED_FLOAT64_ARRAY:
 					case Variant::VECTOR2:
 					case Variant::VECTOR3:
+					case Variant::VECTOR4:
 					case Variant::QUATERNION:
 						result_type.builtin_type = Variant::FLOAT;
 						break;
@@ -3430,6 +3435,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 						break;
 					// Depends on the index.
 					case Variant::TRANSFORM3D:
+					case Variant::PROJECTION:
 					case Variant::PLANE:
 					case Variant::COLOR:
 					case Variant::DICTIONARY:

+ 12 - 0
modules/gdscript/gdscript_byte_codegen.cpp

@@ -84,11 +84,14 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type
 				case Variant::VECTOR3:
 				case Variant::VECTOR3I:
 				case Variant::TRANSFORM2D:
+				case Variant::VECTOR4:
+				case Variant::VECTOR4I:
 				case Variant::PLANE:
 				case Variant::QUATERNION:
 				case Variant::AABB:
 				case Variant::BASIS:
 				case Variant::TRANSFORM3D:
+				case Variant::PROJECTION:
 				case Variant::COLOR:
 				case Variant::STRING_NAME:
 				case Variant::NODE_PATH:
@@ -453,6 +456,12 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia
 		case Variant::TRANSFORM2D:
 			append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM2D, 1);
 			break;
+		case Variant::VECTOR4:
+			append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3, 1);
+			break;
+		case Variant::VECTOR4I:
+			append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3I, 1);
+			break;
 		case Variant::PLANE:
 			append(GDScriptFunction::OPCODE_TYPE_ADJUST_PLANE, 1);
 			break;
@@ -468,6 +477,9 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia
 		case Variant::TRANSFORM3D:
 			append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM3D, 1);
 			break;
+		case Variant::PROJECTION:
+			append(GDScriptFunction::OPCODE_TYPE_ADJUST_PROJECTION, 1);
+			break;
 		case Variant::COLOR:
 			append(GDScriptFunction::OPCODE_TYPE_ADJUST_COLOR, 1);
 			break;

+ 6 - 0
modules/gdscript/gdscript_disassembler.cpp

@@ -639,10 +639,13 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 				DISASSEMBLE_PTRCALL(VECTOR3);
 				DISASSEMBLE_PTRCALL(VECTOR3I);
 				DISASSEMBLE_PTRCALL(TRANSFORM2D);
+				DISASSEMBLE_PTRCALL(VECTOR4);
+				DISASSEMBLE_PTRCALL(VECTOR4I);
 				DISASSEMBLE_PTRCALL(PLANE);
 				DISASSEMBLE_PTRCALL(AABB);
 				DISASSEMBLE_PTRCALL(BASIS);
 				DISASSEMBLE_PTRCALL(TRANSFORM3D);
+				DISASSEMBLE_PTRCALL(PROJECTION);
 				DISASSEMBLE_PTRCALL(COLOR);
 				DISASSEMBLE_PTRCALL(STRING_NAME);
 				DISASSEMBLE_PTRCALL(NODE_PATH);
@@ -1013,11 +1016,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 				DISASSEMBLE_TYPE_ADJUST(VECTOR3);
 				DISASSEMBLE_TYPE_ADJUST(VECTOR3I);
 				DISASSEMBLE_TYPE_ADJUST(TRANSFORM2D);
+				DISASSEMBLE_TYPE_ADJUST(VECTOR4);
+				DISASSEMBLE_TYPE_ADJUST(VECTOR4I);
 				DISASSEMBLE_TYPE_ADJUST(PLANE);
 				DISASSEMBLE_TYPE_ADJUST(QUATERNION);
 				DISASSEMBLE_TYPE_ADJUST(AABB);
 				DISASSEMBLE_TYPE_ADJUST(BASIS);
 				DISASSEMBLE_TYPE_ADJUST(TRANSFORM3D);
+				DISASSEMBLE_TYPE_ADJUST(PROJECTION);
 				DISASSEMBLE_TYPE_ADJUST(COLOR);
 				DISASSEMBLE_TYPE_ADJUST(STRING_NAME);
 				DISASSEMBLE_TYPE_ADJUST(NODE_PATH);

+ 6 - 0
modules/gdscript/gdscript_function.h

@@ -273,11 +273,14 @@ public:
 		OPCODE_CALL_PTRCALL_VECTOR3,
 		OPCODE_CALL_PTRCALL_VECTOR3I,
 		OPCODE_CALL_PTRCALL_TRANSFORM2D,
+		OPCODE_CALL_PTRCALL_VECTOR4,
+		OPCODE_CALL_PTRCALL_VECTOR4I,
 		OPCODE_CALL_PTRCALL_PLANE,
 		OPCODE_CALL_PTRCALL_QUATERNION,
 		OPCODE_CALL_PTRCALL_AABB,
 		OPCODE_CALL_PTRCALL_BASIS,
 		OPCODE_CALL_PTRCALL_TRANSFORM3D,
+		OPCODE_CALL_PTRCALL_PROJECTION,
 		OPCODE_CALL_PTRCALL_COLOR,
 		OPCODE_CALL_PTRCALL_STRING_NAME,
 		OPCODE_CALL_PTRCALL_NODE_PATH,
@@ -363,11 +366,14 @@ public:
 		OPCODE_TYPE_ADJUST_VECTOR3,
 		OPCODE_TYPE_ADJUST_VECTOR3I,
 		OPCODE_TYPE_ADJUST_TRANSFORM2D,
+		OPCODE_TYPE_ADJUST_VECTOR4,
+		OPCODE_TYPE_ADJUST_VECTOR4I,
 		OPCODE_TYPE_ADJUST_PLANE,
 		OPCODE_TYPE_ADJUST_QUATERNION,
 		OPCODE_TYPE_ADJUST_AABB,
 		OPCODE_TYPE_ADJUST_BASIS,
 		OPCODE_TYPE_ADJUST_TRANSFORM3D,
+		OPCODE_TYPE_ADJUST_PROJECTION,
 		OPCODE_TYPE_ADJUST_COLOR,
 		OPCODE_TYPE_ADJUST_STRING_NAME,
 		OPCODE_TYPE_ADJUST_NODE_PATH,

+ 3 - 0
modules/gdscript/gdscript_parser.cpp

@@ -60,11 +60,14 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
 		builtin_types["Transform2D"] = Variant::TRANSFORM2D;
 		builtin_types["Vector3"] = Variant::VECTOR3;
 		builtin_types["Vector3i"] = Variant::VECTOR3I;
+		builtin_types["Vector4"] = Variant::VECTOR3;
+		builtin_types["Vector4i"] = Variant::VECTOR3I;
 		builtin_types["AABB"] = Variant::AABB;
 		builtin_types["Plane"] = Variant::PLANE;
 		builtin_types["Quaternion"] = Variant::QUATERNION;
 		builtin_types["Basis"] = Variant::BASIS;
 		builtin_types["Transform3D"] = Variant::TRANSFORM3D;
+		builtin_types["Projection"] = Variant::PROJECTION;
 		builtin_types["Color"] = Variant::COLOR;
 		builtin_types["RID"] = Variant::RID;
 		builtin_types["Object"] = Variant::OBJECT;

+ 18 - 0
modules/gdscript/gdscript_vm.cpp

@@ -199,11 +199,14 @@ void (*type_init_function_table[])(Variant *) = {
 	&VariantInitializer<Vector3>::init, // VECTOR3.
 	&VariantInitializer<Vector3i>::init, // VECTOR3I.
 	&VariantInitializer<Transform2D>::init, // TRANSFORM2D.
+	&VariantInitializer<Vector4>::init, // VECTOR4.
+	&VariantInitializer<Vector4i>::init, // VECTOR4I.
 	&VariantInitializer<Plane>::init, // PLANE.
 	&VariantInitializer<Quaternion>::init, // QUATERNION.
 	&VariantInitializer<AABB>::init, // AABB.
 	&VariantInitializer<Basis>::init, // BASIS.
 	&VariantInitializer<Transform3D>::init, // TRANSFORM3D.
+	&VariantInitializer<Projection>::init, // PROJECTION.
 	&VariantInitializer<Color>::init, // COLOR.
 	&VariantInitializer<StringName>::init, // STRING_NAME.
 	&VariantInitializer<NodePath>::init, // NODE_PATH.
@@ -282,11 +285,14 @@ void (*type_init_function_table[])(Variant *) = {
 		&&OPCODE_CALL_PTRCALL_VECTOR3,               \
 		&&OPCODE_CALL_PTRCALL_VECTOR3I,              \
 		&&OPCODE_CALL_PTRCALL_TRANSFORM2D,           \
+		&&OPCODE_CALL_PTRCALL_VECTOR4,               \
+		&&OPCODE_CALL_PTRCALL_VECTOR4I,              \
 		&&OPCODE_CALL_PTRCALL_PLANE,                 \
 		&&OPCODE_CALL_PTRCALL_QUATERNION,            \
 		&&OPCODE_CALL_PTRCALL_AABB,                  \
 		&&OPCODE_CALL_PTRCALL_BASIS,                 \
 		&&OPCODE_CALL_PTRCALL_TRANSFORM3D,           \
+		&&OPCODE_CALL_PTRCALL_PROJECTION,            \
 		&&OPCODE_CALL_PTRCALL_COLOR,                 \
 		&&OPCODE_CALL_PTRCALL_STRING_NAME,           \
 		&&OPCODE_CALL_PTRCALL_NODE_PATH,             \
@@ -372,11 +378,14 @@ void (*type_init_function_table[])(Variant *) = {
 		&&OPCODE_TYPE_ADJUST_VECTOR3,                \
 		&&OPCODE_TYPE_ADJUST_VECTOR3I,               \
 		&&OPCODE_TYPE_ADJUST_TRANSFORM2D,            \
+		&&OPCODE_TYPE_ADJUST_VECTOR4,                \
+		&&OPCODE_TYPE_ADJUST_VECTOR4I,               \
 		&&OPCODE_TYPE_ADJUST_PLANE,                  \
 		&&OPCODE_TYPE_ADJUST_QUATERNION,             \
 		&&OPCODE_TYPE_ADJUST_AABB,                   \
 		&&OPCODE_TYPE_ADJUST_BASIS,                  \
 		&&OPCODE_TYPE_ADJUST_TRANSFORM3D,            \
+		&&OPCODE_TYPE_ADJUST_PROJECTION,             \
 		&&OPCODE_TYPE_ADJUST_COLOR,                  \
 		&&OPCODE_TYPE_ADJUST_STRING_NAME,            \
 		&&OPCODE_TYPE_ADJUST_NODE_PATH,              \
@@ -435,6 +444,8 @@ void (*type_init_function_table[])(Variant *) = {
 #define OP_GET_VECTOR3 get_vector3
 #define OP_GET_VECTOR3I get_vector3i
 #define OP_GET_RECT2 get_rect2
+#define OP_GET_VECTOR4 get_vector4
+#define OP_GET_VECTOR4I get_vector4i
 #define OP_GET_RECT2I get_rect2i
 #define OP_GET_QUATERNION get_quaternion
 #define OP_GET_COLOR get_color
@@ -456,6 +467,7 @@ void (*type_init_function_table[])(Variant *) = {
 #define OP_GET_PACKED_COLOR_ARRAY get_color_array
 #define OP_GET_TRANSFORM3D get_transform
 #define OP_GET_TRANSFORM2D get_transform2d
+#define OP_GET_PROJECTION get_projection
 #define OP_GET_PLANE get_plane
 #define OP_GET_AABB get_aabb
 #define OP_GET_BASIS get_basis
@@ -1827,11 +1839,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			OPCODE_CALL_PTR(VECTOR3);
 			OPCODE_CALL_PTR(VECTOR3I);
 			OPCODE_CALL_PTR(TRANSFORM2D);
+			OPCODE_CALL_PTR(VECTOR4);
+			OPCODE_CALL_PTR(VECTOR4I);
 			OPCODE_CALL_PTR(PLANE);
 			OPCODE_CALL_PTR(QUATERNION);
 			OPCODE_CALL_PTR(AABB);
 			OPCODE_CALL_PTR(BASIS);
 			OPCODE_CALL_PTR(TRANSFORM3D);
+			OPCODE_CALL_PTR(PROJECTION);
 			OPCODE_CALL_PTR(COLOR);
 			OPCODE_CALL_PTR(STRING_NAME);
 			OPCODE_CALL_PTR(NODE_PATH);
@@ -3308,11 +3323,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			OPCODE_TYPE_ADJUST(VECTOR3, Vector3);
 			OPCODE_TYPE_ADJUST(VECTOR3I, Vector3i);
 			OPCODE_TYPE_ADJUST(TRANSFORM2D, Transform2D);
+			OPCODE_TYPE_ADJUST(VECTOR4, Vector4);
+			OPCODE_TYPE_ADJUST(VECTOR4I, Vector4i);
 			OPCODE_TYPE_ADJUST(PLANE, Plane);
 			OPCODE_TYPE_ADJUST(QUATERNION, Quaternion);
 			OPCODE_TYPE_ADJUST(AABB, AABB);
 			OPCODE_TYPE_ADJUST(BASIS, Basis);
 			OPCODE_TYPE_ADJUST(TRANSFORM3D, Transform3D);
+			OPCODE_TYPE_ADJUST(PROJECTION, Projection);
 			OPCODE_TYPE_ADJUST(COLOR, Color);
 			OPCODE_TYPE_ADJUST(STRING_NAME, StringName);
 			OPCODE_TYPE_ADJUST(NODE_PATH, NodePath);

+ 1 - 1
modules/gltf/gltf_document.cpp

@@ -5218,7 +5218,7 @@ GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> state, Camera3D *p_
 	Ref<GLTFCamera> c;
 	c.instantiate();
 
-	if (p_camera->get_projection() == Camera3D::Projection::PROJECTION_PERSPECTIVE) {
+	if (p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE) {
 		c->set_perspective(true);
 	}
 	c->set_fov_size(p_camera->get_fov());

+ 2 - 2
modules/mobile_vr/mobile_vr_interface.cpp

@@ -452,10 +452,10 @@ Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Tra
 	return transform_for_eye;
 };
 
-CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+Projection MobileVRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
 	_THREAD_SAFE_METHOD_
 
-	CameraMatrix eye;
+	Projection eye;
 
 	aspect = p_aspect;
 	eye.set_for_hmd(p_view + 1, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far);

+ 1 - 1
modules/mobile_vr/mobile_vr_interface.h

@@ -150,7 +150,7 @@ public:
 	virtual uint32_t get_view_count() override;
 	virtual Transform3D get_camera_transform() override;
 	virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
-	virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+	virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
 	virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
 
 	virtual void process() override;

+ 3 - 0
modules/mono/csharp_script.cpp

@@ -481,11 +481,14 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
 		Variant::VECTOR3,
 		Variant::VECTOR3I,
 		Variant::TRANSFORM2D,
+		Variant::VECTOR4,
+		Variant::VECTOR4I,
 		Variant::PLANE,
 		Variant::QUATERNION,
 		Variant::AABB,
 		Variant::BASIS,
 		Variant::TRANSFORM3D,
+		Variant::PROJECTION,
 		Variant::COLOR,
 		Variant::STRING_NAME,
 		Variant::NODE_PATH,

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

@@ -917,6 +917,8 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
 	ARRAY_ALL(Vector2i);
 	ARRAY_ALL(Vector3);
 	ARRAY_ALL(Vector3i);
+	ARRAY_ALL(Vector4);
+	ARRAY_ALL(Vector4i);
 
 #undef ARRAY_ALL
 #undef ARRAY_IS_EMPTY
@@ -3222,6 +3224,11 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
 			r_iarg.default_argument = "new %s" + r_iarg.default_argument;
 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
 			break;
+		case Variant::VECTOR4:
+		case Variant::VECTOR4I:
+			r_iarg.default_argument = "new %s" + r_iarg.default_argument;
+			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+			break;
 		case Variant::OBJECT:
 			ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
 					"Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
@@ -3276,6 +3283,15 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
 			}
 			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
 		} break;
+		case Variant::PROJECTION: {
+			Projection transform = p_val.operator Projection();
+			if (transform == Projection()) {
+				r_iarg.default_argument = "Projection.Identity";
+			} else {
+				r_iarg.default_argument = "new Projection(new Vector4" + transform.matrix[0].operator String() + ", new Vector4" + transform.matrix[1].operator String() + ", new Vector4" + transform.matrix[2].operator String() + ", new Vector4" + transform.matrix[3].operator String() + ")";
+			}
+			r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+		} break;
 		case Variant::BASIS: {
 			Basis basis = p_val.operator Basis();
 			if (basis == Basis()) {

+ 3 - 0
modules/mono/editor/bindings_generator.h

@@ -590,6 +590,9 @@ class BindingsGenerator {
 		StringName type_Vector2 = StaticCString::create("Vector2");
 		StringName type_Rect2 = StaticCString::create("Rect2");
 		StringName type_Vector3 = StaticCString::create("Vector3");
+		StringName type_Vector3i = StaticCString::create("Vector3i");
+		StringName type_Vector4 = StaticCString::create("Vector4");
+		StringName type_Vector4i = StaticCString::create("Vector4i");
 
 		// Object not included as it must be checked for all derived classes
 		static constexpr int nullable_types_count = 17;

+ 6 - 0
modules/mono/mono_gd/gd_mono_cache.cpp

@@ -108,9 +108,12 @@ void CachedData::clear_godot_api_cache() {
 	class_Transform2D = nullptr;
 	class_Vector3 = nullptr;
 	class_Vector3i = nullptr;
+	class_Vector4 = nullptr;
+	class_Vector4i = nullptr;
 	class_Basis = nullptr;
 	class_Quaternion = nullptr;
 	class_Transform3D = nullptr;
+	class_Projection = nullptr;
 	class_AABB = nullptr;
 	class_Color = nullptr;
 	class_Plane = nullptr;
@@ -239,9 +242,12 @@ void update_godot_api_cache() {
 	CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D));
 	CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3));
 	CACHE_CLASS_AND_CHECK(Vector3i, GODOT_API_CLASS(Vector3i));
+	CACHE_CLASS_AND_CHECK(Vector4, GODOT_API_CLASS(Vector4));
+	CACHE_CLASS_AND_CHECK(Vector4i, GODOT_API_CLASS(Vector4i));
 	CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis));
 	CACHE_CLASS_AND_CHECK(Quaternion, GODOT_API_CLASS(Quaternion));
 	CACHE_CLASS_AND_CHECK(Transform3D, GODOT_API_CLASS(Transform3D));
+	CACHE_CLASS_AND_CHECK(Projection, GODOT_API_CLASS(Projection));
 	CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB));
 	CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
 	CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));

+ 3 - 0
modules/mono/mono_gd/gd_mono_cache.h

@@ -79,9 +79,12 @@ struct CachedData {
 	GDMonoClass *class_Transform2D = nullptr;
 	GDMonoClass *class_Vector3 = nullptr;
 	GDMonoClass *class_Vector3i = nullptr;
+	GDMonoClass *class_Vector4 = nullptr;
+	GDMonoClass *class_Vector4i = nullptr;
 	GDMonoClass *class_Basis = nullptr;
 	GDMonoClass *class_Quaternion = nullptr;
 	GDMonoClass *class_Transform3D = nullptr;
+	GDMonoClass *class_Projection = nullptr;
 	GDMonoClass *class_AABB = nullptr;
 	GDMonoClass *class_Color = nullptr;
 	GDMonoClass *class_Plane = nullptr;

+ 30 - 0
modules/mono/mono_gd/gd_mono_field.cpp

@@ -140,6 +140,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				break;
 			}
 
+			if (tclass == CACHED_CLASS(Vector4)) {
+				GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_value.operator ::Vector4());
+				mono_field_set_value(p_object, mono_field, &from);
+				break;
+			}
+
+			if (tclass == CACHED_CLASS(Vector4i)) {
+				GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_value.operator ::Vector4i());
+				mono_field_set_value(p_object, mono_field, &from);
+				break;
+			}
+
 			if (tclass == CACHED_CLASS(Basis)) {
 				GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis());
 				mono_field_set_value(p_object, mono_field, &from);
@@ -158,6 +170,12 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				break;
 			}
 
+			if (tclass == CACHED_CLASS(Projection)) {
+				GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_value.operator ::Projection());
+				mono_field_set_value(p_object, mono_field, &from);
+				break;
+			}
+
 			if (tclass == CACHED_CLASS(AABB)) {
 				GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB());
 				mono_field_set_value(p_object, mono_field, &from);
@@ -328,6 +346,14 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 					GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i());
 					mono_field_set_value(p_object, mono_field, &from);
 				} break;
+				case Variant::VECTOR4: {
+					GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_value.operator ::Vector4());
+					mono_field_set_value(p_object, mono_field, &from);
+				} break;
+				case Variant::VECTOR4I: {
+					GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_value.operator ::Vector4i());
+					mono_field_set_value(p_object, mono_field, &from);
+				} break;
 				case Variant::TRANSFORM2D: {
 					GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D());
 					mono_field_set_value(p_object, mono_field, &from);
@@ -352,6 +378,10 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 					GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D());
 					mono_field_set_value(p_object, mono_field, &from);
 				} break;
+				case Variant::PROJECTION: {
+					GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_value.operator ::Projection());
+					mono_field_set_value(p_object, mono_field, &from);
+				} break;
 				case Variant::COLOR: {
 					GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color());
 					mono_field_set_value(p_object, mono_field, &from);

+ 22 - 1
modules/mono/mono_gd/gd_mono_marshal.cpp

@@ -99,6 +99,13 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
 			if (vtclass == CACHED_CLASS(Vector3i)) {
 				return Variant::VECTOR3I;
 			}
+			if (vtclass == CACHED_CLASS(Vector4)) {
+				return Variant::VECTOR4;
+			}
+
+			if (vtclass == CACHED_CLASS(Vector4i)) {
+				return Variant::VECTOR4I;
+			}
 
 			if (vtclass == CACHED_CLASS(Basis)) {
 				return Variant::BASIS;
@@ -111,7 +118,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
 			if (vtclass == CACHED_CLASS(Transform3D)) {
 				return Variant::TRANSFORM3D;
 			}
-
+			if (vtclass == CACHED_CLASS(Projection)) {
+				return Variant::PROJECTION;
+			}
 			if (vtclass == CACHED_CLASS(AABB)) {
 				return Variant::AABB;
 			}
@@ -539,6 +548,14 @@ MonoObject *variant_to_mono_object(const Variant &p_var) {
 			GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var.operator ::Transform2D());
 			return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
 		}
+		case Variant::VECTOR4: {
+			GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_var.operator ::Vector4());
+			return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4), &from);
+		}
+		case Variant::VECTOR4I: {
+			GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_var.operator ::Vector4i());
+			return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4i), &from);
+		}
 		case Variant::PLANE: {
 			GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var.operator ::Plane());
 			return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
@@ -559,6 +576,10 @@ MonoObject *variant_to_mono_object(const Variant &p_var) {
 			GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_var.operator ::Transform3D());
 			return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform3D), &from);
 		}
+		case Variant::PROJECTION: {
+			GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_var.operator ::Projection());
+			return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Projection), &from);
+		}
 		case Variant::COLOR: {
 			GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var.operator ::Color());
 			return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);

+ 62 - 3
modules/mono/mono_gd/gd_mono_marshal.h

@@ -256,6 +256,18 @@ enum {
 			offsetof(Vector3, y) == (sizeof(real_t) * 1) &&
 			offsetof(Vector3, z) == (sizeof(real_t) * 2)),
 
+	MATCHES_Vector4 = (MATCHES_real_t && (sizeof(Vector4) == (sizeof(real_t) * 4)) &&
+			offsetof(Vector4, x) == (sizeof(real_t) * 0) &&
+			offsetof(Vector4, y) == (sizeof(real_t) * 1) &&
+			offsetof(Vector4, z) == (sizeof(real_t) * 2) &&
+			offsetof(Vector4, w) == (sizeof(real_t) * 3)),
+
+	MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4i)) &&
+			offsetof(Vector4i, x) == (sizeof(int32_t) * 0) &&
+			offsetof(Vector4i, y) == (sizeof(int32_t) * 1) &&
+			offsetof(Vector4i, z) == (sizeof(int32_t) * 2) &&
+			offsetof(Vector4i, w) == (sizeof(int32_t) * 3)),
+
 	MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) &&
 			offsetof(Vector3i, x) == (sizeof(int32_t) * 0) &&
 			offsetof(Vector3i, y) == (sizeof(int32_t) * 1) &&
@@ -273,6 +285,8 @@ enum {
 			offsetof(Transform3D, basis) == 0 &&
 			offsetof(Transform3D, origin) == sizeof(Basis)),
 
+	MATCHES_Projection = (MATCHES_Vector4 && (sizeof(Projection) == (sizeof(Vector4) * 4))),
+
 	MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) &&
 			offsetof(AABB, position) == (sizeof(Vector3) * 0) &&
 			offsetof(AABB, size) == (sizeof(Vector3) * 1)),
@@ -291,9 +305,9 @@ enum {
 // In the future we may force this if we want to ref return these structs
 #ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY
 /* clang-format off */
-static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
-				MATCHES_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_AABB && MATCHES_Color &&
-				MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i);
+static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && MATCHES_Vector4 &&
+				MATCHES_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_Projection && MATCHES_AABB && MATCHES_Color &&
+				MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i && MATCHES_Vector4i);
 /* clang-format on */
 #endif
 } // namespace InteropLayout
@@ -401,6 +415,32 @@ struct M_Vector3i {
 	}
 };
 
+struct M_Vector4 {
+	real_t x, y, z, w;
+
+	static _FORCE_INLINE_ Vector4 convert_to(const M_Vector4 &p_from) {
+		return Vector4(p_from.x, p_from.y, p_from.z, p_from.w);
+	}
+
+	static _FORCE_INLINE_ M_Vector4 convert_from(const Vector4 &p_from) {
+		M_Vector4 ret = { p_from.x, p_from.y, p_from.z, p_from.w };
+		return ret;
+	}
+};
+
+struct M_Vector4i {
+	int32_t x, y, z, w;
+
+	static _FORCE_INLINE_ Vector4i convert_to(const M_Vector4i &p_from) {
+		return Vector4i(p_from.x, p_from.y, p_from.z, p_from.w);
+	}
+
+	static _FORCE_INLINE_ M_Vector4i convert_from(const Vector4i &p_from) {
+		M_Vector4i ret = { p_from.x, p_from.y, p_from.z, p_from.w };
+		return ret;
+	}
+};
+
 struct M_Basis {
 	M_Vector3 elements[3];
 
@@ -447,6 +487,22 @@ struct M_Transform3D {
 	}
 };
 
+struct M_Projection {
+	M_Vector4 vec1;
+	M_Vector4 vec2;
+	M_Vector4 vec3;
+	M_Vector4 vec4;
+
+	static _FORCE_INLINE_ Projection convert_to(const M_Projection &p_from) {
+		return Projection(M_Vector4::convert_to(p_from.vec1), M_Vector4::convert_to(p_from.vec2), M_Vector4::convert_to(p_from.vec3), M_Vector4::convert_to(p_from.vec4));
+	}
+
+	static _FORCE_INLINE_ M_Projection convert_from(const Projection &p_from) {
+		M_Projection ret = { M_Vector4::convert_from(p_from.matrix[0]), M_Vector4::convert_from(p_from.matrix[1]), M_Vector4::convert_from(p_from.matrix[2]), M_Vector4::convert_from(p_from.matrix[3]) };
+		return ret;
+	}
+};
+
 struct M_AABB {
 	M_Vector3 position;
 	M_Vector3 size;
@@ -533,8 +589,11 @@ DECL_TYPE_MARSHAL_TEMPLATES(Transform2D)
 DECL_TYPE_MARSHAL_TEMPLATES(Vector3)
 DECL_TYPE_MARSHAL_TEMPLATES(Vector3i)
 DECL_TYPE_MARSHAL_TEMPLATES(Basis)
+DECL_TYPE_MARSHAL_TEMPLATES(Vector4)
+DECL_TYPE_MARSHAL_TEMPLATES(Vector4i)
 DECL_TYPE_MARSHAL_TEMPLATES(Quaternion)
 DECL_TYPE_MARSHAL_TEMPLATES(Transform3D)
+DECL_TYPE_MARSHAL_TEMPLATES(Projection)
 DECL_TYPE_MARSHAL_TEMPLATES(AABB)
 DECL_TYPE_MARSHAL_TEMPLATES(Color)
 DECL_TYPE_MARSHAL_TEMPLATES(Plane)

+ 2 - 2
modules/openxr/extensions/openxr_extension_wrapper.h

@@ -32,7 +32,7 @@
 #define OPENXR_EXTENSION_WRAPPER_H
 
 #include "core/error/error_macros.h"
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
 #include "core/templates/hash_map.h"
 #include "core/templates/rid.h"
 
@@ -97,7 +97,7 @@ public:
 	virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0;
 	virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) = 0;
 	virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0;
-	virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) = 0;
+	virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) = 0;
 	virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) = 0;
 
 	OpenXRGraphicsExtensionWrapper(OpenXRAPI *p_openxr_api) :

+ 1 - 1
modules/openxr/extensions/openxr_vulkan_extension.cpp

@@ -420,7 +420,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 	return true;
 }
 
-bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) {
+bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
 	// Even though this is a Vulkan renderer we're using OpenGL coordinate systems
 	XrMatrix4x4f matrix;
 	XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);

+ 1 - 1
modules/openxr/extensions/openxr_vulkan_extension.h

@@ -63,7 +63,7 @@ public:
 	virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override;
 	virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) override;
 	virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override;
-	virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) override;
+	virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) override;
 	virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) override;
 
 private:

+ 1 - 1
modules/openxr/openxr_api.cpp

@@ -1180,7 +1180,7 @@ bool OpenXRAPI::get_view_transform(uint32_t p_view, Transform3D &r_transform) {
 	return true;
 }
 
-bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, CameraMatrix &p_camera_matrix) {
+bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix) {
 	ERR_FAIL_COND_V(!running, false);
 	ERR_FAIL_NULL_V(graphics_extension, false);
 

+ 2 - 2
modules/openxr/openxr_api.h

@@ -32,7 +32,7 @@
 #define OPENXR_DRIVER_H
 
 #include "core/error/error_macros.h"
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
 #include "core/math/transform_3d.h"
 #include "core/math/vector2.h"
 #include "core/os/memory.h"
@@ -249,7 +249,7 @@ public:
 	Size2 get_recommended_target_size();
 	XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
 	bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
-	bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, CameraMatrix &p_camera_matrix);
+	bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix);
 	bool process();
 
 	void pre_render();

+ 2 - 2
modules/openxr/openxr_interface.cpp

@@ -631,8 +631,8 @@ Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Trans
 	return p_cam_transform * xr_server->get_reference_frame() * t;
 }
 
-CameraMatrix OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
-	CameraMatrix cm;
+Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+	Projection cm;
 
 	if (openxr_api) {
 		if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) {

+ 1 - 1
modules/openxr/openxr_interface.h

@@ -121,7 +121,7 @@ public:
 	virtual uint32_t get_view_count() override;
 	virtual Transform3D get_camera_transform() override;
 	virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
-	virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+	virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
 
 	virtual void process() override;
 	virtual void pre_render() override;

+ 3 - 3
modules/raycast/raycast_occlusion_cull.cpp

@@ -78,7 +78,7 @@ void RaycastOcclusionCull::RaycastHZBuffer::resize(const Size2i &p_size) {
 	memset(camera_ray_masks.ptr(), ~0, camera_rays_tile_count * TILE_RAYS * sizeof(uint32_t));
 }
 
-void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool) {
+void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool) {
 	CameraRayThreadData td;
 	td.thread_count = p_thread_work_pool.get_thread_count();
 
@@ -88,7 +88,7 @@ void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D
 	td.camera_dir = -p_cam_transform.basis.get_column(2);
 	td.camera_orthogonal = p_cam_orthogonal;
 
-	CameraMatrix inv_camera_matrix = p_cam_projection.inverse();
+	Projection inv_camera_matrix = p_cam_projection.inverse();
 	Vector3 camera_corner_proj = Vector3(-1.0f, -1.0f, -1.0f);
 	Vector3 camera_corner_view = inv_camera_matrix.xform(camera_corner_proj);
 	td.pixel_corner = p_cam_transform.xform(camera_corner_view);
@@ -524,7 +524,7 @@ void RaycastOcclusionCull::buffer_set_size(RID p_buffer, const Vector2i &p_size)
 	buffers[p_buffer].resize(p_size);
 }
 
-void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) {
+void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) {
 	if (!buffers.has(p_buffer)) {
 		return;
 	}

+ 3 - 3
modules/raycast/raycast_occlusion_cull.h

@@ -32,7 +32,7 @@
 #define OCCLUSION_CULL_RAYCASTER_H
 
 #include "core/io/image.h"
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
 #include "core/object/object.h"
 #include "core/object/ref_counted.h"
 #include "core/templates/local_vector.h"
@@ -76,7 +76,7 @@ public:
 		virtual void clear() override;
 		virtual void resize(const Size2i &p_size) override;
 		void sort_rays(const Vector3 &p_camera_dir, bool p_orthogonal);
-		void update_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool);
+		void update_camera_rays(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool);
 
 		~RaycastHZBuffer();
 	};
@@ -183,7 +183,7 @@ public:
 	virtual HZBuffer *buffer_get_ptr(RID p_buffer) override;
 	virtual void buffer_set_scenario(RID p_buffer, RID p_scenario) override;
 	virtual void buffer_set_size(RID p_buffer, const Vector2i &p_size) override;
-	virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) override;
+	virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) override;
 	virtual RID buffer_get_debug_texture(RID p_buffer) override;
 
 	virtual void set_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) override;

+ 9 - 0
modules/visual_script/editor/visual_script_editor.cpp

@@ -376,6 +376,12 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
 			case Variant::VECTOR3I:
 				color = Color(0.84, 0.49, 0.93);
 				break;
+			case Variant::VECTOR4:
+				color = Color(0.84, 0.49, 0.94);
+				break;
+			case Variant::VECTOR4I:
+				color = Color(0.84, 0.49, 0.94);
+				break;
 			case Variant::TRANSFORM2D:
 				color = Color(0.77, 0.93, 0.41);
 				break;
@@ -4821,12 +4827,15 @@ VisualScriptEditor::VisualScriptEditor() {
 	base_type_map.insert("Rect2i", Variant::RECT2I);
 	base_type_map.insert("Vector3", Variant::VECTOR3);
 	base_type_map.insert("Vector3i", Variant::VECTOR3I);
+	base_type_map.insert("Vector4", Variant::VECTOR4);
+	base_type_map.insert("Vector4i", Variant::VECTOR4I);
 	base_type_map.insert("Transform2D", Variant::TRANSFORM2D);
 	base_type_map.insert("Plane", Variant::PLANE);
 	base_type_map.insert("Quaternion", Variant::QUATERNION);
 	base_type_map.insert("AABB", Variant::AABB);
 	base_type_map.insert("Basis", Variant::BASIS);
 	base_type_map.insert("Transform3D", Variant::TRANSFORM3D);
+	base_type_map.insert("Projection", Variant::PROJECTION);
 	base_type_map.insert("Color", Variant::COLOR);
 	base_type_map.insert("NodePath", Variant::NODE_PATH);
 	base_type_map.insert("RID", Variant::RID);

+ 3 - 0
modules/visual_script/visual_script_nodes.cpp

@@ -4025,6 +4025,8 @@ void register_visual_script_nodes() {
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR2I), create_node_deconst_typed<Variant::Type::VECTOR2I>);
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR3), create_node_deconst_typed<Variant::Type::VECTOR3>);
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR3I), create_node_deconst_typed<Variant::Type::VECTOR3I>);
+	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR4), create_node_deconst_typed<Variant::Type::VECTOR4>);
+	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR4I), create_node_deconst_typed<Variant::Type::VECTOR4I>);
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::COLOR), create_node_deconst_typed<Variant::Type::COLOR>);
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::RECT2), create_node_deconst_typed<Variant::Type::RECT2>);
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::RECT2I), create_node_deconst_typed<Variant::Type::RECT2I>);
@@ -4034,6 +4036,7 @@ void register_visual_script_nodes() {
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::AABB), create_node_deconst_typed<Variant::Type::AABB>);
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::BASIS), create_node_deconst_typed<Variant::Type::BASIS>);
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM3D), create_node_deconst_typed<Variant::Type::TRANSFORM3D>);
+	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::PROJECTION), create_node_deconst_typed<Variant::Type::PROJECTION>);
 	VisualScriptLanguage::singleton->add_register_func("functions/compose_array", create_node_generic<VisualScriptComposeArray>);
 
 	for (int i = 1; i < Variant::VARIANT_MAX; i++) {

+ 2 - 2
modules/webxr/webxr_interface_js.cpp

@@ -363,8 +363,8 @@ Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Tran
 	return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye;
 };
 
-CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
-	CameraMatrix eye;
+Projection WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+	Projection eye;
 
 	float *js_matrix = godot_webxr_get_projection_for_eye(p_view + 1);
 	if (!initialized || js_matrix == nullptr) {

+ 1 - 1
modules/webxr/webxr_interface_js.h

@@ -87,7 +87,7 @@ public:
 	virtual uint32_t get_view_count() override;
 	virtual Transform3D get_camera_transform() override;
 	virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
-	virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+	virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
 	virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
 
 	virtual void process() override;

+ 2 - 0
platform/windows/godot.natvis

@@ -41,10 +41,12 @@
 		<DisplayString Condition="type == Variant::AABB">{_data._aabb}</DisplayString>
 		<DisplayString Condition="type == Variant::BASIS">{_data._basis}</DisplayString>
 		<DisplayString Condition="type == Variant::TRANSFORM3D">{_data._transform}</DisplayString>
+		<DisplayString Condition="type == Variant::PROJECTION">{_data._projection}</DisplayString>
 		<DisplayString Condition="type == Variant::STRING">{*(String *)_data._mem}</DisplayString>
 		<DisplayString Condition="type == Variant::VECTOR2">{*(Vector2 *)_data._mem}</DisplayString>
 		<DisplayString Condition="type == Variant::RECT2">{*(Rect2 *)_data._mem}</DisplayString>
 		<DisplayString Condition="type == Variant::VECTOR3">{*(Vector3 *)_data._mem}</DisplayString>
+		<DisplayString Condition="type == Variant::VECTOR4">{*(Vector4 *)_data._mem}</DisplayString>
 		<DisplayString Condition="type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString>
 		<DisplayString Condition="type == Variant::QUATERNION">{*(Quaternion *)_data._mem}</DisplayString>
 		<DisplayString Condition="type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString>

+ 8 - 8
scene/3d/camera_3d.cpp

@@ -31,7 +31,7 @@
 #include "camera_3d.h"
 
 #include "collision_object_3d.h"
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
 #include "scene/main/viewport.h"
 
 void Camera3D::_update_audio_listener_state() {
@@ -197,7 +197,7 @@ void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, rea
 	update_gizmos();
 }
 
-void Camera3D::set_projection(Camera3D::Projection p_mode) {
+void Camera3D::set_projection(ProjectionType p_mode) {
 	if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
 		mode = p_mode;
 		_update_camera_mode();
@@ -265,7 +265,7 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const {
 	if (mode == PROJECTION_ORTHOGONAL) {
 		ray = Vector3(0, 0, -1);
 	} else {
-		CameraMatrix cm;
+		Projection cm;
 		cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
 		Vector2 screen_he = cm.get_viewport_half_extents();
 		ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -near).normalized();
@@ -314,7 +314,7 @@ Vector<Vector3> Camera3D::get_near_plane_points() const {
 
 	Size2 viewport_size = get_viewport()->get_visible_rect().size;
 
-	CameraMatrix cm;
+	Projection cm;
 
 	if (mode == PROJECTION_ORTHOGONAL) {
 		cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
@@ -340,7 +340,7 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const {
 
 	Size2 viewport_size = get_viewport()->get_visible_rect().size;
 
-	CameraMatrix cm;
+	Projection cm;
 
 	if (mode == PROJECTION_ORTHOGONAL) {
 		cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
@@ -368,7 +368,7 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons
 	}
 	Size2 viewport_size = get_viewport()->get_visible_rect().size;
 
-	CameraMatrix cm;
+	Projection cm;
 
 	if (mode == PROJECTION_ORTHOGONAL) {
 		cm.set_orthogonal(size, viewport_size.aspect(), p_z_depth, far, keep_aspect == KEEP_WIDTH);
@@ -544,7 +544,7 @@ real_t Camera3D::get_far() const {
 	return far;
 }
 
-Camera3D::Projection Camera3D::get_projection() const {
+Camera3D::ProjectionType Camera3D::get_projection() const {
 	return mode;
 }
 
@@ -607,7 +607,7 @@ Vector<Plane> Camera3D::get_frustum() const {
 	ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());
 
 	Size2 viewport_size = get_viewport()->get_visible_rect().size;
-	CameraMatrix cm;
+	Projection cm;
 	if (mode == PROJECTION_PERSPECTIVE) {
 		cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
 	} else {

+ 5 - 5
scene/3d/camera_3d.h

@@ -40,7 +40,7 @@ class Camera3D : public Node3D {
 	GDCLASS(Camera3D, Node3D);
 
 public:
-	enum Projection {
+	enum ProjectionType {
 		PROJECTION_PERSPECTIVE,
 		PROJECTION_ORTHOGONAL,
 		PROJECTION_FRUSTUM
@@ -62,7 +62,7 @@ private:
 	bool current = false;
 	Viewport *viewport = nullptr;
 
-	Projection mode = PROJECTION_PERSPECTIVE;
+	ProjectionType mode = PROJECTION_PERSPECTIVE;
 
 	real_t fov = 0.0;
 	real_t size = 1.0;
@@ -112,7 +112,7 @@ public:
 	void set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far);
 	void set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far);
 	void set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far);
-	void set_projection(Camera3D::Projection p_mode);
+	void set_projection(Camera3D::ProjectionType p_mode);
 
 	void make_current();
 	void clear_current(bool p_enable_next = true);
@@ -127,7 +127,7 @@ public:
 	real_t get_near() const;
 	Vector2 get_frustum_offset() const;
 
-	Projection get_projection() const;
+	ProjectionType get_projection() const;
 
 	void set_fov(real_t p_fov);
 	void set_size(real_t p_size);
@@ -181,7 +181,7 @@ public:
 	~Camera3D();
 };
 
-VARIANT_ENUM_CAST(Camera3D::Projection);
+VARIANT_ENUM_CAST(Camera3D::ProjectionType);
 VARIANT_ENUM_CAST(Camera3D::KeepAspect);
 VARIANT_ENUM_CAST(Camera3D::DopplerTracking);
 

+ 4 - 4
scene/3d/xr_nodes.cpp

@@ -120,7 +120,7 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
 	Vector3 ray;
 
 	// Just use the first view, if multiple views are supported this function has no good result
-	CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+	Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
 	Vector2 screen_he = cm.get_viewport_half_extents();
 	ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized();
 
@@ -143,7 +143,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const {
 	Size2 viewport_size = get_viewport()->get_visible_rect().size;
 
 	// Just use the first view, if multiple views are supported this function has no good result
-	CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+	Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
 
 	Plane p(get_camera_transform().xform_inv(p_pos), 1.0);
 
@@ -173,7 +173,7 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) co
 	Size2 viewport_size = get_viewport()->get_visible_rect().size;
 
 	// Just use the first view, if multiple views are supported this function has no good result
-	CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+	Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
 
 	Vector2 vp_he = cm.get_viewport_half_extents();
 
@@ -202,7 +202,7 @@ Vector<Plane> XRCamera3D::get_frustum() const {
 
 	Size2 viewport_size = get_viewport()->get_visible_rect().size;
 	// TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here.
-	CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
+	Projection cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
 	return cm.get_projection_planes(get_camera_transform());
 };
 

+ 11 - 0
scene/animation/tween.cpp

@@ -464,6 +464,17 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
 			APPLY_EQUATION(columns[2][1]);
 			return r;
 		}
+		case Variant::VECTOR4: {
+			Vector4 i = p_initial_val;
+			Vector4 d = p_delta_val;
+			Vector4 r;
+
+			APPLY_EQUATION(x);
+			APPLY_EQUATION(y);
+			APPLY_EQUATION(z);
+			APPLY_EQUATION(w);
+			return r;
+		}
 
 		case Variant::QUATERNION: {
 			Quaternion i = p_initial_val;

+ 4 - 4
scene/main/shader_globals_override.cpp

@@ -155,7 +155,7 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const
 				pinfo.type = Variant::VECTOR3;
 			} break;
 			case RS::GLOBAL_VAR_TYPE_VEC4: {
-				pinfo.type = Variant::QUATERNION;
+				pinfo.type = Variant::VECTOR4;
 			} break;
 			case RS::GLOBAL_VAR_TYPE_RECT2: {
 				pinfo.type = Variant::RECT2;
@@ -169,15 +169,15 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const
 			case RS::GLOBAL_VAR_TYPE_MAT3: {
 				pinfo.type = Variant::BASIS;
 			} break;
+			case RS::GLOBAL_VAR_TYPE_MAT4: {
+				pinfo.type = Variant::PROJECTION;
+			} break;
 			case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
 				pinfo.type = Variant::TRANSFORM2D;
 			} break;
 			case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
 				pinfo.type = Variant::TRANSFORM3D;
 			} break;
-			case RS::GLOBAL_VAR_TYPE_MAT4: {
-				pinfo.type = Variant::PACKED_INT32_ARRAY;
-			} break;
 			case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
 				pinfo.type = Variant::OBJECT;
 				pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;

+ 2 - 2
servers/rendering/dummy/rasterizer_scene_dummy.h

@@ -149,7 +149,7 @@ public:
 	RID light_instance_create(RID p_light) override { return RID(); }
 	void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override {}
 	void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override {}
-	void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override {}
+	void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override {}
 	void light_instance_mark_visible(RID p_light_instance) override {}
 
 	RID fog_volume_instance_create(RID p_fog_volume) override { return RID(); }
@@ -184,7 +184,7 @@ public:
 	void voxel_gi_set_quality(RS::VoxelGIQuality) override {}
 
 	void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_info = nullptr) override {}
-	void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {}
+	void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {}
 	void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) override {}
 
 	void set_scene_pass(uint64_t p_pass) override {}

+ 1 - 1
servers/rendering/renderer_canvas_render.h

@@ -77,7 +77,7 @@ public:
 		Rect2 rect_cache;
 		Transform2D xform_cache;
 		float radius_cache; //used for shadow far plane
-		//CameraMatrix shadow_matrix_cache;
+		//Projection shadow_matrix_cache;
 
 		Transform2D light_shader_xform;
 		//Vector2 light_shader_pos;

+ 2 - 2
servers/rendering/renderer_rd/cluster_builder_rd.cpp

@@ -374,7 +374,7 @@ void ClusterBuilderRD::setup(Size2i p_screen_size, uint32_t p_max_elements, RID
 	}
 }
 
-void ClusterBuilderRD::begin(const Transform3D &p_view_transform, const CameraMatrix &p_cam_projection, bool p_flip_y) {
+void ClusterBuilderRD::begin(const Transform3D &p_view_transform, const Projection &p_cam_projection, bool p_flip_y) {
 	view_xform = p_view_transform.affine_inverse();
 	projection = p_cam_projection;
 	z_near = projection.get_z_near();
@@ -385,7 +385,7 @@ void ClusterBuilderRD::begin(const Transform3D &p_view_transform, const CameraMa
 		adjusted_projection.adjust_perspective_znear(0.0001);
 	}
 
-	CameraMatrix correction;
+	Projection correction;
 	correction.set_depth_correction(p_flip_y);
 	projection = correction * projection;
 	adjusted_projection = correction * adjusted_projection;

+ 3 - 3
servers/rendering/renderer_rd/cluster_builder_rd.h

@@ -168,8 +168,8 @@ private:
 	uint32_t render_element_max = 0;
 
 	Transform3D view_xform;
-	CameraMatrix adjusted_projection;
-	CameraMatrix projection;
+	Projection adjusted_projection;
+	Projection projection;
 	float z_far = 0;
 	float z_near = 0;
 	bool orthogonal = false;
@@ -220,7 +220,7 @@ private:
 public:
 	void setup(Size2i p_screen_size, uint32_t p_max_elements, RID p_depth_buffer, RID p_depth_buffer_sampler, RID p_color_buffer);
 
-	void begin(const Transform3D &p_view_transform, const CameraMatrix &p_cam_projection, bool p_flip_y);
+	void begin(const Transform3D &p_view_transform, const Projection &p_cam_projection, bool p_flip_y);
 
 	_FORCE_INLINE_ void add_light(LightType p_type, const Transform3D &p_transform, float p_radius, float p_spot_aperture) {
 		if (p_type == LIGHT_TYPE_OMNI && cluster_count_by_type[ELEMENT_TYPE_OMNI_LIGHT] == max_elements_by_type) {

+ 5 - 5
servers/rendering/renderer_rd/effects/ss_effects.cpp

@@ -38,7 +38,7 @@ using namespace RendererRD;
 
 SSEffects *SSEffects::singleton = nullptr;
 
-static _FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array) {
+static _FORCE_INLINE_ void store_camera(const Projection &p_mtx, float *p_array) {
 	for (int i = 0; i < 4; i++) {
 		for (int j = 0; j < 4; j++) {
 			p_array[i * 4 + j] = p_mtx.matrix[i][j];
@@ -381,7 +381,7 @@ SSEffects::~SSEffects() {
 
 /* SS Downsampler */
 
-void SSEffects::downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_mipmaps, RS::EnvironmentSSAOQuality p_ssao_quality, RS::EnvironmentSSILQuality p_ssil_quality, bool p_invalidate_uniform_set, bool p_ssao_half_size, bool p_ssil_half_size, Size2i p_full_screen_size, const CameraMatrix &p_projection) {
+void SSEffects::downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_mipmaps, RS::EnvironmentSSAOQuality p_ssao_quality, RS::EnvironmentSSILQuality p_ssil_quality, bool p_invalidate_uniform_set, bool p_ssao_half_size, bool p_ssil_half_size, Size2i p_full_screen_size, const Projection &p_projection) {
 	UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
 	ERR_FAIL_NULL(uniform_set_cache);
 	MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -641,7 +641,7 @@ void SSEffects::ssil_allocate_buffers(SSILRenderBuffers &p_ssil_buffers, const S
 	}
 }
 
-void SSEffects::screen_space_indirect_lighting(SSILRenderBuffers &p_ssil_buffers, RID p_normal_buffer, const CameraMatrix &p_projection, const CameraMatrix &p_last_projection, const SSILSettings &p_settings) {
+void SSEffects::screen_space_indirect_lighting(SSILRenderBuffers &p_ssil_buffers, RID p_normal_buffer, const Projection &p_projection, const Projection &p_last_projection, const SSILSettings &p_settings) {
 	UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
 	ERR_FAIL_NULL(uniform_set_cache);
 	MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -1087,7 +1087,7 @@ void SSEffects::ssao_allocate_buffers(SSAORenderBuffers &p_ssao_buffers, const S
 	}
 }
 
-void SSEffects::generate_ssao(SSAORenderBuffers &p_ssao_buffers, RID p_normal_buffer, const CameraMatrix &p_projection, const SSAOSettings &p_settings) {
+void SSEffects::generate_ssao(SSAORenderBuffers &p_ssao_buffers, RID p_normal_buffer, const Projection &p_projection, const SSAOSettings &p_settings) {
 	UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
 	ERR_FAIL_NULL(uniform_set_cache);
 	MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -1462,7 +1462,7 @@ void SSEffects::ssr_allocate_buffers(SSRRenderBuffers &p_ssr_buffers, const Rend
 	}
 }
 
-void SSEffects::screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const Color &p_metallic_mask, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const CameraMatrix *p_projections, const Vector3 *p_eye_offsets) {
+void SSEffects::screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const Color &p_metallic_mask, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets) {
 	UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
 	ERR_FAIL_NULL(uniform_set_cache);
 	MaterialStorage *material_storage = MaterialStorage::get_singleton();

+ 4 - 4
servers/rendering/renderer_rd/effects/ss_effects.h

@@ -61,7 +61,7 @@ public:
 
 	/* SS Downsampler */
 
-	void downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_mipmaps, RS::EnvironmentSSAOQuality p_ssao_quality, RS::EnvironmentSSILQuality p_ssil_quality, bool p_invalidate_uniform_set, bool p_ssao_half_size, bool p_ssil_half_size, Size2i p_full_screen_size, const CameraMatrix &p_projection);
+	void downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_mipmaps, RS::EnvironmentSSAOQuality p_ssao_quality, RS::EnvironmentSSILQuality p_ssil_quality, bool p_invalidate_uniform_set, bool p_ssao_half_size, bool p_ssil_half_size, Size2i p_full_screen_size, const Projection &p_projection);
 
 	/* SSIL */
 
@@ -107,7 +107,7 @@ public:
 	};
 
 	void ssil_allocate_buffers(SSILRenderBuffers &p_ssil_buffers, const SSILSettings &p_settings, RID p_linear_depth);
-	void screen_space_indirect_lighting(SSILRenderBuffers &p_ssil_buffers, RID p_normal_buffer, const CameraMatrix &p_projection, const CameraMatrix &p_last_projection, const SSILSettings &p_settings);
+	void screen_space_indirect_lighting(SSILRenderBuffers &p_ssil_buffers, RID p_normal_buffer, const Projection &p_projection, const Projection &p_last_projection, const SSILSettings &p_settings);
 	void ssil_free(SSILRenderBuffers &p_ssil_buffers);
 
 	/* SSAO */
@@ -150,7 +150,7 @@ public:
 	};
 
 	void ssao_allocate_buffers(SSAORenderBuffers &p_ssao_buffers, const SSAOSettings &p_settings, RID p_linear_depth);
-	void generate_ssao(SSAORenderBuffers &p_ssao_buffers, RID p_normal_buffer, const CameraMatrix &p_projection, const SSAOSettings &p_settings);
+	void generate_ssao(SSAORenderBuffers &p_ssao_buffers, RID p_normal_buffer, const Projection &p_projection, const SSAOSettings &p_settings);
 	void ssao_free(SSAORenderBuffers &p_ssao_buffers);
 
 	/* Screen Space Reflection */
@@ -165,7 +165,7 @@ public:
 	};
 
 	void ssr_allocate_buffers(SSRRenderBuffers &p_ssr_buffers, const RenderingDevice::DataFormat p_color_format, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, const Size2i &p_screen_size, const uint32_t p_view_count);
-	void screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const Color &p_metallic_mask, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const CameraMatrix *p_projections, const Vector3 *p_eye_offsets);
+	void screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const Color &p_metallic_mask, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets);
 	void ssr_free(SSRRenderBuffers &p_ssr_buffers);
 
 private:

+ 1 - 1
servers/rendering/renderer_rd/effects_rd.cpp

@@ -174,7 +174,7 @@ void EffectsRD::taa_resolve(RID p_frame, RID p_temp, RID p_depth, RID p_velocity
 	RD::get_singleton()->compute_list_end();
 }
 
-void EffectsRD::sub_surface_scattering(RID p_diffuse, RID p_diffuse2, RID p_depth, const CameraMatrix &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RenderingServer::SubSurfaceScatteringQuality p_quality) {
+void EffectsRD::sub_surface_scattering(RID p_diffuse, RID p_diffuse2, RID p_depth, const Projection &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RenderingServer::SubSurfaceScatteringQuality p_quality) {
 	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
 
 	Plane p = p_camera.xform4(Plane(1, 0, -1, 1));

+ 2 - 2
servers/rendering/renderer_rd/effects_rd.h

@@ -31,7 +31,7 @@
 #ifndef EFFECTS_RD_H
 #define EFFECTS_RD_H
 
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
 #include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
 #include "servers/rendering/renderer_rd/shaders/fsr_upscale.glsl.gen.h"
 #include "servers/rendering/renderer_rd/shaders/luminance_reduce.glsl.gen.h"
@@ -238,7 +238,7 @@ public:
 
 	void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve);
 
-	void sub_surface_scattering(RID p_diffuse, RID p_diffuse2, RID p_depth, const CameraMatrix &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RS::SubSurfaceScatteringQuality p_quality);
+	void sub_surface_scattering(RID p_diffuse, RID p_diffuse2, RID p_depth, const Projection &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RS::SubSurfaceScatteringQuality p_quality);
 
 	void sort_buffer(RID p_uniform_set, int p_size);
 

Some files were not shown because too many files changed in this diff