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