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