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 | |
37 | using namespace godot; |
38 | |
39 | OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::singleton = nullptr; |
40 | |
41 | OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::get_singleton() { |
42 | return singleton; |
43 | } |
44 | |
45 | OpenXRFbPassthroughExtensionWrapper::OpenXRFbPassthroughExtensionWrapper() { |
46 | singleton = this; |
47 | } |
48 | |
49 | OpenXRFbPassthroughExtensionWrapper::~OpenXRFbPassthroughExtensionWrapper() { |
50 | cleanup(); |
51 | } |
52 | |
53 | HashMap<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 | |
62 | void OpenXRFbPassthroughExtensionWrapper::cleanup() { |
63 | fb_passthrough_ext = false; |
64 | fb_triangle_mesh_ext = false; |
65 | } |
66 | |
67 | Viewport *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 | |
84 | void 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 | |
106 | bool OpenXRFbPassthroughExtensionWrapper::is_passthrough_enabled() { |
107 | return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && passthrough_layer != XR_NULL_HANDLE; |
108 | } |
109 | |
110 | bool 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 | |
149 | void 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 | |
166 | XrCompositionLayerBaseHeader *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 | |
175 | void 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 | |
194 | void 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 | |
207 | void OpenXRFbPassthroughExtensionWrapper::on_instance_destroyed() { |
208 | if (fb_passthrough_ext) { |
209 | OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this); |
210 | } |
211 | cleanup(); |
212 | } |
213 | |
214 | bool 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 | |
233 | bool 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 | |