Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/openxr/extensions/openxr_render_model_extension.cpp
21179 views
1
/**************************************************************************/
2
/* openxr_render_model_extension.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_render_model_extension.h"
32
33
#ifdef MODULE_GLTF_ENABLED
34
#include "../openxr_api.h"
35
#include "../openxr_interface.h"
36
37
#include "core/config/project_settings.h"
38
#include "core/string/print_string.h"
39
#include "servers/xr/xr_server.h"
40
41
OpenXRRenderModelExtension *OpenXRRenderModelExtension::singleton = nullptr;
42
43
OpenXRRenderModelExtension *OpenXRRenderModelExtension::get_singleton() {
44
return singleton;
45
}
46
47
void OpenXRRenderModelExtension::_bind_methods() {
48
ClassDB::bind_method(D_METHOD("is_active"), &OpenXRRenderModelExtension::is_active);
49
ClassDB::bind_method(D_METHOD("render_model_create", "render_model_id"), &OpenXRRenderModelExtension::render_model_create);
50
ClassDB::bind_method(D_METHOD("render_model_destroy", "render_model"), &OpenXRRenderModelExtension::render_model_destroy);
51
ClassDB::bind_method(D_METHOD("render_model_get_all"), &OpenXRRenderModelExtension::render_model_get_all);
52
ClassDB::bind_method(D_METHOD("render_model_new_scene_instance", "render_model"), &OpenXRRenderModelExtension::render_model_new_scene_instance);
53
ClassDB::bind_method(D_METHOD("render_model_get_subaction_paths", "render_model"), &OpenXRRenderModelExtension::render_model_get_subaction_paths);
54
ClassDB::bind_method(D_METHOD("render_model_get_top_level_path", "render_model"), &OpenXRRenderModelExtension::render_model_get_top_level_path_as_string);
55
ClassDB::bind_method(D_METHOD("render_model_get_confidence", "render_model"), &OpenXRRenderModelExtension::render_model_get_confidence);
56
ClassDB::bind_method(D_METHOD("render_model_get_root_transform", "render_model"), &OpenXRRenderModelExtension::render_model_get_root_transform);
57
ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_count", "render_model"), &OpenXRRenderModelExtension::render_model_get_animatable_node_count);
58
ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_name", "render_model", "index"), &OpenXRRenderModelExtension::render_model_get_animatable_node_name);
59
ClassDB::bind_method(D_METHOD("render_model_is_animatable_node_visible", "render_model", "index"), &OpenXRRenderModelExtension::render_model_is_animatable_node_visible);
60
ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_transform", "render_model", "index"), &OpenXRRenderModelExtension::render_model_get_animatable_node_transform);
61
62
ADD_SIGNAL(MethodInfo("render_model_added", PropertyInfo(Variant::RID, "render_model")));
63
ADD_SIGNAL(MethodInfo("render_model_removed", PropertyInfo(Variant::RID, "render_model")));
64
ADD_SIGNAL(MethodInfo("render_model_top_level_path_changed", PropertyInfo(Variant::RID, "render_model")));
65
}
66
67
OpenXRRenderModelExtension::OpenXRRenderModelExtension() {
68
singleton = this;
69
}
70
71
OpenXRRenderModelExtension::~OpenXRRenderModelExtension() {
72
singleton = nullptr;
73
}
74
75
HashMap<String, bool *> OpenXRRenderModelExtension::get_requested_extensions(XrVersion p_version) {
76
HashMap<String, bool *> request_extensions;
77
78
if (GLOBAL_GET("xr/openxr/extensions/render_model")) {
79
if (p_version < XR_API_VERSION_1_1_0) {
80
// Extension was promoted in OpenXR 1.1, only include it in OpenXR 1.0.
81
request_extensions[XR_EXT_UUID_EXTENSION_NAME] = &uuid_ext;
82
}
83
request_extensions[XR_EXT_RENDER_MODEL_EXTENSION_NAME] = &render_model_ext;
84
request_extensions[XR_EXT_INTERACTION_RENDER_MODEL_EXTENSION_NAME] = &interaction_render_model_ext;
85
}
86
87
return request_extensions;
88
}
89
90
void OpenXRRenderModelExtension::on_instance_created(const XrInstance p_instance) {
91
// Standard entry points we use.
92
EXT_INIT_XR_FUNC(xrLocateSpace);
93
EXT_INIT_XR_FUNC(xrDestroySpace);
94
EXT_INIT_XR_FUNC(xrPathToString);
95
96
if (render_model_ext) {
97
EXT_INIT_XR_FUNC(xrCreateRenderModelEXT);
98
EXT_INIT_XR_FUNC(xrDestroyRenderModelEXT);
99
EXT_INIT_XR_FUNC(xrGetRenderModelPropertiesEXT);
100
EXT_INIT_XR_FUNC(xrCreateRenderModelSpaceEXT);
101
EXT_INIT_XR_FUNC(xrCreateRenderModelAssetEXT);
102
EXT_INIT_XR_FUNC(xrDestroyRenderModelAssetEXT);
103
EXT_INIT_XR_FUNC(xrGetRenderModelAssetDataEXT);
104
EXT_INIT_XR_FUNC(xrGetRenderModelAssetPropertiesEXT);
105
EXT_INIT_XR_FUNC(xrGetRenderModelStateEXT);
106
}
107
108
if (interaction_render_model_ext) {
109
EXT_INIT_XR_FUNC(xrEnumerateInteractionRenderModelIdsEXT);
110
EXT_INIT_XR_FUNC(xrEnumerateRenderModelSubactionPathsEXT);
111
EXT_INIT_XR_FUNC(xrGetRenderModelPoseTopLevelUserPathEXT);
112
}
113
}
114
115
void OpenXRRenderModelExtension::on_session_created(const XrSession p_session) {
116
_interaction_data_dirty = true;
117
}
118
119
void OpenXRRenderModelExtension::on_instance_destroyed() {
120
xrCreateRenderModelEXT_ptr = nullptr;
121
xrDestroyRenderModelEXT_ptr = nullptr;
122
xrGetRenderModelPropertiesEXT_ptr = nullptr;
123
xrCreateRenderModelSpaceEXT_ptr = nullptr;
124
xrCreateRenderModelAssetEXT_ptr = nullptr;
125
xrDestroyRenderModelAssetEXT_ptr = nullptr;
126
xrGetRenderModelAssetDataEXT_ptr = nullptr;
127
xrGetRenderModelAssetPropertiesEXT_ptr = nullptr;
128
xrGetRenderModelStateEXT_ptr = nullptr;
129
xrEnumerateInteractionRenderModelIdsEXT_ptr = nullptr;
130
xrEnumerateRenderModelSubactionPathsEXT_ptr = nullptr;
131
xrGetRenderModelPoseTopLevelUserPathEXT_ptr = nullptr;
132
133
uuid_ext = false;
134
render_model_ext = false;
135
interaction_render_model_ext = false;
136
}
137
138
void OpenXRRenderModelExtension::on_session_destroyed() {
139
_clear_interaction_data();
140
_clear_render_model_data();
141
142
// We no longer have valid sync data.
143
xr_sync_has_run = false;
144
}
145
146
bool OpenXRRenderModelExtension::on_event_polled(const XrEventDataBuffer &event) {
147
if (event.type == XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) {
148
// Mark interaction data as dirty so that we update it on sync.
149
_interaction_data_dirty = true;
150
151
return true;
152
} else if (event.type == XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) {
153
// If our controller bindings changed, its likely our render models change too.
154
// We should be getting a XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT
155
// but checking for this scenario just in case.
156
_interaction_data_dirty = true;
157
158
// Do not consider this handled, we simply do additional logic.
159
return false;
160
}
161
162
return false;
163
}
164
165
void OpenXRRenderModelExtension::on_sync_actions() {
166
if (!is_active()) {
167
return;
168
}
169
170
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
171
ERR_FAIL_NULL(openxr_api);
172
173
// Mark sync as run
174
xr_sync_has_run = true;
175
176
// Update our interaction data if needed
177
if (_interaction_data_dirty) {
178
_update_interaction_data();
179
}
180
181
// Loop through all of our render models to update our space and state info
182
LocalVector<RID> owned = render_model_owner.get_owned_list();
183
184
for (const RID &rid : owned) {
185
RenderModel *render_model = render_model_owner.get_or_null(rid);
186
if (render_model && render_model->xr_space != XR_NULL_HANDLE) {
187
XrSpaceLocation render_model_location = {
188
XR_TYPE_SPACE_LOCATION, // type
189
nullptr, // next
190
0, // locationFlags
191
{ { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } }, // pose
192
};
193
194
XrResult result = xrLocateSpace(render_model->xr_space, openxr_api->get_play_space(), openxr_api->get_predicted_display_time(), &render_model_location);
195
ERR_CONTINUE_MSG(XR_FAILED(result), "OpenXR: Failed to locate render model space [" + openxr_api->get_error_string(result) + "]");
196
197
render_model->confidence = openxr_api->transform_from_location(render_model_location, render_model->root_transform);
198
199
if (!render_model->node_states.is_empty()) {
200
// Get node states.
201
XrRenderModelStateGetInfoEXT get_state_info = {
202
XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT, // type
203
nullptr, // next
204
openxr_api->get_predicted_display_time() // displayTime
205
};
206
207
XrRenderModelStateEXT state = {
208
XR_TYPE_RENDER_MODEL_STATE_EXT, // type
209
nullptr, // next
210
render_model->animatable_node_count, // nodeStateCount
211
render_model->node_states.ptr(), // nodeStates
212
};
213
214
result = xrGetRenderModelStateEXT(render_model->xr_render_model, &get_state_info, &state);
215
if (XR_FAILED(result)) {
216
ERR_PRINT("OpenXR: Failed to update node states [" + openxr_api->get_error_string(result) + "]");
217
}
218
}
219
220
XrPath new_path = XR_NULL_PATH;
221
222
if (toplevel_paths.is_empty()) {
223
// Set this up just once with paths we support here.
224
toplevel_paths.push_back(openxr_api->get_xr_path("/user/hand/left"));
225
toplevel_paths.push_back(openxr_api->get_xr_path("/user/hand/right"));
226
}
227
228
XrInteractionRenderModelTopLevelUserPathGetInfoEXT info = {
229
XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT, // type
230
nullptr, // next
231
(uint32_t)toplevel_paths.size(), // topLevelUserPathCount
232
toplevel_paths.ptr() // topLevelUserPaths
233
};
234
result = xrGetRenderModelPoseTopLevelUserPathEXT(render_model->xr_render_model, &info, &new_path);
235
if (XR_FAILED(result)) {
236
ERR_PRINT("OpenXR: Failed to update the top level path for render models [" + openxr_api->get_error_string(result) + "]");
237
} else if (new_path != render_model->top_level_path) {
238
print_verbose("OpenXR: Render model top level path changed to " + openxr_api->get_xr_path_name(new_path));
239
240
// Set the new path
241
render_model->top_level_path = new_path;
242
243
// And broadcast it
244
// Note, converting an XrPath to a String has overhead, so we won't do this automatically.
245
emit_signal(SNAME("render_model_top_level_path_changed"), rid);
246
}
247
}
248
}
249
}
250
251
bool OpenXRRenderModelExtension::is_active() const {
252
return render_model_ext && interaction_render_model_ext;
253
}
254
255
void OpenXRRenderModelExtension::_clear_interaction_data() {
256
for (const KeyValue<XrRenderModelIdEXT, RID> &e : interaction_render_models) {
257
render_model_destroy(e.value);
258
}
259
interaction_render_models.clear();
260
}
261
262
bool OpenXRRenderModelExtension::_update_interaction_data() {
263
ERR_FAIL_COND_V_MSG(!interaction_render_model_ext, false, "Interaction render model extension hasn't been enabled.");
264
265
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
266
ERR_FAIL_NULL_V(openxr_api, false);
267
268
XrSession session = openxr_api->get_session();
269
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
270
271
// Check if syncActions has been run at least once or there is no point in getting data.
272
if (!xr_sync_has_run) {
273
// Do not treat this as an error.
274
return true;
275
}
276
277
// If we get this far, no longer mark as dirty.
278
// Else we just repeat the same error over and over again.
279
_interaction_data_dirty = false;
280
281
// Obtain interaction info.
282
XrInteractionRenderModelIdsEnumerateInfoEXT interaction_info = {
283
XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT, // type
284
nullptr, // next
285
};
286
287
// Obtain count.
288
uint32_t interaction_count = 0;
289
XrResult result = xrEnumerateInteractionRenderModelIdsEXT(session, &interaction_info, 0, &interaction_count, nullptr);
290
if (XR_FAILED(result)) {
291
// not successful? then we do nothing.
292
ERR_FAIL_V_MSG(false, "OpenXR: Failed to obtain render model interaction id count [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
293
}
294
295
// Create some storage
296
LocalVector<XrRenderModelIdEXT> render_model_interaction_ids;
297
render_model_interaction_ids.resize(interaction_count);
298
299
// Only need to fetch data if there is something to fetch (/we've got storage).
300
if (!render_model_interaction_ids.is_empty()) {
301
// Obtain interaction ids
302
result = xrEnumerateInteractionRenderModelIdsEXT(session, &interaction_info, render_model_interaction_ids.size(), &interaction_count, render_model_interaction_ids.ptr());
303
if (XR_FAILED(result)) {
304
ERR_FAIL_V_MSG(false, "OpenXR: Failed to obtain render model interaction ids [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
305
}
306
}
307
308
// Remove render models that are no longer tracked
309
LocalVector<XrRenderModelIdEXT> erase_ids;
310
for (const KeyValue<XrRenderModelIdEXT, RID> &e : interaction_render_models) {
311
if (!render_model_interaction_ids.has(e.key)) {
312
if (e.value.is_valid()) {
313
render_model_destroy(e.value);
314
}
315
316
erase_ids.push_back(e.key);
317
}
318
}
319
320
// Remove these from our hashmap
321
for (const XrRenderModelIdEXT &id : erase_ids) {
322
interaction_render_models.erase(id);
323
}
324
325
// Now update our models
326
for (const XrRenderModelIdEXT &id : render_model_interaction_ids) {
327
if (!interaction_render_models.has(id)) {
328
// Even if this fails we add it so we don't repeat trying to create it
329
interaction_render_models[id] = render_model_create(id);
330
}
331
}
332
333
return true;
334
}
335
336
bool OpenXRRenderModelExtension::has_render_model(RID p_render_model) const {
337
return render_model_owner.owns(p_render_model);
338
}
339
340
RID OpenXRRenderModelExtension::render_model_create(XrRenderModelIdEXT p_render_model_id) {
341
ERR_FAIL_COND_V_MSG(!render_model_ext, RID(), "Render model extension hasn't been enabled.");
342
343
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
344
ERR_FAIL_NULL_V(openxr_api, RID());
345
346
XrSession session = openxr_api->get_session();
347
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, RID());
348
349
RenderModel render_model;
350
render_model.xr_render_model_id = p_render_model_id;
351
352
// Get a list of supported glTF extensions.
353
const HashSet<String> supported_gltf_extensions_hash_set = GLTFDocument::get_supported_gltf_extensions_hashset();
354
Vector<CharString> supported_gltf_extensions_char_string; // Just for temp storage of our c-strings.
355
supported_gltf_extensions_char_string.resize(supported_gltf_extensions_hash_set.size());
356
int64_t supported_gltf_extension_index = 0;
357
for (const String &ext : supported_gltf_extensions_hash_set) {
358
supported_gltf_extensions_char_string.set(supported_gltf_extension_index, ext.utf8());
359
supported_gltf_extension_index++;
360
}
361
// Now we can convert them to the `const char *` format.
362
Vector<const char *> supported_gltf_extensions;
363
supported_gltf_extensions.resize(supported_gltf_extensions_char_string.size());
364
for (int64_t i = 0; i < supported_gltf_extensions_char_string.size(); i++) {
365
supported_gltf_extensions.write[i] = supported_gltf_extensions_char_string[i].get_data();
366
}
367
368
XrRenderModelCreateInfoEXT create_info = {
369
XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT, // type
370
nullptr, // next
371
p_render_model_id, // renderModelId
372
uint32_t(supported_gltf_extensions.size()), // gltfExtensionCount
373
supported_gltf_extensions.ptr(), // gltfExtensions
374
};
375
376
XrResult result = xrCreateRenderModelEXT(session, &create_info, &render_model.xr_render_model);
377
if (XR_FAILED(result)) {
378
ERR_FAIL_V_MSG(RID(), "OpenXR: Failed to create render model [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
379
}
380
381
XrRenderModelPropertiesGetInfoEXT properties_info = {
382
XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT, // type
383
nullptr, // next
384
};
385
386
XrRenderModelPropertiesEXT properties = {
387
XR_TYPE_RENDER_MODEL_PROPERTIES_EXT, // type
388
nullptr, // next
389
{}, // cacheId
390
0, // animatableNodeCount
391
};
392
393
result = xrGetRenderModelPropertiesEXT(render_model.xr_render_model, &properties_info, &properties);
394
if (XR_FAILED(result)) {
395
ERR_PRINT("OpenXR: Failed to get render model properties [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
396
} else {
397
render_model.animatable_node_count = properties.animatableNodeCount;
398
render_model.render_model_data = _get_render_model_data(properties.cacheId, properties.animatableNodeCount);
399
}
400
401
// Create space for positioning our asset.
402
XrRenderModelSpaceCreateInfoEXT space_create_info = {
403
XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT, // type
404
nullptr, // next
405
render_model.xr_render_model // renderModel
406
};
407
408
result = xrCreateRenderModelSpaceEXT(session, &space_create_info, &render_model.xr_space);
409
if (XR_FAILED(result)) {
410
ERR_PRINT("OpenXR: Failed to create render model space [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
411
}
412
413
if (render_model.animatable_node_count > 0) {
414
render_model.node_states.resize(render_model.animatable_node_count);
415
}
416
417
RID new_rid = render_model_owner.make_rid(render_model);
418
419
emit_signal(SNAME("render_model_added"), new_rid);
420
421
return new_rid;
422
}
423
424
RID OpenXRRenderModelExtension::_render_model_create(uint64_t p_render_model_id) {
425
RID ret;
426
427
ERR_FAIL_COND_V(p_render_model_id == XR_NULL_RENDER_MODEL_ID_EXT, ret);
428
429
if (is_active()) {
430
ret = render_model_create(XrRenderModelIdEXT(p_render_model_id));
431
}
432
433
return ret;
434
}
435
436
void OpenXRRenderModelExtension::render_model_destroy(RID p_render_model) {
437
ERR_FAIL_COND_MSG(!render_model_ext, "Render model extension hasn't been enabled.");
438
439
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
440
ERR_FAIL_NULL(render_model);
441
442
emit_signal(SNAME("render_model_removed"), p_render_model);
443
444
// Clean up.
445
if (render_model->xr_space != XR_NULL_HANDLE) {
446
xrDestroySpace(render_model->xr_space);
447
}
448
449
render_model->node_states.clear();
450
451
// And destroy our model.
452
XrResult result = xrDestroyRenderModelEXT(render_model->xr_render_model);
453
if (XR_FAILED(result)) {
454
ERR_PRINT("OpenXR: Failed to destroy render model [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
455
}
456
457
render_model_owner.free(p_render_model);
458
}
459
460
TypedArray<RID> OpenXRRenderModelExtension::render_model_get_all() {
461
TypedArray<RID> ret;
462
463
LocalVector<RID> rids = render_model_owner.get_owned_list();
464
465
for (const RID &rid : rids) {
466
ret.push_back(rid);
467
}
468
469
return ret;
470
}
471
472
Node3D *OpenXRRenderModelExtension::render_model_new_scene_instance(RID p_render_model) const {
473
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
474
ERR_FAIL_NULL_V(render_model, nullptr);
475
476
if (render_model->render_model_data.is_null()) {
477
// We never loaded it (don't spam errors here).
478
return nullptr;
479
}
480
481
return render_model->render_model_data->new_scene_instance();
482
}
483
484
PackedStringArray OpenXRRenderModelExtension::render_model_get_subaction_paths(RID p_render_model) {
485
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
486
ERR_FAIL_NULL_V(openxr_api, PackedStringArray());
487
488
XrInstance instance = openxr_api->get_instance();
489
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, PackedStringArray());
490
491
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
492
ERR_FAIL_NULL_V(render_model, PackedStringArray());
493
494
PackedStringArray subaction_paths;
495
496
XrInteractionRenderModelSubactionPathInfoEXT subaction_info = {
497
XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT, // type
498
nullptr, // next
499
};
500
501
uint32_t capacity;
502
503
XrResult result = xrEnumerateRenderModelSubactionPathsEXT(render_model->xr_render_model, &subaction_info, 0, &capacity, nullptr);
504
if (XR_FAILED(result)) {
505
ERR_FAIL_V_MSG(PackedStringArray(), "OpenXR: Failed to obtain render model subaction path count [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
506
}
507
508
if (capacity > 0) {
509
LocalVector<XrPath> paths;
510
511
paths.resize(capacity);
512
513
result = xrEnumerateRenderModelSubactionPathsEXT(render_model->xr_render_model, &subaction_info, capacity, &capacity, paths.ptr());
514
if (XR_FAILED(result)) {
515
ERR_FAIL_V_MSG(PackedStringArray(), "OpenXR: Failed to obtain render model subaction paths [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
516
}
517
518
for (uint32_t i = 0; i < capacity; i++) {
519
char buffer[1024];
520
uint32_t size = 0;
521
xrPathToString(instance, paths[i], 1024, &size, buffer);
522
if (size > 0) {
523
subaction_paths.push_back(String(buffer));
524
}
525
}
526
}
527
528
return subaction_paths;
529
}
530
531
XrPath OpenXRRenderModelExtension::render_model_get_top_level_path(RID p_render_model) const {
532
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
533
ERR_FAIL_NULL_V(render_model, XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE);
534
535
return render_model->top_level_path;
536
}
537
538
String OpenXRRenderModelExtension::render_model_get_top_level_path_as_string(RID p_render_model) const {
539
String ret;
540
541
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
542
ERR_FAIL_NULL_V(openxr_api, ret);
543
544
if (is_active() && has_render_model(p_render_model)) {
545
XrPath path = render_model_get_top_level_path(p_render_model);
546
if (path == XR_NULL_PATH) {
547
return "None";
548
} else {
549
return openxr_api->get_xr_path_name(path);
550
}
551
}
552
553
return ret;
554
}
555
556
XRPose::TrackingConfidence OpenXRRenderModelExtension::render_model_get_confidence(RID p_render_model) const {
557
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
558
ERR_FAIL_NULL_V(render_model, XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE);
559
560
return render_model->confidence;
561
}
562
563
Transform3D OpenXRRenderModelExtension::render_model_get_root_transform(RID p_render_model) const {
564
XRServer *xr_server = XRServer::get_singleton();
565
ERR_FAIL_NULL_V(xr_server, Transform3D());
566
567
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
568
ERR_FAIL_NULL_V(render_model, Transform3D());
569
570
// Scale our root transform
571
real_t world_scale = xr_server->get_world_scale();
572
Transform3D root_transform = render_model->root_transform.scaled(Vector3(world_scale, world_scale, world_scale));
573
574
return xr_server->get_reference_frame() * root_transform;
575
}
576
577
uint32_t OpenXRRenderModelExtension::render_model_get_animatable_node_count(RID p_render_model) const {
578
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
579
ERR_FAIL_NULL_V(render_model, 0);
580
581
return render_model->animatable_node_count;
582
}
583
584
String OpenXRRenderModelExtension::render_model_get_animatable_node_name(RID p_render_model, uint32_t p_index) const {
585
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
586
ERR_FAIL_NULL_V(render_model, String());
587
588
if (render_model->render_model_data.is_null()) {
589
// We never loaded it (don't spam errors here).
590
return String();
591
}
592
593
return render_model->render_model_data->get_node_name(p_index);
594
}
595
596
bool OpenXRRenderModelExtension::render_model_is_animatable_node_visible(RID p_render_model, uint32_t p_index) const {
597
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
598
ERR_FAIL_NULL_V(render_model, false);
599
600
ERR_FAIL_UNSIGNED_INDEX_V(p_index, render_model->animatable_node_count, false);
601
602
if (render_model->node_states.is_empty()) {
603
// Never allocated (don't spam errors here).
604
return false;
605
}
606
607
return render_model->node_states[p_index].isVisible;
608
}
609
610
Transform3D OpenXRRenderModelExtension::render_model_get_animatable_node_transform(RID p_render_model, uint32_t p_index) const {
611
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
612
ERR_FAIL_NULL_V(openxr_api, Transform3D());
613
614
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
615
ERR_FAIL_NULL_V(render_model, Transform3D());
616
617
ERR_FAIL_UNSIGNED_INDEX_V(p_index, render_model->animatable_node_count, Transform3D());
618
619
if (render_model->node_states.is_empty()) {
620
// Never allocated (don't spam errors here).
621
return Transform3D();
622
}
623
624
return openxr_api->transform_from_pose(render_model->node_states[p_index].nodePose);
625
}
626
627
Ref<OpenXRRenderModelData> OpenXRRenderModelExtension::_get_render_model_data(XrUuidEXT p_cache_id, uint32_t p_animatable_node_count) {
628
if (render_model_data_cache.has(p_cache_id)) {
629
return render_model_data_cache[p_cache_id];
630
}
631
632
// We don't have this cached, lets load it up
633
634
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
635
ERR_FAIL_NULL_V(openxr_api, nullptr);
636
637
XrSession session = openxr_api->get_session();
638
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, nullptr);
639
640
XrRenderModelAssetEXT asset;
641
642
XrRenderModelAssetCreateInfoEXT create_info = {
643
XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT, // type
644
nullptr, // next
645
p_cache_id // cacheId
646
};
647
648
XrResult result = xrCreateRenderModelAssetEXT(session, &create_info, &asset);
649
if (XR_FAILED(result)) {
650
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to create render model asset [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
651
}
652
653
Ref<OpenXRRenderModelData> render_model_data = _load_asset(asset, p_animatable_node_count);
654
655
// We're done with this :)
656
result = xrDestroyRenderModelAssetEXT(asset);
657
if (XR_FAILED(result)) {
658
ERR_PRINT("OpenXR: Failed to destroy render model asset [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
659
}
660
661
// And cache it
662
render_model_data_cache[p_cache_id] = render_model_data;
663
664
return render_model_data;
665
}
666
667
Ref<OpenXRRenderModelData> OpenXRRenderModelExtension::_load_asset(XrRenderModelAssetEXT p_asset, uint32_t p_animatable_node_count) {
668
XrRenderModelAssetDataGetInfoEXT get_info = {
669
XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT, // type
670
nullptr, // next
671
};
672
673
XrRenderModelAssetDataEXT asset_data = {
674
XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT, // type
675
nullptr, // next
676
0, // bufferCapacityInput;
677
0, // bufferCountOutput;
678
nullptr // buffer;
679
};
680
681
// Obtain required size for the buffer.
682
XrResult result = xrGetRenderModelAssetDataEXT(p_asset, &get_info, &asset_data);
683
if (XR_FAILED(result)) {
684
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model buffer size [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
685
}
686
ERR_FAIL_COND_V(asset_data.bufferCountOutput == 0, nullptr);
687
688
// Allocate data
689
PackedByteArray buffer;
690
buffer.resize(asset_data.bufferCountOutput);
691
asset_data.buffer = buffer.ptrw();
692
asset_data.bufferCapacityInput = asset_data.bufferCountOutput;
693
694
// Now get our actual data.
695
result = xrGetRenderModelAssetDataEXT(p_asset, &get_info, &asset_data);
696
if (XR_FAILED(result)) {
697
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model buffer [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
698
}
699
700
// Get the names of any animatable nodes
701
PackedStringArray node_names;
702
if (p_animatable_node_count > 0) {
703
Vector<XrRenderModelAssetNodePropertiesEXT> node_properties;
704
node_properties.resize(p_animatable_node_count);
705
706
XrRenderModelAssetPropertiesGetInfoEXT properties_info = {
707
XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT, // type
708
nullptr, // next
709
};
710
711
XrRenderModelAssetPropertiesEXT asset_properties = {
712
XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT, // type
713
nullptr, // next
714
uint32_t(node_properties.size()), // nodePropertyCount
715
node_properties.ptrw(), // nodeProperties
716
};
717
718
result = xrGetRenderModelAssetPropertiesEXT(p_asset, &properties_info, &asset_properties);
719
if (XR_FAILED(result)) {
720
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model property info [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
721
}
722
723
node_names.resize(p_animatable_node_count);
724
String *node_names_ptrw = node_names.ptrw();
725
for (uint32_t i = 0; i < p_animatable_node_count; i++) {
726
node_names_ptrw[i] = String(node_properties[i].uniqueName);
727
}
728
}
729
730
Ref<OpenXRRenderModelData> render_model_data;
731
render_model_data.instantiate();
732
733
render_model_data->parse_gltf_document(buffer);
734
render_model_data->set_node_names(node_names);
735
736
return render_model_data;
737
}
738
739
void OpenXRRenderModelExtension::_clear_render_model_data() {
740
// Clear our toplevel paths filter.
741
toplevel_paths.clear();
742
743
// Clear our render model cache.
744
render_model_data_cache.clear();
745
746
// Loop through all of our render models and destroy them.
747
LocalVector<RID> owned = render_model_owner.get_owned_list();
748
for (const RID &rid : owned) {
749
render_model_destroy(rid);
750
}
751
}
752
753
bool OpenXRRenderModelData::parse_gltf_document(const PackedByteArray &p_bytes) {
754
// State holds our data, document parses GLTF
755
Ref<GLTFState> new_state;
756
new_state.instantiate();
757
Ref<GLTFDocument> new_gltf_document;
758
new_gltf_document.instantiate();
759
760
Error err = new_gltf_document->append_from_buffer(p_bytes, "", new_state);
761
if (err != OK) {
762
ERR_FAIL_V_MSG(false, "OpenXR: Failed to parse GLTF data.");
763
}
764
765
gltf_document = new_gltf_document;
766
gltf_state = new_state;
767
return true;
768
}
769
770
Node3D *OpenXRRenderModelData::new_scene_instance() {
771
ERR_FAIL_COND_V(gltf_document.is_null(), nullptr);
772
ERR_FAIL_COND_V(gltf_state.is_null(), nullptr);
773
774
return Object::cast_to<Node3D>(gltf_document->generate_scene(gltf_state));
775
}
776
777
void OpenXRRenderModelData::set_node_names(const PackedStringArray &p_node_names) {
778
node_names = p_node_names;
779
}
780
781
PackedStringArray OpenXRRenderModelData::get_node_names() const {
782
return node_names;
783
}
784
785
const String OpenXRRenderModelData::get_node_name(uint32_t p_node_index) const {
786
ERR_FAIL_UNSIGNED_INDEX_V(p_node_index, node_names.size(), String());
787
788
return node_names[p_node_index];
789
}
790
791
OpenXRRenderModelData::OpenXRRenderModelData() {
792
}
793
794
OpenXRRenderModelData::~OpenXRRenderModelData() {
795
}
796
#endif // MODULE_GLTF_ENABLED
797
798