1/**************************************************************************/
2/* openxr_fb_passthrough_extension_wrapper.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "openxr_fb_passthrough_extension_wrapper.h"
32
33#include "core/os/os.h"
34#include "scene/main/viewport.h"
35#include "scene/main/window.h"
36
37using namespace godot;
38
39OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::singleton = nullptr;
40
41OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::get_singleton() {
42 return singleton;
43}
44
45OpenXRFbPassthroughExtensionWrapper::OpenXRFbPassthroughExtensionWrapper() {
46 singleton = this;
47}
48
49OpenXRFbPassthroughExtensionWrapper::~OpenXRFbPassthroughExtensionWrapper() {
50 cleanup();
51}
52
53HashMap<String, bool *> OpenXRFbPassthroughExtensionWrapper::get_requested_extensions() {
54 HashMap<String, bool *> request_extensions;
55
56 request_extensions[XR_FB_PASSTHROUGH_EXTENSION_NAME] = &fb_passthrough_ext;
57 request_extensions[XR_FB_TRIANGLE_MESH_EXTENSION_NAME] = &fb_triangle_mesh_ext;
58
59 return request_extensions;
60}
61
62void OpenXRFbPassthroughExtensionWrapper::cleanup() {
63 fb_passthrough_ext = false;
64 fb_triangle_mesh_ext = false;
65}
66
67Viewport *OpenXRFbPassthroughExtensionWrapper::get_main_viewport() {
68 MainLoop *main_loop = OS::get_singleton()->get_main_loop();
69 if (!main_loop) {
70 print_error("Unable to retrieve main loop");
71 return nullptr;
72 }
73
74 SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
75 if (!scene_tree) {
76 print_error("Unable to retrieve scene tree");
77 return nullptr;
78 }
79
80 Viewport *viewport = scene_tree->get_root()->get_viewport();
81 return viewport;
82}
83
84void OpenXRFbPassthroughExtensionWrapper::on_instance_created(const XrInstance instance) {
85 if (fb_passthrough_ext) {
86 bool result = initialize_fb_passthrough_extension(instance);
87 if (!result) {
88 print_error("Failed to initialize fb_passthrough extension");
89 fb_passthrough_ext = false;
90 }
91 }
92
93 if (fb_triangle_mesh_ext) {
94 bool result = initialize_fb_triangle_mesh_extension(instance);
95 if (!result) {
96 print_error("Failed to initialize fb_triangle_mesh extension");
97 fb_triangle_mesh_ext = false;
98 }
99 }
100
101 if (fb_passthrough_ext) {
102 OpenXRAPI::get_singleton()->register_composition_layer_provider(this);
103 }
104}
105
106bool OpenXRFbPassthroughExtensionWrapper::is_passthrough_enabled() {
107 return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && passthrough_layer != XR_NULL_HANDLE;
108}
109
110bool OpenXRFbPassthroughExtensionWrapper::start_passthrough() {
111 if (passthrough_handle == XR_NULL_HANDLE) {
112 return false;
113 }
114
115 if (is_passthrough_enabled()) {
116 return true;
117 }
118
119 // Start the passthrough feature
120 XrResult result = xrPassthroughStartFB(passthrough_handle);
121 if (!is_valid_passthrough_result(result, "Failed to start passthrough")) {
122 stop_passthrough();
123 return false;
124 }
125
126 // Create the passthrough layer
127 XrPassthroughLayerCreateInfoFB passthrough_layer_config = {
128 XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB,
129 nullptr,
130 passthrough_handle,
131 XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB,
132 XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB,
133 };
134 result = xrCreatePassthroughLayerFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_layer_config, &passthrough_layer);
135 if (!is_valid_passthrough_result(result, "Failed to create the passthrough layer")) {
136 stop_passthrough();
137 return false;
138 }
139
140 // Check if the the viewport has transparent background
141 Viewport *viewport = get_main_viewport();
142 if (viewport && !viewport->has_transparent_background()) {
143 print_error("Main viewport doesn't have transparent background! Passthrough may not properly render.");
144 }
145
146 return true;
147}
148
149void OpenXRFbPassthroughExtensionWrapper::on_session_created(const XrSession session) {
150 if (fb_passthrough_ext) {
151 // Create the passthrough feature and start it.
152 XrPassthroughCreateInfoFB passthrough_create_info = {
153 XR_TYPE_PASSTHROUGH_CREATE_INFO_FB,
154 nullptr,
155 0,
156 };
157
158 XrResult result = xrCreatePassthroughFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_create_info, &passthrough_handle);
159 if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to create passthrough")) {
160 passthrough_handle = XR_NULL_HANDLE;
161 return;
162 }
163 }
164}
165
166XrCompositionLayerBaseHeader *OpenXRFbPassthroughExtensionWrapper::get_composition_layer() {
167 if (is_passthrough_enabled()) {
168 composition_passthrough_layer.layerHandle = passthrough_layer;
169 return (XrCompositionLayerBaseHeader *)&composition_passthrough_layer;
170 } else {
171 return nullptr;
172 }
173}
174
175void OpenXRFbPassthroughExtensionWrapper::stop_passthrough() {
176 if (!fb_passthrough_ext) {
177 return;
178 }
179
180 XrResult result;
181 if (passthrough_layer != XR_NULL_HANDLE) {
182 // Destroy the layer
183 result = xrDestroyPassthroughLayerFB(passthrough_layer);
184 OpenXRAPI::get_singleton()->xr_result(result, "Unable to destroy passthrough layer");
185 passthrough_layer = XR_NULL_HANDLE;
186 }
187
188 if (passthrough_handle != XR_NULL_HANDLE) {
189 result = xrPassthroughPauseFB(passthrough_handle);
190 OpenXRAPI::get_singleton()->xr_result(result, "Unable to stop passthrough feature");
191 }
192}
193
194void OpenXRFbPassthroughExtensionWrapper::on_session_destroyed() {
195 if (fb_passthrough_ext) {
196 stop_passthrough();
197
198 XrResult result;
199 if (passthrough_handle != XR_NULL_HANDLE) {
200 result = xrDestroyPassthroughFB(passthrough_handle);
201 OpenXRAPI::get_singleton()->xr_result(result, "Unable to destroy passthrough feature");
202 passthrough_handle = XR_NULL_HANDLE;
203 }
204 }
205}
206
207void OpenXRFbPassthroughExtensionWrapper::on_instance_destroyed() {
208 if (fb_passthrough_ext) {
209 OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this);
210 }
211 cleanup();
212}
213
214bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_passthrough_extension(const XrInstance p_instance) {
215 ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
216
217 EXT_INIT_XR_FUNC_V(xrCreatePassthroughFB);
218 EXT_INIT_XR_FUNC_V(xrDestroyPassthroughFB);
219 EXT_INIT_XR_FUNC_V(xrPassthroughStartFB);
220 EXT_INIT_XR_FUNC_V(xrPassthroughPauseFB);
221 EXT_INIT_XR_FUNC_V(xrCreatePassthroughLayerFB);
222 EXT_INIT_XR_FUNC_V(xrDestroyPassthroughLayerFB);
223 EXT_INIT_XR_FUNC_V(xrPassthroughLayerPauseFB);
224 EXT_INIT_XR_FUNC_V(xrPassthroughLayerResumeFB);
225 EXT_INIT_XR_FUNC_V(xrPassthroughLayerSetStyleFB);
226 EXT_INIT_XR_FUNC_V(xrCreateGeometryInstanceFB);
227 EXT_INIT_XR_FUNC_V(xrDestroyGeometryInstanceFB);
228 EXT_INIT_XR_FUNC_V(xrGeometryInstanceSetTransformFB);
229
230 return true;
231}
232
233bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_triangle_mesh_extension(const XrInstance p_instance) {
234 ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
235
236 EXT_INIT_XR_FUNC_V(xrCreateTriangleMeshFB);
237 EXT_INIT_XR_FUNC_V(xrDestroyTriangleMeshFB);
238 EXT_INIT_XR_FUNC_V(xrTriangleMeshGetVertexBufferFB);
239 EXT_INIT_XR_FUNC_V(xrTriangleMeshGetIndexBufferFB);
240 EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginUpdateFB);
241 EXT_INIT_XR_FUNC_V(xrTriangleMeshEndUpdateFB);
242 EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginVertexBufferUpdateFB);
243 EXT_INIT_XR_FUNC_V(xrTriangleMeshEndVertexBufferUpdateFB);
244
245 return true;
246}
247