Blob Blame History Raw
From 34a07b81ae640dc877a47091ca9a63d1a2cba07b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= <rverschelde@gmail.com>
Date: Thu, 16 Feb 2023 14:47:31 +0100
Subject: [PATCH] Allow unbundling OpenXR (for Linux distros)

Copy XrMatrix4x4f_CreateProjectionFov to our OpenXRUtil, instead of relying
on a private header.
---
 SConstruct                                    |   1 +
 modules/openxr/SCsub                          | 105 +++++++++---------
 .../extensions/openxr_extension_wrapper.h     |   2 -
 .../extensions/openxr_opengl_extension.cpp    |   4 +-
 .../extensions/openxr_vulkan_extension.cpp    |   4 +-
 modules/openxr/openxr_api.h                   |   2 -
 modules/openxr/openxr_util.cpp                |  88 +++++++++++++++
 modules/openxr/openxr_util.h                  |  21 ++++
 platform/linuxbsd/detect.py                   |   3 +
 9 files changed, 172 insertions(+), 58 deletions(-)

diff --git a/SConstruct b/SConstruct
index c67ce9fb9f982..2f892ebff15db 100644
--- a/SConstruct
+++ b/SConstruct
@@ -244,6 +244,7 @@ opts.Add(BoolVariable("builtin_libwebp", "Use the built-in libwebp library", Tru
 opts.Add(BoolVariable("builtin_wslay", "Use the built-in wslay library", True))
 opts.Add(BoolVariable("builtin_mbedtls", "Use the built-in mbedTLS library", True))
 opts.Add(BoolVariable("builtin_miniupnpc", "Use the built-in miniupnpc library", True))
+opts.Add(BoolVariable("builtin_openxr", "Use the built-in OpenXR library", True))
 opts.Add(BoolVariable("builtin_pcre2", "Use the built-in PCRE2 library", True))
 opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-in PCRE2 library", True))
 opts.Add(BoolVariable("builtin_recastnavigation", "Use the built-in Recast navigation library", True))
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index 0dd41675b6b0f..d5abbe4c72558 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -5,22 +5,13 @@ Import("env_modules")
 
 env_openxr = env_modules.Clone()
 
-#################################################
-# Add in our Khronos OpenXR loader
+# Thirdparty source files
 
 thirdparty_obj = []
-thirdparty_dir = "#thirdparty/openxr"
-
-env_openxr.Prepend(
-    CPPPATH=[
-        thirdparty_dir,
-        thirdparty_dir + "/include",
-        thirdparty_dir + "/src",
-        thirdparty_dir + "/src/common",
-        thirdparty_dir + "/src/external/jsoncpp/include",
-    ]
-)
 
+# Khronos OpenXR loader
+
+# Needs even for build against shared library, at least the defines used in public headers.
 if env["platform"] == "android":
     # may need to set OPENXR_ANDROID_VERSION_SUFFIX
     env_openxr.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"])
@@ -41,43 +32,57 @@ elif env["platform"] == "windows":
 # may need to check and set:
 # - XR_USE_TIMESPEC
 
-env_thirdparty = env_openxr.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
-
-if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]:
-    env_thirdparty["CXXFLAGS"].remove("-fno-exceptions")
-env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"])
-
-# add in external jsoncpp dependency
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_reader.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_value.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_writer.cpp")
-
-# add in load
-if env["platform"] != "android":
-    # On Android the openxr_loader is provided by separate plugins for each device
-    # Build the engine using object files
-    khrloader_obj = []
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
-
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp")
-
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
-    env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
-    env.modules_sources += khrloader_obj
-
-env.modules_sources += thirdparty_obj
-
-#################################################
-# And include our module source
+if env["builtin_openxr"]:
+    thirdparty_dir = "#thirdparty/openxr"
+
+    env_openxr.Prepend(
+        CPPPATH=[
+            thirdparty_dir,
+            thirdparty_dir + "/include",
+            thirdparty_dir + "/src",
+            thirdparty_dir + "/src/common",
+            thirdparty_dir + "/src/external/jsoncpp/include",
+        ]
+    )
+
+    env_thirdparty = env_openxr.Clone()
+    env_thirdparty.disable_warnings()
+    env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
+
+    if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]:
+        env_thirdparty["CXXFLAGS"].remove("-fno-exceptions")
+    env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"])
+
+    # add in external jsoncpp dependency
+    thirdparty_jsoncpp_dir = thirdparty_dir + "/src/external/jsoncpp/src/lib_json/"
+    env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_reader.cpp")
+    env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_value.cpp")
+    env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_writer.cpp")
+
+    # add in load
+    if env["platform"] != "android":
+        # On Android the openxr_loader is provided by separate plugins for each device
+        # Build the engine using object files
+        khrloader_obj = []
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
+
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp")
+
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
+        env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
+        env.modules_sources += khrloader_obj
+
+    env.modules_sources += thirdparty_obj
+
+
+# Godot source files
 
 module_obj = []
 
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index 920bfe74b737b..31f8d23268df8 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -36,8 +36,6 @@
 #include "core/templates/hash_map.h"
 #include "core/templates/rid.h"
 
-#include "thirdparty/openxr/src/common/xr_linear.h"
-
 #include <openxr/openxr.h>
 
 class OpenXRAPI;
diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
index 39b5c61e8e7d4..9038e9f45876a 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -278,8 +278,8 @@ bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 }
 
 bool OpenXROpenGLExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
-	XrMatrix4x4f matrix;
-	XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
+	OpenXRUtil::XrMatrix4x4f matrix;
+	OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
 
 	for (int j = 0; j < 4; j++) {
 		for (int i = 0; i < 4; i++) {
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.cpp b/modules/openxr/extensions/openxr_vulkan_extension.cpp
index 90c1c62050420..dd52b3b70ead6 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.cpp
+++ b/modules/openxr/extensions/openxr_vulkan_extension.cpp
@@ -381,8 +381,8 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
 
 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);
+	OpenXRUtil::XrMatrix4x4f matrix;
+	OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
 
 	for (int j = 0; j < 4; j++) {
 		for (int i = 0; i < 4; i++) {
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 96af2bfc4920a..9374cb7afadad 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -48,8 +48,6 @@
 #include "core/templates/vector.h"
 #include "servers/xr/xr_pose.h"
 
-#include "thirdparty/openxr/src/common/xr_linear.h"
-
 #include <openxr/openxr.h>
 
 // Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initializing structs which ensures zeroing out unspecified members.
diff --git a/modules/openxr/openxr_util.cpp b/modules/openxr/openxr_util.cpp
index 0c5cdd7113125..1d44233337814 100644
--- a/modules/openxr/openxr_util.cpp
+++ b/modules/openxr/openxr_util.cpp
@@ -32,6 +32,8 @@
 
 #include <openxr/openxr_reflection.h>
 
+#include <math.h>
+
 #define XR_ENUM_CASE_STR(name, val) \
 	case name:                      \
 		return #name;
@@ -75,3 +77,89 @@ String OpenXRUtil::make_xr_version_string(XrVersion p_version) {
 
 	return version;
 }
+
+// Copied from OpenXR xr_linear.h private header, so we can still link against
+// system-provided packages without relying on our `thirdparty` code.
+
+// Copyright (c) 2017 The Khronos Group Inc.
+// Copyright (c) 2016 Oculus VR, LLC.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+// Creates a projection matrix based on the specified dimensions.
+// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API.
+// The far plane is placed at infinity if farZ <= nearZ.
+// An infinite projection matrix is preferred for rasterization because, except for
+// things *right* up against the near plane, it always provides better precision:
+//              "Tightening the Precision of Perspective Rendering"
+//              Paul Upchurch, Mathieu Desbrun
+//              Journal of Graphics Tools, Volume 16, Issue 1, 2012
+void OpenXRUtil::XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft,
+		const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,
+		const float nearZ, const float farZ) {
+	const float tanAngleWidth = tanAngleRight - tanAngleLeft;
+
+	// Set to tanAngleDown - tanAngleUp for a clip space with positive Y down (Vulkan).
+	// Set to tanAngleUp - tanAngleDown for a clip space with positive Y up (OpenGL / D3D / Metal).
+	const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown);
+
+	// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
+	// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
+	const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0;
+
+	if (farZ <= nearZ) {
+		// place the far plane at infinity
+		result->m[0] = 2.0f / tanAngleWidth;
+		result->m[4] = 0.0f;
+		result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
+		result->m[12] = 0.0f;
+
+		result->m[1] = 0.0f;
+		result->m[5] = 2.0f / tanAngleHeight;
+		result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
+		result->m[13] = 0.0f;
+
+		result->m[2] = 0.0f;
+		result->m[6] = 0.0f;
+		result->m[10] = -1.0f;
+		result->m[14] = -(nearZ + offsetZ);
+
+		result->m[3] = 0.0f;
+		result->m[7] = 0.0f;
+		result->m[11] = -1.0f;
+		result->m[15] = 0.0f;
+	} else {
+		// normal projection
+		result->m[0] = 2.0f / tanAngleWidth;
+		result->m[4] = 0.0f;
+		result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
+		result->m[12] = 0.0f;
+
+		result->m[1] = 0.0f;
+		result->m[5] = 2.0f / tanAngleHeight;
+		result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
+		result->m[13] = 0.0f;
+
+		result->m[2] = 0.0f;
+		result->m[6] = 0.0f;
+		result->m[10] = -(farZ + offsetZ) / (farZ - nearZ);
+		result->m[14] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);
+
+		result->m[3] = 0.0f;
+		result->m[7] = 0.0f;
+		result->m[11] = -1.0f;
+		result->m[15] = 0.0f;
+	}
+}
+
+// Creates a projection matrix based on the specified FOV.
+void OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov,
+		const float nearZ, const float farZ) {
+	const float tanLeft = tanf(fov.angleLeft);
+	const float tanRight = tanf(fov.angleRight);
+
+	const float tanDown = tanf(fov.angleDown);
+	const float tanUp = tanf(fov.angleUp);
+
+	XrMatrix4x4f_CreateProjection(result, graphicsApi, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ);
+}
diff --git a/modules/openxr/openxr_util.h b/modules/openxr/openxr_util.h
index 8ad68c0b02243..3f36ab9fca2b4 100644
--- a/modules/openxr/openxr_util.h
+++ b/modules/openxr/openxr_util.h
@@ -44,6 +44,27 @@ class OpenXRUtil {
 	static String get_action_type_name(XrActionType p_action_type);
 	static String get_environment_blend_mode_name(XrEnvironmentBlendMode p_blend_mode);
 	static String make_xr_version_string(XrVersion p_version);
+
+	// Copied from OpenXR xr_linear.h private header, so we can still link against
+	// system-provided packages without relying on our `thirdparty` code.
+
+	// Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience.
+	typedef struct XrMatrix4x4f {
+		float m[16];
+	} XrMatrix4x4f;
+
+	typedef enum GraphicsAPI {
+		GRAPHICS_VULKAN,
+		GRAPHICS_OPENGL,
+		GRAPHICS_OPENGL_ES,
+		GRAPHICS_D3D
+	} GraphicsAPI;
+
+	static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft,
+			const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,
+			const float nearZ, const float farZ);
+	static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov,
+			const float nearZ, const float farZ);
 };
 
 #endif // OPENXR_UTIL_H
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index dadc03685bfe2..11fd2fc8096c0 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -291,6 +291,9 @@ def configure(env: "Environment"):
         # No pkgconfig file so far, hardcode expected lib name.
         env.Append(LIBS=["embree3"])
 
+    if not env["builtin_openxr"]:
+        env.ParseConfig("pkg-config openxr --cflags --libs")
+
     if env["fontconfig"]:
         if not env["use_sowrap"]:
             if os.system("pkg-config --exists fontconfig") == 0:  # 0 means found