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