Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/openxr/scene/openxr_render_model_manager.cpp
10278 views
1
/**************************************************************************/
2
/* openxr_render_model_manager.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_manager.h"
32
33
#include "../extensions/openxr_render_model_extension.h"
34
#include "../openxr_api.h"
35
#include "core/config/project_settings.h"
36
#include "scene/3d/xr/xr_nodes.h"
37
#include "servers/xr_server.h"
38
39
void OpenXRRenderModelManager::_bind_methods() {
40
ClassDB::bind_method(D_METHOD("get_tracker"), &OpenXRRenderModelManager::get_tracker);
41
ClassDB::bind_method(D_METHOD("set_tracker", "tracker"), &OpenXRRenderModelManager::set_tracker);
42
ADD_PROPERTY(PropertyInfo(Variant::INT, "tracker", PROPERTY_HINT_ENUM, "Any,None set,Left Hand,Right Hand"), "set_tracker", "get_tracker");
43
44
ClassDB::bind_method(D_METHOD("get_make_local_to_pose"), &OpenXRRenderModelManager::get_make_local_to_pose);
45
ClassDB::bind_method(D_METHOD("set_make_local_to_pose", "make_local_to_pose"), &OpenXRRenderModelManager::set_make_local_to_pose);
46
ADD_PROPERTY(PropertyInfo(Variant::STRING, "make_local_to_pose", PROPERTY_HINT_ENUM_SUGGESTION, "aim,grip"), "set_make_local_to_pose", "get_make_local_to_pose");
47
48
ADD_SIGNAL(MethodInfo("render_model_added", PropertyInfo(Variant::OBJECT, "render_model", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRRenderModel")));
49
ADD_SIGNAL(MethodInfo("render_model_removed", PropertyInfo(Variant::OBJECT, "render_model", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRRenderModel")));
50
51
BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_ANY);
52
BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_NONE_SET);
53
BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_LEFT_HAND);
54
BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_RIGHT_HAND);
55
}
56
57
bool OpenXRRenderModelManager::_has_filters() {
58
return tracker != 0;
59
}
60
61
void OpenXRRenderModelManager::_update_models() {
62
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
63
ERR_FAIL_NULL(render_model_extension);
64
65
// Make a copy of our current models.
66
HashMap<RID, Node3D *> org_render_models = render_models;
67
68
// Loop through our interaction data so we add new entries.
69
TypedArray<RID> render_model_rids = render_model_extension->render_model_get_all();
70
for (const RID rid : render_model_rids) {
71
bool filter = false;
72
73
if (tracker != 0) {
74
XrPath model_path = render_model_extension->render_model_get_top_level_path(rid);
75
if (model_path != xr_path) {
76
// ignore this.
77
filter = true;
78
}
79
}
80
81
if (!filter) {
82
if (render_models.has(rid)) {
83
org_render_models.erase(rid);
84
} else {
85
// Create our container node before adding our first render model.
86
if (container == nullptr) {
87
container = memnew(Node3D);
88
add_child(container);
89
}
90
91
OpenXRRenderModel *render_model = memnew(OpenXRRenderModel);
92
render_model->set_render_model(rid);
93
container->add_child(render_model);
94
render_models[rid] = render_model;
95
96
emit_signal(SNAME("render_model_added"), render_model);
97
}
98
}
99
}
100
101
// Remove models we no longer need.
102
for (const KeyValue<RID, Node3D *> &e : org_render_models) {
103
// We sent this just before removing.
104
emit_signal(SNAME("render_model_removed"), e.value);
105
106
if (container) {
107
container->remove_child(e.value);
108
}
109
e.value->queue_free();
110
render_models.erase(e.key);
111
}
112
113
is_dirty = false;
114
}
115
116
void OpenXRRenderModelManager::_on_render_model_added(RID p_render_model) {
117
if (_has_filters()) {
118
// We'll update this in internal process.
119
is_dirty = true;
120
} else {
121
// No filters? Do this right away.
122
_update_models();
123
}
124
}
125
126
void OpenXRRenderModelManager::_on_render_model_removed(RID p_render_model) {
127
if (_has_filters()) {
128
// We'll update this in internal process.
129
is_dirty = true;
130
} else {
131
// No filters? Do this right away.
132
_update_models();
133
}
134
}
135
136
void OpenXRRenderModelManager::_on_render_model_top_level_path_changed(RID p_path) {
137
if (_has_filters()) {
138
// We'll update this in internal process.
139
is_dirty = true;
140
}
141
}
142
143
void OpenXRRenderModelManager::_notification(int p_what) {
144
// Do not run in editor!
145
if (Engine::get_singleton()->is_editor_hint()) {
146
return;
147
}
148
149
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
150
ERR_FAIL_NULL(render_model_extension);
151
if (!render_model_extension->is_active()) {
152
return;
153
}
154
155
switch (p_what) {
156
case NOTIFICATION_ENTER_TREE: {
157
_update_models();
158
159
render_model_extension->connect(SNAME("render_model_added"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_added));
160
render_model_extension->connect(SNAME("render_model_removed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_removed));
161
render_model_extension->connect(SNAME("render_model_top_level_path_changed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_top_level_path_changed));
162
163
if (_has_filters()) {
164
set_process_internal(true);
165
}
166
} break;
167
case NOTIFICATION_EXIT_TREE: {
168
render_model_extension->disconnect(SNAME("render_model_added"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_added));
169
render_model_extension->disconnect(SNAME("render_model_removed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_removed));
170
render_model_extension->disconnect(SNAME("render_model_top_level_path_changed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_top_level_path_changed));
171
172
set_process_internal(false);
173
is_dirty = false;
174
} break;
175
case NOTIFICATION_INTERNAL_PROCESS: {
176
if (is_dirty) {
177
_update_models();
178
}
179
180
if (positional_tracker.is_valid() && !make_local_to_pose.is_empty() && container) {
181
Ref<XRPose> pose = positional_tracker->get_pose(make_local_to_pose);
182
if (pose.is_valid()) {
183
container->set_transform(pose->get_adjusted_transform().affine_inverse());
184
} else {
185
container->set_transform(Transform3D());
186
}
187
}
188
189
if (!_has_filters()) {
190
// No need to keep calling this.
191
set_process_internal(false);
192
}
193
}
194
}
195
}
196
197
PackedStringArray OpenXRRenderModelManager::get_configuration_warnings() const {
198
PackedStringArray warnings;
199
200
XROrigin3D *parent = nullptr;
201
if (tracker == 0 || tracker == 1) {
202
if (!make_local_to_pose.is_empty()) {
203
warnings.push_back("Must specify a tracker to make node local to pose.");
204
}
205
206
parent = Object::cast_to<XROrigin3D>(get_parent());
207
} else {
208
Node *node = get_parent();
209
while (!parent && node) {
210
parent = Object::cast_to<XROrigin3D>(node);
211
212
node = node->get_parent();
213
}
214
}
215
if (!parent) {
216
warnings.push_back("This node must be a child of an XROrigin3D node!");
217
}
218
219
if (!GLOBAL_GET("xr/openxr/extensions/render_model")) {
220
warnings.push_back("The render model extension is not enabled in project settings!");
221
}
222
223
return warnings;
224
}
225
226
void OpenXRRenderModelManager::set_tracker(RenderModelTracker p_tracker) {
227
if (tracker != p_tracker) {
228
tracker = p_tracker;
229
is_dirty = true;
230
231
if (tracker == RENDER_MODEL_TRACKER_ANY || tracker == RENDER_MODEL_TRACKER_NONE_SET) {
232
xr_path = XR_NULL_PATH;
233
} else if (!Engine::get_singleton()->is_editor_hint()) {
234
XRServer *xr_server = XRServer::get_singleton();
235
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
236
if (openxr_api && xr_server) {
237
String toplevel_path;
238
String tracker_name;
239
if (tracker == RENDER_MODEL_TRACKER_LEFT_HAND) {
240
tracker_name = "left_hand";
241
toplevel_path = "/user/hand/left";
242
} else if (tracker == RENDER_MODEL_TRACKER_RIGHT_HAND) {
243
tracker_name = "right_hand";
244
toplevel_path = "/user/hand/right";
245
} else {
246
ERR_FAIL_MSG("Unsupported tracker value set.");
247
}
248
249
positional_tracker = xr_server->get_tracker(tracker_name);
250
if (positional_tracker.is_null()) {
251
WARN_PRINT("OpenXR: Can't find tracker " + tracker_name);
252
}
253
254
xr_path = openxr_api->get_xr_path(toplevel_path);
255
if (xr_path == XR_NULL_PATH) {
256
WARN_PRINT("OpenXR: Can't find path for " + toplevel_path);
257
}
258
}
259
}
260
261
// Even if we now no longer have filters, we must update at least once.
262
set_process_internal(true);
263
}
264
}
265
266
OpenXRRenderModelManager::RenderModelTracker OpenXRRenderModelManager::get_tracker() const {
267
return tracker;
268
}
269
270
void OpenXRRenderModelManager::set_make_local_to_pose(const String &p_action) {
271
if (make_local_to_pose != p_action) {
272
make_local_to_pose = p_action;
273
274
if (container) {
275
// Reset just in case. It'll be set to the correct transform
276
// in our process if required.
277
container->set_transform(Transform3D());
278
}
279
}
280
}
281
282
String OpenXRRenderModelManager::get_make_local_to_pose() const {
283
return make_local_to_pose;
284
}
285
286