Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/servers/xr_server.cpp
9887 views
1
/**************************************************************************/
2
/* xr_server.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 "xr_server.h"
32
#include "core/config/project_settings.h"
33
#include "xr/xr_interface.h"
34
#include "xr/xr_positional_tracker.h"
35
#include "xr_server.compat.inc"
36
37
XRServer::XRMode XRServer::xr_mode = XRMODE_DEFAULT;
38
39
XRServer::XRMode XRServer::get_xr_mode() {
40
return xr_mode;
41
}
42
43
void XRServer::set_xr_mode(XRServer::XRMode p_mode) {
44
xr_mode = p_mode;
45
}
46
47
XRServer *XRServer::singleton = nullptr;
48
49
XRServer *XRServer::get_singleton() {
50
return singleton;
51
}
52
53
void XRServer::_bind_methods() {
54
ClassDB::bind_method(D_METHOD("get_world_scale"), &XRServer::get_world_scale);
55
ClassDB::bind_method(D_METHOD("set_world_scale", "scale"), &XRServer::set_world_scale);
56
ClassDB::bind_method(D_METHOD("get_world_origin"), &XRServer::get_world_origin);
57
ClassDB::bind_method(D_METHOD("set_world_origin", "world_origin"), &XRServer::set_world_origin);
58
ClassDB::bind_method(D_METHOD("get_reference_frame"), &XRServer::get_reference_frame);
59
ClassDB::bind_method(D_METHOD("clear_reference_frame"), &XRServer::clear_reference_frame);
60
ClassDB::bind_method(D_METHOD("center_on_hmd", "rotation_mode", "keep_height"), &XRServer::center_on_hmd);
61
ClassDB::bind_method(D_METHOD("get_hmd_transform"), &XRServer::get_hmd_transform);
62
ClassDB::bind_method(D_METHOD("set_camera_locked_to_origin", "enabled"), &XRServer::set_camera_locked_to_origin);
63
ClassDB::bind_method(D_METHOD("is_camera_locked_to_origin"), &XRServer::is_camera_locked_to_origin);
64
65
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale");
66
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "world_origin"), "set_world_origin", "get_world_origin");
67
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_locked_to_origin"), "set_camera_locked_to_origin", "is_camera_locked_to_origin");
68
69
ClassDB::bind_method(D_METHOD("add_interface", "interface"), &XRServer::add_interface);
70
ClassDB::bind_method(D_METHOD("get_interface_count"), &XRServer::get_interface_count);
71
ClassDB::bind_method(D_METHOD("remove_interface", "interface"), &XRServer::remove_interface);
72
ClassDB::bind_method(D_METHOD("get_interface", "idx"), &XRServer::get_interface);
73
ClassDB::bind_method(D_METHOD("get_interfaces"), &XRServer::get_interfaces);
74
ClassDB::bind_method(D_METHOD("find_interface", "name"), &XRServer::find_interface);
75
76
ClassDB::bind_method(D_METHOD("add_tracker", "tracker"), &XRServer::add_tracker);
77
ClassDB::bind_method(D_METHOD("remove_tracker", "tracker"), &XRServer::remove_tracker);
78
ClassDB::bind_method(D_METHOD("get_trackers", "tracker_types"), &XRServer::get_trackers);
79
ClassDB::bind_method(D_METHOD("get_tracker", "tracker_name"), &XRServer::get_tracker);
80
81
ClassDB::bind_method(D_METHOD("get_primary_interface"), &XRServer::get_primary_interface);
82
ClassDB::bind_method(D_METHOD("set_primary_interface", "interface"), &XRServer::set_primary_interface);
83
84
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "primary_interface"), "set_primary_interface", "get_primary_interface");
85
86
BIND_ENUM_CONSTANT(TRACKER_HEAD);
87
BIND_ENUM_CONSTANT(TRACKER_CONTROLLER);
88
BIND_ENUM_CONSTANT(TRACKER_BASESTATION);
89
BIND_ENUM_CONSTANT(TRACKER_ANCHOR);
90
BIND_ENUM_CONSTANT(TRACKER_HAND);
91
BIND_ENUM_CONSTANT(TRACKER_BODY);
92
BIND_ENUM_CONSTANT(TRACKER_FACE);
93
BIND_ENUM_CONSTANT(TRACKER_ANY_KNOWN);
94
BIND_ENUM_CONSTANT(TRACKER_UNKNOWN);
95
BIND_ENUM_CONSTANT(TRACKER_ANY);
96
97
BIND_ENUM_CONSTANT(RESET_FULL_ROTATION);
98
BIND_ENUM_CONSTANT(RESET_BUT_KEEP_TILT);
99
BIND_ENUM_CONSTANT(DONT_RESET_ROTATION);
100
101
ADD_SIGNAL(MethodInfo("reference_frame_changed"));
102
103
ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING_NAME, "interface_name")));
104
ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING_NAME, "interface_name")));
105
106
ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
107
ADD_SIGNAL(MethodInfo("tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
108
ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
109
}
110
111
double XRServer::get_world_scale() const {
112
RenderingServer *rendering_server = RenderingServer::get_singleton();
113
114
if (rendering_server && rendering_server->is_on_render_thread()) {
115
// Return the value with which we're currently rendering,
116
// if we're on the render thread
117
return render_state.world_scale;
118
} else {
119
// Return our current value
120
return world_scale;
121
}
122
}
123
124
void XRServer::set_world_scale(double p_world_scale) {
125
if (p_world_scale < 0.01) {
126
p_world_scale = 0.01;
127
} else if (p_world_scale > 1000.0) {
128
p_world_scale = 1000.0;
129
}
130
131
world_scale = p_world_scale;
132
set_render_world_scale(world_scale);
133
}
134
135
void XRServer::_set_render_world_scale(double p_world_scale) {
136
// Must be called from rendering thread!
137
ERR_NOT_ON_RENDER_THREAD;
138
139
XRServer *xr_server = XRServer::get_singleton();
140
ERR_FAIL_NULL(xr_server);
141
xr_server->render_state.world_scale = p_world_scale;
142
}
143
144
Transform3D XRServer::get_world_origin() const {
145
RenderingServer *rendering_server = RenderingServer::get_singleton();
146
147
if (rendering_server && rendering_server->is_on_render_thread()) {
148
// Return the value with which we're currently rendering,
149
// if we're on the render thread
150
return render_state.world_origin;
151
} else {
152
// Return our current value
153
return world_origin;
154
}
155
}
156
157
void XRServer::set_world_origin(const Transform3D &p_world_origin) {
158
world_origin = p_world_origin;
159
set_render_world_origin(world_origin);
160
}
161
162
void XRServer::_set_render_world_origin(const Transform3D &p_world_origin) {
163
// Must be called from rendering thread!
164
ERR_NOT_ON_RENDER_THREAD;
165
166
XRServer *xr_server = XRServer::get_singleton();
167
ERR_FAIL_NULL(xr_server);
168
xr_server->render_state.world_origin = p_world_origin;
169
}
170
171
Transform3D XRServer::get_reference_frame() const {
172
RenderingServer *rendering_server = RenderingServer::get_singleton();
173
ERR_FAIL_NULL_V(rendering_server, reference_frame);
174
175
if (rendering_server->is_on_render_thread()) {
176
// Return the value with which we're currently rendering,
177
// if we're on the render thread
178
return render_state.reference_frame;
179
} else {
180
// Return our current value
181
return reference_frame;
182
}
183
}
184
185
void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
186
if (primary_interface.is_null()) {
187
return;
188
}
189
190
// clear our current reference frame or we'll end up double adjusting it
191
reference_frame = Transform3D();
192
193
// requesting our EYE_MONO transform should return our current HMD position
194
Transform3D new_reference_frame = primary_interface->get_camera_transform();
195
196
// remove our tilt
197
if (p_rotation_mode == 1) {
198
// take the Y out of our Z
199
new_reference_frame.basis.set_column(2, Vector3(new_reference_frame.basis.rows[0][2], 0.0, new_reference_frame.basis.rows[2][2]).normalized());
200
201
// Y is straight up
202
new_reference_frame.basis.set_column(1, Vector3(0.0, 1.0, 0.0));
203
204
// and X is our cross reference
205
new_reference_frame.basis.set_column(0, new_reference_frame.basis.get_column(1).cross(new_reference_frame.basis.get_column(2)).normalized());
206
} else if (p_rotation_mode == 2) {
207
// remove our rotation, we're only interesting in centering on position
208
new_reference_frame.basis = Basis();
209
}
210
211
// don't negate our height
212
if (p_keep_height) {
213
new_reference_frame.origin.y = 0.0;
214
}
215
216
reference_frame = new_reference_frame.inverse();
217
set_render_reference_frame(reference_frame);
218
emit_signal(SNAME("reference_frame_changed"));
219
}
220
221
void XRServer::clear_reference_frame() {
222
reference_frame = Transform3D();
223
set_render_reference_frame(reference_frame);
224
emit_signal(SNAME("reference_frame_changed"));
225
}
226
227
void XRServer::_set_render_reference_frame(const Transform3D &p_reference_frame) {
228
// Must be called from rendering thread!
229
ERR_NOT_ON_RENDER_THREAD;
230
231
XRServer *xr_server = XRServer::get_singleton();
232
ERR_FAIL_NULL(xr_server);
233
xr_server->render_state.reference_frame = p_reference_frame;
234
}
235
236
Transform3D XRServer::get_hmd_transform() {
237
Transform3D hmd_transform;
238
if (primary_interface.is_valid()) {
239
hmd_transform = primary_interface->get_camera_transform();
240
}
241
return hmd_transform;
242
}
243
244
void XRServer::set_camera_locked_to_origin(bool p_enable) {
245
camera_locked_to_origin = p_enable;
246
}
247
248
void XRServer::add_interface(const Ref<XRInterface> &p_interface) {
249
ERR_FAIL_COND(p_interface.is_null());
250
251
for (int i = 0; i < interfaces.size(); i++) {
252
if (interfaces[i] == p_interface) {
253
ERR_PRINT("Interface was already added");
254
return;
255
}
256
}
257
258
interfaces.push_back(p_interface);
259
emit_signal(SNAME("interface_added"), p_interface->get_name());
260
}
261
262
void XRServer::remove_interface(const Ref<XRInterface> &p_interface) {
263
ERR_FAIL_COND(p_interface.is_null());
264
265
int idx = -1;
266
for (int i = 0; i < interfaces.size(); i++) {
267
if (interfaces[i] == p_interface) {
268
idx = i;
269
break;
270
}
271
}
272
273
ERR_FAIL_COND_MSG(idx == -1, "Interface not found.");
274
print_verbose("XR: Removed interface \"" + p_interface->get_name() + "\"");
275
emit_signal(SNAME("interface_removed"), p_interface->get_name());
276
interfaces.remove_at(idx);
277
}
278
279
int XRServer::get_interface_count() const {
280
return interfaces.size();
281
}
282
283
Ref<XRInterface> XRServer::get_interface(int p_index) const {
284
ERR_FAIL_INDEX_V(p_index, interfaces.size(), nullptr);
285
286
return interfaces[p_index];
287
}
288
289
Ref<XRInterface> XRServer::find_interface(const String &p_name) const {
290
for (int i = 0; i < interfaces.size(); i++) {
291
if (interfaces[i]->get_name() == p_name) {
292
return interfaces[i];
293
}
294
}
295
return Ref<XRInterface>();
296
}
297
298
TypedArray<Dictionary> XRServer::get_interfaces() const {
299
Array ret;
300
301
for (int i = 0; i < interfaces.size(); i++) {
302
Dictionary iface_info;
303
304
iface_info["id"] = i;
305
iface_info["name"] = interfaces[i]->get_name();
306
307
ret.push_back(iface_info);
308
}
309
310
return ret;
311
}
312
313
Ref<XRInterface> XRServer::get_primary_interface() const {
314
return primary_interface;
315
}
316
317
void XRServer::set_primary_interface(const Ref<XRInterface> &p_primary_interface) {
318
if (p_primary_interface.is_null()) {
319
print_verbose("XR: Clearing primary interface");
320
primary_interface.unref();
321
} else {
322
primary_interface = p_primary_interface;
323
324
print_verbose("XR: Primary interface set to: " + primary_interface->get_name());
325
}
326
}
327
328
void XRServer::add_tracker(const Ref<XRTracker> &p_tracker) {
329
ERR_FAIL_COND(p_tracker.is_null());
330
331
StringName tracker_name = p_tracker->get_tracker_name();
332
if (trackers.has(tracker_name)) {
333
if (trackers[tracker_name] != p_tracker) {
334
// We already have a tracker with this name, we're going to replace it
335
trackers[tracker_name] = p_tracker;
336
emit_signal(SNAME("tracker_updated"), tracker_name, p_tracker->get_tracker_type());
337
}
338
} else {
339
trackers[tracker_name] = p_tracker;
340
emit_signal(SNAME("tracker_added"), tracker_name, p_tracker->get_tracker_type());
341
}
342
}
343
344
void XRServer::remove_tracker(const Ref<XRTracker> &p_tracker) {
345
ERR_FAIL_COND(p_tracker.is_null());
346
347
StringName tracker_name = p_tracker->get_tracker_name();
348
if (trackers.has(tracker_name)) {
349
// we send the signal right before removing it
350
emit_signal(SNAME("tracker_removed"), p_tracker->get_tracker_name(), p_tracker->get_tracker_type());
351
352
// and remove it
353
trackers.erase(tracker_name);
354
}
355
}
356
357
Dictionary XRServer::get_trackers(int p_tracker_types) {
358
Dictionary res;
359
360
for (int i = 0; i < trackers.size(); i++) {
361
Ref<XRPositionalTracker> tracker = trackers.get_value_at_index(i);
362
if (tracker.is_valid() && (tracker->get_tracker_type() & p_tracker_types) != 0) {
363
res[tracker->get_tracker_name()] = tracker;
364
}
365
}
366
367
return res;
368
}
369
370
Ref<XRTracker> XRServer::get_tracker(const StringName &p_name) const {
371
if (trackers.has(p_name)) {
372
return trackers[p_name];
373
} else {
374
// tracker hasn't been registered yet, which is fine, no need to spam the error log...
375
return Ref<XRTracker>();
376
}
377
}
378
379
PackedStringArray XRServer::get_suggested_tracker_names() const {
380
PackedStringArray arr;
381
382
for (int i = 0; i < interfaces.size(); i++) {
383
Ref<XRInterface> interface = interfaces[i];
384
PackedStringArray interface_arr = interface->get_suggested_tracker_names();
385
for (int a = 0; a < interface_arr.size(); a++) {
386
if (!arr.has(interface_arr[a])) {
387
arr.push_back(interface_arr[a]);
388
}
389
}
390
}
391
392
if (arr.is_empty()) {
393
// no suggestions from our tracker? include our defaults
394
arr.push_back(String("head"));
395
arr.push_back(String("left_hand"));
396
arr.push_back(String("right_hand"));
397
}
398
399
return arr;
400
}
401
402
PackedStringArray XRServer::get_suggested_pose_names(const StringName &p_tracker_name) const {
403
PackedStringArray arr;
404
405
for (int i = 0; i < interfaces.size(); i++) {
406
Ref<XRInterface> interface = interfaces[i];
407
PackedStringArray interface_arr = interface->get_suggested_pose_names(p_tracker_name);
408
for (int a = 0; a < interface_arr.size(); a++) {
409
if (!arr.has(interface_arr[a])) {
410
arr.push_back(interface_arr[a]);
411
}
412
}
413
}
414
415
if (arr.is_empty()) {
416
// no suggestions from our tracker? include our defaults
417
arr.push_back(String("default"));
418
419
if ((p_tracker_name == "left_hand") || (p_tracker_name == "right_hand")) {
420
arr.push_back(String("aim"));
421
arr.push_back(String("grip"));
422
arr.push_back(String("skeleton"));
423
}
424
}
425
426
return arr;
427
}
428
429
void XRServer::_process() {
430
// called from our main game loop before we handle physics and game logic
431
// note that we can have multiple interfaces active if we have interfaces that purely handle tracking
432
433
// process all active interfaces
434
for (int i = 0; i < interfaces.size(); i++) {
435
if (interfaces[i].is_null()) {
436
// ignore, not a valid reference
437
} else if (interfaces[i]->is_initialized()) {
438
interfaces.write[i]->process();
439
}
440
}
441
}
442
443
void XRServer::pre_render() {
444
// called from RendererViewport.draw_viewports right before we start drawing our viewports
445
// note that we can have multiple interfaces active if we have interfaces that purely handle tracking
446
447
// process all active interfaces
448
for (int i = 0; i < interfaces.size(); i++) {
449
if (interfaces[i].is_null()) {
450
// ignore, not a valid reference
451
} else if (interfaces[i]->is_initialized()) {
452
interfaces.write[i]->pre_render();
453
}
454
}
455
}
456
457
void XRServer::end_frame() {
458
// called from RenderingServerDefault after Vulkan queues have been submitted
459
460
// process all active interfaces
461
for (int i = 0; i < interfaces.size(); i++) {
462
if (interfaces[i].is_null()) {
463
// ignore, not a valid reference
464
} else if (interfaces[i]->is_initialized()) {
465
interfaces.write[i]->end_frame();
466
}
467
}
468
}
469
470
XRServer::XRServer() {
471
singleton = this;
472
}
473
474
XRServer::~XRServer() {
475
primary_interface.unref();
476
477
interfaces.clear();
478
trackers.clear();
479
480
singleton = nullptr;
481
}
482
483